Merge topic 'export-at-generate-time'

a4263c9 export(): Handle multiple dependent export sets.
66b290e export(): Process the export() command at generate time.
5fe5c32 export(): Set a Makefile on the cmExportBuildFileGenerator.
e383555 cmExportInstallFileGenerator: Fix comment to match reality.
This commit is contained in:
Brad King 2013-10-15 09:32:36 -04:00 committed by CMake Topic Stage
commit 06491955eb
13 changed files with 209 additions and 111 deletions

View File

@ -11,40 +11,38 @@
============================================================================*/
#include "cmExportBuildFileGenerator.h"
#include "cmExportCommand.h"
#include "cmLocalGenerator.h"
#include "cmGlobalGenerator.h"
//----------------------------------------------------------------------------
cmExportBuildFileGenerator::cmExportBuildFileGenerator()
{
this->ExportCommand = 0;
this->Makefile = 0;
}
//----------------------------------------------------------------------------
bool cmExportBuildFileGenerator::GenerateMainFile(std::ostream& os)
{
std::vector<cmTarget*> allTargets;
{
std::string expectedTargets;
std::string sep;
for(std::vector<cmTarget*>::const_iterator
tei = this->Exports->begin();
tei != this->Exports->end(); ++tei)
for(std::vector<std::string>::const_iterator
tei = this->Targets.begin();
tei != this->Targets.end(); ++tei)
{
expectedTargets += sep + this->Namespace + (*tei)->GetExportName();
cmTarget *te = this->Makefile->FindTargetToUse(tei->c_str());
expectedTargets += sep + this->Namespace + te->GetExportName();
sep = " ";
cmTarget* te = *tei;
if(this->ExportedTargets.insert(te).second)
{
allTargets.push_back(te);
this->Exports.push_back(te);
}
else
{
if(this->ExportCommand && this->ExportCommand->ErrorMessage.empty())
{
cmOStringStream e;
e << "given target \"" << te->GetName() << "\" more than once.";
this->ExportCommand->ErrorMessage = e.str();
}
this->Makefile->GetCMakeInstance()
->IssueMessage(cmake::FATAL_ERROR, e.str().c_str(), this->Backtrace);
return false;
}
if (te->GetType() == cmTarget::INTERFACE_LIBRARY)
@ -60,8 +58,8 @@ bool cmExportBuildFileGenerator::GenerateMainFile(std::ostream& os)
// Create all the imported targets.
for(std::vector<cmTarget*>::const_iterator
tei = allTargets.begin();
tei != allTargets.end(); ++tei)
tei = this->Exports.begin();
tei != this->Exports.end(); ++tei)
{
cmTarget* te = *tei;
this->GenerateImportTargetCode(os, te);
@ -116,8 +114,8 @@ cmExportBuildFileGenerator
std::vector<std::string> &missingTargets)
{
for(std::vector<cmTarget*>::const_iterator
tei = this->Exports->begin();
tei != this->Exports->end(); ++tei)
tei = this->Exports.begin();
tei != this->Exports.end(); ++tei)
{
// Collect import properties for this target.
cmTarget* target = *tei;
@ -198,40 +196,95 @@ cmExportBuildFileGenerator
//----------------------------------------------------------------------------
void
cmExportBuildFileGenerator::HandleMissingTarget(
std::string& link_libs, std::vector<std::string>&,
cmMakefile*, cmTarget* depender, cmTarget* dependee)
std::string& link_libs, std::vector<std::string>& missingTargets,
cmMakefile* mf, cmTarget* depender, cmTarget* dependee)
{
// The target is not in the export.
if(!this->AppendMode)
{
const std::string name = dependee->GetName();
std::vector<std::string> namespaces = this->FindNamespaces(mf, name);
int targetOccurrences = (int)namespaces.size();
if (targetOccurrences == 1)
{
std::string missingTarget = namespaces[0];
missingTarget += dependee->GetExportName();
link_libs += missingTarget;
missingTargets.push_back(missingTarget);
return;
}
else
{
// We are not appending, so all exported targets should be
// known here. This is probably user-error.
this->ComplainAboutMissingTarget(depender, dependee);
this->ComplainAboutMissingTarget(depender, dependee, targetOccurrences);
}
}
// Assume the target will be exported by another command.
// Append it with the export namespace.
link_libs += this->Namespace;
link_libs += dependee->GetExportName();
// if generate time {}
}
//----------------------------------------------------------------------------
std::vector<std::string>
cmExportBuildFileGenerator
::FindNamespaces(cmMakefile* mf, const std::string& name)
{
std::vector<std::string> namespaces;
cmGlobalGenerator* gg = mf->GetLocalGenerator()->GetGlobalGenerator();
std::map<std::string, cmExportBuildFileGenerator*>& exportSets
= gg->GetBuildExportSets();
for(std::map<std::string, cmExportBuildFileGenerator*>::const_iterator
expIt = exportSets.begin(); expIt != exportSets.end(); ++expIt)
{
const cmExportBuildFileGenerator* exportSet = expIt->second;
std::vector<std::string> const& targets = exportSet->GetTargets();
if (std::find(targets.begin(), targets.end(), name) != targets.end())
{
namespaces.push_back(exportSet->GetNamespace());
}
}
return namespaces;
}
//----------------------------------------------------------------------------
void
cmExportBuildFileGenerator
::ComplainAboutMissingTarget(cmTarget* depender,
cmTarget* dependee)
cmTarget* dependee,
int occurrences)
{
if(!this->ExportCommand || !this->ExportCommand->ErrorMessage.empty())
if(cmSystemTools::GetErrorOccuredFlag())
{
return;
}
cmOStringStream e;
e << "called with target \"" << depender->GetName()
<< "\" which requires target \"" << dependee->GetName()
<< "\" that is not in the export list.\n"
<< "If the required target is not easy to reference in this call, "
e << "export called with target \"" << depender->GetName()
<< "\" which requires target \"" << dependee->GetName() << "\" ";
if (occurrences == 0)
{
e << "that is not in the export set.\n";
}
else
{
e << "that is not in this export set, but " << occurrences
<< " times in others.\n";
}
e << "If the required target is not easy to reference in this call, "
<< "consider using the APPEND option with multiple separate calls.";
this->ExportCommand->ErrorMessage = e.str();
this->Makefile->GetCMakeInstance()
->IssueMessage(cmake::FATAL_ERROR, e.str().c_str(), this->Backtrace);
}
std::string

View File

@ -13,8 +13,7 @@
#define cmExportBuildFileGenerator_h
#include "cmExportFileGenerator.h"
class cmExportCommand;
#include "cmListFileCache.h"
/** \class cmExportBuildFileGenerator
* \brief Generate a file exporting targets from a build tree.
@ -31,14 +30,22 @@ public:
cmExportBuildFileGenerator();
/** Set the list of targets to export. */
void SetExports(std::vector<cmTarget*> const* exports)
{ this->Exports = exports; }
void SetTargets(std::vector<std::string> const& targets)
{ this->Targets = targets; }
std::vector<std::string> const& GetTargets() const
{ return this->Targets; }
void AppendTargets(std::vector<std::string> const& targets)
{ this->Targets.insert(this->Targets.end(),
targets.begin(), targets.end()); }
/** Set whether to append generated code to the output file. */
void SetAppendMode(bool append) { this->AppendMode = append; }
/** Set the command instance through which errors should be reported. */
void SetCommand(cmExportCommand* cmd) { this->ExportCommand = cmd; }
void SetMakefile(cmMakefile *mf) {
this->Makefile = mf;
this->Makefile->GetBacktrace(this->Backtrace);
}
protected:
// Implement virtual methods from the superclass.
virtual bool GenerateMainFile(std::ostream& os);
@ -53,7 +60,8 @@ protected:
cmTarget* dependee);
void ComplainAboutMissingTarget(cmTarget* depender,
cmTarget* dependee);
cmTarget* dependee,
int occurrences);
/** Fill in properties indicating built file locations. */
void SetImportLocationProperty(const char* config,
@ -63,8 +71,13 @@ protected:
std::string InstallNameDir(cmTarget* target, const std::string& config);
std::vector<cmTarget*> const* Exports;
cmExportCommand* ExportCommand;
std::vector<std::string>
FindNamespaces(cmMakefile* mf, const std::string& name);
std::vector<std::string> Targets;
std::vector<cmTarget*> Exports;
cmMakefile* Makefile;
cmListFileBacktrace Backtrace;
};
#endif

View File

@ -108,8 +108,6 @@ bool cmExportCommand
fname += this->Filename.GetString();
}
// Collect the targets to be exported.
std::vector<cmTarget*> targets;
for(std::vector<std::string>::const_iterator
currentTarget = this->Targets.GetVector().begin();
currentTarget != this->Targets.GetVector().end();
@ -128,15 +126,7 @@ bool cmExportCommand
this->Makefile->GetLocalGenerator()->
GetGlobalGenerator()->FindTarget(0, currentTarget->c_str()))
{
if((target->GetType() == cmTarget::EXECUTABLE) ||
(target->GetType() == cmTarget::STATIC_LIBRARY) ||
(target->GetType() == cmTarget::SHARED_LIBRARY) ||
(target->GetType() == cmTarget::MODULE_LIBRARY) ||
(target->GetType() == cmTarget::INTERFACE_LIBRARY))
{
targets.push_back(target);
}
else if(target->GetType() == cmTarget::OBJECT_LIBRARY)
if(target->GetType() == cmTarget::OBJECT_LIBRARY)
{
cmOStringStream e;
e << "given OBJECT library \"" << *currentTarget
@ -144,37 +134,28 @@ bool cmExportCommand
this->SetError(e.str().c_str());
return false;
}
else
{
cmOStringStream e;
e << "given target \"" << *currentTarget
<< "\" which is not an executable or library.";
this->SetError(e.str().c_str());
return false;
}
}
else
cmGlobalGenerator *gg = this->Makefile->GetLocalGenerator()
->GetGlobalGenerator();
if (this->Append.IsEnabled())
{
cmOStringStream e;
e << "given target \"" << *currentTarget
<< "\" which is not built by this project.";
this->SetError(e.str().c_str());
return false;
if (cmExportBuildFileGenerator *ebfg = gg->GetExportedTargetsFile(fname))
{
ebfg->AppendTargets(this->Targets.GetVector());
return true;
}
}
// Setup export file generation.
cmExportBuildFileGenerator ebfg;
ebfg.SetExportFile(fname.c_str());
ebfg.SetNamespace(this->Namespace.GetCString());
ebfg.SetAppendMode(this->Append.IsEnabled());
ebfg.SetExports(&targets);
ebfg.SetCommand(this);
ebfg.SetExportOld(this->ExportOld.IsEnabled());
cmGlobalGenerator *gg = this->Makefile->GetLocalGenerator()
->GetGlobalGenerator();
gg->AddExportedTargetsFile(fname);
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;
@ -185,27 +166,15 @@ bool cmExportCommand
ci = configurationTypes.begin();
ci != configurationTypes.end(); ++ci)
{
ebfg.AddConfiguration(ci->c_str());
ebfg->AddConfiguration(ci->c_str());
}
}
else
{
ebfg.AddConfiguration("");
ebfg->AddConfiguration("");
}
// Generate the import file.
if(!ebfg.GenerateImportFile() && this->ErrorMessage.empty())
{
this->SetError("could not write export file.");
return false;
}
// Report generated error message if any.
if(!this->ErrorMessage.empty())
{
this->SetError(this->ErrorMessage.c_str());
return false;
}
gg->AddBuildExportSet(ebfg);
return true;
}

View File

@ -85,7 +85,9 @@ public:
"should never be installed. "
"See the install(EXPORT) command to export targets from an "
"installation tree."
CM_LOCATION_UNDEFINED_BEHAVIOR("passing it to this command")
"\n"
"The properties set on the generated IMPORTED targets will have the "
"same values as the final values of the input TARGETS."
"\n"
" export(PACKAGE <name>)\n"
"Store the current build directory in the CMake user package registry "

View File

@ -51,6 +51,12 @@ void cmExportFileGenerator::SetExportFile(const char* mainFile)
cmSystemTools::GetFilenameLastExtension(this->MainImportFile);
}
//----------------------------------------------------------------------------
const char* cmExportFileGenerator::GetMainExportFileName() const
{
return this->MainImportFile.c_str();
}
//----------------------------------------------------------------------------
bool cmExportFileGenerator::GenerateImportFile()
{

View File

@ -33,9 +33,11 @@ public:
/** Set the full path to the export file to generate. */
void SetExportFile(const char* mainFile);
const char *GetMainExportFileName() const;
/** Set the namespace in which to place exported target names. */
void SetNamespace(const char* ns) { this->Namespace = ns; }
std::string GetNamespace() const { return this->Namespace; }
void SetExportOld(bool exportOld) { this->ExportOld = exportOld; }

View File

@ -432,8 +432,8 @@ cmExportInstallFileGenerator::HandleMissingTarget(
}
else
{
// We are not appending, so all exported targets should be
// known here. This is probably user-error.
// All exported targets should be known here and should be unique.
// This is probably user-error.
this->ComplainAboutMissingTarget(depender, dependee, targetOccurrences);
}
}

View File

@ -27,6 +27,7 @@
#include "cmGeneratorTarget.h"
#include "cmGeneratorExpression.h"
#include "cmGeneratorExpressionEvaluationFile.h"
#include "cmExportBuildFileGenerator.h"
#include <cmsys/Directory.hxx>
@ -77,6 +78,12 @@ cmGlobalGenerator::~cmGlobalGenerator()
{
delete *li;
}
for(std::map<std::string, cmExportBuildFileGenerator*>::iterator
i = this->BuildExportSets.begin();
i != this->BuildExportSets.end(); ++i)
{
delete i->second;
}
this->LocalGenerators.clear();
if (this->ExtraGenerator)
@ -183,6 +190,34 @@ void cmGlobalGenerator::ResolveLanguageCompiler(const std::string &lang,
doc.c_str(), cmCacheManager::FILEPATH);
}
void cmGlobalGenerator::AddBuildExportSet(cmExportBuildFileGenerator* gen)
{
this->BuildExportSets[gen->GetMainExportFileName()] = gen;
}
bool cmGlobalGenerator::GenerateImportFile(const std::string &file)
{
std::map<std::string, cmExportBuildFileGenerator*>::iterator it
= this->BuildExportSets.find(file);
if (it != this->BuildExportSets.end())
{
bool result = it->second->GenerateImportFile();
delete it->second;
it->second = 0;
this->BuildExportSets.erase(it);
return result;
}
return false;
}
bool
cmGlobalGenerator::IsExportedTargetsFile(const std::string &filename) const
{
const std::map<std::string, cmExportBuildFileGenerator*>::const_iterator it
= this->BuildExportSets.find(filename);
return it != this->BuildExportSets.end();
}
// Find the make program for the generator, required for try compiles
void cmGlobalGenerator::FindMakeProgram(cmMakefile* mf)
{
@ -966,6 +1001,14 @@ void cmGlobalGenerator::Configure()
}
}
cmExportBuildFileGenerator*
cmGlobalGenerator::GetExportedTargetsFile(const std::string &filename) const
{
std::map<std::string, cmExportBuildFileGenerator*>::const_iterator it
= this->BuildExportSets.find(filename);
return it == this->BuildExportSets.end() ? 0 : it->second;
}
bool cmGlobalGenerator::CheckALLOW_DUPLICATE_CUSTOM_TARGETS()
{
// If the property is not enabled then okay.
@ -1091,6 +1134,19 @@ void cmGlobalGenerator::Generate()
}
this->SetCurrentLocalGenerator(0);
for (std::map<std::string, cmExportBuildFileGenerator*>::iterator
it = this->BuildExportSets.begin(); it != this->BuildExportSets.end();
++it)
{
if (!it->second->GenerateImportFile()
&& !cmSystemTools::GetErrorOccuredFlag())
{
this->GetCMakeInstance()
->IssueMessage(cmake::FATAL_ERROR, "Could not write export file.",
cmListFileBacktrace());
return;
}
}
// Update rule hashes.
this->CheckRuleHashes();

View File

@ -31,6 +31,7 @@ class cmExternalMakefileProjectGenerator;
class cmTarget;
class cmInstallTargetGenerator;
class cmInstallFilesGenerator;
class cmExportBuildFileGenerator;
/** \class cmGlobalGenerator
* \brief Responable for overseeing the generation process for the entire tree
@ -293,18 +294,13 @@ public:
void ProcessEvaluationFiles();
void AddExportedTargetsFile(const std::string &filename)
{
this->ExportedTargetsFiles.insert(filename);
}
bool IsExportedTargetsFile(const std::string &filename) const
{
const std::set<std::string>::const_iterator it
= this->ExportedTargetsFiles.find(filename);
return it != this->ExportedTargetsFiles.end();
}
std::map<std::string, cmExportBuildFileGenerator*>& GetBuildExportSets()
{return this->BuildExportSets;}
void AddBuildExportSet(cmExportBuildFileGenerator*);
bool IsExportedTargetsFile(const std::string &filename) const;
bool GenerateImportFile(const std::string &file);
cmExportBuildFileGenerator*
GetExportedTargetsFile(const std::string &filename) const;
protected:
typedef std::vector<cmLocalGenerator*> GeneratorVector;
// for a project collect all its targets by following depend
@ -356,6 +352,7 @@ protected:
bool InstallTargetEnabled;
// Sets of named target exports
cmExportSetMap ExportSets;
std::map<std::string, cmExportBuildFileGenerator*> BuildExportSets;
// Manifest of all targets that will be built for each configuration.
// This is computed just before local generators generate.
@ -384,7 +381,6 @@ private:
std::map<cmStdString, cmStdString> ExtensionToLanguage;
std::map<cmStdString, int> LanguageToLinkerPreference;
std::map<cmStdString, cmStdString> LanguageToOriginalSharedLibFlags;
std::set<std::string> ExportedTargetsFiles;
// Record hashes for rules and outputs.
struct RuleHash { char Data[32]; };

View File

@ -127,6 +127,7 @@ bool cmIncludeCommand
return false;
}
}
gg->GenerateImportFile(fname_abs);
}
std::string fullFilePath;

View File

@ -1,4 +1,4 @@
CMake Error at CMP0022-export.cmake:11 \(export\):
CMake Error in CMakeLists.txt:
Target "cmp0022NEW" has policy CMP0022 enabled, but also has old-style
LINK_INTERFACE_LIBRARIES properties populated, but it was exported without
the EXPORT_LINK_INTERFACE_LIBRARIES to export the old-style properties

View File

@ -1,6 +1,4 @@
CMake Error: CMake can not determine linker language for target: NoLanguage
CMake Error at NoLanguage.cmake:2 \(export\):
CMake Error in CMakeLists.txt:
Exporting the target "NoLanguage" is not allowed since its linker language
cannot be determined
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)

View File

@ -0,0 +1,2 @@
enum some_compilers { need_more_than_nothing };