| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
| file Copyright.txt or https://cmake.org/licensing for details. */ |
| #include "cmGeneratorTarget.h" |
| |
| #include "cmsys/RegularExpression.hxx" |
| #include <algorithm> |
| #include <assert.h> |
| #include <errno.h> |
| #include <iterator> |
| #include <memory> // IWYU pragma: keep |
| #include <queue> |
| #include <sstream> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unordered_set> |
| |
| #include "cmAlgorithms.h" |
| #include "cmComputeLinkInformation.h" |
| #include "cmCustomCommand.h" |
| #include "cmCustomCommandGenerator.h" |
| #include "cmCustomCommandLines.h" |
| #include "cmGeneratorExpression.h" |
| #include "cmGeneratorExpressionDAGChecker.h" |
| #include "cmGlobalGenerator.h" |
| #include "cmLocalGenerator.h" |
| #include "cmMakefile.h" |
| #include "cmPropertyMap.h" |
| #include "cmSourceFile.h" |
| #include "cmSourceFileLocation.h" |
| #include "cmState.h" |
| #include "cmSystemTools.h" |
| #include "cmTarget.h" |
| #include "cmTargetLinkLibraryType.h" |
| #include "cmTargetPropertyComputer.h" |
| #include "cmake.h" |
| |
| class cmMessenger; |
| |
| template <> |
| const char* cmTargetPropertyComputer::GetSources<cmGeneratorTarget>( |
| cmGeneratorTarget const* tgt, cmMessenger* /* messenger */, |
| cmListFileBacktrace const& /* context */) |
| { |
| return tgt->GetSourcesProperty(); |
| } |
| |
| template <> |
| const char* |
| cmTargetPropertyComputer::ComputeLocationForBuild<cmGeneratorTarget>( |
| cmGeneratorTarget const* tgt) |
| { |
| return tgt->GetLocation(""); |
| } |
| |
| template <> |
| const char* cmTargetPropertyComputer::ComputeLocation<cmGeneratorTarget>( |
| cmGeneratorTarget const* tgt, const std::string& config) |
| { |
| return tgt->GetLocation(config); |
| } |
| |
| class cmGeneratorTarget::TargetPropertyEntry |
| { |
| static cmLinkImplItem NoLinkImplItem; |
| |
| public: |
| TargetPropertyEntry(std::unique_ptr<cmCompiledGeneratorExpression> cge, |
| cmLinkImplItem const& item = NoLinkImplItem) |
| : ge(std::move(cge)) |
| , LinkImplItem(item) |
| { |
| } |
| const std::unique_ptr<cmCompiledGeneratorExpression> ge; |
| cmLinkImplItem const& LinkImplItem; |
| }; |
| cmLinkImplItem cmGeneratorTarget::TargetPropertyEntry::NoLinkImplItem; |
| |
| void CreatePropertyGeneratorExpressions( |
| cmStringRange entries, cmBacktraceRange backtraces, |
| std::vector<cmGeneratorTarget::TargetPropertyEntry*>& items, |
| bool evaluateForBuildsystem = false) |
| { |
| std::vector<cmListFileBacktrace>::const_iterator btIt = backtraces.begin(); |
| for (std::vector<std::string>::const_iterator it = entries.begin(); |
| it != entries.end(); ++it, ++btIt) { |
| cmGeneratorExpression ge(*btIt); |
| std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(*it); |
| cge->SetEvaluateForBuildsystem(evaluateForBuildsystem); |
| items.push_back( |
| new cmGeneratorTarget::TargetPropertyEntry(std::move(cge))); |
| } |
| } |
| |
| cmGeneratorTarget::cmGeneratorTarget(cmTarget* t, cmLocalGenerator* lg) |
| : Target(t) |
| , FortranModuleDirectoryCreated(false) |
| , SourceFileFlagsConstructed(false) |
| , PolicyWarnedCMP0022(false) |
| , PolicyReportedCMP0069(false) |
| , DebugIncludesDone(false) |
| , DebugCompileOptionsDone(false) |
| , DebugCompileFeaturesDone(false) |
| , DebugCompileDefinitionsDone(false) |
| , DebugLinkOptionsDone(false) |
| , DebugLinkDirectoriesDone(false) |
| , DebugSourcesDone(false) |
| , LinkImplementationLanguageIsContextDependent(true) |
| , UtilityItemsDone(false) |
| { |
| this->Makefile = this->Target->GetMakefile(); |
| this->LocalGenerator = lg; |
| this->GlobalGenerator = this->LocalGenerator->GetGlobalGenerator(); |
| |
| this->GlobalGenerator->ComputeTargetObjectDirectory(this); |
| |
| CreatePropertyGeneratorExpressions(t->GetIncludeDirectoriesEntries(), |
| t->GetIncludeDirectoriesBacktraces(), |
| this->IncludeDirectoriesEntries); |
| |
| CreatePropertyGeneratorExpressions(t->GetCompileOptionsEntries(), |
| t->GetCompileOptionsBacktraces(), |
| this->CompileOptionsEntries); |
| |
| CreatePropertyGeneratorExpressions(t->GetCompileFeaturesEntries(), |
| t->GetCompileFeaturesBacktraces(), |
| this->CompileFeaturesEntries); |
| |
| CreatePropertyGeneratorExpressions(t->GetCompileDefinitionsEntries(), |
| t->GetCompileDefinitionsBacktraces(), |
| this->CompileDefinitionsEntries); |
| |
| CreatePropertyGeneratorExpressions(t->GetLinkOptionsEntries(), |
| t->GetLinkOptionsBacktraces(), |
| this->LinkOptionsEntries); |
| |
| CreatePropertyGeneratorExpressions(t->GetLinkDirectoriesEntries(), |
| t->GetLinkDirectoriesBacktraces(), |
| this->LinkDirectoriesEntries); |
| |
| CreatePropertyGeneratorExpressions(t->GetSourceEntries(), |
| t->GetSourceBacktraces(), |
| this->SourceEntries, true); |
| |
| this->DLLPlatform = |
| !this->Makefile->GetSafeDefinition("CMAKE_IMPORT_LIBRARY_SUFFIX").empty(); |
| |
| this->PolicyMap = t->PolicyMap; |
| } |
| |
| cmGeneratorTarget::~cmGeneratorTarget() |
| { |
| cmDeleteAll(this->IncludeDirectoriesEntries); |
| cmDeleteAll(this->CompileOptionsEntries); |
| cmDeleteAll(this->CompileFeaturesEntries); |
| cmDeleteAll(this->CompileDefinitionsEntries); |
| cmDeleteAll(this->LinkOptionsEntries); |
| cmDeleteAll(this->LinkDirectoriesEntries); |
| cmDeleteAll(this->SourceEntries); |
| cmDeleteAll(this->LinkInformation); |
| } |
| |
| const char* cmGeneratorTarget::GetSourcesProperty() const |
| { |
| std::vector<std::string> values; |
| for (TargetPropertyEntry* se : this->SourceEntries) { |
| values.push_back(se->ge->GetInput()); |
| } |
| static std::string value; |
| value.clear(); |
| value = cmJoin(values, ";"); |
| return value.c_str(); |
| } |
| |
| cmGlobalGenerator* cmGeneratorTarget::GetGlobalGenerator() const |
| { |
| return this->GetLocalGenerator()->GetGlobalGenerator(); |
| } |
| |
| cmLocalGenerator* cmGeneratorTarget::GetLocalGenerator() const |
| { |
| return this->LocalGenerator; |
| } |
| |
| cmStateEnums::TargetType cmGeneratorTarget::GetType() const |
| { |
| return this->Target->GetType(); |
| } |
| |
| const std::string& cmGeneratorTarget::GetName() const |
| { |
| return this->Target->GetName(); |
| } |
| |
| std::string cmGeneratorTarget::GetExportName() const |
| { |
| const char* exportName = this->GetProperty("EXPORT_NAME"); |
| |
| if (exportName && *exportName) { |
| if (!cmGeneratorExpression::IsValidTargetName(exportName)) { |
| std::ostringstream e; |
| e << "EXPORT_NAME property \"" << exportName << "\" for \"" |
| << this->GetName() << "\": is not valid."; |
| cmSystemTools::Error(e.str().c_str()); |
| return ""; |
| } |
| return exportName; |
| } |
| return this->GetName(); |
| } |
| |
| const char* cmGeneratorTarget::GetProperty(const std::string& prop) const |
| { |
| if (!cmTargetPropertyComputer::PassesWhitelist( |
| this->GetType(), prop, this->Makefile->GetMessenger(), |
| this->GetBacktrace())) { |
| return nullptr; |
| } |
| if (const char* result = cmTargetPropertyComputer::GetProperty( |
| this, prop, this->Makefile->GetMessenger(), this->GetBacktrace())) { |
| return result; |
| } |
| if (cmSystemTools::GetFatalErrorOccured()) { |
| return nullptr; |
| } |
| return this->Target->GetProperty(prop); |
| } |
| |
| const char* cmGeneratorTarget::GetSafeProperty(const std::string& prop) const |
| { |
| const char* ret = this->GetProperty(prop); |
| if (!ret) { |
| return ""; |
| } |
| return ret; |
| } |
| |
| const char* cmGeneratorTarget::GetOutputTargetType( |
| cmStateEnums::ArtifactType artifact) const |
| { |
| switch (this->GetType()) { |
| case cmStateEnums::SHARED_LIBRARY: |
| if (this->IsDLLPlatform()) { |
| switch (artifact) { |
| case cmStateEnums::RuntimeBinaryArtifact: |
| // A DLL shared library is treated as a runtime target. |
| return "RUNTIME"; |
| case cmStateEnums::ImportLibraryArtifact: |
| // A DLL import library is treated as an archive target. |
| return "ARCHIVE"; |
| } |
| } else { |
| // For non-DLL platforms shared libraries are treated as |
| // library targets. |
| return "LIBRARY"; |
| } |
| break; |
| case cmStateEnums::STATIC_LIBRARY: |
| // Static libraries are always treated as archive targets. |
| return "ARCHIVE"; |
| case cmStateEnums::MODULE_LIBRARY: |
| switch (artifact) { |
| case cmStateEnums::RuntimeBinaryArtifact: |
| // Module libraries are always treated as library targets. |
| return "LIBRARY"; |
| case cmStateEnums::ImportLibraryArtifact: |
| // Module import libraries are treated as archive targets. |
| return "ARCHIVE"; |
| } |
| break; |
| case cmStateEnums::OBJECT_LIBRARY: |
| // Object libraries are always treated as object targets. |
| return "OBJECT"; |
| case cmStateEnums::EXECUTABLE: |
| switch (artifact) { |
| case cmStateEnums::RuntimeBinaryArtifact: |
| // Executables are always treated as runtime targets. |
| return "RUNTIME"; |
| case cmStateEnums::ImportLibraryArtifact: |
| // Executable import libraries are treated as archive targets. |
| return "ARCHIVE"; |
| } |
| break; |
| default: |
| break; |
| } |
| return ""; |
| } |
| |
| std::string cmGeneratorTarget::GetOutputName( |
| const std::string& config, cmStateEnums::ArtifactType artifact) const |
| { |
| // Lookup/compute/cache the output name for this configuration. |
| OutputNameKey key(config, artifact); |
| cmGeneratorTarget::OutputNameMapType::iterator i = |
| this->OutputNameMap.find(key); |
| if (i == this->OutputNameMap.end()) { |
| // Add empty name in map to detect potential recursion. |
| OutputNameMapType::value_type entry(key, ""); |
| i = this->OutputNameMap.insert(entry).first; |
| |
| // Compute output name. |
| std::vector<std::string> props; |
| std::string type = this->GetOutputTargetType(artifact); |
| std::string configUpper = cmSystemTools::UpperCase(config); |
| if (!type.empty() && !configUpper.empty()) { |
| // <ARCHIVE|LIBRARY|RUNTIME>_OUTPUT_NAME_<CONFIG> |
| props.push_back(type + "_OUTPUT_NAME_" + configUpper); |
| } |
| if (!type.empty()) { |
| // <ARCHIVE|LIBRARY|RUNTIME>_OUTPUT_NAME |
| props.push_back(type + "_OUTPUT_NAME"); |
| } |
| if (!configUpper.empty()) { |
| // OUTPUT_NAME_<CONFIG> |
| props.push_back("OUTPUT_NAME_" + configUpper); |
| // <CONFIG>_OUTPUT_NAME |
| props.push_back(configUpper + "_OUTPUT_NAME"); |
| } |
| // OUTPUT_NAME |
| props.push_back("OUTPUT_NAME"); |
| |
| std::string outName; |
| for (std::string const& p : props) { |
| if (const char* outNameProp = this->GetProperty(p)) { |
| outName = outNameProp; |
| break; |
| } |
| } |
| |
| if (outName.empty()) { |
| outName = this->GetName(); |
| } |
| |
| // Now evaluate genex and update the previously-prepared map entry. |
| cmGeneratorExpression ge; |
| std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(outName); |
| i->second = cge->Evaluate(this->LocalGenerator, config); |
| } else if (i->second.empty()) { |
| // An empty map entry indicates we have been called recursively |
| // from the above block. |
| this->LocalGenerator->GetCMakeInstance()->IssueMessage( |
| cmake::FATAL_ERROR, |
| "Target '" + this->GetName() + "' OUTPUT_NAME depends on itself.", |
| this->GetBacktrace()); |
| } |
| return i->second; |
| } |
| |
| void cmGeneratorTarget::ClearSourcesCache() |
| { |
| this->KindedSourcesMap.clear(); |
| this->LinkImplementationLanguageIsContextDependent = true; |
| this->Objects.clear(); |
| } |
| |
| void cmGeneratorTarget::AddSourceCommon(const std::string& src) |
| { |
| cmListFileBacktrace lfbt = this->Makefile->GetBacktrace(); |
| cmGeneratorExpression ge(lfbt); |
| std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(src); |
| cge->SetEvaluateForBuildsystem(true); |
| this->SourceEntries.push_back(new TargetPropertyEntry(std::move(cge))); |
| this->ClearSourcesCache(); |
| } |
| |
| void cmGeneratorTarget::AddSource(const std::string& src) |
| { |
| this->Target->AddSource(src); |
| this->AddSourceCommon(src); |
| } |
| |
| void cmGeneratorTarget::AddTracedSources(std::vector<std::string> const& srcs) |
| { |
| this->Target->AddTracedSources(srcs); |
| if (!srcs.empty()) { |
| this->AddSourceCommon(cmJoin(srcs, ";")); |
| } |
| } |
| |
| void cmGeneratorTarget::AddIncludeDirectory(const std::string& src, |
| bool before) |
| { |
| this->Target->InsertInclude(src, this->Makefile->GetBacktrace(), before); |
| cmListFileBacktrace lfbt = this->Makefile->GetBacktrace(); |
| cmGeneratorExpression ge(lfbt); |
| std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(src); |
| cge->SetEvaluateForBuildsystem(true); |
| // Insert before begin/end |
| std::vector<TargetPropertyEntry*>::iterator pos = before |
| ? this->IncludeDirectoriesEntries.begin() |
| : this->IncludeDirectoriesEntries.end(); |
| this->IncludeDirectoriesEntries.insert( |
| pos, new TargetPropertyEntry(std::move(cge))); |
| } |
| |
| std::vector<cmSourceFile*> const* cmGeneratorTarget::GetSourceDepends( |
| cmSourceFile const* sf) const |
| { |
| SourceEntriesType::const_iterator i = this->SourceDepends.find(sf); |
| if (i != this->SourceDepends.end()) { |
| return &i->second.Depends; |
| } |
| return nullptr; |
| } |
| |
| static void handleSystemIncludesDep( |
| cmLocalGenerator* lg, cmGeneratorTarget const* depTgt, |
| const std::string& config, cmGeneratorTarget const* headTarget, |
| cmGeneratorExpressionDAGChecker* dagChecker, |
| std::vector<std::string>& result, bool excludeImported, |
| std::string const& language) |
| { |
| if (const char* dirs = |
| depTgt->GetProperty("INTERFACE_SYSTEM_INCLUDE_DIRECTORIES")) { |
| cmGeneratorExpression ge; |
| cmSystemTools::ExpandListArgument( |
| ge.Parse(dirs)->Evaluate(lg, config, false, headTarget, depTgt, |
| dagChecker, language), |
| result); |
| } |
| if (!depTgt->IsImported() || excludeImported) { |
| return; |
| } |
| |
| if (const char* dirs = |
| depTgt->GetProperty("INTERFACE_INCLUDE_DIRECTORIES")) { |
| cmGeneratorExpression ge; |
| cmSystemTools::ExpandListArgument( |
| ge.Parse(dirs)->Evaluate(lg, config, false, headTarget, depTgt, |
| dagChecker, language), |
| result); |
| } |
| } |
| |
| /* clang-format off */ |
| #define IMPLEMENT_VISIT(KIND) \ |
| { \ |
| KindedSources const& kinded = this->GetKindedSources(config); \ |
| for (SourceAndKind const& s : kinded.Sources) { \ |
| if (s.Kind == KIND) { \ |
| data.push_back(s.Source); \ |
| } \ |
| } \ |
| } |
| /* clang-format on */ |
| |
| void cmGeneratorTarget::GetObjectSources( |
| std::vector<cmSourceFile const*>& data, const std::string& config) const |
| { |
| IMPLEMENT_VISIT(SourceKindObjectSource); |
| |
| if (!this->Objects.empty()) { |
| return; |
| } |
| |
| for (cmSourceFile const* it : data) { |
| this->Objects[it]; |
| } |
| |
| this->LocalGenerator->ComputeObjectFilenames(this->Objects, this); |
| } |
| |
| void cmGeneratorTarget::ComputeObjectMapping() |
| { |
| if (!this->Objects.empty()) { |
| return; |
| } |
| |
| std::vector<std::string> configs; |
| this->Makefile->GetConfigurations(configs); |
| if (configs.empty()) { |
| configs.emplace_back(); |
| } |
| for (std::string const& c : configs) { |
| std::vector<cmSourceFile const*> sourceFiles; |
| this->GetObjectSources(sourceFiles, c); |
| } |
| } |
| |
| const char* cmGeneratorTarget::GetFeature(const std::string& feature, |
| const std::string& config) const |
| { |
| if (!config.empty()) { |
| std::string featureConfig = feature; |
| featureConfig += "_"; |
| featureConfig += cmSystemTools::UpperCase(config); |
| if (const char* value = this->GetProperty(featureConfig)) { |
| return value; |
| } |
| } |
| if (const char* value = this->GetProperty(feature)) { |
| return value; |
| } |
| return this->LocalGenerator->GetFeature(feature, config); |
| } |
| |
| bool cmGeneratorTarget::IsIPOEnabled(std::string const& lang, |
| std::string const& config) const |
| { |
| const char* feature = "INTERPROCEDURAL_OPTIMIZATION"; |
| const bool result = cmSystemTools::IsOn(this->GetFeature(feature, config)); |
| |
| if (!result) { |
| // 'INTERPROCEDURAL_OPTIMIZATION' is off, no need to check policies |
| return false; |
| } |
| |
| if (lang != "C" && lang != "CXX" && lang != "Fortran") { |
| // We do not define IPO behavior for other languages. |
| return false; |
| } |
| |
| cmPolicies::PolicyStatus cmp0069 = this->GetPolicyStatusCMP0069(); |
| |
| if (cmp0069 == cmPolicies::OLD || cmp0069 == cmPolicies::WARN) { |
| if (this->Makefile->IsOn("_CMAKE_" + lang + "_IPO_LEGACY_BEHAVIOR")) { |
| return true; |
| } |
| if (this->PolicyReportedCMP0069) { |
| // problem is already reported, no need to issue a message |
| return false; |
| } |
| const bool in_try_compile = |
| this->LocalGenerator->GetCMakeInstance()->GetIsInTryCompile(); |
| if (cmp0069 == cmPolicies::WARN && !in_try_compile) { |
| std::ostringstream w; |
| w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0069) << "\n"; |
| w << "INTERPROCEDURAL_OPTIMIZATION property will be ignored for target " |
| << "'" << this->GetName() << "'."; |
| this->LocalGenerator->GetCMakeInstance()->IssueMessage( |
| cmake::AUTHOR_WARNING, w.str(), this->GetBacktrace()); |
| |
| this->PolicyReportedCMP0069 = true; |
| } |
| return false; |
| } |
| |
| // Note: check consistency with messages from CheckIPOSupported |
| const char* message = nullptr; |
| if (!this->Makefile->IsOn("_CMAKE_" + lang + "_IPO_SUPPORTED_BY_CMAKE")) { |
| message = "CMake doesn't support IPO for current compiler"; |
| } else if (!this->Makefile->IsOn("_CMAKE_" + lang + |
| "_IPO_MAY_BE_SUPPORTED_BY_COMPILER")) { |
| message = "Compiler doesn't support IPO"; |
| } else if (!this->GlobalGenerator->IsIPOSupported()) { |
| message = "CMake doesn't support IPO for current generator"; |
| } |
| |
| if (!message) { |
| // No error/warning messages |
| return true; |
| } |
| |
| if (this->PolicyReportedCMP0069) { |
| // problem is already reported, no need to issue a message |
| return false; |
| } |
| |
| this->PolicyReportedCMP0069 = true; |
| |
| this->LocalGenerator->GetCMakeInstance()->IssueMessage( |
| cmake::FATAL_ERROR, message, this->GetBacktrace()); |
| return false; |
| } |
| |
| const std::string& cmGeneratorTarget::GetObjectName(cmSourceFile const* file) |
| { |
| this->ComputeObjectMapping(); |
| return this->Objects[file]; |
| } |
| |
| const char* cmGeneratorTarget::GetCustomObjectExtension() const |
| { |
| static std::string extension; |
| const bool has_ptx_extension = |
| this->GetPropertyAsBool("CUDA_PTX_COMPILATION"); |
| if (has_ptx_extension) { |
| extension = ".ptx"; |
| return extension.c_str(); |
| } |
| return nullptr; |
| } |
| |
| void cmGeneratorTarget::AddExplicitObjectName(cmSourceFile const* sf) |
| { |
| this->ExplicitObjectName.insert(sf); |
| } |
| |
| bool cmGeneratorTarget::HasExplicitObjectName(cmSourceFile const* file) const |
| { |
| const_cast<cmGeneratorTarget*>(this)->ComputeObjectMapping(); |
| std::set<cmSourceFile const*>::const_iterator it = |
| this->ExplicitObjectName.find(file); |
| return it != this->ExplicitObjectName.end(); |
| } |
| |
| void cmGeneratorTarget::GetModuleDefinitionSources( |
| std::vector<cmSourceFile const*>& data, const std::string& config) const |
| { |
| IMPLEMENT_VISIT(SourceKindModuleDefinition); |
| } |
| |
| void cmGeneratorTarget::GetHeaderSources( |
| std::vector<cmSourceFile const*>& data, const std::string& config) const |
| { |
| IMPLEMENT_VISIT(SourceKindHeader); |
| } |
| |
| void cmGeneratorTarget::GetExtraSources(std::vector<cmSourceFile const*>& data, |
| const std::string& config) const |
| { |
| IMPLEMENT_VISIT(SourceKindExtra); |
| } |
| |
| void cmGeneratorTarget::GetCustomCommands( |
| std::vector<cmSourceFile const*>& data, const std::string& config) const |
| { |
| IMPLEMENT_VISIT(SourceKindCustomCommand); |
| } |
| |
| void cmGeneratorTarget::GetExternalObjects( |
| std::vector<cmSourceFile const*>& data, const std::string& config) const |
| { |
| IMPLEMENT_VISIT(SourceKindExternalObject); |
| } |
| |
| void cmGeneratorTarget::GetExpectedResxHeaders(std::set<std::string>& headers, |
| const std::string& config) const |
| { |
| KindedSources const& kinded = this->GetKindedSources(config); |
| headers = kinded.ExpectedResxHeaders; |
| } |
| |
| void cmGeneratorTarget::GetResxSources(std::vector<cmSourceFile const*>& data, |
| const std::string& config) const |
| { |
| IMPLEMENT_VISIT(SourceKindResx); |
| } |
| |
| void cmGeneratorTarget::GetAppManifest(std::vector<cmSourceFile const*>& data, |
| const std::string& config) const |
| { |
| IMPLEMENT_VISIT(SourceKindAppManifest); |
| } |
| |
| void cmGeneratorTarget::GetManifests(std::vector<cmSourceFile const*>& data, |
| const std::string& config) const |
| { |
| IMPLEMENT_VISIT(SourceKindManifest); |
| } |
| |
| void cmGeneratorTarget::GetCertificates(std::vector<cmSourceFile const*>& data, |
| const std::string& config) const |
| { |
| IMPLEMENT_VISIT(SourceKindCertificate); |
| } |
| |
| void cmGeneratorTarget::GetExpectedXamlHeaders(std::set<std::string>& headers, |
| const std::string& config) const |
| { |
| KindedSources const& kinded = this->GetKindedSources(config); |
| headers = kinded.ExpectedXamlHeaders; |
| } |
| |
| void cmGeneratorTarget::GetExpectedXamlSources(std::set<std::string>& srcs, |
| const std::string& config) const |
| { |
| KindedSources const& kinded = this->GetKindedSources(config); |
| srcs = kinded.ExpectedXamlSources; |
| } |
| |
| std::set<cmLinkItem> const& cmGeneratorTarget::GetUtilityItems() const |
| { |
| if (!this->UtilityItemsDone) { |
| this->UtilityItemsDone = true; |
| std::set<std::string> const& utilities = this->GetUtilities(); |
| for (std::string const& i : utilities) { |
| if (cmGeneratorTarget* gt = |
| this->LocalGenerator->FindGeneratorTargetToUse(i)) { |
| this->UtilityItems.insert(cmLinkItem(gt)); |
| } else { |
| this->UtilityItems.insert(cmLinkItem(i)); |
| } |
| } |
| } |
| return this->UtilityItems; |
| } |
| |
| void cmGeneratorTarget::GetXamlSources(std::vector<cmSourceFile const*>& data, |
| const std::string& config) const |
| { |
| IMPLEMENT_VISIT(SourceKindXaml); |
| } |
| |
| const char* cmGeneratorTarget::GetLocation(const std::string& config) const |
| { |
| static std::string location; |
| if (this->IsImported()) { |
| location = this->Target->ImportedGetFullPath( |
| config, cmStateEnums::RuntimeBinaryArtifact); |
| } else { |
| location = this->GetFullPath(config, cmStateEnums::RuntimeBinaryArtifact); |
| } |
| return location.c_str(); |
| } |
| |
| std::vector<cmCustomCommand> const& cmGeneratorTarget::GetPreBuildCommands() |
| const |
| { |
| return this->Target->GetPreBuildCommands(); |
| } |
| |
| std::vector<cmCustomCommand> const& cmGeneratorTarget::GetPreLinkCommands() |
| const |
| { |
| return this->Target->GetPreLinkCommands(); |
| } |
| |
| std::vector<cmCustomCommand> const& cmGeneratorTarget::GetPostBuildCommands() |
| const |
| { |
| return this->Target->GetPostBuildCommands(); |
| } |
| |
| bool cmGeneratorTarget::IsImported() const |
| { |
| return this->Target->IsImported(); |
| } |
| |
| bool cmGeneratorTarget::IsImportedGloballyVisible() const |
| { |
| return this->Target->IsImportedGloballyVisible(); |
| } |
| |
| const char* cmGeneratorTarget::GetLocationForBuild() const |
| { |
| static std::string location; |
| if (this->IsImported()) { |
| location = this->Target->ImportedGetFullPath( |
| "", cmStateEnums::RuntimeBinaryArtifact); |
| return location.c_str(); |
| } |
| |
| // Now handle the deprecated build-time configuration location. |
| location = this->GetDirectory(); |
| const char* cfgid = this->Makefile->GetDefinition("CMAKE_CFG_INTDIR"); |
| if (cfgid && strcmp(cfgid, ".") != 0) { |
| location += "/"; |
| location += cfgid; |
| } |
| |
| if (this->IsAppBundleOnApple()) { |
| std::string macdir = this->BuildBundleDirectory("", "", FullLevel); |
| if (!macdir.empty()) { |
| location += "/"; |
| location += macdir; |
| } |
| } |
| location += "/"; |
| location += this->GetFullName("", cmStateEnums::RuntimeBinaryArtifact); |
| return location.c_str(); |
| } |
| |
| bool cmGeneratorTarget::IsSystemIncludeDirectory( |
| const std::string& dir, const std::string& config, |
| const std::string& language) const |
| { |
| assert(this->GetType() != cmStateEnums::INTERFACE_LIBRARY); |
| std::string config_upper; |
| if (!config.empty()) { |
| config_upper = cmSystemTools::UpperCase(config); |
| } |
| |
| typedef std::map<std::string, std::vector<std::string>> IncludeCacheType; |
| IncludeCacheType::const_iterator iter = |
| this->SystemIncludesCache.find(config_upper); |
| |
| if (iter == this->SystemIncludesCache.end()) { |
| cmGeneratorExpressionDAGChecker dagChecker( |
| this, "SYSTEM_INCLUDE_DIRECTORIES", nullptr, nullptr); |
| |
| bool excludeImported = this->GetPropertyAsBool("NO_SYSTEM_FROM_IMPORTED"); |
| |
| std::vector<std::string> result; |
| for (std::string const& it : this->Target->GetSystemIncludeDirectories()) { |
| cmGeneratorExpression ge; |
| cmSystemTools::ExpandListArgument( |
| ge.Parse(it)->Evaluate(this->LocalGenerator, config, false, this, |
| &dagChecker, language), |
| result); |
| } |
| |
| std::vector<cmGeneratorTarget const*> const& deps = |
| this->GetLinkImplementationClosure(config); |
| for (cmGeneratorTarget const* dep : deps) { |
| handleSystemIncludesDep(this->LocalGenerator, dep, config, this, |
| &dagChecker, result, excludeImported, language); |
| } |
| |
| std::for_each(result.begin(), result.end(), |
| cmSystemTools::ConvertToUnixSlashes); |
| std::sort(result.begin(), result.end()); |
| result.erase(std::unique(result.begin(), result.end()), result.end()); |
| |
| IncludeCacheType::value_type entry(config_upper, result); |
| iter = this->SystemIncludesCache.insert(entry).first; |
| } |
| |
| return std::binary_search(iter->second.begin(), iter->second.end(), dir); |
| } |
| |
| bool cmGeneratorTarget::GetPropertyAsBool(const std::string& prop) const |
| { |
| return this->Target->GetPropertyAsBool(prop); |
| } |
| |
| static void AddInterfaceEntries( |
| cmGeneratorTarget const* thisTarget, std::string const& config, |
| std::string const& prop, |
| std::vector<cmGeneratorTarget::TargetPropertyEntry*>& entries) |
| { |
| if (cmLinkImplementationLibraries const* impl = |
| thisTarget->GetLinkImplementationLibraries(config)) { |
| for (cmLinkImplItem const& lib : impl->Libraries) { |
| if (lib.Target) { |
| std::string uniqueName = |
| thisTarget->GetGlobalGenerator()->IndexGeneratorTargetUniquely( |
| lib.Target); |
| std::string genex = |
| "$<TARGET_PROPERTY:" + std::move(uniqueName) + "," + prop + ">"; |
| cmGeneratorExpression ge(lib.Backtrace); |
| std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(genex); |
| cge->SetEvaluateForBuildsystem(true); |
| entries.push_back( |
| new cmGeneratorTarget::TargetPropertyEntry(std::move(cge), lib)); |
| } |
| } |
| } |
| } |
| |
| static void AddObjectEntries( |
| cmGeneratorTarget const* thisTarget, std::string const& config, |
| std::vector<cmGeneratorTarget::TargetPropertyEntry*>& entries) |
| { |
| if (cmLinkImplementationLibraries const* impl = |
| thisTarget->GetLinkImplementationLibraries(config)) { |
| for (cmLinkImplItem const& lib : impl->Libraries) { |
| if (lib.Target && |
| lib.Target->GetType() == cmStateEnums::OBJECT_LIBRARY) { |
| std::string uniqueName = |
| thisTarget->GetGlobalGenerator()->IndexGeneratorTargetUniquely( |
| lib.Target); |
| std::string genex = "$<TARGET_OBJECTS:" + std::move(uniqueName) + ">"; |
| cmGeneratorExpression ge(lib.Backtrace); |
| std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(genex); |
| cge->SetEvaluateForBuildsystem(true); |
| entries.push_back( |
| new cmGeneratorTarget::TargetPropertyEntry(std::move(cge), lib)); |
| } |
| } |
| } |
| } |
| |
| static bool processSources( |
| cmGeneratorTarget const* tgt, |
| const std::vector<cmGeneratorTarget::TargetPropertyEntry*>& entries, |
| std::vector<std::string>& srcs, std::unordered_set<std::string>& uniqueSrcs, |
| cmGeneratorExpressionDAGChecker* dagChecker, std::string const& config, |
| bool debugSources) |
| { |
| cmMakefile* mf = tgt->Target->GetMakefile(); |
| |
| bool contextDependent = false; |
| |
| for (cmGeneratorTarget::TargetPropertyEntry* entry : entries) { |
| cmLinkImplItem const& item = entry->LinkImplItem; |
| std::string const& targetName = item.AsStr(); |
| std::vector<std::string> entrySources; |
| cmSystemTools::ExpandListArgument( |
| entry->ge->Evaluate(tgt->GetLocalGenerator(), config, false, tgt, tgt, |
| dagChecker), |
| entrySources); |
| |
| if (entry->ge->GetHadContextSensitiveCondition()) { |
| contextDependent = true; |
| } |
| |
| for (std::string& src : entrySources) { |
| cmSourceFile* sf = mf->GetOrCreateSource(src); |
| std::string e; |
| std::string fullPath = sf->GetFullPath(&e); |
| if (fullPath.empty()) { |
| if (!e.empty()) { |
| cmake* cm = tgt->GetLocalGenerator()->GetCMakeInstance(); |
| cm->IssueMessage(cmake::FATAL_ERROR, e, tgt->GetBacktrace()); |
| } |
| return contextDependent; |
| } |
| |
| if (!targetName.empty() && !cmSystemTools::FileIsFullPath(src)) { |
| std::ostringstream err; |
| if (!targetName.empty()) { |
| err << "Target \"" << targetName |
| << "\" contains relative path in its INTERFACE_SOURCES:\n \"" |
| << src << "\""; |
| } else { |
| err << "Found relative path while evaluating sources of \"" |
| << tgt->GetName() << "\":\n \"" << src << "\"\n"; |
| } |
| tgt->GetLocalGenerator()->IssueMessage(cmake::FATAL_ERROR, err.str()); |
| return contextDependent; |
| } |
| src = fullPath; |
| } |
| std::string usedSources; |
| for (std::string const& src : entrySources) { |
| if (uniqueSrcs.insert(src).second) { |
| srcs.push_back(src); |
| if (debugSources) { |
| usedSources += " * " + src + "\n"; |
| } |
| } |
| } |
| if (!usedSources.empty()) { |
| tgt->GetLocalGenerator()->GetCMakeInstance()->IssueMessage( |
| cmake::LOG, |
| std::string("Used sources for target ") + tgt->GetName() + ":\n" + |
| usedSources, |
| entry->ge->GetBacktrace()); |
| } |
| } |
| return contextDependent; |
| } |
| |
| void cmGeneratorTarget::GetSourceFiles(std::vector<std::string>& files, |
| const std::string& config) const |
| { |
| assert(this->GetType() != cmStateEnums::INTERFACE_LIBRARY); |
| |
| if (!this->LocalGenerator->GetGlobalGenerator()->GetConfigureDoneCMP0026()) { |
| // At configure-time, this method can be called as part of getting the |
| // LOCATION property or to export() a file to be include()d. However |
| // there is no cmGeneratorTarget at configure-time, so search the SOURCES |
| // for TARGET_OBJECTS instead for backwards compatibility with OLD |
| // behavior of CMP0024 and CMP0026 only. |
| |
| cmStringRange sourceEntries = this->Target->GetSourceEntries(); |
| for (std::string const& entry : sourceEntries) { |
| std::vector<std::string> items; |
| cmSystemTools::ExpandListArgument(entry, items); |
| for (std::string const& item : items) { |
| if (cmHasLiteralPrefix(item, "$<TARGET_OBJECTS:") && |
| item[item.size() - 1] == '>') { |
| continue; |
| } |
| files.push_back(item); |
| } |
| } |
| return; |
| } |
| |
| std::vector<std::string> debugProperties; |
| const char* debugProp = |
| this->Makefile->GetDefinition("CMAKE_DEBUG_TARGET_PROPERTIES"); |
| if (debugProp) { |
| cmSystemTools::ExpandListArgument(debugProp, debugProperties); |
| } |
| |
| bool debugSources = !this->DebugSourcesDone && |
| std::find(debugProperties.begin(), debugProperties.end(), "SOURCES") != |
| debugProperties.end(); |
| |
| if (this->LocalGenerator->GetGlobalGenerator()->GetConfigureDoneCMP0026()) { |
| this->DebugSourcesDone = true; |
| } |
| |
| cmGeneratorExpressionDAGChecker dagChecker(this, "SOURCES", nullptr, |
| nullptr); |
| |
| std::unordered_set<std::string> uniqueSrcs; |
| bool contextDependentDirectSources = |
| processSources(this, this->SourceEntries, files, uniqueSrcs, &dagChecker, |
| config, debugSources); |
| |
| // Collect INTERFACE_SOURCES of all direct link-dependencies. |
| std::vector<cmGeneratorTarget::TargetPropertyEntry*> |
| linkInterfaceSourcesEntries; |
| AddInterfaceEntries(this, config, "INTERFACE_SOURCES", |
| linkInterfaceSourcesEntries); |
| std::vector<std::string>::size_type numFilesBefore = files.size(); |
| bool contextDependentInterfaceSources = |
| processSources(this, linkInterfaceSourcesEntries, files, uniqueSrcs, |
| &dagChecker, config, debugSources); |
| |
| // Collect TARGET_OBJECTS of direct object link-dependencies. |
| std::vector<cmGeneratorTarget::TargetPropertyEntry*> linkObjectsEntries; |
| AddObjectEntries(this, config, linkObjectsEntries); |
| std::vector<std::string>::size_type numFilesBefore2 = files.size(); |
| bool contextDependentObjects = |
| processSources(this, linkObjectsEntries, files, uniqueSrcs, &dagChecker, |
| config, debugSources); |
| |
| if (!contextDependentDirectSources && |
| !(contextDependentInterfaceSources && numFilesBefore < files.size()) && |
| !(contextDependentObjects && numFilesBefore2 < files.size())) { |
| this->LinkImplementationLanguageIsContextDependent = false; |
| } |
| |
| cmDeleteAll(linkInterfaceSourcesEntries); |
| cmDeleteAll(linkObjectsEntries); |
| } |
| |
| void cmGeneratorTarget::GetSourceFiles(std::vector<cmSourceFile*>& files, |
| const std::string& config) const |
| { |
| if (!this->GlobalGenerator->GetConfigureDoneCMP0026()) { |
| // Since we are still configuring not all sources may exist yet, |
| // so we need to avoid full source classification because that |
| // requires the absolute paths to all sources to be determined. |
| // Since this is only for compatibility with old policies that |
| // projects should not depend on anymore, just compute the files |
| // without memoizing them. |
| std::vector<std::string> srcs; |
| this->GetSourceFiles(srcs, config); |
| std::set<cmSourceFile*> emitted; |
| for (std::string const& s : srcs) { |
| cmSourceFile* sf = this->Makefile->GetOrCreateSource(s); |
| if (emitted.insert(sf).second) { |
| files.push_back(sf); |
| } |
| } |
| return; |
| } |
| |
| KindedSources const& kinded = this->GetKindedSources(config); |
| files.reserve(kinded.Sources.size()); |
| for (SourceAndKind const& si : kinded.Sources) { |
| files.push_back(si.Source); |
| } |
| } |
| |
| void cmGeneratorTarget::GetSourceFilesWithoutObjectLibraries( |
| std::vector<cmSourceFile*>& files, const std::string& config) const |
| { |
| KindedSources const& kinded = this->GetKindedSources(config); |
| files.reserve(kinded.Sources.size()); |
| for (SourceAndKind const& si : kinded.Sources) { |
| if (si.Source->GetObjectLibrary().empty()) { |
| files.push_back(si.Source); |
| } |
| } |
| } |
| |
| cmGeneratorTarget::KindedSources const& cmGeneratorTarget::GetKindedSources( |
| std::string const& config) const |
| { |
| // If we already processed one configuration and found no dependenc |
| // on configuration then always use the one result. |
| if (!this->LinkImplementationLanguageIsContextDependent) { |
| return this->KindedSourcesMap.begin()->second; |
| } |
| |
| // Lookup any existing link implementation for this configuration. |
| std::string const key = cmSystemTools::UpperCase(config); |
| KindedSourcesMapType::iterator it = this->KindedSourcesMap.find(key); |
| if (it != this->KindedSourcesMap.end()) { |
| if (!it->second.Initialized) { |
| std::ostringstream e; |
| e << "The SOURCES of \"" << this->GetName() |
| << "\" use a generator expression that depends on the " |
| "SOURCES themselves."; |
| this->GlobalGenerator->GetCMakeInstance()->IssueMessage( |
| cmake::FATAL_ERROR, e.str(), this->GetBacktrace()); |
| static KindedSources empty; |
| return empty; |
| } |
| return it->second; |
| } |
| |
| // Add an entry to the map for this configuration. |
| KindedSources& files = this->KindedSourcesMap[key]; |
| this->ComputeKindedSources(files, config); |
| files.Initialized = true; |
| return files; |
| } |
| |
| void cmGeneratorTarget::ComputeKindedSources(KindedSources& files, |
| std::string const& config) const |
| { |
| // Get the source file paths by string. |
| std::vector<std::string> srcs; |
| this->GetSourceFiles(srcs, config); |
| |
| cmsys::RegularExpression header_regex(CM_HEADER_REGEX); |
| std::vector<cmSourceFile*> badObjLib; |
| |
| std::set<cmSourceFile*> emitted; |
| for (std::string const& s : srcs) { |
| // Create each source at most once. |
| cmSourceFile* sf = this->Makefile->GetOrCreateSource(s); |
| if (!emitted.insert(sf).second) { |
| continue; |
| } |
| |
| // Compute the kind (classification) of this source file. |
| SourceKind kind; |
| std::string ext = cmSystemTools::LowerCase(sf->GetExtension()); |
| if (sf->GetCustomCommand()) { |
| kind = SourceKindCustomCommand; |
| } else if (this->Target->GetType() == cmStateEnums::UTILITY) { |
| kind = SourceKindExtra; |
| } else if (sf->GetPropertyAsBool("HEADER_FILE_ONLY")) { |
| kind = SourceKindHeader; |
| } else if (sf->GetPropertyAsBool("EXTERNAL_OBJECT")) { |
| kind = SourceKindExternalObject; |
| } else if (!sf->GetLanguage().empty()) { |
| kind = SourceKindObjectSource; |
| } else if (ext == "def") { |
| kind = SourceKindModuleDefinition; |
| if (this->GetType() == cmStateEnums::OBJECT_LIBRARY) { |
| badObjLib.push_back(sf); |
| } |
| } else if (ext == "idl") { |
| kind = SourceKindIDL; |
| if (this->GetType() == cmStateEnums::OBJECT_LIBRARY) { |
| badObjLib.push_back(sf); |
| } |
| } else if (ext == "resx") { |
| kind = SourceKindResx; |
| // Build and save the name of the corresponding .h file |
| // This relationship will be used later when building the project files. |
| // Both names would have been auto generated from Visual Studio |
| // where the user supplied the file name and Visual Studio |
| // appended the suffix. |
| std::string resx = sf->GetFullPath(); |
| std::string hFileName = resx.substr(0, resx.find_last_of('.')) + ".h"; |
| files.ExpectedResxHeaders.insert(hFileName); |
| } else if (ext == "appxmanifest") { |
| kind = SourceKindAppManifest; |
| } else if (ext == "manifest") { |
| kind = SourceKindManifest; |
| } else if (ext == "pfx") { |
| kind = SourceKindCertificate; |
| } else if (ext == "xaml") { |
| kind = SourceKindXaml; |
| // Build and save the name of the corresponding .h and .cpp file |
| // This relationship will be used later when building the project files. |
| // Both names would have been auto generated from Visual Studio |
| // where the user supplied the file name and Visual Studio |
| // appended the suffix. |
| std::string xaml = sf->GetFullPath(); |
| std::string hFileName = xaml + ".h"; |
| std::string cppFileName = xaml + ".cpp"; |
| files.ExpectedXamlHeaders.insert(hFileName); |
| files.ExpectedXamlSources.insert(cppFileName); |
| } else if (header_regex.find(sf->GetFullPath())) { |
| kind = SourceKindHeader; |
| } else { |
| kind = SourceKindExtra; |
| } |
| |
| // Save this classified source file in the result vector. |
| files.Sources.push_back({ sf, kind }); |
| } |
| |
| if (!badObjLib.empty()) { |
| std::ostringstream e; |
| e << "OBJECT library \"" << this->GetName() << "\" contains:\n"; |
| for (cmSourceFile* i : badObjLib) { |
| e << " " << i->GetLocation().GetName() << "\n"; |
| } |
| e << "but may contain only sources that compile, header files, and " |
| "other files that would not affect linking of a normal library."; |
| this->GlobalGenerator->GetCMakeInstance()->IssueMessage( |
| cmake::FATAL_ERROR, e.str(), this->GetBacktrace()); |
| } |
| } |
| |
| std::vector<cmGeneratorTarget::AllConfigSource> const& |
| cmGeneratorTarget::GetAllConfigSources() const |
| { |
| if (this->AllConfigSources.empty()) { |
| this->ComputeAllConfigSources(); |
| } |
| return this->AllConfigSources; |
| } |
| |
| void cmGeneratorTarget::ComputeAllConfigSources() const |
| { |
| std::vector<std::string> configs; |
| this->Makefile->GetConfigurations(configs); |
| |
| std::map<cmSourceFile const*, size_t> index; |
| |
| for (size_t ci = 0; ci < configs.size(); ++ci) { |
| KindedSources const& sources = this->GetKindedSources(configs[ci]); |
| for (SourceAndKind const& src : sources.Sources) { |
| std::map<cmSourceFile const*, size_t>::iterator mi = |
| index.find(src.Source); |
| if (mi == index.end()) { |
| AllConfigSource acs; |
| acs.Source = src.Source; |
| acs.Kind = src.Kind; |
| this->AllConfigSources.push_back(std::move(acs)); |
| std::map<cmSourceFile const*, size_t>::value_type entry( |
| src.Source, this->AllConfigSources.size() - 1); |
| mi = index.insert(entry).first; |
| } |
| this->AllConfigSources[mi->second].Configs.push_back(ci); |
| } |
| } |
| } |
| |
| std::string cmGeneratorTarget::GetCompilePDBName( |
| const std::string& config) const |
| { |
| std::string prefix; |
| std::string base; |
| std::string suffix; |
| this->GetFullNameInternal(config, cmStateEnums::RuntimeBinaryArtifact, |
| prefix, base, suffix); |
| |
| // Check for a per-configuration output directory target property. |
| std::string configUpper = cmSystemTools::UpperCase(config); |
| std::string configProp = "COMPILE_PDB_NAME_"; |
| configProp += configUpper; |
| const char* config_name = this->GetProperty(configProp); |
| if (config_name && *config_name) { |
| return prefix + config_name + ".pdb"; |
| } |
| |
| const char* name = this->GetProperty("COMPILE_PDB_NAME"); |
| if (name && *name) { |
| return prefix + name + ".pdb"; |
| } |
| |
| return ""; |
| } |
| |
| std::string cmGeneratorTarget::GetCompilePDBPath( |
| const std::string& config) const |
| { |
| std::string dir = this->GetCompilePDBDirectory(config); |
| std::string name = this->GetCompilePDBName(config); |
| if (dir.empty() && !name.empty() && this->HaveWellDefinedOutputFiles()) { |
| dir = this->GetPDBDirectory(config); |
| } |
| if (!dir.empty()) { |
| dir += "/"; |
| } |
| return dir + name; |
| } |
| |
| bool cmGeneratorTarget::HasSOName(const std::string& config) const |
| { |
| // soname is supported only for shared libraries and modules, |
| // and then only when the platform supports an soname flag. |
| return ((this->GetType() == cmStateEnums::SHARED_LIBRARY) && |
| !this->GetPropertyAsBool("NO_SONAME") && |
| this->Makefile->GetSONameFlag(this->GetLinkerLanguage(config))); |
| } |
| |
| bool cmGeneratorTarget::NeedRelinkBeforeInstall( |
| const std::string& config) const |
| { |
| // Only executables and shared libraries can have an rpath and may |
| // need relinking. |
| if (this->GetType() != cmStateEnums::EXECUTABLE && |
| this->GetType() != cmStateEnums::SHARED_LIBRARY && |
| this->GetType() != cmStateEnums::MODULE_LIBRARY) { |
| return false; |
| } |
| |
| // If there is no install location this target will not be installed |
| // and therefore does not need relinking. |
| if (!this->Target->GetHaveInstallRule()) { |
| return false; |
| } |
| |
| // If skipping all rpaths completely then no relinking is needed. |
| if (this->Makefile->IsOn("CMAKE_SKIP_RPATH")) { |
| return false; |
| } |
| |
| // If building with the install-tree rpath no relinking is needed. |
| if (this->GetPropertyAsBool("BUILD_WITH_INSTALL_RPATH")) { |
| return false; |
| } |
| |
| // If chrpath is going to be used no relinking is needed. |
| if (this->IsChrpathUsed(config)) { |
| return false; |
| } |
| |
| // Check for rpath support on this platform. |
| std::string ll = this->GetLinkerLanguage(config); |
| if (!ll.empty()) { |
| std::string flagVar = "CMAKE_SHARED_LIBRARY_RUNTIME_"; |
| flagVar += ll; |
| flagVar += "_FLAG"; |
| if (!this->Makefile->IsSet(flagVar)) { |
| // There is no rpath support on this platform so nothing needs |
| // relinking. |
| return false; |
| } |
| } else { |
| // No linker language is known. This error will be reported by |
| // other code. |
| return false; |
| } |
| |
| // If either a build or install tree rpath is set then the rpath |
| // will likely change between the build tree and install tree and |
| // this target must be relinked. |
| bool have_rpath = |
| this->HaveBuildTreeRPATH(config) || this->HaveInstallTreeRPATH(); |
| bool is_ninja = |
| this->LocalGenerator->GetGlobalGenerator()->GetName() == "Ninja"; |
| |
| if (have_rpath && is_ninja) { |
| std::ostringstream w; |
| /* clang-format off */ |
| w << |
| "The install of the " << this->GetName() << " target requires " |
| "changing an RPATH from the build tree, but this is not supported " |
| "with the Ninja generator unless on an ELF-based platform. The " |
| "CMAKE_BUILD_WITH_INSTALL_RPATH variable may be set to avoid this " |
| "relinking step." |
| ; |
| /* clang-format on */ |
| |
| cmake* cm = this->LocalGenerator->GetCMakeInstance(); |
| cm->IssueMessage(cmake::FATAL_ERROR, w.str(), this->GetBacktrace()); |
| } |
| |
| return have_rpath; |
| } |
| |
| bool cmGeneratorTarget::IsChrpathUsed(const std::string& config) const |
| { |
| // Only certain target types have an rpath. |
| if (!(this->GetType() == cmStateEnums::SHARED_LIBRARY || |
| this->GetType() == cmStateEnums::MODULE_LIBRARY || |
| this->GetType() == cmStateEnums::EXECUTABLE)) { |
| return false; |
| } |
| |
| // If the target will not be installed we do not need to change its |
| // rpath. |
| if (!this->Target->GetHaveInstallRule()) { |
| return false; |
| } |
| |
| // Skip chrpath if skipping rpath altogether. |
| if (this->Makefile->IsOn("CMAKE_SKIP_RPATH")) { |
| return false; |
| } |
| |
| // Skip chrpath if it does not need to be changed at install time. |
| if (this->GetPropertyAsBool("BUILD_WITH_INSTALL_RPATH")) { |
| return false; |
| } |
| |
| // Allow the user to disable builtin chrpath explicitly. |
| if (this->Makefile->IsOn("CMAKE_NO_BUILTIN_CHRPATH")) { |
| return false; |
| } |
| |
| if (this->Makefile->IsOn("CMAKE_PLATFORM_HAS_INSTALLNAME")) { |
| return true; |
| } |
| |
| #if defined(CMAKE_USE_ELF_PARSER) |
| // Enable if the rpath flag uses a separator and the target uses ELF |
| // binaries. |
| std::string ll = this->GetLinkerLanguage(config); |
| if (!ll.empty()) { |
| std::string sepVar = "CMAKE_SHARED_LIBRARY_RUNTIME_"; |
| sepVar += ll; |
| sepVar += "_FLAG_SEP"; |
| const char* sep = this->Makefile->GetDefinition(sepVar); |
| if (sep && *sep) { |
| // TODO: Add ELF check to ABI detection and get rid of |
| // CMAKE_EXECUTABLE_FORMAT. |
| if (const char* fmt = |
| this->Makefile->GetDefinition("CMAKE_EXECUTABLE_FORMAT")) { |
| return strcmp(fmt, "ELF") == 0; |
| } |
| } |
| } |
| #endif |
| static_cast<void>(config); |
| return false; |
| } |
| |
| bool cmGeneratorTarget::IsImportedSharedLibWithoutSOName( |
| const std::string& config) const |
| { |
| if (this->IsImported() && this->GetType() == cmStateEnums::SHARED_LIBRARY) { |
| if (cmGeneratorTarget::ImportInfo const* info = |
| this->GetImportInfo(config)) { |
| return info->NoSOName; |
| } |
| } |
| return false; |
| } |
| |
| bool cmGeneratorTarget::HasMacOSXRpathInstallNameDir( |
| const std::string& config) const |
| { |
| bool install_name_is_rpath = false; |
| bool macosx_rpath = false; |
| |
| if (!this->IsImported()) { |
| if (this->GetType() != cmStateEnums::SHARED_LIBRARY) { |
| return false; |
| } |
| const char* install_name = this->GetProperty("INSTALL_NAME_DIR"); |
| bool use_install_name = this->MacOSXUseInstallNameDir(); |
| if (install_name && use_install_name && |
| std::string(install_name) == "@rpath") { |
| install_name_is_rpath = true; |
| } else if (install_name && use_install_name) { |
| return false; |
| } |
| if (!install_name_is_rpath) { |
| macosx_rpath = this->MacOSXRpathInstallNameDirDefault(); |
| } |
| } else { |
| // Lookup the imported soname. |
| if (cmGeneratorTarget::ImportInfo const* info = |
| this->GetImportInfo(config)) { |
| if (!info->NoSOName && !info->SOName.empty()) { |
| if (info->SOName.find("@rpath/") == 0) { |
| install_name_is_rpath = true; |
| } |
| } else { |
| std::string install_name; |
| cmSystemTools::GuessLibraryInstallName(info->Location, install_name); |
| if (install_name.find("@rpath") != std::string::npos) { |
| install_name_is_rpath = true; |
| } |
| } |
| } |
| } |
| |
| if (!install_name_is_rpath && !macosx_rpath) { |
| return false; |
| } |
| |
| if (!this->Makefile->IsSet("CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG")) { |
| std::ostringstream w; |
| w << "Attempting to use "; |
| if (macosx_rpath) { |
| w << "MACOSX_RPATH"; |
| } else { |
| w << "@rpath"; |
| } |
| w << " without CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG being set."; |
| w << " This could be because you are using a Mac OS X version"; |
| w << " less than 10.5 or because CMake's platform configuration is"; |
| w << " corrupt."; |
| cmake* cm = this->LocalGenerator->GetCMakeInstance(); |
| cm->IssueMessage(cmake::FATAL_ERROR, w.str(), this->GetBacktrace()); |
| } |
| |
| return true; |
| } |
| |
| bool cmGeneratorTarget::MacOSXRpathInstallNameDirDefault() const |
| { |
| // we can't do rpaths when unsupported |
| if (!this->Makefile->IsSet("CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG")) { |
| return false; |
| } |
| |
| const char* macosx_rpath_str = this->GetProperty("MACOSX_RPATH"); |
| if (macosx_rpath_str) { |
| return this->GetPropertyAsBool("MACOSX_RPATH"); |
| } |
| |
| cmPolicies::PolicyStatus cmp0042 = this->GetPolicyStatusCMP0042(); |
| |
| if (cmp0042 == cmPolicies::WARN) { |
| this->LocalGenerator->GetGlobalGenerator()->AddCMP0042WarnTarget( |
| this->GetName()); |
| } |
| |
| return cmp0042 == cmPolicies::NEW; |
| } |
| |
| bool cmGeneratorTarget::MacOSXUseInstallNameDir() const |
| { |
| const char* build_with_install_name = |
| this->GetProperty("BUILD_WITH_INSTALL_NAME_DIR"); |
| if (build_with_install_name) { |
| return cmSystemTools::IsOn(build_with_install_name); |
| } |
| |
| cmPolicies::PolicyStatus cmp0068 = this->GetPolicyStatusCMP0068(); |
| if (cmp0068 == cmPolicies::NEW) { |
| return false; |
| } |
| |
| bool use_install_name = this->GetPropertyAsBool("BUILD_WITH_INSTALL_RPATH"); |
| |
| if (use_install_name && cmp0068 == cmPolicies::WARN) { |
| this->LocalGenerator->GetGlobalGenerator()->AddCMP0068WarnTarget( |
| this->GetName()); |
| } |
| |
| return use_install_name; |
| } |
| |
| bool cmGeneratorTarget::CanGenerateInstallNameDir( |
| InstallNameType name_type) const |
| { |
| cmPolicies::PolicyStatus cmp0068 = this->GetPolicyStatusCMP0068(); |
| |
| if (cmp0068 == cmPolicies::NEW) { |
| return true; |
| } |
| |
| bool skip = this->Makefile->IsOn("CMAKE_SKIP_RPATH"); |
| if (name_type == INSTALL_NAME_FOR_INSTALL) { |
| skip |= this->Makefile->IsOn("CMAKE_SKIP_INSTALL_RPATH"); |
| } else { |
| skip |= this->GetPropertyAsBool("SKIP_BUILD_RPATH"); |
| } |
| |
| if (skip && cmp0068 == cmPolicies::WARN) { |
| this->LocalGenerator->GetGlobalGenerator()->AddCMP0068WarnTarget( |
| this->GetName()); |
| } |
| |
| return !skip; |
| } |
| |
| std::string cmGeneratorTarget::GetSOName(const std::string& config) const |
| { |
| if (this->IsImported()) { |
| // Lookup the imported soname. |
| if (cmGeneratorTarget::ImportInfo const* info = |
| this->GetImportInfo(config)) { |
| if (info->NoSOName) { |
| // The imported library has no builtin soname so the name |
| // searched at runtime will be just the filename. |
| return cmSystemTools::GetFilenameName(info->Location); |
| } |
| // Use the soname given if any. |
| if (info->SOName.find("@rpath/") == 0) { |
| return info->SOName.substr(6); |
| } |
| return info->SOName; |
| } |
| return ""; |
| } |
| // Compute the soname that will be built. |
| std::string name; |
| std::string soName; |
| std::string realName; |
| std::string impName; |
| std::string pdbName; |
| this->GetLibraryNames(name, soName, realName, impName, pdbName, config); |
| return soName; |
| } |
| |
| static bool shouldAddFullLevel(cmGeneratorTarget::BundleDirectoryLevel level) |
| { |
| return level == cmGeneratorTarget::FullLevel; |
| } |
| |
| static bool shouldAddContentLevel( |
| cmGeneratorTarget::BundleDirectoryLevel level) |
| { |
| return level == cmGeneratorTarget::ContentLevel || shouldAddFullLevel(level); |
| } |
| |
| std::string cmGeneratorTarget::GetAppBundleDirectory( |
| const std::string& config, BundleDirectoryLevel level) const |
| { |
| std::string fpath = |
| this->GetFullName(config, cmStateEnums::RuntimeBinaryArtifact); |
| fpath += "."; |
| const char* ext = this->GetProperty("BUNDLE_EXTENSION"); |
| if (!ext) { |
| ext = "app"; |
| } |
| fpath += ext; |
| if (shouldAddContentLevel(level) && |
| !this->Makefile->PlatformIsAppleEmbedded()) { |
| fpath += "/Contents"; |
| if (shouldAddFullLevel(level)) { |
| fpath += "/MacOS"; |
| } |
| } |
| return fpath; |
| } |
| |
| bool cmGeneratorTarget::IsBundleOnApple() const |
| { |
| return this->IsFrameworkOnApple() || this->IsAppBundleOnApple() || |
| this->IsCFBundleOnApple(); |
| } |
| |
| std::string cmGeneratorTarget::GetCFBundleDirectory( |
| const std::string& config, BundleDirectoryLevel level) const |
| { |
| std::string fpath; |
| fpath += this->GetOutputName(config, cmStateEnums::RuntimeBinaryArtifact); |
| fpath += "."; |
| const char* ext = this->GetProperty("BUNDLE_EXTENSION"); |
| if (!ext) { |
| if (this->IsXCTestOnApple()) { |
| ext = "xctest"; |
| } else { |
| ext = "bundle"; |
| } |
| } |
| fpath += ext; |
| if (shouldAddContentLevel(level) && |
| !this->Makefile->PlatformIsAppleEmbedded()) { |
| fpath += "/Contents"; |
| if (shouldAddFullLevel(level)) { |
| fpath += "/MacOS"; |
| } |
| } |
| return fpath; |
| } |
| |
| std::string cmGeneratorTarget::GetFrameworkDirectory( |
| const std::string& config, BundleDirectoryLevel level) const |
| { |
| std::string fpath; |
| fpath += this->GetOutputName(config, cmStateEnums::RuntimeBinaryArtifact); |
| fpath += "."; |
| const char* ext = this->GetProperty("BUNDLE_EXTENSION"); |
| if (!ext) { |
| ext = "framework"; |
| } |
| fpath += ext; |
| if (shouldAddFullLevel(level) && |
| !this->Makefile->PlatformIsAppleEmbedded()) { |
| fpath += "/Versions/"; |
| fpath += this->GetFrameworkVersion(); |
| } |
| return fpath; |
| } |
| |
| std::string cmGeneratorTarget::GetFullName( |
| const std::string& config, cmStateEnums::ArtifactType artifact) const |
| { |
| if (this->IsImported()) { |
| return this->GetFullNameImported(config, artifact); |
| } |
| return this->GetFullNameInternal(config, artifact); |
| } |
| |
| std::string cmGeneratorTarget::GetInstallNameDirForBuildTree( |
| const std::string& config) const |
| { |
| if (this->Makefile->IsOn("CMAKE_PLATFORM_HAS_INSTALLNAME")) { |
| |
| // If building directly for installation then the build tree install_name |
| // is the same as the install tree. |
| if (this->MacOSXUseInstallNameDir()) { |
| return this->GetInstallNameDirForInstallTree(); |
| } |
| |
| // Use the build tree directory for the target. |
| if (this->CanGenerateInstallNameDir(INSTALL_NAME_FOR_BUILD)) { |
| std::string dir; |
| if (this->MacOSXRpathInstallNameDirDefault()) { |
| dir = "@rpath"; |
| } else { |
| dir = this->GetDirectory(config); |
| } |
| dir += "/"; |
| return dir; |
| } |
| } |
| return ""; |
| } |
| |
| std::string cmGeneratorTarget::GetInstallNameDirForInstallTree() const |
| { |
| if (this->Makefile->IsOn("CMAKE_PLATFORM_HAS_INSTALLNAME")) { |
| std::string dir; |
| const char* install_name_dir = this->GetProperty("INSTALL_NAME_DIR"); |
| |
| if (this->CanGenerateInstallNameDir(INSTALL_NAME_FOR_INSTALL)) { |
| if (install_name_dir && *install_name_dir) { |
| dir = install_name_dir; |
| dir += "/"; |
| } |
| } |
| if (!install_name_dir) { |
| if (this->MacOSXRpathInstallNameDirDefault()) { |
| dir = "@rpath/"; |
| } |
| } |
| return dir; |
| } |
| return ""; |
| } |
| |
| cmListFileBacktrace cmGeneratorTarget::GetBacktrace() const |
| { |
| return this->Target->GetBacktrace(); |
| } |
| |
| const std::set<std::string>& cmGeneratorTarget::GetUtilities() const |
| { |
| return this->Target->GetUtilities(); |
| } |
| |
| const cmListFileBacktrace* cmGeneratorTarget::GetUtilityBacktrace( |
| const std::string& u) const |
| { |
| return this->Target->GetUtilityBacktrace(u); |
| } |
| |
| bool cmGeneratorTarget::HaveWellDefinedOutputFiles() const |
| { |
| return this->GetType() == cmStateEnums::STATIC_LIBRARY || |
| this->GetType() == cmStateEnums::SHARED_LIBRARY || |
| this->GetType() == cmStateEnums::MODULE_LIBRARY || |
| this->GetType() == cmStateEnums::OBJECT_LIBRARY || |
| this->GetType() == cmStateEnums::EXECUTABLE; |
| } |
| |
| const char* cmGeneratorTarget::GetExportMacro() const |
| { |
| // Define the symbol for targets that export symbols. |
| if (this->GetType() == cmStateEnums::SHARED_LIBRARY || |
| this->GetType() == cmStateEnums::MODULE_LIBRARY || |
| this->IsExecutableWithExports()) { |
| if (const char* custom_export_name = this->GetProperty("DEFINE_SYMBOL")) { |
| this->ExportMacro = custom_export_name; |
| } else { |
| std::string in = this->GetName(); |
| in += "_EXPORTS"; |
| this->ExportMacro = cmSystemTools::MakeCidentifier(in); |
| } |
| return this->ExportMacro.c_str(); |
| } |
| return nullptr; |
| } |
| |
| class cmTargetCollectLinkLanguages |
| { |
| public: |
| cmTargetCollectLinkLanguages(cmGeneratorTarget const* target, |
| const std::string& config, |
| std::unordered_set<std::string>& languages, |
| cmGeneratorTarget const* head) |
| : Config(config) |
| , Languages(languages) |
| , HeadTarget(head) |
| , Target(target) |
| { |
| this->Visited.insert(target); |
| } |
| |
| void Visit(cmLinkItem const& item) |
| { |
| if (!item.Target) { |
| if (item.AsStr().find("::") != std::string::npos) { |
| bool noMessage = false; |
| cmake::MessageType messageType = cmake::FATAL_ERROR; |
| std::ostringstream e; |
| switch (this->Target->GetLocalGenerator()->GetPolicyStatus( |
| cmPolicies::CMP0028)) { |
| case cmPolicies::WARN: { |
| e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0028) << "\n"; |
| messageType = cmake::AUTHOR_WARNING; |
| } break; |
| case cmPolicies::OLD: |
| noMessage = true; |
| case cmPolicies::REQUIRED_IF_USED: |
| case cmPolicies::REQUIRED_ALWAYS: |
| case cmPolicies::NEW: |
| // Issue the fatal message. |
| break; |
| } |
| |
| if (!noMessage) { |
| e << "Target \"" << this->Target->GetName() |
| << "\" links to target \"" << item.AsStr() |
| << "\" but the target was not found. Perhaps a find_package() " |
| "call is missing for an IMPORTED target, or an ALIAS target is " |
| "missing?"; |
| this->Target->GetLocalGenerator()->GetCMakeInstance()->IssueMessage( |
| messageType, e.str(), this->Target->GetBacktrace()); |
| } |
| } |
| return; |
| } |
| if (!this->Visited.insert(item.Target).second) { |
| return; |
| } |
| cmLinkInterface const* iface = |
| item.Target->GetLinkInterface(this->Config, this->HeadTarget); |
| if (!iface) { |
| return; |
| } |
| |
| for (std::string const& language : iface->Languages) { |
| this->Languages.insert(language); |
| } |
| |
| for (cmLinkItem const& lib : iface->Libraries) { |
| this->Visit(lib); |
| } |
| } |
| |
| private: |
| std::string Config; |
| std::unordered_set<std::string>& Languages; |
| cmGeneratorTarget const* HeadTarget; |
| const cmGeneratorTarget* Target; |
| std::set<cmGeneratorTarget const*> Visited; |
| }; |
| |
| cmGeneratorTarget::LinkClosure const* cmGeneratorTarget::GetLinkClosure( |
| const std::string& config) const |
| { |
| std::string key(cmSystemTools::UpperCase(config)); |
| LinkClosureMapType::iterator i = this->LinkClosureMap.find(key); |
| if (i == this->LinkClosureMap.end()) { |
| LinkClosure lc; |
| this->ComputeLinkClosure(config, lc); |
| LinkClosureMapType::value_type entry(key, lc); |
| i = this->LinkClosureMap.insert(entry).first; |
| } |
| return &i->second; |
| } |
| |
| class cmTargetSelectLinker |
| { |
| int Preference; |
| cmGeneratorTarget const* Target; |
| cmGlobalGenerator* GG; |
| std::set<std::string> Preferred; |
| |
| public: |
| cmTargetSelectLinker(cmGeneratorTarget const* target) |
| : Preference(0) |
| , Target(target) |
| { |
| this->GG = this->Target->GetLocalGenerator()->GetGlobalGenerator(); |
| } |
| void Consider(const char* lang) |
| { |
| int preference = this->GG->GetLinkerPreference(lang); |
| if (preference > this->Preference) { |
| this->Preference = preference; |
| this->Preferred.clear(); |
| } |
| if (preference == this->Preference) { |
| this->Preferred.insert(lang); |
| } |
| } |
| std::string Choose() |
| { |
| if (this->Preferred.empty()) { |
| return ""; |
| } |
| if (this->Preferred.size() > 1) { |
| std::ostringstream e; |
| e << "Target " << this->Target->GetName() |
| << " contains multiple languages with the highest linker preference" |
| << " (" << this->Preference << "):\n"; |
| for (std::string const& li : this->Preferred) { |
| e << " " << li << "\n"; |
| } |
| e << "Set the LINKER_LANGUAGE property for this target."; |
| cmake* cm = this->Target->GetLocalGenerator()->GetCMakeInstance(); |
| cm->IssueMessage(cmake::FATAL_ERROR, e.str(), |
| this->Target->GetBacktrace()); |
| } |
| return *this->Preferred.begin(); |
| } |
| }; |
| |
| void cmGeneratorTarget::ComputeLinkClosure(const std::string& config, |
| LinkClosure& lc) const |
| { |
| // Get languages built in this target. |
| std::unordered_set<std::string> languages; |
| cmLinkImplementation const* impl = this->GetLinkImplementation(config); |
| assert(impl); |
| for (std::string const& li : impl->Languages) { |
| languages.insert(li); |
| } |
| |
| // Add interface languages from linked targets. |
| cmTargetCollectLinkLanguages cll(this, config, languages, this); |
| for (cmLinkImplItem const& lib : impl->Libraries) { |
| cll.Visit(lib); |
| } |
| |
| // Store the transitive closure of languages. |
| for (std::string const& lang : languages) { |
| lc.Languages.push_back(lang); |
| } |
| |
| // Choose the language whose linker should be used. |
| if (this->GetProperty("HAS_CXX")) { |
| lc.LinkerLanguage = "CXX"; |
| } else if (const char* linkerLang = this->GetProperty("LINKER_LANGUAGE")) { |
| lc.LinkerLanguage = linkerLang; |
| } else { |
| // Find the language with the highest preference value. |
| cmTargetSelectLinker tsl(this); |
| |
| // First select from the languages compiled directly in this target. |
| for (std::string const& l : impl->Languages) { |
| tsl.Consider(l.c_str()); |
| } |
| |
| // Now consider languages that propagate from linked targets. |
| for (std::string const& lang : languages) { |
| std::string propagates = |
| "CMAKE_" + lang + "_LINKER_PREFERENCE_PROPAGATES"; |
| if (this->Makefile->IsOn(propagates)) { |
| tsl.Consider(lang.c_str()); |
| } |
| } |
| |
| lc.LinkerLanguage = tsl.Choose(); |
| } |
| } |
| |
| void cmGeneratorTarget::GetFullNameComponents( |
| std::string& prefix, std::string& base, std::string& suffix, |
| const std::string& config, cmStateEnums::ArtifactType artifact) const |
| { |
| this->GetFullNameInternal(config, artifact, prefix, base, suffix); |
| } |
| |
| std::string cmGeneratorTarget::BuildBundleDirectory( |
| const std::string& base, const std::string& config, |
| BundleDirectoryLevel level) const |
| { |
| std::string fpath = base; |
| if (this->IsAppBundleOnApple()) { |
| fpath += this->GetAppBundleDirectory(config, level); |
| } |
| if (this->IsFrameworkOnApple()) { |
| fpath += this->GetFrameworkDirectory(config, level); |
| } |
| if (this->IsCFBundleOnApple()) { |
| fpath += this->GetCFBundleDirectory(config, level); |
| } |
| return fpath; |
| } |
| |
| std::string cmGeneratorTarget::GetMacContentDirectory( |
| const std::string& config, cmStateEnums::ArtifactType artifact) const |
| { |
| // Start with the output directory for the target. |
| std::string fpath = this->GetDirectory(config, artifact); |
| fpath += "/"; |
| BundleDirectoryLevel level = ContentLevel; |
| if (this->IsFrameworkOnApple()) { |
| // additional files with a framework go into the version specific |
| // directory |
| level = FullLevel; |
| } |
| fpath = this->BuildBundleDirectory(fpath, config, level); |
| return fpath; |
| } |
| |
| std::string cmGeneratorTarget::GetEffectiveFolderName() const |
| { |
| std::string effectiveFolder; |
| |
| if (!this->GlobalGenerator->UseFolderProperty()) { |
| return effectiveFolder; |
| } |
| |
| const char* targetFolder = this->GetProperty("FOLDER"); |
| if (targetFolder) { |
| effectiveFolder += targetFolder; |
| } |
| |
| return effectiveFolder; |
| } |
| |
| cmGeneratorTarget::CompileInfo const* cmGeneratorTarget::GetCompileInfo( |
| const std::string& config) const |
| { |
| // There is no compile information for imported targets. |
| if (this->IsImported()) { |
| return nullptr; |
| } |
| |
| if (this->GetType() > cmStateEnums::OBJECT_LIBRARY) { |
| std::string msg = "cmTarget::GetCompileInfo called for "; |
| msg += this->GetName(); |
| msg += " which has type "; |
| msg += cmState::GetTargetTypeName(this->GetType()); |
| this->LocalGenerator->IssueMessage(cmake::INTERNAL_ERROR, msg); |
| return nullptr; |
| } |
| |
| // Lookup/compute/cache the compile information for this configuration. |
| std::string config_upper; |
| if (!config.empty()) { |
| config_upper = cmSystemTools::UpperCase(config); |
| } |
| CompileInfoMapType::const_iterator i = |
| this->CompileInfoMap.find(config_upper); |
| if (i == this->CompileInfoMap.end()) { |
| CompileInfo info; |
| this->ComputePDBOutputDir("COMPILE_PDB", config, info.CompilePdbDir); |
| CompileInfoMapType::value_type entry(config_upper, info); |
| i = this->CompileInfoMap.insert(entry).first; |
| } |
| return &i->second; |
| } |
| |
| cmGeneratorTarget::ModuleDefinitionInfo const* |
| cmGeneratorTarget::GetModuleDefinitionInfo(std::string const& config) const |
| { |
| // A module definition file only makes sense on certain target types. |
| if (this->GetType() != cmStateEnums::SHARED_LIBRARY && |
| this->GetType() != cmStateEnums::MODULE_LIBRARY && |
| !this->IsExecutableWithExports()) { |
| return nullptr; |
| } |
| |
| // Lookup/compute/cache the compile information for this configuration. |
| std::string config_upper; |
| if (!config.empty()) { |
| config_upper = cmSystemTools::UpperCase(config); |
| } |
| ModuleDefinitionInfoMapType::const_iterator i = |
| this->ModuleDefinitionInfoMap.find(config_upper); |
| if (i == this->ModuleDefinitionInfoMap.end()) { |
| ModuleDefinitionInfo info; |
| this->ComputeModuleDefinitionInfo(config, info); |
| ModuleDefinitionInfoMapType::value_type entry(config_upper, info); |
| i = this->ModuleDefinitionInfoMap.insert(entry).first; |
| } |
| return &i->second; |
| } |
| |
| void cmGeneratorTarget::ComputeModuleDefinitionInfo( |
| std::string const& config, ModuleDefinitionInfo& info) const |
| { |
| this->GetModuleDefinitionSources(info.Sources, config); |
| info.WindowsExportAllSymbols = |
| this->Makefile->IsOn("CMAKE_SUPPORT_WINDOWS_EXPORT_ALL_SYMBOLS") && |
| this->GetPropertyAsBool("WINDOWS_EXPORT_ALL_SYMBOLS"); |
| #if defined(_WIN32) && defined(CMAKE_BUILD_WITH_CMAKE) |
| info.DefFileGenerated = |
| info.WindowsExportAllSymbols || info.Sources.size() > 1; |
| #else |
| // Our __create_def helper is only available on Windows. |
| info.DefFileGenerated = false; |
| #endif |
| if (info.DefFileGenerated) { |
| info.DefFile = this->ObjectDirectory /* has slash */ + "exports.def"; |
| } else if (!info.Sources.empty()) { |
| info.DefFile = info.Sources.front()->GetFullPath(); |
| } |
| } |
| |
| bool cmGeneratorTarget::IsDLLPlatform() const |
| { |
| return this->DLLPlatform; |
| } |
| |
| void cmGeneratorTarget::GetAutoUicOptions(std::vector<std::string>& result, |
| const std::string& config) const |
| { |
| const char* prop = |
| this->GetLinkInterfaceDependentStringProperty("AUTOUIC_OPTIONS", config); |
| if (!prop) { |
| return; |
| } |
| cmGeneratorExpression ge; |
| |
| cmGeneratorExpressionDAGChecker dagChecker(this, "AUTOUIC_OPTIONS", nullptr, |
| nullptr); |
| cmSystemTools::ExpandListArgument( |
| ge.Parse(prop)->Evaluate(this->LocalGenerator, config, false, this, |
| &dagChecker), |
| result); |
| } |
| |
| void processILibs(const std::string& config, |
| cmGeneratorTarget const* headTarget, cmLinkItem const& item, |
| cmGlobalGenerator* gg, |
| std::vector<cmGeneratorTarget const*>& tgts, |
| std::set<cmGeneratorTarget const*>& emitted) |
| { |
| if (item.Target && emitted.insert(item.Target).second) { |
| tgts.push_back(item.Target); |
| if (cmLinkInterfaceLibraries const* iface = |
| item.Target->GetLinkInterfaceLibraries(config, headTarget, true)) { |
| for (cmLinkItem const& lib : iface->Libraries) { |
| processILibs(config, headTarget, lib, gg, tgts, emitted); |
| } |
| } |
| } |
| } |
| |
| const std::vector<const cmGeneratorTarget*>& |
| cmGeneratorTarget::GetLinkImplementationClosure( |
| const std::string& config) const |
| { |
| LinkImplClosure& tgts = this->LinkImplClosureMap[config]; |
| if (!tgts.Done) { |
| tgts.Done = true; |
| std::set<cmGeneratorTarget const*> emitted; |
| |
| cmLinkImplementationLibraries const* impl = |
| this->GetLinkImplementationLibraries(config); |
| |
| for (cmLinkImplItem const& lib : impl->Libraries) { |
| processILibs(config, this, lib, |
| this->LocalGenerator->GetGlobalGenerator(), tgts, emitted); |
| } |
| } |
| return tgts; |
| } |
| |
| class cmTargetTraceDependencies |
| { |
| public: |
| cmTargetTraceDependencies(cmGeneratorTarget* target); |
| void Trace(); |
| |
| private: |
| cmGeneratorTarget* GeneratorTarget; |
| cmMakefile* Makefile; |
| cmLocalGenerator* LocalGenerator; |
| cmGlobalGenerator const* GlobalGenerator; |
| typedef cmGeneratorTarget::SourceEntry SourceEntry; |
| SourceEntry* CurrentEntry; |
| std::queue<cmSourceFile*> SourceQueue; |
| std::set<cmSourceFile*> SourcesQueued; |
| typedef std::map<std::string, cmSourceFile*> NameMapType; |
| NameMapType NameMap; |
| std::vector<std::string> NewSources; |
| |
| void QueueSource(cmSourceFile* sf); |
| void FollowName(std::string const& name); |
| void FollowNames(std::vector<std::string> const& names); |
| bool IsUtility(std::string const& dep); |
| void CheckCustomCommand(cmCustomCommand const& cc); |
| void CheckCustomCommands(const std::vector<cmCustomCommand>& commands); |
| void FollowCommandDepends(cmCustomCommand const& cc, |
| const std::string& config, |
| std::set<std::string>& emitted); |
| }; |
| |
| cmTargetTraceDependencies::cmTargetTraceDependencies(cmGeneratorTarget* target) |
| : GeneratorTarget(target) |
| { |
| // Convenience. |
| this->Makefile = target->Target->GetMakefile(); |
| this->LocalGenerator = target->GetLocalGenerator(); |
| this->GlobalGenerator = this->LocalGenerator->GetGlobalGenerator(); |
| this->CurrentEntry = nullptr; |
| |
| // Queue all the source files already specified for the target. |
| if (target->GetType() != cmStateEnums::INTERFACE_LIBRARY) { |
| std::vector<std::string> configs; |
| this->Makefile->GetConfigurations(configs); |
| if (configs.empty()) { |
| configs.emplace_back(); |
| } |
| std::set<cmSourceFile*> emitted; |
| for (std::string const& c : configs) { |
| std::vector<cmSourceFile*> sources; |
| this->GeneratorTarget->GetSourceFiles(sources, c); |
| for (cmSourceFile* sf : sources) { |
| const std::set<cmGeneratorTarget const*> tgts = |
| this->GlobalGenerator->GetFilenameTargetDepends(sf); |
| if (tgts.find(this->GeneratorTarget) != tgts.end()) { |
| std::ostringstream e; |
| e << "Evaluation output file\n \"" << sf->GetFullPath() |
| << "\"\ndepends on the sources of a target it is used in. This " |
| "is a dependency loop and is not allowed."; |
| this->GeneratorTarget->LocalGenerator->IssueMessage( |
| cmake::FATAL_ERROR, e.str()); |
| return; |
| } |
| if (emitted.insert(sf).second && |
| this->SourcesQueued.insert(sf).second) { |
| this->SourceQueue.push(sf); |
| } |
| } |
| } |
| } |
| |
| // Queue pre-build, pre-link, and post-build rule dependencies. |
| this->CheckCustomCommands(this->GeneratorTarget->GetPreBuildCommands()); |
| this->CheckCustomCommands(this->GeneratorTarget->GetPreLinkCommands()); |
| this->CheckCustomCommands(this->GeneratorTarget->GetPostBuildCommands()); |
| } |
| |
| void cmTargetTraceDependencies::Trace() |
| { |
| // Process one dependency at a time until the queue is empty. |
| while (!this->SourceQueue.empty()) { |
| // Get the next source from the queue. |
| cmSourceFile* sf = this->SourceQueue.front(); |
| this->SourceQueue.pop(); |
| this->CurrentEntry = &this->GeneratorTarget->SourceDepends[sf]; |
| |
| // Queue dependencies added explicitly by the user. |
| if (const char* additionalDeps = sf->GetProperty("OBJECT_DEPENDS")) { |
| std::vector<std::string> objDeps; |
| cmSystemTools::ExpandListArgument(additionalDeps, objDeps); |
| for (std::string& objDep : objDeps) { |
| if (cmSystemTools::FileIsFullPath(objDep)) { |
| objDep = cmSystemTools::CollapseFullPath(objDep); |
| } |
| } |
| this->FollowNames(objDeps); |
| } |
| |
| // Queue the source needed to generate this file, if any. |
| this->FollowName(sf->GetFullPath()); |
| |
| // Queue dependencies added programmatically by commands. |
| this->FollowNames(sf->GetDepends()); |
| |
| // Queue custom command dependencies. |
| if (cmCustomCommand const* cc = sf->GetCustomCommand()) { |
| this->CheckCustomCommand(*cc); |
| } |
| } |
| this->CurrentEntry = nullptr; |
| |
| this->GeneratorTarget->AddTracedSources(this->NewSources); |
| } |
| |
| void cmTargetTraceDependencies::QueueSource(cmSourceFile* sf) |
| { |
| if (this->SourcesQueued.insert(sf).second) { |
| this->SourceQueue.push(sf); |
| |
| // Make sure this file is in the target at the end. |
| this->NewSources.push_back(sf->GetFullPath()); |
| } |
| } |
| |
| void cmTargetTraceDependencies::FollowName(std::string const& name) |
| { |
| NameMapType::iterator i = this->NameMap.find(name); |
| if (i == this->NameMap.end()) { |
| // Check if we know how to generate this file. |
| cmSourceFile* sf = this->Makefile->GetSourceFileWithOutput(name); |
| NameMapType::value_type entry(name, sf); |
| i = this->NameMap.insert(entry).first; |
| } |
| if (cmSourceFile* sf = i->second) { |
| // Record the dependency we just followed. |
| if (this->CurrentEntry) { |
| this->CurrentEntry->Depends.push_back(sf); |
| } |
| this->QueueSource(sf); |
| } |
| } |
| |
| void cmTargetTraceDependencies::FollowNames( |
| std::vector<std::string> const& names) |
| { |
| for (std::string const& name : names) { |
| this->FollowName(name); |
| } |
| } |
| |
| bool cmTargetTraceDependencies::IsUtility(std::string const& dep) |
| { |
| // Dependencies on targets (utilities) are supposed to be named by |
| // just the target name. However for compatibility we support |
| // naming the output file generated by the target (assuming there is |
| // no output-name property which old code would not have set). In |
| // that case the target name will be the file basename of the |
| // dependency. |
| std::string util = cmSystemTools::GetFilenameName(dep); |
| if (cmSystemTools::GetFilenameLastExtension(util) == ".exe") { |
| util = cmSystemTools::GetFilenameWithoutLastExtension(util); |
| } |
| |
| // Check for a target with this name. |
| if (cmGeneratorTarget* t = |
| this->GeneratorTarget->GetLocalGenerator()->FindGeneratorTargetToUse( |
| util)) { |
| // If we find the target and the dep was given as a full path, |
| // then make sure it was not a full path to something else, and |
| // the fact that the name matched a target was just a coincidence. |
| if (cmSystemTools::FileIsFullPath(dep)) { |
| if (t->GetType() >= cmStateEnums::EXECUTABLE && |
| t->GetType() <= cmStateEnums::MODULE_LIBRARY) { |
| // This is really only for compatibility so we do not need to |
| // worry about configuration names and output names. |
| std::string tLocation = t->GetLocationForBuild(); |
| tLocation = cmSystemTools::GetFilenamePath(tLocation); |
| std::string depLocation = cmSystemTools::GetFilenamePath(dep); |
| depLocation = cmSystemTools::CollapseFullPath(depLocation); |
| tLocation = cmSystemTools::CollapseFullPath(tLocation); |
| if (depLocation == tLocation) { |
| this->GeneratorTarget->Target->AddUtility(util); |
| return true; |
| } |
| } |
| } else { |
| // The original name of the dependency was not a full path. It |
| // must name a target, so add the target-level dependency. |
| this->GeneratorTarget->Target->AddUtility(util); |
| return true; |
| } |
| } |
| |
| // The dependency does not name a target built in this project. |
| return false; |
| } |
| |
| void cmTargetTraceDependencies::CheckCustomCommand(cmCustomCommand const& cc) |
| { |
| // Transform command names that reference targets built in this |
| // project to corresponding target-level dependencies. |
| cmGeneratorExpression ge(cc.GetBacktrace()); |
| |
| // Add target-level dependencies referenced by generator expressions. |
| std::set<cmGeneratorTarget*> targets; |
| |
| for (cmCustomCommandLine const& cCmdLine : cc.GetCommandLines()) { |
| std::string const& command = *cCmdLine.begin(); |
| // Check for a target with this name. |
| if (cmGeneratorTarget* t = |
| this->LocalGenerator->FindGeneratorTargetToUse(command)) { |
| if (t->GetType() == cmStateEnums::EXECUTABLE) { |
| // The command refers to an executable target built in |
| // this project. Add the target-level dependency to make |
| // sure the executable is up to date before this custom |
| // command possibly runs. |
| this->GeneratorTarget->Target->AddUtility(command); |
| } |
| } |
| |
| // Check for target references in generator expressions. |
| for (std::string const& cl : cCmdLine) { |
| const std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(cl); |
| cge->Evaluate(this->GeneratorTarget->GetLocalGenerator(), "", true); |
| std::set<cmGeneratorTarget*> geTargets = cge->GetTargets(); |
| targets.insert(geTargets.begin(), geTargets.end()); |
| } |
| } |
| |
| for (cmGeneratorTarget* target : targets) { |
| this->GeneratorTarget->Target->AddUtility(target->GetName()); |
| } |
| |
| // Queue the custom command dependencies. |
| std::vector<std::string> configs; |
| std::set<std::string> emitted; |
| this->Makefile->GetConfigurations(configs); |
| if (configs.empty()) { |
| configs.emplace_back(); |
| } |
| for (std::string const& conf : configs) { |
| this->FollowCommandDepends(cc, conf, emitted); |
| } |
| } |
| |
| void cmTargetTraceDependencies::FollowCommandDepends( |
| cmCustomCommand const& cc, const std::string& config, |
| std::set<std::string>& emitted) |
| { |
| cmCustomCommandGenerator ccg(cc, config, |
| this->GeneratorTarget->LocalGenerator); |
| |
| const std::vector<std::string>& depends = ccg.GetDepends(); |
| |
| for (std::string const& dep : depends) { |
| if (emitted.insert(dep).second) { |
| if (!this->IsUtility(dep)) { |
| // The dependency does not name a target and may be a file we |
| // know how to generate. Queue it. |
| this->FollowName(dep); |
| } |
| } |
| } |
| } |
| |
| void cmTargetTraceDependencies::CheckCustomCommands( |
| const std::vector<cmCustomCommand>& commands) |
| { |
| for (cmCustomCommand const& command : commands) { |
| this->CheckCustomCommand(command); |
| } |
| } |
| |
| void cmGeneratorTarget::TraceDependencies() |
| { |
| // CMake-generated targets have no dependencies to trace. Normally tracing |
| // would find nothing anyway, but when building CMake itself the "install" |
| // target command ends up referencing the "cmake" target but we do not |
| // really want the dependency because "install" depend on "all" anyway. |
| if (this->GetType() == cmStateEnums::GLOBAL_TARGET) { |
| return; |
| } |
| |
| // Use a helper object to trace the dependencies. |
| cmTargetTraceDependencies tracer(this); |
| tracer.Trace(); |
| } |
| |
| std::string cmGeneratorTarget::GetCompilePDBDirectory( |
| const std::string& config) const |
| { |
| if (CompileInfo const* info = this->GetCompileInfo(config)) { |
| return info->CompilePdbDir; |
| } |
| return ""; |
| } |
| |
| void cmGeneratorTarget::GetAppleArchs(const std::string& config, |
| std::vector<std::string>& archVec) const |
| { |
| const char* archs = nullptr; |
| if (!config.empty()) { |
| std::string defVarName = "OSX_ARCHITECTURES_"; |
| defVarName += cmSystemTools::UpperCase(config); |
| archs = this->GetProperty(defVarName); |
| } |
| if (!archs) { |
| archs = this->GetProperty("OSX_ARCHITECTURES"); |
| } |
| if (archs) { |
| cmSystemTools::ExpandListArgument(std::string(archs), archVec); |
| } |
| } |
| |
| //---------------------------------------------------------------------------- |
| std::string cmGeneratorTarget::GetFeatureSpecificLinkRuleVariable( |
| std::string const& var, std::string const& lang, |
| std::string const& config) const |
| { |
| if (this->IsIPOEnabled(lang, config)) { |
| std::string varIPO = var + "_IPO"; |
| if (this->Makefile->IsDefinitionSet(varIPO)) { |
| return varIPO; |
| } |
| } |
| |
| return var; |
| } |
| |
| //---------------------------------------------------------------------------- |
| std::string cmGeneratorTarget::GetCreateRuleVariable( |
| std::string const& lang, std::string const& config) const |
| { |
| switch (this->GetType()) { |
| case cmStateEnums::STATIC_LIBRARY: { |
| std::string var = "CMAKE_" + lang + "_CREATE_STATIC_LIBRARY"; |
| return this->GetFeatureSpecificLinkRuleVariable(var, lang, config); |
| } |
| case cmStateEnums::SHARED_LIBRARY: |
| return "CMAKE_" + lang + "_CREATE_SHARED_LIBRARY"; |
| case cmStateEnums::MODULE_LIBRARY: |
| return "CMAKE_" + lang + "_CREATE_SHARED_MODULE"; |
| case cmStateEnums::EXECUTABLE: |
| return "CMAKE_" + lang + "_LINK_EXECUTABLE"; |
| default: |
| break; |
| } |
| return ""; |
| } |
| static void processIncludeDirectories( |
| cmGeneratorTarget const* tgt, |
| const std::vector<cmGeneratorTarget::TargetPropertyEntry*>& entries, |
| std::vector<std::string>& includes, |
| std::unordered_set<std::string>& uniqueIncludes, |
| cmGeneratorExpressionDAGChecker* dagChecker, const std::string& config, |
| bool debugIncludes, const std::string& language) |
| { |
| for (cmGeneratorTarget::TargetPropertyEntry* entry : entries) { |
| cmLinkImplItem const& item = entry->LinkImplItem; |
| std::string const& targetName = item.AsStr(); |
| bool const fromImported = item.Target && item.Target->IsImported(); |
| bool const checkCMP0027 = item.FromGenex; |
| std::vector<std::string> entryIncludes; |
| cmSystemTools::ExpandListArgument( |
| entry->ge->Evaluate(tgt->GetLocalGenerator(), config, false, tgt, |
| dagChecker, language), |
| entryIncludes); |
| |
| std::string usedIncludes; |
| for (std::string& entryInclude : entryIncludes) { |
| if (fromImported && !cmSystemTools::FileExists(entryInclude)) { |
| std::ostringstream e; |
| cmake::MessageType messageType = cmake::FATAL_ERROR; |
| if (checkCMP0027) { |
| switch (tgt->GetPolicyStatusCMP0027()) { |
| case cmPolicies::WARN: |
| e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0027) << "\n"; |
| CM_FALLTHROUGH; |
| case cmPolicies::OLD: |
| messageType = cmake::AUTHOR_WARNING; |
| break; |
| case cmPolicies::REQUIRED_ALWAYS: |
| case cmPolicies::REQUIRED_IF_USED: |
| case cmPolicies::NEW: |
| break; |
| } |
| } |
| /* clang-format off */ |
| e << "Imported target \"" << targetName << "\" includes " |
| "non-existent path\n \"" << entryInclude << "\"\nin its " |
| "INTERFACE_INCLUDE_DIRECTORIES. Possible reasons include:\n" |
| "* The path was deleted, renamed, or moved to another " |
| "location.\n" |
| "* An install or uninstall procedure did not complete " |
| "successfully.\n" |
| "* The installation package was faulty and references files it " |
| "does not provide.\n"; |
| /* clang-format on */ |
| tgt->GetLocalGenerator()->IssueMessage(messageType, e.str()); |
| return; |
| } |
| |
| if (!cmSystemTools::FileIsFullPath(entryInclude)) { |
| std::ostringstream e; |
| bool noMessage = false; |
| cmake::MessageType messageType = cmake::FATAL_ERROR; |
| if (!targetName.empty()) { |
| /* clang-format off */ |
| e << "Target \"" << targetName << "\" contains relative " |
| "path in its INTERFACE_INCLUDE_DIRECTORIES:\n" |
| " \"" << entryInclude << "\""; |
| /* clang-format on */ |
| } else { |
| switch (tgt->GetPolicyStatusCMP0021()) { |
| case cmPolicies::WARN: { |
| e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0021) << |