mirror of
https://github.com/reactos/CMake.git
synced 2025-01-26 13:57:07 +00:00
cbe7e8fae4
Teach the export command to handle export sets defined by invocations of install(TARGETS ... EXPORT foo). This makes maintenance of targets exported to both the build tree and install tree trivial.
402 lines
11 KiB
C++
402 lines
11 KiB
C++
/*============================================================================
|
|
CMake - Cross Platform Makefile Generator
|
|
Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
|
|
|
|
Distributed under the OSI-approved BSD License (the "License");
|
|
see accompanying file Copyright.txt for details.
|
|
|
|
This software is distributed WITHOUT ANY WARRANTY; without even the
|
|
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
See the License for more information.
|
|
============================================================================*/
|
|
#include "cmExportCommand.h"
|
|
#include "cmGlobalGenerator.h"
|
|
#include "cmLocalGenerator.h"
|
|
#include "cmGeneratedFileStream.h"
|
|
#include "cmake.h"
|
|
|
|
#include <cmsys/RegularExpression.hxx>
|
|
#include <cmsys/Encoding.hxx>
|
|
|
|
#include "cmExportBuildFileGenerator.h"
|
|
|
|
#if defined(__HAIKU__)
|
|
#include <FindDirectory.h>
|
|
#include <StorageDefs.h>
|
|
#endif
|
|
|
|
cmExportCommand::cmExportCommand()
|
|
:cmCommand()
|
|
,ArgumentGroup()
|
|
,Targets(&Helper, "TARGETS")
|
|
,Append(&Helper, "APPEND", &ArgumentGroup)
|
|
,ExportSetName(&Helper, "EXPORT", &ArgumentGroup)
|
|
,Namespace(&Helper, "NAMESPACE", &ArgumentGroup)
|
|
,Filename(&Helper, "FILE", &ArgumentGroup)
|
|
,ExportOld(&Helper, "EXPORT_LINK_INTERFACE_LIBRARIES", &ArgumentGroup)
|
|
{
|
|
this->ExportSet = 0;
|
|
}
|
|
|
|
|
|
// cmExportCommand
|
|
bool cmExportCommand
|
|
::InitialPass(std::vector<std::string> const& args, cmExecutionStatus &)
|
|
{
|
|
if(args.size() < 2 )
|
|
{
|
|
this->SetError("called with too few arguments");
|
|
return false;
|
|
}
|
|
|
|
if(args[0] == "PACKAGE")
|
|
{
|
|
return this->HandlePackage(args);
|
|
}
|
|
else if (args[0] == "EXPORT")
|
|
{
|
|
this->ExportSetName.Follows(0);
|
|
this->ArgumentGroup.Follows(&this->ExportSetName);
|
|
}
|
|
else
|
|
{
|
|
this->Targets.Follows(0);
|
|
this->ArgumentGroup.Follows(&this->Targets);
|
|
}
|
|
|
|
std::vector<std::string> unknownArgs;
|
|
this->Helper.Parse(&args, &unknownArgs);
|
|
|
|
if (!unknownArgs.empty())
|
|
{
|
|
this->SetError("Unknown arguments.");
|
|
return false;
|
|
}
|
|
|
|
std::string fname;
|
|
if(!this->Filename.WasFound())
|
|
{
|
|
if (args[0] != "EXPORT")
|
|
{
|
|
this->SetError("FILE <filename> option missing.");
|
|
return false;
|
|
}
|
|
fname = this->ExportSetName.GetString() + ".cmake";
|
|
}
|
|
else
|
|
{
|
|
// Make sure the file has a .cmake extension.
|
|
if(cmSystemTools::GetFilenameLastExtension(this->Filename.GetCString())
|
|
!= ".cmake")
|
|
{
|
|
cmOStringStream e;
|
|
e << "FILE option given filename \"" << this->Filename.GetString()
|
|
<< "\" which does not have an extension of \".cmake\".\n";
|
|
this->SetError(e.str().c_str());
|
|
return false;
|
|
}
|
|
fname = this->Filename.GetString();
|
|
}
|
|
|
|
// Get the file to write.
|
|
if(cmSystemTools::FileIsFullPath(fname.c_str()))
|
|
{
|
|
if(!this->Makefile->CanIWriteThisFile(fname.c_str()))
|
|
{
|
|
cmOStringStream e;
|
|
e << "FILE option given filename \"" << fname
|
|
<< "\" which is in the source tree.\n";
|
|
this->SetError(e.str().c_str());
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Interpret relative paths with respect to the current build dir.
|
|
std::string dir = this->Makefile->GetCurrentOutputDirectory();
|
|
fname = dir + "/" + fname;
|
|
}
|
|
|
|
std::vector<std::string> targets;
|
|
|
|
cmGlobalGenerator *gg = this->Makefile->GetLocalGenerator()
|
|
->GetGlobalGenerator();
|
|
|
|
if(args[0] == "EXPORT")
|
|
{
|
|
if (this->Append.IsEnabled())
|
|
{
|
|
cmOStringStream e;
|
|
e << "EXPORT signature does not recognise the APPEND option.";
|
|
this->SetError(e.str().c_str());
|
|
return false;
|
|
}
|
|
|
|
if (this->ExportOld.IsEnabled())
|
|
{
|
|
cmOStringStream e;
|
|
e << "EXPORT signature does not recognise the "
|
|
"EXPORT_LINK_INTERFACE_LIBRARIES option.";
|
|
this->SetError(e.str().c_str());
|
|
return false;
|
|
}
|
|
|
|
cmExportSetMap &setMap = gg->GetExportSets();
|
|
std::string setName = this->ExportSetName.GetString();
|
|
if (setMap.find(setName) == setMap.end())
|
|
{
|
|
cmOStringStream e;
|
|
e << "Export set \"" << setName << "\" not found.";
|
|
this->SetError(e.str().c_str());
|
|
return false;
|
|
}
|
|
this->ExportSet = setMap[setName];
|
|
}
|
|
else if (this->Targets.WasFound())
|
|
{
|
|
for(std::vector<std::string>::const_iterator
|
|
currentTarget = this->Targets.GetVector().begin();
|
|
currentTarget != this->Targets.GetVector().end();
|
|
++currentTarget)
|
|
{
|
|
if (this->Makefile->IsAlias(currentTarget->c_str()))
|
|
{
|
|
cmOStringStream e;
|
|
e << "given ALIAS target \"" << *currentTarget
|
|
<< "\" which may not be exported.";
|
|
this->SetError(e.str().c_str());
|
|
return false;
|
|
}
|
|
|
|
if(cmTarget* target = gg->FindTarget(0, currentTarget->c_str()))
|
|
{
|
|
if(target->GetType() == cmTarget::OBJECT_LIBRARY)
|
|
{
|
|
cmOStringStream e;
|
|
e << "given OBJECT library \"" << *currentTarget
|
|
<< "\" which may not be exported.";
|
|
this->SetError(e.str().c_str());
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
cmOStringStream e;
|
|
e << "given target \"" << *currentTarget
|
|
<< "\" which is not built by this project.";
|
|
this->SetError(e.str().c_str());
|
|
return false;
|
|
}
|
|
targets.push_back(*currentTarget);
|
|
}
|
|
if (this->Append.IsEnabled())
|
|
{
|
|
if (cmExportBuildFileGenerator *ebfg = gg->GetExportedTargetsFile(fname))
|
|
{
|
|
ebfg->AppendTargets(targets);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
this->SetError("EXPORT or TARGETS specifier missing.");
|
|
return false;
|
|
}
|
|
|
|
// Setup export file generation.
|
|
cmExportBuildFileGenerator *ebfg = new cmExportBuildFileGenerator;
|
|
ebfg->SetExportFile(fname.c_str());
|
|
ebfg->SetNamespace(this->Namespace.GetCString());
|
|
ebfg->SetAppendMode(this->Append.IsEnabled());
|
|
if (this->ExportSet)
|
|
{
|
|
ebfg->SetExportSet(this->ExportSet);
|
|
}
|
|
else
|
|
{
|
|
ebfg->SetTargets(targets);
|
|
}
|
|
ebfg->SetMakefile(this->Makefile);
|
|
ebfg->SetExportOld(this->ExportOld.IsEnabled());
|
|
|
|
// Compute the set of configurations exported.
|
|
std::vector<std::string> configurationTypes;
|
|
this->Makefile->GetConfigurations(configurationTypes);
|
|
if(!configurationTypes.empty())
|
|
{
|
|
for(std::vector<std::string>::const_iterator
|
|
ci = configurationTypes.begin();
|
|
ci != configurationTypes.end(); ++ci)
|
|
{
|
|
ebfg->AddConfiguration(ci->c_str());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ebfg->AddConfiguration("");
|
|
}
|
|
|
|
gg->AddBuildExportSet(ebfg);
|
|
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool cmExportCommand::HandlePackage(std::vector<std::string> const& args)
|
|
{
|
|
// Parse PACKAGE mode arguments.
|
|
enum Doing { DoingNone, DoingPackage };
|
|
Doing doing = DoingPackage;
|
|
std::string package;
|
|
for(unsigned int i=1; i < args.size(); ++i)
|
|
{
|
|
if(doing == DoingPackage)
|
|
{
|
|
package = args[i];
|
|
doing = DoingNone;
|
|
}
|
|
else
|
|
{
|
|
cmOStringStream e;
|
|
e << "PACKAGE given unknown argument: " << args[i];
|
|
this->SetError(e.str().c_str());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Verify the package name.
|
|
if(package.empty())
|
|
{
|
|
this->SetError("PACKAGE must be given a package name.");
|
|
return false;
|
|
}
|
|
const char* packageExpr = "^[A-Za-z0-9_.-]+$";
|
|
cmsys::RegularExpression packageRegex(packageExpr);
|
|
if(!packageRegex.find(package.c_str()))
|
|
{
|
|
cmOStringStream e;
|
|
e << "PACKAGE given invalid package name \"" << package << "\". "
|
|
<< "Package names must match \"" << packageExpr << "\".";
|
|
this->SetError(e.str().c_str());
|
|
return false;
|
|
}
|
|
|
|
// We store the current build directory in the registry as a value
|
|
// named by a hash of its own content. This is deterministic and is
|
|
// unique with high probability.
|
|
const char* outDir = this->Makefile->GetCurrentOutputDirectory();
|
|
std::string hash = cmSystemTools::ComputeStringMD5(outDir);
|
|
#if defined(_WIN32) && !defined(__CYGWIN__)
|
|
this->StorePackageRegistryWin(package, outDir, hash.c_str());
|
|
#else
|
|
this->StorePackageRegistryDir(package, outDir, hash.c_str());
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
#if defined(_WIN32) && !defined(__CYGWIN__)
|
|
# include <windows.h>
|
|
# undef GetCurrentDirectory
|
|
//----------------------------------------------------------------------------
|
|
void cmExportCommand::ReportRegistryError(std::string const& msg,
|
|
std::string const& key,
|
|
long err)
|
|
{
|
|
cmOStringStream e;
|
|
e << msg << "\n"
|
|
<< " HKEY_CURRENT_USER\\" << key << "\n";
|
|
wchar_t winmsg[1024];
|
|
if(FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM |
|
|
FORMAT_MESSAGE_IGNORE_INSERTS, 0, err,
|
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
|
winmsg, 1024, 0) > 0)
|
|
{
|
|
e << "Windows reported:\n"
|
|
<< " " << cmsys::Encoding::ToNarrow(winmsg);
|
|
}
|
|
this->Makefile->IssueMessage(cmake::WARNING, e.str());
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void cmExportCommand::StorePackageRegistryWin(std::string const& package,
|
|
const char* content,
|
|
const char* hash)
|
|
{
|
|
std::string key = "Software\\Kitware\\CMake\\Packages\\";
|
|
key += package;
|
|
HKEY hKey;
|
|
LONG err = RegCreateKeyExW(HKEY_CURRENT_USER,
|
|
cmsys::Encoding::ToWide(key).c_str(),
|
|
0, 0, REG_OPTION_NON_VOLATILE,
|
|
KEY_SET_VALUE, 0, &hKey, 0);
|
|
if(err != ERROR_SUCCESS)
|
|
{
|
|
this->ReportRegistryError(
|
|
"Cannot create/open registry key", key, err);
|
|
return;
|
|
}
|
|
|
|
std::wstring wcontent = cmsys::Encoding::ToWide(content);
|
|
err = RegSetValueExW(hKey, cmsys::Encoding::ToWide(hash).c_str(),
|
|
0, REG_SZ, (BYTE const*)wcontent.c_str(),
|
|
static_cast<DWORD>(wcontent.size()+1)*sizeof(wchar_t));
|
|
RegCloseKey(hKey);
|
|
if(err != ERROR_SUCCESS)
|
|
{
|
|
cmOStringStream msg;
|
|
msg << "Cannot set registry value \"" << hash << "\" under key";
|
|
this->ReportRegistryError(msg.str(), key, err);
|
|
return;
|
|
}
|
|
}
|
|
#else
|
|
//----------------------------------------------------------------------------
|
|
void cmExportCommand::StorePackageRegistryDir(std::string const& package,
|
|
const char* content,
|
|
const char* hash)
|
|
{
|
|
#if defined(__HAIKU__)
|
|
char dir[B_PATH_NAME_LENGTH];
|
|
if (find_directory(B_USER_SETTINGS_DIRECTORY, -1, false, dir, sizeof(dir)) !=
|
|
B_OK)
|
|
{
|
|
return;
|
|
}
|
|
std::string fname = dir;
|
|
fname += "/cmake/packages/";
|
|
fname += package;
|
|
#else
|
|
const char* home = cmSystemTools::GetEnv("HOME");
|
|
if(!home)
|
|
{
|
|
return;
|
|
}
|
|
std::string fname = home;
|
|
cmSystemTools::ConvertToUnixSlashes(fname);
|
|
fname += "/.cmake/packages/";
|
|
fname += package;
|
|
#endif
|
|
cmSystemTools::MakeDirectory(fname.c_str());
|
|
fname += "/";
|
|
fname += hash;
|
|
if(!cmSystemTools::FileExists(fname.c_str()))
|
|
{
|
|
cmGeneratedFileStream entry(fname.c_str(), true);
|
|
if(entry)
|
|
{
|
|
entry << content << "\n";
|
|
}
|
|
else
|
|
{
|
|
cmOStringStream e;
|
|
e << "Cannot create package registry file:\n"
|
|
<< " " << fname << "\n"
|
|
<< cmSystemTools::GetLastSystemError() << "\n";
|
|
this->Makefile->IssueMessage(cmake::WARNING, e.str());
|
|
}
|
|
}
|
|
}
|
|
#endif
|