diff --git a/Source/cmExportBuildFileGenerator.cxx b/Source/cmExportBuildFileGenerator.cxx index 243e5ce54..50835e295 100644 --- a/Source/cmExportBuildFileGenerator.cxx +++ b/Source/cmExportBuildFileGenerator.cxx @@ -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 allTargets; { std::string expectedTargets; std::string sep; - for(std::vector::const_iterator - tei = this->Exports->begin(); - tei != this->Exports->end(); ++tei) + for(std::vector::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(); - } + cmOStringStream e; + e << "given target \"" << te->GetName() << "\" more than once."; + 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::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 &missingTargets) { for(std::vector::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&, - cmMakefile*, cmTarget* depender, cmTarget* dependee) + std::string& link_libs, std::vector& missingTargets, + cmMakefile* mf, cmTarget* depender, cmTarget* dependee) { // The target is not in the export. if(!this->AppendMode) { - // We are not appending, so all exported targets should be - // known here. This is probably user-error. - this->ComplainAboutMissingTarget(depender, dependee); + const std::string name = dependee->GetName(); + std::vector 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, 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 +cmExportBuildFileGenerator +::FindNamespaces(cmMakefile* mf, const std::string& name) +{ + std::vector namespaces; + cmGlobalGenerator* gg = mf->GetLocalGenerator()->GetGlobalGenerator(); + + std::map& exportSets + = gg->GetBuildExportSets(); + + for(std::map::const_iterator + expIt = exportSets.begin(); expIt != exportSets.end(); ++expIt) + { + const cmExportBuildFileGenerator* exportSet = expIt->second; + std::vector 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 diff --git a/Source/cmExportBuildFileGenerator.h b/Source/cmExportBuildFileGenerator.h index 3ffdf8b13..2fbd98f66 100644 --- a/Source/cmExportBuildFileGenerator.h +++ b/Source/cmExportBuildFileGenerator.h @@ -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 const* exports) - { this->Exports = exports; } + void SetTargets(std::vector const& targets) + { this->Targets = targets; } + std::vector const& GetTargets() const + { return this->Targets; } + void AppendTargets(std::vector 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 const* Exports; - cmExportCommand* ExportCommand; + std::vector + FindNamespaces(cmMakefile* mf, const std::string& name); + + std::vector Targets; + std::vector Exports; + cmMakefile* Makefile; + cmListFileBacktrace Backtrace; }; #endif diff --git a/Source/cmExportCommand.cxx b/Source/cmExportCommand.cxx index f335b8bc5..86ddc3f44 100644 --- a/Source/cmExportCommand.cxx +++ b/Source/cmExportCommand.cxx @@ -108,8 +108,6 @@ bool cmExportCommand fname += this->Filename.GetString(); } - // Collect the targets to be exported. - std::vector targets; for(std::vector::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()) + { + if (cmExportBuildFileGenerator *ebfg = gg->GetExportedTargetsFile(fname)) { - cmOStringStream e; - e << "given target \"" << *currentTarget - << "\" which is not built by this project."; - this->SetError(e.str().c_str()); - return false; + 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 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; } diff --git a/Source/cmExportCommand.h b/Source/cmExportCommand.h index 87c345284..38e8d9a6d 100644 --- a/Source/cmExportCommand.h +++ b/Source/cmExportCommand.h @@ -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 )\n" "Store the current build directory in the CMake user package registry " diff --git a/Source/cmExportFileGenerator.cxx b/Source/cmExportFileGenerator.cxx index 25c57100c..b01e4998f 100644 --- a/Source/cmExportFileGenerator.cxx +++ b/Source/cmExportFileGenerator.cxx @@ -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() { diff --git a/Source/cmExportFileGenerator.h b/Source/cmExportFileGenerator.h index 9628b9681..f3d08070f 100644 --- a/Source/cmExportFileGenerator.h +++ b/Source/cmExportFileGenerator.h @@ -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; } diff --git a/Source/cmExportInstallFileGenerator.cxx b/Source/cmExportInstallFileGenerator.cxx index c71008e2c..133944ee5 100644 --- a/Source/cmExportInstallFileGenerator.cxx +++ b/Source/cmExportInstallFileGenerator.cxx @@ -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); } } diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx index eacf85ba2..fb205bece 100644 --- a/Source/cmGlobalGenerator.cxx +++ b/Source/cmGlobalGenerator.cxx @@ -27,6 +27,7 @@ #include "cmGeneratorTarget.h" #include "cmGeneratorExpression.h" #include "cmGeneratorExpressionEvaluationFile.h" +#include "cmExportBuildFileGenerator.h" #include @@ -77,6 +78,12 @@ cmGlobalGenerator::~cmGlobalGenerator() { delete *li; } + for(std::map::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::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::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::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::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(); diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h index b8860f1f2..c930b2b6e 100644 --- a/Source/cmGlobalGenerator.h +++ b/Source/cmGlobalGenerator.h @@ -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::const_iterator it - = this->ExportedTargetsFiles.find(filename); - return it != this->ExportedTargetsFiles.end(); - } - + std::map& 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 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 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 ExtensionToLanguage; std::map LanguageToLinkerPreference; std::map LanguageToOriginalSharedLibFlags; - std::set ExportedTargetsFiles; // Record hashes for rules and outputs. struct RuleHash { char Data[32]; }; diff --git a/Source/cmIncludeCommand.cxx b/Source/cmIncludeCommand.cxx index 39d4993eb..5b931713c 100644 --- a/Source/cmIncludeCommand.cxx +++ b/Source/cmIncludeCommand.cxx @@ -127,6 +127,7 @@ bool cmIncludeCommand return false; } } + gg->GenerateImportFile(fname_abs); } std::string fullFilePath; diff --git a/Tests/RunCMake/CMP0022/CMP0022-export-stderr.txt b/Tests/RunCMake/CMP0022/CMP0022-export-stderr.txt index ae7627e1e..405dd8dc8 100644 --- a/Tests/RunCMake/CMP0022/CMP0022-export-stderr.txt +++ b/Tests/RunCMake/CMP0022/CMP0022-export-stderr.txt @@ -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 diff --git a/Tests/RunCMake/ExportWithoutLanguage/NoLanguage-stderr.txt b/Tests/RunCMake/ExportWithoutLanguage/NoLanguage-stderr.txt index 67a0ae37c..5658d85d4 100644 --- a/Tests/RunCMake/ExportWithoutLanguage/NoLanguage-stderr.txt +++ b/Tests/RunCMake/ExportWithoutLanguage/NoLanguage-stderr.txt @@ -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\) diff --git a/Tests/RunCMake/ExportWithoutLanguage/header.h b/Tests/RunCMake/ExportWithoutLanguage/header.h new file mode 100644 index 000000000..0c803ed03 --- /dev/null +++ b/Tests/RunCMake/ExportWithoutLanguage/header.h @@ -0,0 +1,2 @@ + +enum some_compilers { need_more_than_nothing };