CMake/Source/cmExportCommand.cxx
Stephen Kelly 66b290e7e2 export(): Process the export() command at generate time.
Make the API for adding targets string based so that it can easily
use cmGeneratorTarget.

Teach the cmIncludeCommand to generate the exported file at
configure-time instead if it is to be include()d.

The RunCMake.ExportWithoutLanguage test now needs a dummy header.h
file as expected error from export() is now reported after the
missing file error.
2013-10-11 12:46:10 +02:00

335 lines
9.6 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 "cmExportBuildFileGenerator.h"
#if defined(__HAIKU__)
#include <FindDirectory.h>
#include <StorageDefs.h>
#endif
cmExportCommand::cmExportCommand()
:cmCommand()
,ArgumentGroup()
,Targets(&Helper, "TARGETS")
,Append(&Helper, "APPEND", &ArgumentGroup)
,Namespace(&Helper, "NAMESPACE", &ArgumentGroup)
,Filename(&Helper, "FILE", &ArgumentGroup)
,ExportOld(&Helper, "EXPORT_LINK_INTERFACE_LIBRARIES", &ArgumentGroup)
{
// at first TARGETS
this->Targets.Follows(0);
// and after that the other options in any order
this->ArgumentGroup.Follows(&this->Targets);
}
// 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);
}
std::vector<std::string> unknownArgs;
this->Helper.Parse(&args, &unknownArgs);
if (!unknownArgs.empty())
{
this->SetError("Unknown arguments.");
return false;
}
if (this->Targets.WasFound() == false)
{
this->SetError("TARGETS option missing.");
return false;
}
if(!this->Filename.WasFound())
{
this->SetError("FILE <filename> option missing.");
return false;
}
// 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;
}
// Get the file to write.
std::string fname = this->Filename.GetString();
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.
fname = this->Makefile->GetCurrentOutputDirectory();
fname += "/";
fname += this->Filename.GetString();
}
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 =
this->Makefile->GetLocalGenerator()->
GetGlobalGenerator()->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;
}
}
}
cmGlobalGenerator *gg = this->Makefile->GetLocalGenerator()
->GetGlobalGenerator();
if (this->Append.IsEnabled())
{
if (cmExportBuildFileGenerator *ebfg = gg->GetExportedTargetsFile(fname))
{
ebfg->AppendTargets(this->Targets.GetVector());
return true;
}
}
// Setup export file generation.
cmExportBuildFileGenerator *ebfg = new cmExportBuildFileGenerator;
ebfg->SetExportFile(fname.c_str());
ebfg->SetNamespace(this->Namespace.GetCString());
ebfg->SetAppendMode(this->Append.IsEnabled());
ebfg->SetTargets(this->Targets.GetVector());
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";
char winmsg[1024];
if(FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS, 0, err,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
winmsg, 1024, 0) > 0)
{
e << "Windows reported:\n"
<< " " << 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 = RegCreateKeyEx(HKEY_CURRENT_USER,
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;
}
err = RegSetValueEx(hKey, hash, 0, REG_SZ, (BYTE const*)content,
static_cast<DWORD>(strlen(content)+1));
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