| /* 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 <utility> |
| |
| #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 "cmMessageType.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()); |
| 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.emplace_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( |
| MessageType::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, bool before) |
| { |
| cmListFileBacktrace lfbt = this->Makefile->GetBacktrace(); |
| cmGeneratorExpression ge(lfbt); |
| std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(src); |
| cge->SetEvaluateForBuildsystem(true); |
| this->SourceEntries.insert(before ? this->SourceEntries.begin() |
| : this->SourceEntries.end(), |
| new TargetPropertyEntry(std::move(cge))); |
| this->ClearSourcesCache(); |
| } |
| |
| void cmGeneratorTarget::AddSource(const std::string& src, bool before) |
| { |
| this->Target->AddSource(src, before); |
| this->AddSourceCommon(src, before); |
| } |
| |
| 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); |
| this->IncludeDirectoriesEntries.insert( |
| before ? this->IncludeDirectoriesEntries.begin() |
| : this->IncludeDirectoriesEntries.end(), |
| 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) \ |
| do { \ |
| KindedSources const& kinded = this->GetKindedSources(config); \ |
| for (SourceAndKind const& s : kinded.Sources) { \ |
| if (s.Kind == KIND) { \ |
| data.push_back(s.Source.Value); \ |
| } \ |
| } \ |
| } while (false) |
| /* 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); |
| } |
| |
| const char* cmGeneratorTarget::GetLinkPIEProperty( |
| const std::string& config) const |
| { |
| static std::string PICValue; |
| |
| PICValue = this->GetLinkInterfaceDependentStringAsBoolProperty( |
| "POSITION_INDEPENDENT_CODE", config); |
| |
| if (PICValue == "(unset)") { |
| // POSITION_INDEPENDENT_CODE is not set |
| return nullptr; |
| } |
| |
| switch (this->GetPolicyStatusCMP0083()) { |
| case cmPolicies::WARN: { |
| std::ostringstream e; |
| e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0083); |
| this->LocalGenerator->IssueMessage(MessageType::AUTHOR_WARNING, e.str()); |
| CM_FALLTHROUGH; |
| } |
| case cmPolicies::OLD: |
| return nullptr; |
| default: |
| // nothing to do |
| break; |
| } |
| |
| return PICValue.c_str(); |
| } |
| |
| 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( |
| MessageType::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( |
| MessageType::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<BT<std::string>> const& utilities = this->GetUtilities(); |
| for (BT<std::string> const& i : utilities) { |
| if (cmGeneratorTarget* gt = |
| this->LocalGenerator->FindGeneratorTargetToUse(i.Value)) { |
| this->UtilityItems.insert(cmLinkItem(gt, i.Backtrace)); |
| } else { |
| this->UtilityItems.insert(cmLinkItem(i.Value, i.Backtrace)); |
| } |
| } |
| } |
| 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<BT<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(MessageType::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(MessageType::FATAL_ERROR, |
| err.str()); |
| return contextDependent; |
| } |
| src = fullPath; |
| } |
| std::string usedSources; |
| for (std::string const& src : entrySources) { |
| if (uniqueSrcs.insert(src).second) { |
| srcs.emplace_back(src, entry->ge->GetBacktrace()); |
| if (debugSources) { |
| usedSources += " * " + src + "\n"; |
| } |
| } |
| } |
| if (!usedSources.empty()) { |
| tgt->GetLocalGenerator()->GetCMakeInstance()->IssueMessage( |
| MessageType::LOG, |
| std::string("Used sources for target ") + tgt->GetName() + ":\n" + |
| usedSources, |
| entry->ge->GetBacktrace()); |
| } |
| } |
| return contextDependent; |
| } |
| |
| std::vector<BT<std::string>> cmGeneratorTarget::GetSourceFilePaths( |
| std::string const& config) const |
| { |
| std::vector<BT<std::string>> files; |
| 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.back() == '>') { |
| continue; |
| } |
| files.emplace_back(item); |
| } |
| } |
| return files; |
| } |
| |
| 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); |
| return files; |
| } |
| |
| void cmGeneratorTarget::GetSourceFiles(std::vector<cmSourceFile*>& files, |
| const std::string& config) const |
| { |
| std::vector<BT<cmSourceFile*>> tmp = this->GetSourceFiles(config); |
| files.reserve(tmp.size()); |
| for (BT<cmSourceFile*>& v : tmp) { |
| files.push_back(v.Value); |
| } |
| } |
| |
| std::vector<BT<cmSourceFile*>> cmGeneratorTarget::GetSourceFiles( |
| std::string const& config) const |
| { |
| std::vector<BT<cmSourceFile*>> files; |
| 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<BT<std::string>> srcs = this->GetSourceFilePaths(config); |
| std::set<cmSourceFile*> emitted; |
| for (BT<std::string> const& s : srcs) { |
| cmSourceFile* sf = this->Makefile->GetOrCreateSource(s.Value); |
| if (emitted.insert(sf).second) { |
| files.emplace_back(sf, s.Backtrace); |
| } |
| } |
| return files; |
| } |
| |
| KindedSources const& kinded = this->GetKindedSources(config); |
| files.reserve(kinded.Sources.size()); |
| for (SourceAndKind const& si : kinded.Sources) { |
| files.push_back(si.Source); |
| } |
| return files; |
| } |
| |
| void cmGeneratorTarget::GetSourceFilesWithoutObjectLibraries( |
| std::vector<cmSourceFile*>& files, const std::string& config) const |
| { |
| std::vector<BT<cmSourceFile*>> tmp = |
| this->GetSourceFilesWithoutObjectLibraries(config); |
| files.reserve(tmp.size()); |
| for (BT<cmSourceFile*>& v : tmp) { |
| files.push_back(v.Value); |
| } |
| } |
| |
| std::vector<BT<cmSourceFile*>> |
| cmGeneratorTarget::GetSourceFilesWithoutObjectLibraries( |
| std::string const& config) const |
| { |
| std::vector<BT<cmSourceFile*>> files; |
| KindedSources const& kinded = this->GetKindedSources(config); |
| files.reserve(kinded.Sources.size()); |
| for (SourceAndKind const& si : kinded.Sources) { |
| if (si.Source.Value->GetObjectLibrary().empty()) { |
| files.push_back(si.Source); |
| } |
| } |
| return files; |
| } |
| |
| 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( |
| MessageType::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<BT<std::string>> srcs = this->GetSourceFilePaths(config); |
| |
| cmsys::RegularExpression header_regex(CM_HEADER_REGEX); |
| std::vector<cmSourceFile*> badObjLib; |
| |
| std::set<cmSourceFile*> emitted; |
| for (BT<std::string> const& s : srcs) { |
| // Create each source at most once. |
| cmSourceFile* sf = this->Makefile->GetOrCreateSource(s.Value); |
| 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({ BT<cmSourceFile*>(sf, s.Backtrace), 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( |
| MessageType::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.Value); |
| if (mi == index.end()) { |
| AllConfigSource acs; |
| acs.Source = src.Source.Value; |
| acs.Kind = src.Kind; |
| this->AllConfigSources.push_back(std::move(acs)); |
| std::map<cmSourceFile const*, size_t>::value_type entry( |
| src.Source.Value, 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(MessageType::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(MessageType::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<BT<std::string>>& cmGeneratorTarget::GetUtilities() const |
| { |
| return this->Target->GetUtilities(); |
| } |
| |
| 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 std::string* 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; |
| } |
| return nullptr; |
| } |
| |
| class cmTargetCollectLinkLanguages |
| { |
| public: |
| cmTargetCollectLinkLanguages(cmGeneratorTarget const* target, |
| std::string config, |
| std::unordered_set<std::string>& languages, |
| cmGeneratorTarget const* head) |
| : Config(std::move(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; |
| MessageType messageType = MessageType::FATAL_ERROR; |
| std::ostringstream e; |
| switch (this->Target->GetLocalGenerator()->GetPolicyStatus( |
| cmPolicies::CMP0028)) { |
| case cmPolicies::WARN: { |
| e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0028) << "\n"; |
| messageType = MessageType::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(MessageType::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(MessageType::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( |
| MessageType::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<BT<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; |
| MessageType messageType = MessageType::FATAL_ERROR; |
| if (checkCMP0027) { |
| switch (tgt->GetPolicyStatusCMP0027()) { |
| case cmPolicies::WARN: |
| e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0027) << "\n"; |
| CM_FALLTHROUGH; |
| case cmPolicies::OLD: |
| messageType = MessageType::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; |
| MessageType messageType = MessageType::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) << "\n"; |
| messageType = MessageType::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; |
| } |
| e << "Found relative path while evaluating include directories of " |
| "\"" |
| << tgt->GetName() << "\":\n \"" << entryInclude << "\"\n"; |
| } |
| if (!noMessage) { |
| tgt->GetLocalGenerator()->IssueMessage(messageType, e.str()); |
| if (messageType == MessageType::FATAL_ERROR) { |
| return; |
| } |
| } |
| } |
| |
| if (!cmSystemTools::IsOff(entryInclude)) { |
| cmSystemTools::ConvertToUnixSlashes(entryInclude); |
| } |
| std::string inc = entryInclude; |
| |
| if (uniqueIncludes.insert(inc).second) { |
| includes.emplace_back(inc, entry->ge->GetBacktrace()); |
| if (debugIncludes) { |
| usedIncludes += " * " + inc + "\n"; |
| } |
| } |
| } |
| if (!usedIncludes.empty()) { |
| tgt->GetLocalGenerator()->GetCMakeInstance()->IssueMessage( |
| MessageType::LOG, |
| std::string("Used includes for target ") + tgt->GetName() + ":\n" + |
| usedIncludes, |
| entry->ge->GetBacktrace()); |
| } |
| } |
| } |
| |
| std::vector<BT<std::string>> cmGeneratorTarget::GetIncludeDirectories( |
| const std::string& config, const std::string& lang) const |
| { |
| std::vector<BT<std::string>> includes; |
| std::unordered_set<std::string> uniqueIncludes; |
| |
| cmGeneratorExpressionDAGChecker dagChecker(this, "INCLUDE_DIRECTORIES", |
| nullptr, nullptr); |
| |
| std::vector<std::string> debugProperties; |
| const char* debugProp = |
| this->Makefile->GetDefinition("CMAKE_DEBUG_TARGET_PROPERTIES"); |
| if (debugProp) { |
| cmSystemTools::ExpandListArgument(debugProp, debugProperties); |
| } |
| |
| bool debugIncludes = !this->DebugIncludesDone && |
| std::find(debugProperties.begin(), debugProperties.end(), |
| "INCLUDE_DIRECTORIES") != debugProperties.end(); |
| |
| if (this->GlobalGenerator->GetConfigureDoneCMP0026()) { |
| this->DebugIncludesDone = true; |
| } |
| |
| processIncludeDirectories(this, this->IncludeDirectoriesEntries, includes, |
| uniqueIncludes, &dagChecker, config, debugIncludes, |
| lang); |
| |
| std::vector<cmGeneratorTarget::TargetPropertyEntry*> |
| linkInterfaceIncludeDirectoriesEntries; |
| AddInterfaceEntries(this, config, "INTERFACE_INCLUDE_DIRECTORIES", |
| linkInterfaceIncludeDirectoriesEntries); |
| |
| if (this->Makefile->IsOn("APPLE")) { |
| cmLinkImplementationLibraries const* impl = |
| this->GetLinkImplementationLibraries(config); |
| for (cmLinkImplItem const& lib : impl->Libraries) { |
| std::string libDir = cmSystemTools::CollapseFullPath(lib.AsStr()); |
| |
| static cmsys::RegularExpression frameworkCheck( |
| "(.*\\.framework)(/Versions/[^/]+)?/[^/]+$"); |
| if (!frameworkCheck.find(libDir)) { |
| continue; |
| } |
| |
| libDir = frameworkCheck.match(1); |
| |
| cmGeneratorExpression ge; |
| std::unique_ptr<cmCompiledGeneratorExpression> cge = |
| ge.Parse(libDir.c_str()); |
| linkInterfaceIncludeDirectoriesEntries.push_back( |
| new cmGeneratorTarget::TargetPropertyEntry(std::move(cge))); |
| } |
| } |
| |
| processIncludeDirectories(this, linkInterfaceIncludeDirectoriesEntries, |
| includes, uniqueIncludes, &dagChecker, config, |
| debugIncludes, lang); |
| |
| cmDeleteAll(linkInterfaceIncludeDirectoriesEntries); |
| |
| return includes; |
| } |
| |
| enum class OptionsParse |
| { |
| None, |
| Shell |
| }; |
| |
| static void processOptionsInternal( |
| cmGeneratorTarget const* tgt, |
| const std::vector<cmGeneratorTarget::TargetPropertyEntry*>& entries, |
| std::vector<BT<std::string>>& options, |
| std::unordered_set<std::string>& uniqueOptions, |
| cmGeneratorExpressionDAGChecker* dagChecker, const std::string& config, |
| bool debugOptions, const char* logName, std::string const& language, |
| OptionsParse parse) |
| { |
| for (cmGeneratorTarget::TargetPropertyEntry* entry : entries) { |
| std::vector<std::string> entryOptions; |
| cmSystemTools::ExpandListArgument( |
| entry->ge->Evaluate(tgt->GetLocalGenerator(), config, false, tgt, |
| dagChecker, language), |
| entryOptions); |
| std::string usedOptions; |
| for (std::string const& opt : entryOptions) { |
| if (uniqueOptions.insert(opt).second) { |
| if (parse == OptionsParse::Shell && |
| cmHasLiteralPrefix(opt, "SHELL:")) { |
| std::vector<std::string> tmp; |
| cmSystemTools::ParseUnixCommandLine(opt.c_str() + 6, tmp); |
| for (std::string& o : tmp) { |
| options.emplace_back(std::move(o), entry->ge->GetBacktrace()); |
| } |
| } else { |
| options.emplace_back(opt, entry->ge->GetBacktrace()); |
| } |
| if (debugOptions) { |
| usedOptions += " * " + opt + "\n"; |
| } |
| } |
| } |
| if (!usedOptions.empty()) { |
| tgt->GetLocalGenerator()->GetCMakeInstance()->IssueMessage( |
| MessageType::LOG, |
| std::string("Used ") + logName + std::string(" for target ") + |
| tgt->GetName() + ":\n" + usedOptions, |
| entry->ge->GetBacktrace()); |
| } |
| } |
| } |
| |
| static void processCompileOptions( |
| cmGeneratorTarget const* tgt, |
| const std::vector<cmGeneratorTarget::TargetPropertyEntry*>& entries, |
| std::vector<BT<std::string>>& options, |
| std::unordered_set<std::string>& uniqueOptions, |
| cmGeneratorExpressionDAGChecker* dagChecker, const std::string& config, |
| bool debugOptions, std::string const& language) |
| { |
| processOptionsInternal(tgt, entries, options, uniqueOptions, dagChecker, |
| config, debugOptions, "compile options", language, |
| OptionsParse::Shell); |
| } |
| |
| void cmGeneratorTarget::GetCompileOptions(std::vector<std::string>& result, |
| const std::string& config, |
| const std::string& language) const |
| { |
| std::vector<BT<std::string>> tmp = this->GetCompileOptions(config, language); |
| result.reserve(tmp.size()); |
| for (BT<std::string>& v : tmp) { |
| result.emplace_back(std::move(v.Value)); |
| } |
| } |
| |
| std::vector<BT<std::string>> cmGeneratorTarget::GetCompileOptions( |
| std::string const& config, std::string const& language) const |
| { |
| std::vector<BT<std::string>> result; |
| std::unordered_set<std::string> uniqueOptions; |
| |
| cmGeneratorExpressionDAGChecker dagChecker(this, "COMPILE_OPTIONS", nullptr, |
| nullptr); |
| |
| std::vector<std::string> debugProperties; |
| const char* debugProp = |
| this->Makefile->GetDefinition("CMAKE_DEBUG_TARGET_PROPERTIES"); |
| if (debugProp) { |
| cmSystemTools::ExpandListArgument(debugProp, debugProperties); |
| } |
| |
| bool debugOptions = !this->DebugCompileOptionsDone && |
| std::find(debugProperties.begin(), debugProperties.end(), |
| "COMPILE_OPTIONS") != debugProperties.end(); |
| |
| if (this->GlobalGenerator->GetConfigureDoneCMP0026()) { |
| this->DebugCompileOptionsDone = true; |
| } |
| |
| processCompileOptions(this, this->CompileOptionsEntries, result, |
| uniqueOptions, &dagChecker, config, debugOptions, |
| language); |
| |
| std::vector<cmGeneratorTarget::TargetPropertyEntry*> |
| linkInterfaceCompileOptionsEntries; |
| |
| AddInterfaceEntries(this, config, "INTERFACE_COMPILE_OPTIONS", |
| linkInterfaceCompileOptionsEntries); |
| |
| processCompileOptions(this, linkInterfaceCompileOptionsEntries, result, |
| uniqueOptions, &dagChecker, config, debugOptions, |
| language); |
| |
| cmDeleteAll(linkInterfaceCompileOptionsEntries); |
| return result; |
| } |
| |
| static void processCompileFeatures( |
| cmGeneratorTarget const* tgt, |
| const std::vector<cmGeneratorTarget::TargetPropertyEntry*>& entries, |
| std::vector<BT<std::string>>& options, |
| std::unordered_set<std::string>& uniqueOptions, |
| cmGeneratorExpressionDAGChecker* dagChecker, const std::string& config, |
| bool debugOptions) |
| { |
| processOptionsInternal(tgt, entries, options, uniqueOptions, dagChecker, |
| config, debugOptions, "compile features", |
| std::string(), OptionsParse::None); |
| } |
| |
| void cmGeneratorTarget::GetCompileFeatures(std::vector<std::string>& result, |
| const std::string& config) const |
| { |
| std::vector<BT<std::string>> tmp = this->GetCompileFeatures(config); |
| result.reserve(tmp.size()); |
| for (BT<std::string>& v : tmp) { |
| result.emplace_back(std::move(v.Value)); |
| } |
| } |
| |
| std::vector<BT<std::string>> cmGeneratorTarget::GetCompileFeatures( |
| std::string const& config) const |
| { |
| std::vector<BT<std::string>> result; |
| std::unordered_set<std::string> uniqueFeatures; |
| |
| cmGeneratorExpressionDAGChecker dagChecker(this, "COMPILE_FEATURES", nullptr, |
| nullptr); |
| |
| std::vector<std::string> debugProperties; |
| const char* debugProp = |
| this->Makefile->GetDefinition("CMAKE_DEBUG_TARGET_PROPERTIES"); |
| if (debugProp) { |
| cmSystemTools::ExpandListArgument(debugProp, debugProperties); |
| } |
| |
| bool debugFeatures = !this->DebugCompileFeaturesDone && |
| std::find(debugProperties.begin(), debugProperties.end(), |
| "COMPILE_FEATURES") != debugProperties.end(); |
| |
| if (this->GlobalGenerator->GetConfigureDoneCMP0026()) { |
| this->DebugCompileFeaturesDone = true; |
| } |
| |
| processCompileFeatures(this, this->CompileFeaturesEntries, result, |
| uniqueFeatures, &dagChecker, config, debugFeatures); |
| |
| std::vector<cmGeneratorTarget::TargetPropertyEntry*> |
| linkInterfaceCompileFeaturesEntries; |
| AddInterfaceEntries(this, config, "INTERFACE_COMPILE_FEATURES", |
| linkInterfaceCompileFeaturesEntries); |
| |
| processCompileFeatures(this, linkInterfaceCompileFeaturesEntries, result, |
| uniqueFeatures, &dagChecker, config, debugFeatures); |
| |
| cmDeleteAll(linkInterfaceCompileFeaturesEntries); |
| return result; |
| } |
| |
| static void processCompileDefinitions( |
| cmGeneratorTarget const* tgt, |
| const std::vector<cmGeneratorTarget::TargetPropertyEntry*>& entries, |
| std::vector<BT<std::string>>& options, |
| std::unordered_set<std::string>& uniqueOptions, |
| cmGeneratorExpressionDAGChecker* dagChecker, const std::string& config, |
| bool debugOptions, std::string const& language) |
| { |
| processOptionsInternal(tgt, entries, options, uniqueOptions, dagChecker, |
| config, debugOptions, "compile definitions", language, |
| OptionsParse::None); |
| } |
| |
| void cmGeneratorTarget::GetCompileDefinitions( |
| std::vector<std::string>& result, const std::string& config, |
| const std::string& language) const |
| { |
| std::vector<BT<std::string>> tmp = |
| this->GetCompileDefinitions(config, language); |
| result.reserve(tmp.size()); |
| for (BT<std::string>& v : tmp) { |
| result.emplace_back(std::move(v.Value)); |
| } |
| } |
| |
| std::vector<BT<std::string>> cmGeneratorTarget::GetCompileDefinitions( |
| std::string const& config, std::string const& language) const |
| { |
| std::vector<BT<std::string>> list; |
| std::unordered_set<std::string> uniqueOptions; |
| |
| cmGeneratorExpressionDAGChecker dagChecker(this, "COMPILE_DEFINITIONS", |
| nullptr, nullptr); |
| |
| std::vector<std::string> debugProperties; |
| const char* debugProp = |
| this->Makefile->GetDefinition("CMAKE_DEBUG_TARGET_PROPERTIES"); |
| if (debugProp) { |
| cmSystemTools::ExpandListArgument(debugProp, debugProperties); |
| } |
| |
| bool debugDefines = !this->DebugCompileDefinitionsDone && |
| std::find(debugProperties.begin(), debugProperties.end(), |
| "COMPILE_DEFINITIONS") != debugProperties.end(); |
| |
| if (this->GlobalGenerator->GetConfigureDoneCMP0026()) { |
| this->DebugCompileDefinitionsDone = true; |
| } |
| |
| processCompileDefinitions(this, this->CompileDefinitionsEntries, list, |
| uniqueOptions, &dagChecker, config, debugDefines, |
| language); |
| |
| std::vector<cmGeneratorTarget::TargetPropertyEntry*> |
| linkInterfaceCompileDefinitionsEntries; |
| AddInterfaceEntries(this, config, "INTERFACE_COMPILE_DEFINITIONS", |
| linkInterfaceCompileDefinitionsEntries); |
| if (!config.empty()) { |
| std::string configPropName = |
| "COMPILE_DEFINITIONS_" + cmSystemTools::UpperCase(config); |
| const char* configProp = this->GetProperty(configPropName); |
| if (configProp) { |
| switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0043)) { |
| case cmPolicies::WARN: { |
| std::ostringstream e; |
| e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0043); |
| this->LocalGenerator->IssueMessage(MessageType::AUTHOR_WARNING, |
| e.str()); |
| CM_FALLTHROUGH; |
| } |
| case cmPolicies::OLD: { |
| cmGeneratorExpression ge; |
| std::unique_ptr<cmCompiledGeneratorExpression> cge = |
| ge.Parse(configProp); |
| linkInterfaceCompileDefinitionsEntries.push_back( |
| new cmGeneratorTarget::TargetPropertyEntry(std::move(cge))); |
| } break; |
| case cmPolicies::NEW: |
| case cmPolicies::REQUIRED_ALWAYS: |
| case cmPolicies::REQUIRED_IF_USED: |
| break; |
| } |
| } |
| } |
| |
| processCompileDefinitions(this, linkInterfaceCompileDefinitionsEntries, list, |
| uniqueOptions, &dagChecker, config, debugDefines, |
| language); |
| |
| cmDeleteAll(linkInterfaceCompileDefinitionsEntries); |
| return list; |
| } |
| |
| namespace { |
| void processLinkOptions( |
| cmGeneratorTarget const* tgt, |
| const std::vector<cmGeneratorTarget::TargetPropertyEntry*>& entries, |
| std::vector<BT<std::string>>& options, |
| std::unordered_set<std::string>& uniqueOptions, |
| cmGeneratorExpressionDAGChecker* dagChecker, const std::string& config, |
| bool debugOptions, std::string const& language) |
| { |
| processOptionsInternal(tgt, entries, options, uniqueOptions, dagChecker, |
| config, debugOptions, "link options", language, |
| OptionsParse::Shell); |
| } |
| } |
| |
| void cmGeneratorTarget::GetLinkOptions(std::vector<std::string>& result, |
| const std::string& config, |
| const std::string& language) const |
| { |
| std::vector<BT<std::string>> tmp = this->GetLinkOptions(config, language); |
| result.reserve(tmp.size()); |
| for (BT<std::string>& v : tmp) { |
| result.emplace_back(std::move(v.Value)); |
| } |
| } |
| |
| std::vector<BT<std::string>> cmGeneratorTarget::GetLinkOptions( |
| std::string const& config, std::string const& language) const |
| { |
| std::vector<BT<std::string>> result; |
| std::unordered_set<std::string> uniqueOptions; |
| |
| cmGeneratorExpressionDAGChecker dagChecker(this, "LINK_OPTIONS", nullptr, |
| nullptr); |
| |
| std::vector<std::string> debugProperties; |
| const char* debugProp = |
| this->Makefile->GetDefinition("CMAKE_DEBUG_TARGET_PROPERTIES"); |
| if (debugProp) { |
| cmSystemTools::ExpandListArgument(debugProp, debugProperties); |
| } |
| |
| bool debugOptions = !this->DebugLinkOptionsDone && |
| std::find(debugProperties.begin(), debugProperties.end(), |
| "LINK_OPTIONS") != debugProperties.end(); |
| |
| if (this->GlobalGenerator->GetConfigureDoneCMP0026()) { |
| this->DebugLinkOptionsDone = true; |
| } |
| |
| processLinkOptions(this, this->LinkOptionsEntries, result, uniqueOptions, |
| &dagChecker, config, debugOptions, language); |
| |
| std::vector<cmGeneratorTarget::TargetPropertyEntry*> |
| linkInterfaceLinkOptionsEntries; |
| |
| AddInterfaceEntries(this, config, "INTERFACE_LINK_OPTIONS", |
| linkInterfaceLinkOptionsEntries); |
| |
| processLinkOptions(this, linkInterfaceLinkOptionsEntries, result, |
| uniqueOptions, &dagChecker, config, debugOptions, |
| language); |
| |
| cmDeleteAll(linkInterfaceLinkOptionsEntries); |
| |
| // Last step: replace "LINKER:" prefixed elements by |
| // actual linker wrapper |
| const std::string wrapper(this->Makefile->GetSafeDefinition( |
| "CMAKE_" + language + "_LINKER_WRAPPER_FLAG")); |
| std::vector<std::string> wrapperFlag; |
| cmSystemTools::ExpandListArgument(wrapper, wrapperFlag); |
| const std::string wrapperSep(this->Makefile->GetSafeDefinition( |
| "CMAKE_" + language + "_LINKER_WRAPPER_FLAG_SEP")); |
| bool concatFlagAndArgs = true; |
| if (!wrapperFlag.empty() && wrapperFlag.back() == " ") { |
| concatFlagAndArgs = false; |
| wrapperFlag.pop_back(); |
| } |
| |
| const std::string LINKER{ "LINKER:" }; |
| const std::string SHELL{ "SHELL:" }; |
| const std::string LINKER_SHELL = LINKER + SHELL; |
| |
| std::vector<BT<std::string>>::iterator entry; |
| while ((entry = std::find_if(result.begin(), result.end(), |
| [&LINKER](BT<std::string> const& item) -> bool { |
| return item.Value.compare(0, LINKER.length(), |
| LINKER) == 0; |
| })) != result.end()) { |
| std::string value = std::move(entry->Value); |
| cmListFileBacktrace bt = std::move(entry->Backtrace); |
| entry = result.erase(entry); |
| |
| std::vector<std::string> linkerOptions; |
| if (value.compare(0, LINKER_SHELL.length(), LINKER_SHELL) == 0) { |
| cmSystemTools::ParseUnixCommandLine( |
| value.c_str() + LINKER_SHELL.length(), linkerOptions); |
| } else { |
| linkerOptions = |
| cmSystemTools::tokenize(value.substr(LINKER.length()), ","); |
| } |
| |
| if (linkerOptions.empty() || |
| (linkerOptions.size() == 1 && linkerOptions.front().empty())) { |
| continue; |
| } |
| |
| // for now, raise an error if prefix SHELL: is part of arguments |
| if (std::find_if(linkerOptions.begin(), linkerOptions.end(), |
| [&SHELL](const std::string& item) -> bool { |
| return item.find(SHELL) != std::string::npos; |
| }) != linkerOptions.end()) { |
| this->LocalGenerator->GetCMakeInstance()->IssueMessage( |
| MessageType::FATAL_ERROR, |
| "'SHELL:' prefix is not supported as part of 'LINKER:' arguments.", |
| this->GetBacktrace()); |
| return result; |
| } |
| |
| std::vector<BT<std::string>> options; |
| if (wrapperFlag.empty()) { |
| // nothing specified, insert elements as is |
| options.reserve(linkerOptions.size()); |
| for (std::string& o : linkerOptions) { |
| options.emplace_back(std::move(o), bt); |
| } |
| } else { |
| if (!wrapperSep.empty()) { |
| if (concatFlagAndArgs) { |
| // insert flag elements except last one |
| for (auto i = wrapperFlag.begin(); i != wrapperFlag.end() - 1; ++i) { |
| options.emplace_back(*i, bt); |
| } |
| // concatenate last flag element and all LINKER list values |
| // in one option |
| options.emplace_back( |
| wrapperFlag.back() + cmJoin(linkerOptions, wrapperSep), bt); |
| } else { |
| for (std::string const& i : wrapperFlag) { |
| options.emplace_back(i, bt); |
| } |
| // concatenate all LINKER list values in one option |
| options.emplace_back(cmJoin(linkerOptions, wrapperSep), bt); |
| } |
| } else { |
| // prefix each element of LINKER list with wrapper |
| if (concatFlagAndArgs) { |
| std::transform(linkerOptions.begin(), linkerOptions.end(), |
| linkerOptions.begin(), |
| [&wrapperFlag](std::string const& o) -> std::string { |
| return wrapperFlag.back() + o; |
| }); |
| } |
| for (std::string& o : linkerOptions) { |
| for (auto i = wrapperFlag.begin(), |
| e = concatFlagAndArgs ? wrapperFlag.end() - 1 |
| : wrapperFlag.end(); |
| i != e; ++i) { |
| options.emplace_back(*i, bt); |
| } |
| options.emplace_back(std::move(o), bt); |
| } |
| } |
| } |
| result.insert(entry, options.begin(), options.end()); |
| } |
| return result; |
| } |
| |
| namespace { |
| void processStaticLibraryLinkOptions( |
| cmGeneratorTarget const* tgt, |
| const std::vector<cmGeneratorTarget::TargetPropertyEntry*>& entries, |
| std::vector<BT<std::string>>& options, |
| std::unordered_set<std::string>& uniqueOptions, |
| cmGeneratorExpressionDAGChecker* dagChecker, const std::string& config, |
| std::string const& language) |
| { |
| processOptionsInternal(tgt, entries, options, uniqueOptions, dagChecker, |
| config, false, "static library link options", |
| language, OptionsParse::Shell); |
| } |
| } |
| |
| void cmGeneratorTarget::GetStaticLibraryLinkOptions( |
| std::vector<std::string>& result, const std::string& config, |
| const std::string& language) const |
| { |
| std::vector<BT<std::string>> tmp = |
| this->GetStaticLibraryLinkOptions(config, language); |
| result.reserve(tmp.size()); |
| for (BT<std::string>& v : tmp) { |
| result.emplace_back(std::move(v.Value)); |
| } |
| } |
| |
| std::vector<BT<std::string>> cmGeneratorTarget::GetStaticLibraryLinkOptions( |
| std::string const& config, std::string const& language) const |
| { |
| std::vector<BT<std::string>> result; |
| std::vector<cmGeneratorTarget::TargetPropertyEntry*> entries; |
| std::unordered_set<std::string> uniqueOptions; |
| |
| cmGeneratorExpressionDAGChecker dagChecker(this, "STATIC_LIBRARY_OPTIONS", |
| nullptr, nullptr); |
| |
| if (const char* linkOptions = this->GetProperty("STATIC_LIBRARY_OPTIONS")) { |
| std::vector<std::string> options; |
| cmGeneratorExpression ge; |
| cmSystemTools::ExpandListArgument(linkOptions, options); |
| for (const auto& option : options) { |
| std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(option); |
| entries.push_back( |
| new cmGeneratorTarget::TargetPropertyEntry(std::move(cge))); |
| } |
| } |
| processStaticLibraryLinkOptions(this, entries, result, uniqueOptions, |
| &dagChecker, config, language); |
| |
| cmDeleteAll(entries); |
| return result; |
| } |
| |
| namespace { |
| void processLinkDirectories( |
| cmGeneratorTarget const* tgt, |
| const std::vector<cmGeneratorTarget::TargetPropertyEntry*>& entries, |
| std::vector<BT<std::string>>& directories, |
| std::unordered_set<std::string>& uniqueDirectories, |
| cmGeneratorExpressionDAGChecker* dagChecker, const std::string& config, |
| bool debugDirectories, std::string const& language) |
| { |
| for (cmGeneratorTarget::TargetPropertyEntry* entry : entries) { |
| cmLinkImplItem const& item = entry->LinkImplItem; |
| std::string const& targetName = item.AsStr(); |
| |
| std::vector<std::string> entryDirectories; |
| cmSystemTools::ExpandListArgument( |
| entry->ge->Evaluate(tgt->GetLocalGenerator(), config, false, tgt, |
| dagChecker, language), |
| entryDirectories); |
| |
| std::string usedDirectories; |
| for (std::string& entryDirectory : entryDirectories) { |
| if (!cmSystemTools::FileIsFullPath(entryDirectory)) { |
| std::ostringstream e; |
| bool noMessage = false; |
| MessageType messageType = MessageType::FATAL_ERROR; |
| if (!targetName.empty()) { |
| /* clang-format off */ |
| e << "Target \"" << targetName << "\" contains relative " |
| "path in its INTERFACE_LINK_DIRECTORIES:\n" |
| " \"" << entryDirectory << "\""; |
| /* clang-format on */ |
| } else { |
| switch (tgt->GetPolicyStatusCMP0081()) { |
| case cmPolicies::WARN: { |
| e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0081) << "\n"; |
| messageType = MessageType::AUTHOR_WARNING; |
| } break; |
| case cmPolicies::OLD: |
| noMessage = true; |
| break; |
| case cmPolicies::REQUIRED_IF_USED: |
| case cmPolicies::REQUIRED_ALWAYS: |
| case cmPolicies::NEW: |
| // Issue the fatal message. |
| break; |
| } |
| e << "Found relative path while evaluating link directories of " |
| "\"" |
| << tgt->GetName() << "\":\n \"" << entryDirectory << "\"\n"; |
| } |
| if (!noMessage) { |
| tgt->GetLocalGenerator()->IssueMessage(messageType, e.str()); |
| if (messageType == MessageType::FATAL_ERROR) { |
| return; |
| } |
| } |
| } |
| |
| // Sanitize the path the same way the link_directories command does |
| // in case projects set the LINK_DIRECTORIES property directly. |
| cmSystemTools::ConvertToUnixSlashes(entryDirectory); |
| if (uniqueDirectories.insert(entryDirectory).second) { |
| directories.emplace_back(entryDirectory); |
| if (debugDirectories) { |
| usedDirectories += " * " + entryDirectory + "\n"; |
| } |
| } |
| } |
| if (!usedDirectories.empty()) { |
| tgt->GetLocalGenerator()->GetCMakeInstance()->IssueMessage( |
| MessageType::LOG, |
| std::string("Used link directories for target ") + tgt->GetName() + |
| ":\n" + usedDirectories, |
| entry->ge->GetBacktrace()); |
| } |
| } |
| } |
| } |
| |
| void cmGeneratorTarget::GetLinkDirectories(std::vector<std::string>& result, |
| const std::string& config, |
| const std::string& language) const |
| { |
| std::vector<BT<std::string>> tmp = |
| this->GetLinkDirectories(config, language); |
| result.reserve(tmp.size()); |
| for (BT<std::string>& v : tmp) { |
| result.emplace_back(std::move(v.Value)); |
| } |
| } |
| |
| std::vector<BT<std::string>> cmGeneratorTarget::GetLinkDirectories( |
| std::string const& config, std::string const& language) const |
| { |
| std::vector<BT<std::string>> result; |
| std::unordered_set<std::string> uniqueDirectories; |
| |
| cmGeneratorExpressionDAGChecker dagChecker(this, "LINK_DIRECTORIES", nullptr, |
| nullptr); |
| |
| std::vector<std::string> debugProperties; |
| const char* debugProp = |
| this->Makefile->GetDefinition("CMAKE_DEBUG_TARGET_PROPERTIES"); |
| if (debugProp) { |
| cmSystemTools::ExpandListArgument(debugProp, debugProperties); |
| } |
| |
| bool debugDirectories = !this->DebugLinkDirectoriesDone && |
| std::find(debugProperties.begin(), debugProperties.end(), |
| "LINK_DIRECTORIES") != debugProperties.end(); |
| |
| if (this->GlobalGenerator->GetConfigureDoneCMP0026()) { |
| this->DebugLinkDirectoriesDone = true; |
| } |
| |
| processLinkDirectories(this, this->LinkDirectoriesEntries, result, |
| uniqueDirectories, &dagChecker, config, |
| debugDirectories, language); |
| |
| std::vector<cmGeneratorTarget::TargetPropertyEntry*> |
| linkInterfaceLinkDirectoriesEntries; |
| |
| AddInterfaceEntries(this, config, "INTERFACE_LINK_DIRECTORIES", |
| linkInterfaceLinkDirectoriesEntries); |
| |
| processLinkDirectories(this, linkInterfaceLinkDirectoriesEntries, result, |
| uniqueDirectories, &dagChecker, config, |
| debugDirectories, language); |
| |
| cmDeleteAll(linkInterfaceLinkDirectoriesEntries); |
| return result; |
| } |
| |
| namespace { |
| void processLinkDepends( |
| cmGeneratorTarget const* tgt, |
| const std::vector<cmGeneratorTarget::TargetPropertyEntry*>& entries, |
| std::vector<BT<std::string>>& options, |
| std::unordered_set<std::string>& uniqueOptions, |
| cmGeneratorExpressionDAGChecker* dagChecker, const std::string& config, |
| std::string const& language) |
| { |
| processOptionsInternal(tgt, entries, options, uniqueOptions, dagChecker, |
| config, false, "link depends", language, |
| OptionsParse::None); |
| } |
| } |
| |
| void cmGeneratorTarget::GetLinkDepends(std::vector<std::string>& result, |
| const std::string& config, |
| const std::string& language) const |
| { |
| std::vector<BT<std::string>> tmp = this->GetLinkDepends(config, language); |
| result.reserve(tmp.size()); |
| for (BT<std::string>& v : tmp) { |
| result.emplace_back(std::move(v.Value)); |
| } |
| } |
| |
| std::vector<BT<std::string>> cmGeneratorTarget::GetLinkDepends( |
| std::string const& config, std::string const& language) const |
| { |
| std::vector<BT<std::string>> result; |
| std::vector<cmGeneratorTarget::TargetPropertyEntry*> linkDependsEntries; |
| std::unordered_set<std::string> uniqueOptions; |
| cmGeneratorExpressionDAGChecker dagChecker(this, "LINK_DEPENDS", nullptr, |
| nullptr); |
| |
| if (const char* linkDepends = this->GetProperty("LINK_DEPENDS")) { |
| std::vector<std::string> depends; |
| cmGeneratorExpression ge; |
| cmSystemTools::ExpandListArgument(linkDepends, depends); |
| for (const auto& depend : depends) { |
| std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(depend); |
| linkDependsEntries.push_back( |
| new cmGeneratorTarget::TargetPropertyEntry(std::move(cge))); |
| } |
| } |
| AddInterfaceEntries(this, config, "INTERFACE_LINK_DEPENDS", |
| linkDependsEntries); |
| processLinkDepends(this, linkDependsEntries, result, uniqueOptions, |
| &dagChecker, config, language); |
| |
| cmDeleteAll(linkDependsEntries); |
| return result; |
| } |
| |
| void cmGeneratorTarget::ComputeTargetManifest(const std::string& config) const |
| { |
| if (this->IsImported()) { |
| return; |
| } |
| cmGlobalGenerator* gg = this->LocalGenerator->GetGlobalGenerator(); |
| |
| // Get the names. |
| std::string name; |
| std::string soName; |
| std::string realName; |
| std::string impName; |
| std::string pdbName; |
| if (this->GetType() == cmStateEnums::EXECUTABLE) { |
| this->GetExecutableNames(name, realName, impName, pdbName, config); |
| } else if (this->GetType() == cmStateEnums::STATIC_LIBRARY || |
| this->GetType() == cmStateEnums::SHARED_LIBRARY || |
| this->GetType() == cmStateEnums::MODULE_LIBRARY) { |
| this->GetLibraryNames(name, soName, realName, impName, pdbName, config); |
| } else { |
| return; |
| } |
| |
| // Get the directory. |
| std::string dir = |
| this->GetDirectory(config, cmStateEnums::RuntimeBinaryArtifact); |
| |
| // Add each name. |
| std::string f; |
| if (!name.empty()) { |
| f = dir; |
| f += "/"; |
| f += name; |
| gg->AddToManifest(f); |
| } |
| if (!soName.empty()) { |
| f = dir; |
| f += "/"; |
| f += soName; |
| gg->AddToManifest(f); |
| } |
| if (!realName.empty()) { |
| f = dir; |
| f += "/"; |
| f += realName; |
| gg->AddToManifest(f); |
| } |
| if (!pdbName.empty()) { |
| f = dir; |
| f += "/"; |
| f += pdbName; |
| gg->AddToManifest(f); |
| } |
| if (!impName.empty()) { |
| f = this->GetDirectory(config, cmStateEnums::ImportLibraryArtifact); |
| f += "/"; |
| f += impName; |
| gg->AddToManifest(f); |
| } |
| } |
| |
| bool cmGeneratorTarget::ComputeCompileFeatures(std::string const& config) const |
| { |
| std::vector<BT<std::string>> features = this->GetCompileFeatures(config); |
| for (BT<std::string> const& f : features) { |
| if (!this->Makefile->AddRequiredTargetFeature(this->Target, f.Value)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| std::string cmGeneratorTarget::GetImportedLibName( |
| std::string const& config) const |
| { |
| if (cmGeneratorTarget::ImportInfo const* info = |
| this->GetImportInfo(config)) { |
| return info->LibName; |
| } |
| return std::string(); |
| } |
| |
| std::string cmGeneratorTarget::GetFullPath(const std::string& config, |
| cmStateEnums::ArtifactType artifact, |
| bool realname) const |
| { |
| if (this->IsImported()) { |
| return this->Target->ImportedGetFullPath(config, artifact); |
| } |
| return this->NormalGetFullPath(config, artifact, realname); |
| } |
| |
| std::string cmGeneratorTarget::NormalGetFullPath( |
| const std::string& config, cmStateEnums::ArtifactType artifact, |
| bool realname) const |
| { |
| std::string fpath = this->GetDirectory(config, artifact); |
| fpath += "/"; |
| if (this->IsAppBundleOnApple()) { |
| fpath = this->BuildBundleDirectory(fpath, config, FullLevel); |
| fpath += "/"; |
| } |
| |
| // Add the full name of the target. |
| switch (artifact) { |
| case cmStateEnums::RuntimeBinaryArtifact: |
| if (realname) { |
| fpath += this->NormalGetRealName(config); |
| } else { |
| fpath += |
| this->GetFullName(config, cmStateEnums::RuntimeBinaryArtifact); |
| } |
| break; |
| case cmStateEnums::ImportLibraryArtifact: |
| fpath += this->GetFullName(config, cmStateEnums::ImportLibraryArtifact); |
| break; |
| } |
| return fpath; |
| } |
| |
| std::string cmGeneratorTarget::NormalGetRealName( |
| const std::string& config) const |
| { |
| // This should not be called for imported targets. |
| // TODO: Split cmTarget into a class hierarchy to get compile-time |
| // enforcement of the limited imported target API. |
| if (this->IsImported()) { |
| std::string msg = "NormalGetRealName called on imported target: "; |
| msg += this->GetName(); |
| this->LocalGenerator->IssueMessage(MessageType::INTERNAL_ERROR, msg); |
| } |
| |
| if (this->GetType() == cmStateEnums::EXECUTABLE) { |
| // Compute the real name that will be built. |
| std::string name; |
| std::string realName; |
| std::string impName; |
| std::string pdbName; |
| this->GetExecutableNames(name, realName, impName, pdbName, config); |
| return realName; |
| } |
| // Compute the real name 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 realName; |
| } |
| |
| void cmGeneratorTarget::GetLibraryNames(std::string& name, std::string& soName, |
| std::string& realName, |
| std::string& impName, |
| std::string& pdbName, |
| const std::string& config) const |
| { |
| // This should not be called for imported targets. |
| // TODO: Split cmTarget into a class hierarchy to get compile-time |
| // enforcement of the limited imported target API. |
| if (this->IsImported()) { |
| std::string msg = "GetLibraryNames called on imported target: "; |
| msg += this->GetName(); |
| this->LocalGenerator->IssueMessage(MessageType::INTERNAL_ERROR, msg); |
| return; |
| } |
| |
| // Check for library version properties. |
| const char* version = this->GetProperty("VERSION"); |
| const char* soversion = this->GetProperty("SOVERSION"); |
| if (!this->HasSOName(config) || |
| this->Makefile->IsOn("CMAKE_PLATFORM_NO_VERSIONED_SONAME") || |
| this->IsFrameworkOnApple()) { |
| // Versioning is supported only for shared libraries and modules, |
| // and then only when the platform supports an soname flag. |
| version = nullptr; |
| soversion = nullptr; |
| } |
| if (version && !soversion) { |
| // The soversion must be set if the library version is set. Use |
| // the library version as the soversion. |
| soversion = version; |
| } |
| if (!version && soversion) { |
| // Use the soversion as the library version. |
| version = soversion; |
| } |
| |
| // Get the components of the library name. |
| std::string prefix; |
| std::string base; |
| std::string suffix; |
| this->GetFullNameInternal(config, cmStateEnums::RuntimeBinaryArtifact, |
| prefix, base, suffix); |
| |
| // The library name. |
| name = prefix + base + suffix; |
| |
| if (this->IsFrameworkOnApple()) { |
| realName = prefix; |
| if (!this->Makefile->PlatformIsAppleEmbedded()) { |
| realName += "Versions/"; |
| realName += this->GetFrameworkVersion(); |
| realName += "/"; |
| } |
| realName += base; |
| soName = realName; |
| } else { |
| // The library's soname. |
| this->ComputeVersionedName(soName, prefix, base, suffix, name, soversion); |
| |
| // The library's real name on disk. |
| this->ComputeVersionedName(realName, prefix, base, suffix, name, version); |
| } |
| |
| // The import library name. |
| if (this->GetType() == cmStateEnums::SHARED_LIBRARY || |
| this->GetType() == cmStateEnums::MODULE_LIBRARY) { |
| impName = |
| this->GetFullNameInternal(config, cmStateEnums::ImportLibraryArtifact); |
| } else { |
| impName.clear(); |
| } |
| |
| // The program database file name. |
| pdbName = this->GetPDBName(config); |
| } |
| |
| void cmGeneratorTarget::GetExecutableNames(std::string& name, |
| std::string& realName, |
| std::string& impName, |
| std::string& pdbName, |
| const std::string& config) const |
| { |
| // This should not be called for imported targets. |
| // TODO: Split cmTarget into a class hierarchy to get compile-time |
| // enforcement of the limited imported target API. |
| if (this->IsImported()) { |
| std::string msg = "GetExecutableNames called on imported target: "; |
| msg += this->GetName(); |
| this->LocalGenerator->IssueMessage(MessageType::INTERNAL_ERROR, msg); |
| } |
| |
| // This versioning is supported only for executables and then only |
| // when the platform supports symbolic links. |
| #if defined(_WIN32) && !defined(__CYGWIN__) |
| const char* version = 0; |
| #else |
| // Check for executable version properties. |
| const char* version = this->GetProperty("VERSION"); |
| if (this->GetType() != cmStateEnums::EXECUTABLE || |
| this->Makefile->IsOn("XCODE")) { |
| version = nullptr; |
| } |
| #endif |
| |
| // Get the components of the executable name. |
| std::string prefix; |
| std::string base; |
| std::string suffix; |
| this->GetFullNameInternal(config, cmStateEnums::RuntimeBinaryArtifact, |
| prefix, base, suffix); |
| |
| // The executable name. |
| name = prefix + base + suffix; |
| |
| // The executable's real name on disk. |
| #if defined(__CYGWIN__) |
| realName = prefix + base; |
| #else |
| realName = name; |
| #endif |
| if (version) { |
| realName += "-"; |
| realName += version; |
| } |
| #if defined(__CYGWIN__) |
| realName += suffix; |
| #endif |
| |
| // The import library name. |
| impName = |
| this->GetFullNameInternal(config, cmStateEnums::ImportLibraryArtifact); |
| |
| // The program database file name. |
| pdbName = this->GetPDBName(config); |
| } |
| |
| std::string cmGeneratorTarget::GetFullNameInternal( |
| const std::string& config, cmStateEnums::ArtifactType artifact) const |
| { |
| std::string prefix; |
| std::string base; |
| std::string suffix; |
| this->GetFullNameInternal(config, artifact, prefix, base, suffix); |
| return prefix + base + suffix; |
| } |
| |
| std::string cmGeneratorTarget::ImportedGetLocation( |
| const std::string& config) const |
| { |
| assert(this->IsImported()); |
| return this->Target->ImportedGetFullPath( |
| config, cmStateEnums::RuntimeBinaryArtifact); |
| } |
| |
| std::string cmGeneratorTarget::GetFullNameImported( |
| const std::string& config, cmStateEnums::ArtifactType artifact) const |
| { |
| return cmSystemTools::GetFilenameName( |
| this->Target->ImportedGetFullPath(config, artifact)); |
| } |
| |
| void cmGeneratorTarget::GetFullNameInternal( |
| const std::string& config, cmStateEnums::ArtifactType artifact, |
| std::string& outPrefix, std::string& outBase, std::string& outSuffix) const |
| { |
| // Use just the target name for non-main target types. |
| if (this->GetType() != cmStateEnums::STATIC_LIBRARY && |
| this->GetType() != cmStateEnums::SHARED_LIBRARY && |
| this->GetType() != cmStateEnums::MODULE_LIBRARY && |
| this->GetType() != cmStateEnums::EXECUTABLE) { |
| outPrefix.clear(); |
| outBase = this->GetName(); |
| outSuffix.clear(); |
| return; |
| } |
| |
| const bool isImportedLibraryArtifact = |
| (artifact == cmStateEnums::ImportLibraryArtifact); |
| |
| // Return an empty name for the import library if this platform |
| // does not support import libraries. |
| if (isImportedLibraryArtifact && |
| !this->Makefile->GetDefinition("CMAKE_IMPORT_LIBRARY_SUFFIX")) { |
| outPrefix.clear(); |
| outBase.clear(); |
| outSuffix.clear(); |
| return; |
| } |
| |
| // The implib option is only allowed for shared libraries, module |
| // libraries, and executables. |
| if (this->GetType() != cmStateEnums::SHARED_LIBRARY && |
| this->GetType() != cmStateEnums::MODULE_LIBRARY && |
| this->GetType() != cmStateEnums::EXECUTABLE) { |
| artifact = cmStateEnums::RuntimeBinaryArtifact; |
| } |
| |
| // Compute the full name for main target types. |
| const char* targetPrefix = |
| (isImportedLibraryArtifact ? this->GetProperty("IMPORT_PREFIX") |
| : this->GetProperty("PREFIX")); |
| const char* targetSuffix = |
| (isImportedLibraryArtifact ? this->GetProperty("IMPORT_SUFFIX") |
| : this->GetProperty("SUFFIX")); |
| const char* configPostfix = nullptr; |
| if (!config.empty()) { |
| std::string configProp = cmSystemTools::UpperCase(config); |
| configProp += "_POSTFIX"; |
| configPostfix = this->GetProperty(configProp); |
| // Mac application bundles and frameworks have no postfix. |
| if (configPostfix && |
| (this->IsAppBundleOnApple() || this->IsFrameworkOnApple())) { |
| configPostfix = nullptr; |
| } |
| } |
| const char* prefixVar = this->Target->GetPrefixVariableInternal(artifact); |
| const char* suffixVar = this->Target->GetSuffixVariableInternal(artifact); |
| |
| // Check for language-specific default prefix and suffix. |
| std::string ll = this->GetLinkerLanguage(config); |
| if (!ll.empty()) { |
| if (!targetSuffix && suffixVar && *suffixVar) { |
| std::string langSuff = suffixVar + std::string("_") + ll; |
| targetSuffix = this->Makefile->GetDefinition(langSuff); |
| } |
| if (!targetPrefix && prefixVar && *prefixVar) { |
| std::string langPrefix = prefixVar + std::string("_") + ll; |
| targetPrefix = this->Makefile->GetDefinition(langPrefix); |
| } |
| } |
| |
| // if there is no prefix on the target use the cmake definition |
| if (!targetPrefix && prefixVar) { |
| targetPrefix = this->Makefile->GetSafeDefinition(prefixVar).c_str(); |
| } |
| // if there is no suffix on the target use the cmake definition |
| if (!targetSuffix && suffixVar) { |
| targetSuffix = this->Makefile->GetSafeDefinition(suffixVar).c_str(); |
| } |
| |
| // frameworks have directory prefix but no suffix |
| std::string fw_prefix; |
| if (this->IsFrameworkOnApple()) { |
| fw_prefix = this->GetFrameworkDirectory(config, ContentLevel); |
| fw_prefix += "/"; |
| targetPrefix = fw_prefix.c_str(); |
| targetSuffix = nullptr; |
| } |
| |
| if (this->IsCFBundleOnApple()) { |
| fw_prefix = this->GetCFBundleDirectory(config, FullLevel); |
| fw_prefix += "/"; |
| targetPrefix = fw_prefix.c_str(); |
| targetSuffix = nullptr; |
| } |
| |
| // Begin the final name with the prefix. |
| outPrefix = targetPrefix ? targetPrefix : ""; |
| |
| // Append the target name or property-specified name. |
| outBase += this->GetOutputName(config, artifact); |
| |
| // Append the per-configuration postfix. |
| outBase += configPostfix ? configPostfix : ""; |
| |
| // Name shared libraries with their version number on some platforms. |
| if (const char* soversion = this->GetProperty("SOVERSION")) { |
| if (this->GetType() == cmStateEnums::SHARED_LIBRARY && |
| !isImportedLibraryArtifact && |
| this->Makefile->IsOn("CMAKE_SHARED_LIBRARY_NAME_WITH_VERSION")) { |
| outBase += "-"; |
| outBase += soversion; |
| } |
| } |
| |
| // Append the suffix. |
| outSuffix = targetSuffix ? targetSuffix : ""; |
| } |
| |
| std::string cmGeneratorTarget::GetLinkerLanguage( |
| const std::string& config) const |
| { |
| return this->GetLinkClosure(config)->LinkerLanguage; |
| } |
| |
| std::string cmGeneratorTarget::GetPDBName(const std::string& config) const |
| { |
| std::string prefix; |
| std::string base; |
| std::string suffix; |
| this->GetFullNameInternal(config, cmStateEnums::RuntimeBinaryArtifact, |
| prefix, base, suffix); |
| |
| std::vector<std::string> props; |
| std::string configUpper = cmSystemTools::UpperCase(config); |
| if (!configUpper.empty()) { |
| // PDB_NAME_<CONFIG> |
| props.push_back("PDB_NAME_" + configUpper); |
| } |
| |
| // PDB_NAME |
| props.emplace_back("PDB_NAME"); |
| |
| for (std::string const& p : props) { |
| if (const char* outName = this->GetProperty(p)) { |
| base = outName; |
| break; |
| } |
| } |
| return prefix + base + ".pdb"; |
| } |
| |
| std::string cmGeneratorTarget::GetObjectDirectory( |
| std::string const& config) const |
| { |
| std::string obj_dir = |
| this->GlobalGenerator->ExpandCFGIntDir(this->ObjectDirectory, config); |
| #if defined(__APPLE__) |
| // find and replace $(PROJECT_NAME) xcode placeholder |
| const std::string projectName = this->LocalGenerator->GetProjectName(); |
| cmSystemTools::ReplaceString(obj_dir, "$(PROJECT_NAME)", projectName); |
| // Replace Xcode's placeholder for the object file directory since |
| // installation and export scripts need to know the real directory. |
| // Xcode has build-time settings (e.g. for sanitizers) that affect this, |
| // but we use the default here. Users that want to enable sanitizers |
| // will do so at the cost of object library installation and export. |
| cmSystemTools::ReplaceString(obj_dir, "$(OBJECT_FILE_DIR_normal:base)", |
| "Objects-normal"); |
| #endif |
| return obj_dir; |
| } |
| |
| void cmGeneratorTarget::GetTargetObjectNames( |
| std::string const& config, std::vector<std::string>& objects) const |
| { |
| std::vector<cmSourceFile const*> objectSources; |
| this->GetObjectSources(objectSources, config); |
| std::map<cmSourceFile const*, std::string> mapping; |
| |
| for (cmSourceFile const* sf : objectSources) { |
| mapping[sf]; |
| } |
| |
| this->LocalGenerator->ComputeObjectFilenames(mapping, this); |
| |
| for (cmSourceFile const* src : objectSources) { |
| // Find the object file name corresponding to this source file. |
| std::map<cmSourceFile const*, std::string>::const_iterator map_it = |
| mapping.find(src); |
| // It must exist because we populated the mapping just above. |
| assert(!map_it->second.empty()); |
| objects.push_back(map_it->second); |
| } |
| } |
| |
| bool cmGeneratorTarget::StrictTargetComparison::operator()( |
| cmGeneratorTarget const* t1, cmGeneratorTarget const* t2) const |
| { |
| int nameResult = strcmp(t1->GetName().c_str(), t2->GetName().c_str()); |
| if (nameResult == 0) { |
| return strcmp( |
| t1->GetLocalGenerator()->GetCurrentBinaryDirectory().c_str(), |
| t2->GetLocalGenerator()->GetCurrentBinaryDirectory().c_str()) < 0; |
| } |
| return nameResult < 0; |
| } |
| |
| struct cmGeneratorTarget::SourceFileFlags |
| cmGeneratorTarget::GetTargetSourceFileFlags(const cmSourceFile* sf) const |
| { |
| struct SourceFileFlags flags; |
| this->ConstructSourceFileFlags(); |
| std::map<cmSourceFile const*, SourceFileFlags>::iterator si = |
| this->SourceFlagsMap.find(sf); |
| if (si != this->SourceFlagsMap.end()) { |
| flags = si->second; |
| } else { |
| // Handle the MACOSX_PACKAGE_LOCATION property on source files that |
| // were not listed in one of the other lists. |
| if (const char* location = sf->GetProperty("MACOSX_PACKAGE_LOCATION")) { |
| flags.MacFolder = location; |
| const bool stripResources = |
| this->GlobalGenerator->ShouldStripResourcePath(this->Makefile); |
| if (strcmp(location, "Resources") == 0) { |
| flags.Type = cmGeneratorTarget::SourceFileTypeResource; |
| if (stripResources) { |
| flags.MacFolder = ""; |
| } |
| } else if (cmSystemTools::StringStartsWith(location, "Resources/")) { |
| flags.Type = cmGeneratorTarget::SourceFileTypeDeepResource; |
| if (stripResources) { |
| flags.MacFolder += strlen("Resources/"); |
| } |
| } else { |
| flags.Type = cmGeneratorTarget::SourceFileTypeMacContent; |
| } |
| } |
| } |
| return flags; |
| } |
| |
| void cmGeneratorTarget::ConstructSourceFileFlags() const |
| { |
| if (this->SourceFileFlagsConstructed) { |
| return; |
| } |
| this->SourceFileFlagsConstructed = true; |
| |
| // Process public headers to mark the source files. |
| if (const char* files = this->GetProperty("PUBLIC_HEADER")) { |
| std::vector<std::string> relFiles; |
| cmSystemTools::ExpandListArgument(files, relFiles); |
| for (std::string const& relFile : relFiles) { |
| if (cmSourceFile* sf = this->Makefile->GetSource(relFile)) { |
| SourceFileFlags& flags = this->SourceFlagsMap[sf]; |
| flags.MacFolder = "Headers"; |
| flags.Type = cmGeneratorTarget::SourceFileTypePublicHeader; |
| } |
| } |
| } |
| |
| // Process private headers after public headers so that they take |
| // precedence if a file is listed in both. |
| if (const char* files = this->GetProperty("PRIVATE_HEADER")) { |
| std::vector<std::string> relFiles; |
| cmSystemTools::ExpandListArgument(files, relFiles); |
| for (std::string const& relFile : relFiles) { |
| if (cmSourceFile* sf = this->Makefile->GetSource(relFile)) { |
| SourceFileFlags& flags = this->SourceFlagsMap[sf]; |
| flags.MacFolder = "PrivateHeaders"; |
| flags.Type = cmGeneratorTarget::SourceFileTypePrivateHeader; |
| } |
| } |
| } |
| |
| // Mark sources listed as resources. |
| if (const char* files = this->GetProperty("RESOURCE")) { |
| std::vector<std::string> relFiles; |
| cmSystemTools::ExpandListArgument(files, relFiles); |
| for (std::string const& relFile : relFiles) { |
| if (cmSourceFile* sf = this->Makefile->GetSource(relFile)) { |
| SourceFileFlags& flags = this->SourceFlagsMap[sf]; |
| flags.MacFolder = ""; |
| if (!this->GlobalGenerator->ShouldStripResourcePath(this->Makefile)) { |
| flags.MacFolder = "Resources"; |
| } |
| flags.Type = cmGeneratorTarget::SourceFileTypeResource; |
| } |
| } |
| } |
| } |
| |
| const cmGeneratorTarget::CompatibleInterfacesBase& |
| cmGeneratorTarget::GetCompatibleInterfaces(std::string const& config) const |
| { |
| cmGeneratorTarget::CompatibleInterfaces& compat = |
| this->CompatibleInterfacesMap[config]; |
| if (!compat.Done) { |
| compat.Done = true; |
| compat.PropsBool.insert("POSITION_INDEPENDENT_CODE"); |
| compat.PropsString.insert("AUTOUIC_OPTIONS"); |
| std::vector<cmGeneratorTarget const*> const& deps = |
| this->GetLinkImplementationClosure(config); |
| for (cmGeneratorTarget const* li : deps) { |
| #define CM_READ_COMPATIBLE_INTERFACE(X, x) \ |
| if (const char* prop = li->GetProperty("COMPATIBLE_INTERFACE_" #X)) { \ |
| std::vector<std::string> props; \ |
| cmSystemTools::ExpandListArgument(prop, props); \ |
| compat.Props##x.insert(props.begin(), props.end()); \ |
| } |
| CM_READ_COMPATIBLE_INTERFACE(BOOL, Bool) |
| CM_READ_COMPATIBLE_INTERFACE(STRING, String) |
| CM_READ_COMPATIBLE_INTERFACE(NUMBER_MIN, NumberMin) |
| CM_READ_COMPATIBLE_INTERFACE(NUMBER_MAX, NumberMax) |
| #undef CM_READ_COMPATIBLE_INTERFACE |
| } |
| } |
| return compat; |
| } |
| |
| bool cmGeneratorTarget::IsLinkInterfaceDependentBoolProperty( |
| const std::string& p, const std::string& config) const |
| { |
| if (this->GetType() == cmStateEnums::OBJECT_LIBRARY || |
| this->GetType() == cmStateEnums::INTERFACE_LIBRARY) { |
| return false; |
| } |
| return this->GetCompatibleInterfaces(config).PropsBool.count(p) > 0; |
| } |
| |
| bool cmGeneratorTarget::IsLinkInterfaceDependentStringProperty( |
| const std::string& p, const std::string& config) const |
| { |
| if (this->GetType() == cmStateEnums::OBJECT_LIBRARY || |
| this->GetType() == cmStateEnums::INTERFACE_LIBRARY) { |
| return false; |
| } |
| return this->GetCompatibleInterfaces(config).PropsString.count(p) > 0; |
| } |
| |
| bool cmGeneratorTarget::IsLinkInterfaceDependentNumberMinProperty( |
| const std::string& p, const std::string& config) const |
| { |
| if (this->GetType() == cmStateEnums::OBJECT_LIBRARY || |
| this->GetType() == cmStateEnums::INTERFACE_LIBRARY) { |
| return false; |
| } |
| return this->GetCompatibleInterfaces(config).PropsNumberMin.count(p) > 0; |
| } |
| |
| bool cmGeneratorTarget::IsLinkInterfaceDependentNumberMaxProperty( |
| const std::string& p, const std::string& config) const |
| { |
| if (this->GetType() == cmStateEnums::OBJECT_LIBRARY || |
| this->GetType() == cmStateEnums::INTERFACE_LIBRARY) { |
| return false; |
| } |
| return this->GetCompatibleInterfaces(config).PropsNumberMax.count(p) > 0; |
| } |
| |
| enum CompatibleType |
| { |
| BoolType, |
| StringType, |
| NumberMinType, |
| NumberMaxType |
| }; |
| |
| template <typename PropertyType> |
| PropertyType getLinkInterfaceDependentProperty(cmGeneratorTarget const* tgt, |
| const std::string& prop, |
| const std::string& config, |
| CompatibleType, PropertyType*); |
| |
| template <> |
| bool getLinkInterfaceDependentProperty(cmGeneratorTarget const* tgt, |
| const std::string& prop, |
| const std::string& config, |
| CompatibleType /*unused*/, |
| bool* /*unused*/) |
| { |
| return tgt->GetLinkInterfaceDependentBoolProperty(prop, config); |
| } |
| |
| template <> |
| const char* getLinkInterfaceDependentProperty(cmGeneratorTarget const* tgt, |
| const std::string& prop, |
| const std::string& config, |
| CompatibleType t, |
| const char** /*unused*/) |
| { |
| switch (t) { |
| case BoolType: |
| assert(false && |
| "String compatibility check function called for boolean"); |
| return nullptr; |
| case StringType: |
| return tgt->GetLinkInterfaceDependentStringProperty(prop, config); |
| case NumberMinType: |
| return tgt->GetLinkInterfaceDependentNumberMinProperty(prop, config); |
| case NumberMaxType: |
| return tgt->GetLinkInterfaceDependentNumberMaxProperty(prop, config); |
| } |
| assert(false && "Unreachable!"); |
| return nullptr; |
| } |
| |
| template <typename PropertyType> |
| void checkPropertyConsistency(cmGeneratorTarget const* depender, |
| cmGeneratorTarget const* dependee, |
| const std::string& propName, |
| std::set<std::string>& emitted, |
| const std::string& config, CompatibleType t, |
| PropertyType* /*unused*/) |
| { |
| const char* prop = dependee->GetProperty(propName); |
| if (!prop) { |
| return; |
| } |
| |
| std::vector<std::string> props; |
| cmSystemTools::ExpandListArgument(prop, props); |
| std::string pdir = cmSystemTools::GetCMakeRoot(); |
| pdir += "/Help/prop_tgt/"; |
| |
| for (std::string const& p : props) { |
| std::string pname = cmSystemTools::HelpFileName(p); |
| std::string pfile = pdir + pname + ".rst"; |
| if (cmSystemTools::FileExists(pfile, true)) { |
| std::ostringstream e; |
| e << "Target \"" << dependee->GetName() << "\" has property \"" << p |
| << "\" listed in its " << propName |
| << " property. " |
| "This is not allowed. Only user-defined properties may appear " |
| "listed in the " |
| << propName << " property."; |
| depender->GetLocalGenerator()->IssueMessage(MessageType::FATAL_ERROR, |
| e.str()); |
| return; |
| } |
| if (emitted.insert(p).second) { |
| getLinkInterfaceDependentProperty<PropertyType>(depender, p, config, t, |
| nullptr); |
| if (cmSystemTools::GetErrorOccuredFlag()) { |
| return; |
| } |
| } |
| } |
| } |
| |
| static std::string intersect(const std::set<std::string>& s1, |
| const std::set<std::string>& s2) |
| { |
| std::set<std::string> intersect; |
| std::set_intersection(s1.begin(), s1.end(), s2.begin(), s2.end(), |
| std::inserter(intersect, intersect.begin())); |
| if (!intersect.empty()) { |
| return *intersect.begin(); |
| } |
| return ""; |
| } |
| |
| static std::string intersect(const std::set<std::string>& s1, |
| const std::set<std::string>& s2, |
| const std::set<std::string>& s3) |
| { |
| std::string result; |
| result = intersect(s1, s2); |
| if (!result.empty()) { |
| return result; |
| } |
| result = intersect(s1, s3); |
| if (!result.empty()) { |
| return result; |
| } |
| return intersect(s2, s3); |
| } |
| |
| static std::string intersect(const std::set<std::string>& s1, |
| const std::set<std::string>& s2, |
| const std::set<std::string>& s3, |
| const std::set<std::string>& s4) |
| { |
| std::string result; |
| result = intersect(s1, s2); |
| if (!result.empty()) { |
| return result; |
| } |
| result = intersect(s1, s3); |
| if (!result.empty()) { |
| return result; |
| } |
| result = intersect(s1, s4); |
| if (!result.empty()) { |
| return result; |
| } |
| return intersect(s2, s3, s4); |
| } |
| |
| void cmGeneratorTarget::CheckPropertyCompatibility( |
| cmComputeLinkInformation* info, const std::string& config) const |
| { |
| const cmComputeLinkInformation::ItemVector& deps = info->GetItems(); |
| |
| std::set<std::string> emittedBools; |
| static const std::string strBool = "COMPATIBLE_INTERFACE_BOOL"; |
| std::set<std::string> emittedStrings; |
| static const std::string strString = "COMPATIBLE_INTERFACE_STRING"; |
| std::set<std::string> emittedMinNumbers; |
| static const std::string strNumMin = "COMPATIBLE_INTERFACE_NUMBER_MIN"; |
| std::set<std::string> emittedMaxNumbers; |
| static const std::string strNumMax = "COMPATIBLE_INTERFACE_NUMBER_MAX"; |
| |
| for (auto const& dep : deps) { |
| if (!dep.Target) { |
| continue; |
| } |
| |
| checkPropertyConsistency<bool>(this, dep.Target, strBool, emittedBools, |
| config, BoolType, nullptr); |
| if (cmSystemTools::GetErrorOccuredFlag()) { |
| return; |
| } |
| checkPropertyConsistency<const char*>(this, dep.Target, strString, |
| emittedStrings, config, StringType, |
| nullptr); |
| if (cmSystemTools::GetErrorOccuredFlag()) { |
| return; |
| } |
| checkPropertyConsistency<const char*>(this, dep.Target, strNumMin, |
| emittedMinNumbers, config, |
| NumberMinType, nullptr); |
| if (cmSystemTools::GetErrorOccuredFlag()) { |
| return; |
| } |
| checkPropertyConsistency<const char*>(this, dep.Target, strNumMax, |
| emittedMaxNumbers, config, |
| NumberMaxType, nullptr); |
| if (cmSystemTools::GetErrorOccuredFlag()) { |
| return; |
| } |
| } |
| |
| std::string prop = intersect(emittedBools, emittedStrings, emittedMinNumbers, |
| emittedMaxNumbers); |
| |
| if (!prop.empty()) { |
| // Use a sorted std::vector to keep the error message sorted. |
| std::vector<std::string> props; |
| std::set<std::string>::const_iterator i = emittedBools.find(prop); |
| if (i != emittedBools.end()) { |
| props.push_back(strBool); |
| } |
| i = emittedStrings.find(prop); |
| if (i != emittedStrings.end()) { |
| props.push_back(strString); |
| } |
| i = emittedMinNumbers.find(prop); |
| if (i != emittedMinNumbers.end()) { |
| props.push_back(strNumMin); |
| } |
| i = emittedMaxNumbers.find(prop); |
| if (i != emittedMaxNumbers.end()) { |
| props.push_back(strNumMax); |
| } |
| std::sort(props.begin(), props.end()); |
| |
| std::string propsString = cmJoin(cmMakeRange(props).retreat(1), ", "); |
| propsString += " and the " + props.back(); |
| |
| std::ostringstream e; |
| e << "Property \"" << prop << "\" appears in both the " << propsString |
| << " property in the dependencies of target \"" << this->GetName() |
| << "\". This is not allowed. A property may only require compatibility " |
| "in a boolean interpretation, a numeric minimum, a numeric maximum " |
| "or a " |
| "string interpretation, but not a mixture."; |
| this->LocalGenerator->IssueMessage(MessageType::FATAL_ERROR, e.str()); |
| } |
| } |
| |
| template <typename PropertyType> |
| std::string valueAsString(PropertyType); |
| template <> |
| std::string valueAsString<bool>(bool value) |
| { |
| return value ? "TRUE" : "FALSE"; |
| } |
| template <> |
| std::string valueAsString<const char*>(const char* value) |
| { |
| return value ? value : "(unset)"; |
| } |
| template <> |
| std::string valueAsString<std::string>(std::string value) |
| { |
| return value; |
| } |
| template <> |
| std::string valueAsString<std::nullptr_t>(std::nullptr_t /*unused*/) |
| { |
| return "(unset)"; |
| } |
| |
| std::string compatibilityType(CompatibleType t) |
| { |
| switch (t) { |
| case BoolType: |
| return "Boolean compatibility"; |
| case StringType: |
| return "String compatibility"; |
| case NumberMaxType: |
| return "Numeric maximum compatibility"; |
| case NumberMinType: |
| return "Numeric minimum compatibility"; |
| } |
| assert(false && "Unreachable!"); |
| return ""; |
| } |
| |
| std::string compatibilityAgree(CompatibleType t, bool dominant) |
| { |
| switch (t) { |
| case BoolType: |
| case StringType: |
| return dominant ? "(Disagree)\n" : "(Agree)\n"; |
| case NumberMaxType: |
| case NumberMinType: |
| return dominant ? "(Dominant)\n" : "(Ignored)\n"; |
| } |
| assert(false && "Unreachable!"); |
| return ""; |
| } |
| |
| template <typename PropertyType> |
| PropertyType getTypedProperty( |
| cmGeneratorTarget const* tgt, const std::string& prop, |
| cmGeneratorExpressionInterpreter* genexInterpreter = nullptr); |
| |
| template <> |
| bool getTypedProperty<bool>(cmGeneratorTarget const* tgt, |
| const std::string& prop, |
| cmGeneratorExpressionInterpreter* genexInterpreter) |
| { |
| if (genexInterpreter == nullptr) { |
| return tgt->GetPropertyAsBool(prop); |
| } |
| |
| const char* value = tgt->GetProperty(prop); |
| return cmSystemTools::IsOn(genexInterpreter->Evaluate(value, prop)); |
| } |
| |
| template <> |
| const char* getTypedProperty<const char*>( |
| cmGeneratorTarget const* tgt, const std::string& prop, |
| cmGeneratorExpressionInterpreter* genexInterpreter) |
| { |
| const char* value = tgt->GetProperty(prop); |
| |
| if (genexInterpreter == nullptr) { |
| return value; |
| } |
| |
| return genexInterpreter->Evaluate(value, prop).c_str(); |
| } |
| |
| template <> |
| std::string getTypedProperty<std::string>( |
| cmGeneratorTarget const* tgt, const std::string& prop, |
| cmGeneratorExpressionInterpreter* genexInterpreter) |
| { |
| const char* value = tgt->GetProperty(prop); |
| |
| if (genexInterpreter == nullptr) { |
| return valueAsString(value); |
| } |
| |
| return genexInterpreter->Evaluate(value, prop); |
| } |
| |
| template <typename PropertyType> |
| PropertyType impliedValue(PropertyType); |
| template <> |
| bool impliedValue<bool>(bool /*unused*/) |
| { |
| return false; |
| } |
| template <> |
| const char* impliedValue<const char*>(const char* /*unused*/) |
| { |
| return ""; |
| } |
| template <> |
| std::string impliedValue<std::string>(std::string /*unused*/) // NOLINT(*) |
| { |
| return std::string(); |
| } |
| |
| template <typename PropertyType> |
| std::pair<bool, PropertyType> consistentProperty(PropertyType lhs, |
| PropertyType rhs, |
| CompatibleType t); |
| |
| template <> |
| std::pair<bool, bool> consistentProperty(bool lhs, bool rhs, |
| CompatibleType /*unused*/) |
| { |
| return std::make_pair(lhs == rhs, lhs); |
| } |
| |
| std::pair<bool, const char*> consistentStringProperty(const char* lhs, |
| const char* rhs) |
| { |
| const bool b = strcmp(lhs, rhs) == 0; |
| return std::make_pair(b, b ? lhs : nullptr); |
| } |
| |
| std::pair<bool, std::string> consistentStringProperty(const std::string& lhs, |
| const std::string& rhs) |
| { |
| const bool b = lhs == rhs; |
| return std::make_pair(b, b ? lhs : valueAsString(nullptr)); |
| } |
| |
| std::pair<bool, const char*> consistentNumberProperty(const char* lhs, |
| const char* rhs, |
| CompatibleType t) |
| { |
| char* pEnd; |
| |
| const char* const null_ptr = nullptr; |
| |
| long lnum = strtol(lhs, &pEnd, 0); |
| if (pEnd == lhs || *pEnd != '\0' || errno == ERANGE) { |
| return std::pair<bool, const char*>(false, null_ptr); |
| } |
| |
| long rnum = strtol(rhs, &pEnd, 0); |
| if (pEnd == rhs || *pEnd != '\0' || errno == ERANGE) { |
| return std::pair<bool, const char*>(false, null_ptr); |
| } |
| |
| if (t == NumberMaxType) { |
| return std::make_pair(true, std::max(lnum, rnum) == lnum ? lhs : rhs); |
| } |
| return std::make_pair(true, std::min(lnum, rnum) == lnum ? lhs : rhs); |
| } |
| |
| template <> |
| std::pair<bool, const char*> consistentProperty(const char* lhs, |
| const char* rhs, |
| CompatibleType t) |
| { |
| if (!lhs && !rhs) { |
| return std::make_pair(true, lhs); |
| } |
| if (!lhs) { |
| return std::make_pair(true, rhs); |
| } |
| if (!rhs) { |
| return std::make_pair(true, lhs); |
| } |
| |
| const char* const null_ptr = nullptr; |
| |
| switch (t) { |
| case BoolType: { |
| bool same = cmSystemTools::IsOn(lhs) == cmSystemTools::IsOn(rhs); |
| return std::make_pair(same, same ? lhs : nullptr); |
| } |
| case StringType: |
| return consistentStringProperty(lhs, rhs); |
| case NumberMinType: |
| case NumberMaxType: |
| return consistentNumberProperty(lhs, rhs, t); |
| } |
| assert(false && "Unreachable!"); |
| return std::pair<bool, const char*>(false, null_ptr); |
| } |
| |
| std::pair<bool, std::string> consistentProperty(const std::string& lhs, |
| const std::string& rhs, |
| CompatibleType t) |
| { |
| const std::string null_ptr = valueAsString(nullptr); |
| |
| if (lhs == null_ptr && rhs == null_ptr) { |
| return std::make_pair(true, lhs); |
| } |
| if (lhs == null_ptr) { |
| return std::make_pair(true, rhs); |
| } |
| if (rhs == null_ptr) { |
| return std::make_pair(true, lhs); |
| } |
| |
| switch (t) { |
| case BoolType: { |
| bool same = cmSystemTools::IsOn(lhs) == cmSystemTools::IsOn(rhs); |
| return std::make_pair(same, same ? lhs : null_ptr); |
| } |
| case StringType: |
| return consistentStringProperty(lhs, rhs); |
| case NumberMinType: |
| case NumberMaxType: { |
| auto value = consistentNumberProperty(lhs.c_str(), rhs.c_str(), t); |
| return std::make_pair( |
| value.first, value.first ? std::string(value.second) : null_ptr); |
| } |
| } |
| assert(false && "Unreachable!"); |
| return std::pair<bool, std::string>(false, null_ptr); |
| } |
| |
| template <typename PropertyType> |
| PropertyType checkInterfacePropertyCompatibility(cmGeneratorTarget const* tgt, |
| const std::string& p, |
| const std::string& config, |
| const char* defaultValue, |
| CompatibleType t, |
| PropertyType* /*unused*/) |
| { |
| PropertyType propContent = getTypedProperty<PropertyType>(tgt, p); |
| |
| std::vector<std::string> headPropKeys = tgt->GetPropertyKeys(); |
| const bool explicitlySet = |
| std::find(headPropKeys.begin(), headPropKeys.end(), p) != |
| headPropKeys.end(); |
| |
| const bool impliedByUse = tgt->IsNullImpliedByLinkLibraries(p); |
| assert((impliedByUse ^ explicitlySet) || (!impliedByUse && !explicitlySet)); |
| |
| std::vector<cmGeneratorTarget const*> const& deps = |
| tgt->GetLinkImplementationClosure(config); |
| |
| if (deps.empty()) { |
| return propContent; |
| } |
| bool propInitialized = explicitlySet; |
| |
| std::string report = " * Target \""; |
| report += tgt->GetName(); |
| if (explicitlySet) { |
| report += "\" has property content \""; |
| report += valueAsString<PropertyType>(propContent); |
| report += "\"\n"; |
| } else if (impliedByUse) { |
| report += "\" property is implied by use.\n"; |
| } else { |
| report += "\" property not set.\n"; |
| } |
| |
| std::string interfaceProperty = "INTERFACE_" + p; |
| std::unique_ptr<cmGeneratorExpressionInterpreter> genexInterpreter( |
| p == "POSITION_INDEPENDENT_CODE" ? new cmGeneratorExpressionInterpreter( |
| tgt->GetLocalGenerator(), config, tgt) |
| : nullptr); |
| |
| for (cmGeneratorTarget const* theTarget : deps) { |
| // An error should be reported if one dependency |
| // has INTERFACE_POSITION_INDEPENDENT_CODE ON and the other |
| // has INTERFACE_POSITION_INDEPENDENT_CODE OFF, or if the |
| // target itself has a POSITION_INDEPENDENT_CODE which disagrees |
| // with a dependency. |
| |
| std::vector<std::string> propKeys = theTarget->GetPropertyKeys(); |
| |
| const bool ifaceIsSet = std::find(propKeys.begin(), propKeys.end(), |
| interfaceProperty) != propKeys.end(); |
| PropertyType ifacePropContent = getTypedProperty<PropertyType>( |
| theTarget, interfaceProperty, genexInterpreter.get()); |
| |
| std::string reportEntry; |
| if (ifaceIsSet) { |
| reportEntry += " * Target \""; |
| reportEntry += theTarget->GetName(); |
| reportEntry += "\" property value \""; |
| reportEntry += valueAsString<PropertyType>(ifacePropContent); |
| reportEntry += "\" "; |
| } |
| |
| if (explicitlySet) { |
| if (ifaceIsSet) { |
| std::pair<bool, PropertyType> consistent = |
| consistentProperty(propContent, ifacePropContent, t); |
| report += reportEntry; |
| report += compatibilityAgree(t, propContent != consistent.second); |
| if (!consistent.first) { |
| std::ostringstream e; |
| e << "Property " << p << " on target \"" << tgt->GetName() |
| << "\" does\nnot match the " |
| "INTERFACE_" |
| << p |
| << " property requirement\nof " |
| "dependency \"" |
| << theTarget->GetName() << "\".\n"; |
| cmSystemTools::Error(e.str()); |
| break; |
| } |
| propContent = consistent.second; |
| continue; |
| } |
| // Explicitly set on target and not set in iface. Can't disagree. |
| continue; |
| } |
| if (impliedByUse) { |
| propContent = impliedValue<PropertyType>(propContent); |
| |
| if (ifaceIsSet) { |
| std::pair<bool, PropertyType> consistent = |
| consistentProperty(propContent, ifacePropContent, t); |
| report += reportEntry; |
| report += compatibilityAgree(t, propContent != consistent.second); |
| if (!consistent.first) { |
| std::ostringstream e; |
| e << "Property " << p << " on target \"" << tgt->GetName() |
| << "\" is\nimplied to be " << defaultValue |
| << " because it was used to determine the link libraries\n" |
| "already. The INTERFACE_" |
| << p << " property on\ndependency \"" << theTarget->GetName() |
| << "\" is in conflict.\n"; |
| cmSystemTools::Error(e.str()); |
| break; |
| } |
| propContent = consistent.second; |
| continue; |
| } |
| // Implicitly set on target and not set in iface. Can't disagree. |
| continue; |
| } |
| if (ifaceIsSet) { |
| if (propInitialized) { |
| std::pair<bool, PropertyType> consistent = |
| consistentProperty(propContent, ifacePropContent, t); |
| report += reportEntry; |
| report += compatibilityAgree(t, propContent != consistent.second); |
| if (!consistent.first) { |
| std::ostringstream e; |
| e << "The INTERFACE_" << p << " property of \"" |
| << theTarget->GetName() << "\" does\nnot agree with the value of " |
| << p << " already determined\nfor \"" << tgt->GetName() << "\".\n"; |
| cmSystemTools::Error(e.str()); |
| break; |
| } |
| propContent = consistent.second; |
| continue; |
| } |
| report += reportEntry + "(Interface set)\n"; |
| propContent = ifacePropContent; |
| propInitialized = true; |
| } else { |
| // Not set. Nothing to agree on. |
| continue; |
| } |
| } |
| |
| tgt->ReportPropertyOrigin(p, valueAsString<PropertyType>(propContent), |
| report, compatibilityType(t)); |
| return propContent; |
| } |
| |
| bool cmGeneratorTarget::GetLinkInterfaceDependentBoolProperty( |
| const std::string& p, const std::string& config) const |
| { |
| return checkInterfacePropertyCompatibility<bool>(this, p, config, "FALSE", |
| BoolType, nullptr); |
| } |
| |
| std::string cmGeneratorTarget::GetLinkInterfaceDependentStringAsBoolProperty( |
| const std::string& p, const std::string& config) const |
| { |
| return checkInterfacePropertyCompatibility<std::string>( |
| this, p, config, "FALSE", BoolType, nullptr); |
| } |
| |
| const char* cmGeneratorTarget::GetLinkInterfaceDependentStringProperty( |
| const std::string& p, const std::string& config) const |
| { |
| return checkInterfacePropertyCompatibility<const char*>( |
| this, p, config, "empty", StringType, nullptr); |
| } |
| |
| const char* cmGeneratorTarget::GetLinkInterfaceDependentNumberMinProperty( |
| const std::string& p, const std::string& config) const |
| { |
| return checkInterfacePropertyCompatibility<const char*>( |
| this, p, config, "empty", NumberMinType, nullptr); |
| } |
| |
| const char* cmGeneratorTarget::GetLinkInterfaceDependentNumberMaxProperty( |
| const std::string& p, const std::string& config) const |
| { |
| return checkInterfacePropertyCompatibility<const char*>( |
| this, p, config, "empty", NumberMaxType, nullptr); |
| } |
| |
| cmComputeLinkInformation* cmGeneratorTarget::GetLinkInformation( |
| const std::string& config) const |
| { |
| // Lookup any existing information for this configuration. |
| std::string key(cmSystemTools::UpperCase(config)); |
| cmTargetLinkInformationMap::iterator i = this->LinkInformation.find(key); |
| if (i == this->LinkInformation.end()) { |
| // Compute information for this configuration. |
| cmComputeLinkInformation* info = |
| new cmComputeLinkInformation(this, config); |
| if (!info || !info->Compute()) { |
| delete info; |
| info = nullptr; |
| } |
| |
| // Store the information for this configuration. |
| cmTargetLinkInformationMap::value_type entry(key, info); |
| i = this->LinkInformation.insert(entry).first; |
| |
| if (info) { |
| this->CheckPropertyCompatibility(info, config); |
| } |
| } |
| return i->second; |
| } |
| |
| void cmGeneratorTarget::GetTargetVersion(int& major, int& minor) const |
| { |
| int patch; |
| this->GetTargetVersion(false, major, minor, patch); |
| } |
| |
| void cmGeneratorTarget::GetTargetVersion(bool soversion, int& major, |
| int& minor, int& patch) const |
| { |
| // Set the default values. |
| major = 0; |
| minor = 0; |
| patch = 0; |
| |
| assert(this->GetType() != cmStateEnums::INTERFACE_LIBRARY); |
| |
| // Look for a VERSION or SOVERSION property. |
| const char* prop = soversion ? "SOVERSION" : "VERSION"; |
| if (const char* version = this->GetProperty(prop)) { |
| // Try to parse the version number and store the results that were |
| // successfully parsed. |
| int parsed_major; |
| int parsed_minor; |
| int parsed_patch; |
| switch (sscanf(version, "%d.%d.%d", &parsed_major, &parsed_minor, |
| &parsed_patch)) { |
| case 3: |
| patch = parsed_patch; |
| CM_FALLTHROUGH; |
| case 2: |
| minor = parsed_minor; |
| CM_FALLTHROUGH; |
| case 1: |
| major = parsed_major; |
| CM_FALLTHROUGH; |
| default: |
| break; |
| } |
| } |
| } |
| |
| std::string cmGeneratorTarget::GetFortranModuleDirectory( |
| std::string const& working_dir) const |
| { |
| if (!this->FortranModuleDirectoryCreated) { |
| this->FortranModuleDirectory = |
| this->CreateFortranModuleDirectory(working_dir); |
| this->FortranModuleDirectoryCreated = true; |
| } |
| |
| return this->FortranModuleDirectory; |
| } |
| |
| std::string cmGeneratorTarget::CreateFortranModuleDirectory( |
| std::string const& working_dir) const |
| { |
| std::string mod_dir; |
| std::string target_mod_dir; |
| if (const char* prop = this->GetProperty("Fortran_MODULE_DIRECTORY")) { |
| target_mod_dir = prop; |
| } else { |
| std::string const& default_mod_dir = |
| this->LocalGenerator->GetCurrentBinaryDirectory(); |
| if (default_mod_dir != working_dir) { |
| target_mod_dir = default_mod_dir; |
| } |
| } |
| const char* moddir_flag = |
| this->Makefile->GetDefinition("CMAKE_Fortran_MODDIR_FLAG"); |
| if (!target_mod_dir.empty() && moddir_flag) { |
| // Compute the full path to the module directory. |
| if (cmSystemTools::FileIsFullPath(target_mod_dir)) { |
| // Already a full path. |
| mod_dir = target_mod_dir; |
| } else { |
| // Interpret relative to the current output directory. |
| mod_dir = this->LocalGenerator->GetCurrentBinaryDirectory(); |
| mod_dir += "/"; |
| mod_dir += target_mod_dir; |
| } |
| |
| // Make sure the module output directory exists. |
| cmSystemTools::MakeDirectory(mod_dir); |
| } |
| return mod_dir; |
| } |
| |
| std::string cmGeneratorTarget::GetFrameworkVersion() const |
| { |
| assert(this->GetType() != cmStateEnums::INTERFACE_LIBRARY); |
| |
| if (const char* fversion = this->GetProperty("FRAMEWORK_VERSION")) { |
| return fversion; |
| } |
| if (const char* tversion = this->GetProperty("VERSION")) { |
| return tversion; |
| } |
| return "A"; |
| } |
| |
| void cmGeneratorTarget::ComputeVersionedName(std::string& vName, |
| std::string const& prefix, |
| std::string const& base, |
| std::string const& suffix, |
| std::string const& name, |
| const char* version) const |
| { |
| vName = this->Makefile->IsOn("APPLE") ? (prefix + base) : name; |
| if (version) { |
| vName += "."; |
| vName += version; |
| } |
| vName += this->Makefile->IsOn("APPLE") ? suffix : std::string(); |
| } |
| |
| std::vector<std::string> cmGeneratorTarget::GetPropertyKeys() const |
| { |
| cmPropertyMap const& propsObject = this->Target->GetProperties(); |
| std::vector<std::string> props; |
| props.reserve(propsObject.size()); |
| for (auto const& it : propsObject) { |
| props.push_back(it.first); |
| } |
| return props; |
| } |
| |
| void cmGeneratorTarget::ReportPropertyOrigin( |
| const std::string& p, const std::string& result, const std::string& report, |
| const std::string& compatibilityType) const |
| { |
| std::vector<std::string> debugProperties; |
| const char* debugProp = this->Target->GetMakefile()->GetDefinition( |
| "CMAKE_DEBUG_TARGET_PROPERTIES"); |
| if (debugProp) { |
| cmSystemTools::ExpandListArgument(debugProp, debugProperties); |
| } |
| |
| bool debugOrigin = !this->DebugCompatiblePropertiesDone[p] && |
| std::find(debugProperties.begin(), debugProperties.end(), p) != |
| debugProperties.end(); |
| |
| if (this->GlobalGenerator->GetConfigureDoneCMP0026()) { |
| this->DebugCompatiblePropertiesDone[p] = true; |
| } |
| if (!debugOrigin) { |
| return; |
| } |
| |
| std::string areport = compatibilityType; |
| areport += std::string(" of property \"") + p + "\" for target \""; |
| areport += std::string(this->GetName()); |
| areport += "\" (result: \""; |
| areport += result; |
| areport += "\"):\n" + report; |
| |
| this->LocalGenerator->GetCMakeInstance()->IssueMessage(MessageType::LOG, |
| areport); |
| } |
| |
| void cmGeneratorTarget::LookupLinkItems(std::vector<std::string> const& names, |
| cmListFileBacktrace const& bt, |
| std::vector<cmLinkItem>& items) const |
| { |
| for (std::string const& n : names) { |
| std::string name = this->CheckCMP0004(n); |
| if (name == this->GetName() || name.empty()) { |
| continue; |
| } |
| items.push_back(this->ResolveLinkItem(name, bt)); |
| } |
| } |
| |
| void cmGeneratorTarget::ExpandLinkItems( |
| std::string const& prop, std::string const& value, std::string const& config, |
| cmGeneratorTarget const* headTarget, bool usage_requirements_only, |
| std::vector<cmLinkItem>& items, bool& hadHeadSensitiveCondition) const |
| { |
| cmGeneratorExpression ge; |
| cmGeneratorExpressionDAGChecker dagChecker(this, prop, nullptr, nullptr); |
| // The $<LINK_ONLY> expression may be in a link interface to specify private |
| // link dependencies that are otherwise excluded from usage requirements. |
| if (usage_requirements_only) { |
| dagChecker.SetTransitivePropertiesOnly(); |
| } |
| std::vector<std::string> libs; |
| std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(value); |
| cmSystemTools::ExpandListArgument(cge->Evaluate(this->LocalGenerator, config, |
| false, headTarget, this, |
| &dagChecker), |
| libs); |
| this->LookupLinkItems(libs, cge->GetBacktrace(), items); |
| hadHeadSensitiveCondition = cge->GetHadHeadSensitiveCondition(); |
| } |
| |
| cmLinkInterface const* cmGeneratorTarget::GetLinkInterface( |
| const std::string& config, cmGeneratorTarget const* head) const |
| { |
| // Imported targets have their own link interface. |
| if (this->IsImported()) { |
| return this->GetImportLinkInterface(config, head, false); |
| } |
| |
| // Link interfaces are not supported for executables that do not |
| // export symbols. |
| if (this->GetType() == cmStateEnums::EXECUTABLE && |
| !this->IsExecutableWithExports()) { |
| return nullptr; |
| } |
| |
| // Lookup any existing link interface for this configuration. |
| cmHeadToLinkInterfaceMap& hm = this->GetHeadToLinkInterfaceMap(config); |
| |
| // If the link interface does not depend on the head target |
| // then return the one we computed first. |
| if (!hm.empty() && !hm.begin()->second.HadHeadSensitiveCondition) { |
| return &hm.begin()->second; |
| } |
| |
| cmOptionalLinkInterface& iface = hm[head]; |
| if (!iface.LibrariesDone) { |
| iface.LibrariesDone = true; |
| this->ComputeLinkInterfaceLibraries(config, iface, head, false); |
| } |
| if (!iface.AllDone) { |
| iface.AllDone = true; |
| if (iface.Exists) { |
| this->ComputeLinkInterface(config, iface, head); |
| } |
| } |
| |
| return iface.Exists ? &iface : nullptr; |
| } |
| |
| void cmGeneratorTarget::ComputeLinkInterface( |
| const std::string& config, cmOptionalLinkInterface& iface, |
| cmGeneratorTarget const* headTarget) const |
| { |
| if (iface.ExplicitLibraries) { |
| if (this->GetType() == cmStateEnums::SHARED_LIBRARY || |
| this->GetType() == cmStateEnums::STATIC_LIBRARY || |
| this->GetType() == cmStateEnums::INTERFACE_LIBRARY) { |
| // Shared libraries may have runtime implementation dependencies |
| // on other shared libraries that are not in the interface. |
| std::set<cmLinkItem> emitted; |
| for (cmLinkItem const& lib : iface.Libraries) { |
| emitted.insert(lib); |
| } |
| if (this->GetType() != cmStateEnums::INTERFACE_LIBRARY) { |
| cmLinkImplementation const* impl = this->GetLinkImplementation(config); |
| for (cmLinkImplItem const& lib : impl->Libraries) { |
| if (emitted.insert(lib).second) { |
| if (lib.Target) { |
| // This is a runtime dependency on another shared library. |
| if (lib.Target->GetType() == cmStateEnums::SHARED_LIBRARY) { |
| iface.SharedDeps.push_back(lib); |
| } |
| } else { |
| // TODO: Recognize shared library file names. Perhaps this |
| // should be moved to cmComputeLinkInformation, but that creates |
| // a chicken-and-egg problem since this list is needed for its |
| // construction. |
| } |
| } |
| } |
| } |
| } |
| } else if (this->GetPolicyStatusCMP0022() == cmPolicies::WARN || |
| this->GetPolicyStatusCMP0022() == cmPolicies::OLD) { |
| // The link implementation is the default link interface. |
| cmLinkImplementationLibraries const* impl = |
| this->GetLinkImplementationLibrariesInternal(config, headTarget); |
| iface.ImplementationIsInterface = true; |
| iface.WrongConfigLibraries = impl->WrongConfigLibraries; |
| } |
| |
| if (this->LinkLanguagePropagatesToDependents()) { |
| // Targets using this archive need its language runtime libraries. |
| if (cmLinkImplementation const* impl = |
| this->GetLinkImplementation(config)) { |
| iface.Languages = impl->Languages; |
| } |
| } |
| |
| if (this->GetType() == cmStateEnums::STATIC_LIBRARY) { |
| // Construct the property name suffix for this configuration. |
| std::string suffix = "_"; |
| if (!config.empty()) { |
| suffix += cmSystemTools::UpperCase(config); |
| } else { |
| suffix += "NOCONFIG"; |
| } |
| |
| // How many repetitions are needed if this library has cyclic |
| // dependencies? |
| std::string propName = "LINK_INTERFACE_MULTIPLICITY"; |
| propName += suffix; |
| if (const char* config_reps = this->GetProperty(propName)) { |
| sscanf(config_reps, "%u", &iface.Multiplicity); |
| } else if (const char* reps = |
| this->GetProperty("LINK_INTERFACE_MULTIPLICITY")) { |
| sscanf(reps, "%u", &iface.Multiplicity); |
| } |
| } |
| } |
| |
| const cmLinkInterfaceLibraries* cmGeneratorTarget::GetLinkInterfaceLibraries( |
| const std::string& config, cmGeneratorTarget const* head, |
| bool usage_requirements_only) const |
| { |
| // Imported targets have their own link interface. |
| if (this->IsImported()) { |
| return this->GetImportLinkInterface(config, head, usage_requirements_only); |
| } |
| |
| // Link interfaces are not supported for executables that do not |
| // export symbols. |
| if (this->GetType() == cmStateEnums::EXECUTABLE && |
| !this->IsExecutableWithExports()) { |
| return nullptr; |
| } |
| |
| // Lookup any existing link interface for this configuration. |
| std::string CONFIG = cmSystemTools::UpperCase(config); |
| cmHeadToLinkInterfaceMap& hm = |
| (usage_requirements_only |
| ? this->GetHeadToLinkInterfaceUsageRequirementsMap(config) |
| : this->GetHeadToLinkInterfaceMap(config)); |
| |
| // If the link interface does not depend on the head target |
| // then return the one we computed first. |
| if (!hm.empty() && !hm.begin()->second.HadHeadSensitiveCondition) { |
| return &hm.begin()->second; |
| } |
| |
| cmOptionalLinkInterface& iface = hm[head]; |
| if (!iface.LibrariesDone) { |
| iface.LibrariesDone = true; |
| this->ComputeLinkInterfaceLibraries(config, iface, head, |
| usage_requirements_only); |
| } |
| |
| return iface.Exists ? &iface : nullptr; |
| } |
| |
| std::string cmGeneratorTarget::GetDirectory( |
| const std::string& config, cmStateEnums::ArtifactType artifact) const |
| { |
| if (this->IsImported()) { |
| // Return the directory from which the target is imported. |
| return cmSystemTools::GetFilenamePath( |
| this->Target->ImportedGetFullPath(config, artifact)); |
| } |
| if (OutputInfo const* info = this->GetOutputInfo(config)) { |
| // Return the directory in which the target will be built. |
| switch (artifact) { |
| case cmStateEnums::RuntimeBinaryArtifact: |
| return info->OutDir; |
| case cmStateEnums::ImportLibraryArtifact: |
| return info->ImpDir; |
| } |
| } |
| return ""; |
| } |
| |
| bool cmGeneratorTarget::UsesDefaultOutputDir( |
| const std::string& config, cmStateEnums::ArtifactType artifact) const |
| { |
| std::string dir; |
| return this->ComputeOutputDir(config, artifact, dir); |
| } |
| |
| cmGeneratorTarget::OutputInfo const* cmGeneratorTarget::GetOutputInfo( |
| const std::string& config) const |
| { |
| // There is no output information for imported targets. |
| if (this->IsImported()) { |
| return nullptr; |
| } |
| |
| // Only libraries and executables have well-defined output files. |
| if (!this->HaveWellDefinedOutputFiles()) { |
| std::string msg = "cmGeneratorTarget::GetOutputInfo called for "; |
| msg += this->GetName(); |
| msg += " which has type "; |
| msg += cmState::GetTargetTypeName(this->GetType()); |
| this->LocalGenerator->IssueMessage(MessageType::INTERNAL_ERROR, msg); |
| return nullptr; |
| } |
| |
| // Lookup/compute/cache the output information for this configuration. |
| std::string config_upper; |
| if (!config.empty()) { |
| config_upper = cmSystemTools::UpperCase(config); |
| } |
| OutputInfoMapType::iterator i = this->OutputInfoMap.find(config_upper); |
| if (i == this->OutputInfoMap.end()) { |
| // Add empty info in map to detect potential recursion. |
| OutputInfo info; |
| OutputInfoMapType::value_type entry(config_upper, info); |
| i = this->OutputInfoMap.insert(entry).first; |
| |
| // Compute output directories. |
| this->ComputeOutputDir(config, cmStateEnums::RuntimeBinaryArtifact, |
| info.OutDir); |
| this->ComputeOutputDir(config, cmStateEnums::ImportLibraryArtifact, |
| info.ImpDir); |
| if (!this->ComputePDBOutputDir("PDB", config, info.PdbDir)) { |
| info.PdbDir = info.OutDir; |
| } |
| |
| // Now update the previously-prepared map entry. |
| i->second = info; |
| } else if (i->second.empty()) { |
| // An empty map entry indicates we have been called recursively |
| // from the above block. |
| this->LocalGenerator->GetCMakeInstance()->IssueMessage( |
| MessageType::FATAL_ERROR, |
| "Target '" + this->GetName() + "' OUTPUT_DIRECTORY depends on itself.", |
| this->GetBacktrace()); |
| return nullptr; |
| } |
| return &i->second; |
| } |
| |
| bool cmGeneratorTarget::ComputeOutputDir(const std::string& config, |
| cmStateEnums::ArtifactType artifact, |
| std::string& out) const |
| { |
| bool usesDefaultOutputDir = false; |
| std::string conf = config; |
| |
| // Look for a target property defining the target output directory |
| // based on the target type. |
| std::string targetTypeName = this->GetOutputTargetType(artifact); |
| const char* propertyName = nullptr; |
| std::string propertyNameStr = targetTypeName; |
| if (!propertyNameStr.empty()) { |
| propertyNameStr += "_OUTPUT_DIRECTORY"; |
| propertyName = propertyNameStr.c_str(); |
| } |
| |
| // Check for a per-configuration output directory target property. |
| std::string configUpper = cmSystemTools::UpperCase(conf); |
| const char* configProp = nullptr; |
| std::string configPropStr = targetTypeName; |
| if (!configPropStr.empty()) { |
| configPropStr += "_OUTPUT_DIRECTORY_"; |
| configPropStr += configUpper; |
| configProp = configPropStr.c_str(); |
| } |
| |
| // Select an output directory. |
| if (const char* config_outdir = this->GetProperty(configProp)) { |
| // Use the user-specified per-configuration output directory. |
| cmGeneratorExpression ge; |
| std::unique_ptr<cmCompiledGeneratorExpression> cge = |
| ge.Parse(config_outdir); |
| out = cge->Evaluate(this->LocalGenerator, config); |
| |
| // Skip per-configuration subdirectory. |
| conf.clear(); |
| } else if (const char* outdir = this->GetProperty(propertyName)) { |
| // Use the user-specified output directory. |
| cmGeneratorExpression ge; |
| std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(outdir); |
| out = cge->Evaluate(this->LocalGenerator, config); |
| |
| // Skip per-configuration subdirectory if the value contained a |
| // generator expression. |
| if (out != outdir) { |
| conf.clear(); |
| } |
| } else if (this->GetType() == cmStateEnums::EXECUTABLE) { |
| // Lookup the output path for executables. |
| out = this->Makefile->GetSafeDefinition("EXECUTABLE_OUTPUT_PATH"); |
| } else if (this->GetType() == cmStateEnums::STATIC_LIBRARY || |
| this->GetType() == cmStateEnums::SHARED_LIBRARY || |
| this->GetType() == cmStateEnums::MODULE_LIBRARY) { |
| // Lookup the output path for libraries. |
| out = this->Makefile->GetSafeDefinition("LIBRARY_OUTPUT_PATH"); |
| } |
| if (out.empty()) { |
| // Default to the current output directory. |
| usesDefaultOutputDir = true; |
| out = "."; |
| } |
| |
| // Convert the output path to a full path in case it is |
| // specified as a relative path. Treat a relative path as |
| // relative to the current output directory for this makefile. |
| out = (cmSystemTools::CollapseFullPath( |
| out, this->LocalGenerator->GetCurrentBinaryDirectory())); |
| |
| // The generator may add the configuration's subdirectory. |
| if (!conf.empty()) { |
| bool useEPN = |
| this->GlobalGenerator->UseEffectivePlatformName(this->Makefile); |
| std::string suffix = |
| usesDefaultOutputDir && useEPN ? "${EFFECTIVE_PLATFORM_NAME}" : ""; |
| this->LocalGenerator->GetGlobalGenerator()->AppendDirectoryForConfig( |
| "/", conf, suffix, out); |
| } |
| |
| return usesDefaultOutputDir; |
| } |
| |
| bool cmGeneratorTarget::ComputePDBOutputDir(const std::string& kind, |
| const std::string& config, |
| std::string& out) const |
| { |
| // Look for a target property defining the target output directory |
| // based on the target type. |
| const char* propertyName = nullptr; |
| std::string propertyNameStr = kind; |
| if (!propertyNameStr.empty()) { |
| propertyNameStr += "_OUTPUT_DIRECTORY"; |
| propertyName = propertyNameStr.c_str(); |
| } |
| std::string conf = config; |
| |
| // Check for a per-configuration output directory target property. |
| std::string configUpper = cmSystemTools::UpperCase(conf); |
| const char* configProp = nullptr; |
| std::string configPropStr = kind; |
| if (!configPropStr.empty()) { |
| configPropStr += "_OUTPUT_DIRECTORY_"; |
| configPropStr += configUpper; |
| configProp = configPropStr.c_str(); |
| } |
| |
| // Select an output directory. |
| if (const char* config_outdir = this->GetProperty(configProp)) { |
| // Use the user-specified per-configuration output directory. |
| cmGeneratorExpression ge; |
| std::unique_ptr<cmCompiledGeneratorExpression> cge = |
| ge.Parse(config_outdir); |
| out = cge->Evaluate(this->LocalGenerator, config); |
| |
| // Skip per-configuration subdirectory. |
| conf.clear(); |
| } else if (const char* outdir = this->GetProperty(propertyName)) { |
| // Use the user-specified output directory. |
| cmGeneratorExpression ge; |
| std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(outdir); |
| out = cge->Evaluate(this->LocalGenerator, config); |
| |
| // Skip per-configuration subdirectory if the value contained a |
| // generator expression. |
| if (out != outdir) { |
| conf.clear(); |
| } |
| } |
| if (out.empty()) { |
| return false; |
| } |
| |
| // Convert the output path to a full path in case it is |
| // specified as a relative path. Treat a relative path as |
| // relative to the current output directory for this makefile. |
| out = (cmSystemTools::CollapseFullPath( |
| out, this->LocalGenerator->GetCurrentBinaryDirectory())); |
| |
| // The generator may add the configuration's subdirectory. |
| if (!conf.empty()) { |
| this->LocalGenerator->GetGlobalGenerator()->AppendDirectoryForConfig( |
| "/", conf, "", out); |
| } |
| return true; |
| } |
| |
| bool cmGeneratorTarget::HaveInstallTreeRPATH() const |
| { |
| const char* install_rpath = this->GetProperty("INSTALL_RPATH"); |
| return (install_rpath && *install_rpath) && |
| !this->Makefile->IsOn("CMAKE_SKIP_INSTALL_RPATH"); |
| } |
| |
| void cmGeneratorTarget::ComputeLinkInterfaceLibraries( |
| const std::string& config, cmOptionalLinkInterface& iface, |
| cmGeneratorTarget const* headTarget, bool usage_requirements_only) const |
| { |
| // Construct the property name suffix for this configuration. |
| std::string suffix = "_"; |
| if (!config.empty()) { |
| suffix += cmSystemTools::UpperCase(config); |
| } else { |
| suffix += "NOCONFIG"; |
| } |
| |
| // An explicit list of interface libraries may be set for shared |
| // libraries and executables that export symbols. |
| const char* explicitLibraries = nullptr; |
| std::string linkIfaceProp; |
| if (this->GetPolicyStatusCMP0022() != cmPolicies::OLD && |
| this->GetPolicyStatusCMP0022() != cmPolicies::WARN) { |
| // CMP0022 NEW behavior is to use INTERFACE_LINK_LIBRARIES. |
| linkIfaceProp = "INTERFACE_LINK_LIBRARIES"; |
| explicitLibraries = this->GetProperty(linkIfaceProp); |
| } else if (this->GetType() == cmStateEnums::SHARED_LIBRARY || |
| this->IsExecutableWithExports()) { |
| // CMP0022 OLD behavior is to use LINK_INTERFACE_LIBRARIES if set on a |
| // shared lib or executable. |
| |
| // Lookup the per-configuration property. |
| linkIfaceProp = "LINK_INTERFACE_LIBRARIES"; |
| linkIfaceProp += suffix; |
| explicitLibraries = this->GetProperty(linkIfaceProp); |
| |
| // If not set, try the generic property. |
| if (!explicitLibraries) { |
| linkIfaceProp = "LINK_INTERFACE_LIBRARIES"; |
| explicitLibraries = this->GetProperty(linkIfaceProp); |
| } |
| } |
| |
| if (explicitLibraries && |
| this->GetPolicyStatusCMP0022() == cmPolicies::WARN && |
| !this->PolicyWarnedCMP0022) { |
| // Compare the explicitly set old link interface properties to the |
| // preferred new link interface property one and warn if different. |
| const char* newExplicitLibraries = |
| this->GetProperty("INTERFACE_LINK_LIBRARIES"); |
| if (newExplicitLibraries && |
| strcmp(newExplicitLibraries, explicitLibraries) != 0) { |
| std::ostringstream w; |
| /* clang-format off */ |
| w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0022) << "\n" |
| "Target \"" << this->GetName() << "\" has an " |
| "INTERFACE_LINK_LIBRARIES property which differs from its " << |
| linkIfaceProp << " properties." |
| "\n" |
| "INTERFACE_LINK_LIBRARIES:\n" |
| " " << newExplicitLibraries << "\n" << |
| linkIfaceProp << ":\n" |
| " " << explicitLibraries << "\n"; |
| /* clang-format on */ |
| this->LocalGenerator->IssueMessage(MessageType::AUTHOR_WARNING, w.str()); |
| this->PolicyWarnedCMP0022 = true; |
| } |
| } |
| |
| // There is no implicit link interface for executables or modules |
| // so if none was explicitly set then there is no link interface. |
| if (!explicitLibraries && |
| (this->GetType() == cmStateEnums::EXECUTABLE || |
| (this->GetType() == cmStateEnums::MODULE_LIBRARY))) { |
| return; |
| } |
| iface.Exists = true; |
| iface.ExplicitLibraries = explicitLibraries; |
| |
| if (explicitLibraries) { |
| // The interface libraries have been explicitly set. |
| this->ExpandLinkItems(linkIfaceProp, explicitLibraries, config, headTarget, |
| usage_requirements_only, iface.Libraries, |
| iface.HadHeadSensitiveCondition); |
| } else if (this->GetPolicyStatusCMP0022() == cmPolicies::WARN || |
| this->GetPolicyStatusCMP0022() == cmPolicies::OLD) |
| // If CMP0022 is NEW then the plain tll signature sets the |
| // INTERFACE_LINK_LIBRARIES, so if we get here then the project |
| // cleared the property explicitly and we should not fall back |
| // to the link implementation. |
| { |
| // The link implementation is the default link interface. |
| cmLinkImplementationLibraries const* impl = |
| this->GetLinkImplementationLibrariesInternal(config, headTarget); |
| iface.Libraries.insert(iface.Libraries.end(), impl->Libraries.begin(), |
| impl->Libraries.end()); |
| if (this->GetPolicyStatusCMP0022() == cmPolicies::WARN && |
| !this->PolicyWarnedCMP0022 && !usage_requirements_only) { |
| // Compare the link implementation fallback link interface to the |
| // preferred new link interface property and warn if different. |
| std::vector<cmLinkItem> ifaceLibs; |
| static const std::string newProp = "INTERFACE_LINK_LIBRARIES"; |
| if (const char* newExplicitLibraries = this->GetProperty(newProp)) { |
| bool hadHeadSensitiveConditionDummy = false; |
| this->ExpandLinkItems(newProp, newExplicitLibraries, config, |
| headTarget, usage_requirements_only, ifaceLibs, |
| hadHeadSensitiveConditionDummy); |
| } |
| if (ifaceLibs != iface.Libraries) { |
| std::string oldLibraries = cmJoin(impl->Libraries, ";"); |
| std::string newLibraries = cmJoin(ifaceLibs, ";"); |
| if (oldLibraries.empty()) { |
| oldLibraries = "(empty)"; |
| } |
| if (newLibraries.empty()) { |
| newLibraries = "(empty)"; |
| } |
| |
| std::ostringstream w; |
| /* clang-format off */ |
| w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0022) << "\n" |
| "Target \"" << this->GetName() << "\" has an " |
| "INTERFACE_LINK_LIBRARIES property. " |
| "This should be preferred as the source of the link interface " |
| "for this library but because CMP0022 is not set CMake is " |
| "ignoring the property and using the link implementation " |
| "as the link interface instead." |
| "\n" |
| "INTERFACE_LINK_LIBRARIES:\n" |
| " " << newLibraries << "\n" |
| "Link implementation:\n" |
| " " << oldLibraries << "\n"; |
| /* clang-format on */ |
| this->LocalGenerator->IssueMessage(MessageType::AUTHOR_WARNING, |
| w.str()); |
| this->PolicyWarnedCMP0022 = true; |
| } |
| } |
| } |
| } |
| |
| const cmLinkInterface* cmGeneratorTarget::GetImportLinkInterface( |
| const std::string& config, cmGeneratorTarget const* headTarget, |
| bool usage_requirements_only) const |
| { |
| cmGeneratorTarget::ImportInfo const* info = this->GetImportInfo(config); |
| if (!info) { |
| return nullptr; |
| } |
| |
| std::string CONFIG = cmSystemTools::UpperCase(config); |
| cmHeadToLinkInterfaceMap& hm = |
| (usage_requirements_only |
| ? this->GetHeadToLinkInterfaceUsageRequirementsMap(config) |
| : this->GetHeadToLinkInterfaceMap(config)); |
| |
| // If the link interface does not depend on the head target |
| // then return the one we computed first. |
| if (!hm.empty() && !hm.begin()->second.HadHeadSensitiveCondition) { |
| return &hm.begin()->second; |
| } |
| |
| cmOptionalLinkInterface& iface = hm[headTarget]; |
| if (!iface.AllDone) { |
| iface.AllDone = true; |
| iface.Multiplicity = info->Multiplicity; |
| cmSystemTools::ExpandListArgument(info->Languages, iface.Languages); |
| this->ExpandLinkItems(info->LibrariesProp, info->Libraries, config, |
| headTarget, usage_requirements_only, iface.Libraries, |
| iface.HadHeadSensitiveCondition); |
| std::vector<std::string> deps; |
| cmSystemTools::ExpandListArgument(info->SharedDeps, deps); |
| this->LookupLinkItems(deps, cmListFileBacktrace(), iface.SharedDeps); |
| } |
| |
| return &iface; |
| } |
| |
| cmGeneratorTarget::ImportInfo const* cmGeneratorTarget::GetImportInfo( |
| const std::string& config) const |
| { |
| // There is no imported information for non-imported targets. |
| if (!this->IsImported()) { |
| return nullptr; |
| } |
| |
| // Lookup/compute/cache the import information for this |
| // configuration. |
| std::string config_upper; |
| if (!config.empty()) { |
| config_upper = cmSystemTools::UpperCase(config); |
| } else { |
| config_upper = "NOCONFIG"; |
| } |
| |
| ImportInfoMapType::const_iterator i = this->ImportInfoMap.find(config_upper); |
| if (i == this->ImportInfoMap.end()) { |
| ImportInfo info; |
| this->ComputeImportInfo(config_upper, info); |
| ImportInfoMapType::value_type entry(config_upper, info); |
| i = this->ImportInfoMap.insert(entry).first; |
| } |
| |
| if (this->GetType() == cmStateEnums::INTERFACE_LIBRARY) { |
| return &i->second; |
| } |
| // If the location is empty then the target is not available for |
| // this configuration. |
| if (i->second.Location.empty() && i->second.ImportLibrary.empty()) { |
| return nullptr; |
| } |
| |
| // Return the import information. |
| return &i->second; |
| } |
| |
| void cmGeneratorTarget::ComputeImportInfo(std::string const& desired_config, |
| ImportInfo& info) const |
| { |
| // This method finds information about an imported target from its |
| // properties. The "IMPORTED_" namespace is reserved for properties |
| // defined by the project exporting the target. |
| |
| // Initialize members. |
| info.NoSOName = false; |
| |
| const char* loc = nullptr; |
| const char* imp = nullptr; |
| std::string suffix; |
| if (!this->Target->GetMappedConfig(desired_config, &loc, &imp, suffix)) { |
| return; |
| } |
| |
| // Get the link interface. |
| { |
| std::string linkProp = "INTERFACE_LINK_LIBRARIES"; |
| const char* propertyLibs = this->GetProperty(linkProp); |
| |
| if (this->GetType() != cmStateEnums::INTERFACE_LIBRARY) { |
| if (!propertyLibs) { |
| linkProp = "IMPORTED_LINK_INTERFACE_LIBRARIES"; |
| linkProp += suffix; |
| propertyLibs = this->GetProperty(linkProp); |
| } |
| |
| if (!propertyLibs) { |
| linkProp = "IMPORTED_LINK_INTERFACE_LIBRARIES"; |
| propertyLibs = this->GetProperty(linkProp); |
| } |
| } |
| if (propertyLibs) { |
| info.LibrariesProp = linkProp; |
| info.Libraries = propertyLibs; |
| } |
| } |
| if (this->GetType() == cmStateEnums::INTERFACE_LIBRARY) { |
| if (loc) { |
| info.LibName = loc; |
| } |
| return; |
| } |
| |
| // A provided configuration has been chosen. Load the |
| // configuration's properties. |
| |
| // Get the location. |
| if (loc) { |
| info.Location = loc; |
| } else { |
| std::string impProp = "IMPORTED_LOCATION"; |
| impProp += suffix; |
| if (const char* config_location = this->GetProperty(impProp)) { |
| info.Location = config_location; |
| } else if (const char* location = this->GetProperty("IMPORTED_LOCATION")) { |
| info.Location = location; |
| } |
| } |
| |
| // Get the soname. |
| if (this->GetType() == cmStateEnums::SHARED_LIBRARY) { |
| std::string soProp = "IMPORTED_SONAME"; |
| soProp += suffix; |
| if (const char* config_soname = this->GetProperty(soProp)) { |
| info.SOName = config_soname; |
| } else if (const char* soname = this->GetProperty("IMPORTED_SONAME")) { |
| info.SOName = soname; |
| } |
| } |
| |
| // Get the "no-soname" mark. |
| if (this->GetType() == cmStateEnums::SHARED_LIBRARY) { |
| std::string soProp = "IMPORTED_NO_SONAME"; |
| soProp += suffix; |
| if (const char* config_no_soname = this->GetProperty(soProp)) { |
| info.NoSOName = cmSystemTools::IsOn(config_no_soname); |
| } else if (const char* no_soname = |
| this->GetProperty("IMPORTED_NO_SONAME")) { |
| info.NoSOName = cmSystemTools::IsOn(no_soname); |
| } |
| } |
| |
| // Get the import library. |
| if (imp) { |
| info.ImportLibrary = imp; |
| } else if (this->GetType() == cmStateEnums::SHARED_LIBRARY || |
| this->IsExecutableWithExports()) { |
| std::string impProp = "IMPORTED_IMPLIB"; |
| impProp += suffix; |
| if (const char* config_implib = this->GetProperty(impProp)) { |
| info.ImportLibrary = config_implib; |
| } else if (const char* implib = this->GetProperty("IMPORTED_IMPLIB")) { |
| info.ImportLibrary = implib; |
| } |
| } |
| |
| // Get the link dependencies. |
| { |
| std::string linkProp = "IMPORTED_LINK_DEPENDENT_LIBRARIES"; |
| linkProp += suffix; |
| if (const char* config_libs = this->GetProperty(linkProp)) { |
| info.SharedDeps = config_libs; |
| } else if (const char* libs = |
| this->GetProperty("IMPORTED_LINK_DEPENDENT_LIBRARIES")) { |
| info.SharedDeps = libs; |
| } |
| } |
| |
| // Get the link languages. |
| if (this->LinkLanguagePropagatesToDependents()) { |
| std::string linkProp = "IMPORTED_LINK_INTERFACE_LANGUAGES"; |
| linkProp += suffix; |
| if (const char* config_libs = this->GetProperty(linkProp)) { |
| info.Languages = config_libs; |
| } else if (const char* libs = |
| this->GetProperty("IMPORTED_LINK_INTERFACE_LANGUAGES")) { |
| info.Languages = libs; |
| } |
| } |
| |
| // Get information if target is managed assembly. |
| { |
| std::string linkProp = "IMPORTED_COMMON_LANGUAGE_RUNTIME"; |
| if (auto pc = this->GetProperty(linkProp + suffix)) { |
| info.Managed = this->CheckManagedType(pc); |
| } else if (auto p = this->GetProperty(linkProp)) { |
| info.Managed = this->CheckManagedType(p); |
| } |
| } |
| |
| // Get the cyclic repetition count. |
| if (this->GetType() == cmStateEnums::STATIC_LIBRARY) { |
| std::string linkProp = "IMPORTED_LINK_INTERFACE_MULTIPLICITY"; |
| linkProp += suffix; |
| if (const char* config_reps = this->GetProperty(linkProp)) { |
| sscanf(config_reps, "%u", &info.Multiplicity); |
| } else if (const char* reps = |
| this->GetProperty("IMPORTED_LINK_INTERFACE_MULTIPLICITY")) { |
| sscanf(reps, "%u", &info.Multiplicity); |
| } |
| } |
| } |
| |
| cmHeadToLinkInterfaceMap& cmGeneratorTarget::GetHeadToLinkInterfaceMap( |
| const std::string& config) const |
| { |
| std::string CONFIG = cmSystemTools::UpperCase(config); |
| return this->LinkInterfaceMap[CONFIG]; |
| } |
| |
| cmHeadToLinkInterfaceMap& |
| cmGeneratorTarget::GetHeadToLinkInterfaceUsageRequirementsMap( |
| const std::string& config) const |
| { |
| std::string CONFIG = cmSystemTools::UpperCase(config); |
| return this->LinkInterfaceUsageRequirementsOnlyMap[CONFIG]; |
| } |
| |
| const cmLinkImplementation* cmGeneratorTarget::GetLinkImplementation( |
| const std::string& config) const |
| { |
| // There is no link implementation for imported targets. |
| if (this->IsImported()) { |
| return nullptr; |
| } |
| |
| std::string CONFIG = cmSystemTools::UpperCase(config); |
| cmOptionalLinkImplementation& impl = this->LinkImplMap[CONFIG][this]; |
| if (!impl.LibrariesDone) { |
| impl.LibrariesDone = true; |
| this->ComputeLinkImplementationLibraries(config, impl, this); |
| } |
| if (!impl.LanguagesDone) { |
| impl.LanguagesDone = true; |
| this->ComputeLinkImplementationLanguages(config, impl); |
| } |
| return &impl; |
| } |
| |
| bool cmGeneratorTarget::GetConfigCommonSourceFiles( |
| std::vector<cmSourceFile*>& files) const |
| { |
| std::vector<std::string> configs; |
| this->Makefile->GetConfigurations(configs); |
| if (configs.empty()) { |
| configs.emplace_back(); |
| } |
| |
| std::vector<std::string>::const_iterator it = configs.begin(); |
| const std::string& firstConfig = *it; |
| this->GetSourceFilesWithoutObjectLibraries(files, firstConfig); |
| |
| for (; it != configs.end(); ++it) { |
| std::vector<cmSourceFile*> configFiles; |
| this->GetSourceFilesWithoutObjectLibraries(configFiles, *it); |
| if (configFiles != files) { |
| std::string firstConfigFiles; |
| const char* sep = ""; |
| for (cmSourceFile* f : files) { |
| firstConfigFiles += sep; |
| firstConfigFiles += f->GetFullPath(); |
| sep = "\n "; |
| } |
| |
| std::string thisConfigFiles; |
| sep = ""; |
| for (cmSourceFile* f : configFiles) { |
| thisConfigFiles += sep; |
| thisConfigFiles += f->GetFullPath(); |
| sep = "\n "; |
| } |
| std::ostringstream e; |
| /* clang-format off */ |
| e << "Target \"" << this->GetName() |
| << "\" has source files which vary by " |
| "configuration. This is not supported by the \"" |
| << this->GlobalGenerator->GetName() |
| << "\" generator.\n" |
| "Config \"" << firstConfig << "\":\n" |
| " " << firstConfigFiles << "\n" |
| "Config \"" << *it << "\":\n" |
| " " << thisConfigFiles << "\n"; |
| /* clang-format on */ |
| this->LocalGenerator->IssueMessage(MessageType::FATAL_ERROR, e.str()); |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| void cmGeneratorTarget::GetObjectLibrariesCMP0026( |
| std::vector<cmGeneratorTarget*>& objlibs) const |
| { |
| // 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 rng = this->Target->GetSourceEntries(); |
| for (std::string const& entry : rng) { |
| std::vector<std::string> files; |
| cmSystemTools::ExpandListArgument(entry, files); |
| for (std::string const& li : files) { |
| if (cmHasLiteralPrefix(li, "$<TARGET_OBJECTS:") && li.back() == '>') { |
| std::string objLibName = li.substr(17, li.size() - 18); |
| |
| if (cmGeneratorExpression::Find(objLibName) != std::string::npos) { |
| continue; |
| } |
| cmGeneratorTarget* objLib = |
| this->LocalGenerator->FindGeneratorTargetToUse(objLibName); |
| if (objLib) { |
| objlibs.push_back(objLib); |
| } |
| } |
| } |
| } |
| } |
| |
| std::string cmGeneratorTarget::CheckCMP0004(std::string const& item) const |
| { |
| // Strip whitespace off the library names because we used to do this |
| // in case variables were expanded at generate time. We no longer |
| // do the expansion but users link to libraries like " ${VAR} ". |
| std::string lib = item; |
| std::string::size_type pos = lib.find_first_not_of(" \t\r\n"); |
| if (pos != std::string::npos) { |
| lib = lib.substr(pos); |
| } |
| pos = lib.find_last_not_of(" \t\r\n"); |
| if (pos != std::string::npos) { |
| lib = lib.substr(0, pos + 1); |
| } |
| if (lib != item) { |
| cmake* cm = this->LocalGenerator->GetCMakeInstance(); |
| switch (this->GetPolicyStatusCMP0004()) { |
| case cmPolicies::WARN: { |
| std::ostringstream w; |
| w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0004) << "\n" |
| << "Target \"" << this->GetName() << "\" links to item \"" << item |
| << "\" which has leading or trailing whitespace."; |
| cm->IssueMessage(MessageType::AUTHOR_WARNING, w.str(), |
| this->GetBacktrace()); |
| } |
| case cmPolicies::OLD: |
| break; |
| case cmPolicies::NEW: { |
| std::ostringstream e; |
| e << "Target \"" << this->GetName() << "\" links to item \"" << item |
| << "\" which has leading or trailing whitespace. " |
| << "This is now an error according to policy CMP0004."; |
| cm->IssueMessage(MessageType::FATAL_ERROR, e.str(), |
| this->GetBacktrace()); |
| } break; |
| case cmPolicies::REQUIRED_IF_USED: |
| case cmPolicies::REQUIRED_ALWAYS: { |
| std::ostringstream e; |
| e << cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0004) << "\n" |
| << "Target \"" << this->GetName() << "\" links to item \"" << item |
| << "\" which has leading or trailing whitespace."; |
| cm->IssueMessage(MessageType::FATAL_ERROR, e.str(), |
| this->GetBacktrace()); |
| } break; |
| } |
| } |
| return lib; |
| } |
| |
| void cmGeneratorTarget::GetLanguages(std::set<std::string>& languages, |
| const std::string& config) const |
| { |
| std::vector<cmSourceFile*> sourceFiles; |
| this->GetSourceFiles(sourceFiles, config); |
| for (cmSourceFile* src : sourceFiles) { |
| const std::string& lang = src->GetLanguage(); |
| if (!lang.empty()) { |
| languages.insert(lang); |
| } |
| } |
| |
| std::vector<cmGeneratorTarget*> objectLibraries; |
| std::vector<cmSourceFile const*> externalObjects; |
| if (!this->GlobalGenerator->GetConfigureDoneCMP0026()) { |
| std::vector<cmGeneratorTarget*> objectTargets; |
| this->GetObjectLibrariesCMP0026(objectTargets); |
| objectLibraries.reserve(objectTargets.size()); |
| for (cmGeneratorTarget* gt : objectTargets) { |
| objectLibraries.push_back(gt); |
| } |
| } else { |
| this->GetExternalObjects(externalObjects, config); |
| for (cmSourceFile const* extObj : externalObjects) { |
| std::string objLib = extObj->GetObjectLibrary(); |
| if (cmGeneratorTarget* tgt = |
| this->LocalGenerator->FindGeneratorTargetToUse(objLib)) { |
| auto const objLibIt = |
| std::find_if(objectLibraries.cbegin(), objectLibraries.cend(), |
| [tgt](cmGeneratorTarget* t) { return t == tgt; }); |
| if (objectLibraries.cend() == objLibIt) { |
| objectLibraries.push_back(tgt); |
| } |
| } |
| } |
| } |
| for (cmGeneratorTarget* objLib : objectLibraries) { |
| objLib->GetLanguages(languages, config); |
| } |
| } |
| |
| bool cmGeneratorTarget::IsCSharpOnly() const |
| { |
| // Only certain target types may compile CSharp. |
| if (this->GetType() != cmStateEnums::SHARED_LIBRARY && |
| this->GetType() != cmStateEnums::STATIC_LIBRARY && |
| this->GetType() != cmStateEnums::EXECUTABLE) { |
| return false; |
| } |
| std::set<std::string> languages; |
| this->GetLanguages(languages, ""); |
| // Consider an explicit linker language property, but *not* the |
| // computed linker language that may depend on linked targets. |
| const char* linkLang = this->GetProperty("LINKER_LANGUAGE"); |
| if (linkLang && *linkLang) { |
| languages.insert(linkLang); |
| } |
| return languages.size() == 1 && languages.count("CSharp") > 0; |
| } |
| |
| void cmGeneratorTarget::ComputeLinkImplementationLanguages( |
| const std::string& config, cmOptionalLinkImplementation& impl) const |
| { |
| // This target needs runtime libraries for its source languages. |
| std::set<std::string> languages; |
| // Get languages used in our source files. |
| this->GetLanguages(languages, config); |
| // Copy the set of languages to the link implementation. |
| impl.Languages.insert(impl.Languages.begin(), languages.begin(), |
| languages.end()); |
| } |
| |
| bool cmGeneratorTarget::HaveBuildTreeRPATH(const std::string& config) const |
| { |
| if (this->GetPropertyAsBool("SKIP_BUILD_RPATH")) { |
| return false; |
| } |
| if (this->GetProperty("BUILD_RPATH")) { |
| return true; |
| } |
| if (cmLinkImplementationLibraries const* impl = |
| this->GetLinkImplementationLibraries(config)) { |
| return !impl->Libraries.empty(); |
| } |
| return false; |
| } |
| |
| cmLinkImplementationLibraries const* |
| cmGeneratorTarget::GetLinkImplementationLibraries( |
| const std::string& config) const |
| { |
| return this->GetLinkImplementationLibrariesInternal(config, this); |
| } |
| |
| cmLinkImplementationLibraries const* |
| cmGeneratorTarget::GetLinkImplementationLibrariesInternal( |
| const std::string& config, cmGeneratorTarget const* head) const |
| { |
| // There is no link implementation for imported targets. |
| if (this->IsImported()) { |
| return nullptr; |
| } |
| |
| // Populate the link implementation libraries for this configuration. |
| std::string CONFIG = cmSystemTools::UpperCase(config); |
| HeadToLinkImplementationMap& hm = this->LinkImplMap[CONFIG]; |
| |
| // If the link implementation does not depend on the head target |
| // then return the one we computed first. |
| if (!hm.empty() && !hm.begin()->second.HadHeadSensitiveCondition) { |
| return &hm.begin()->second; |
| } |
| |
| cmOptionalLinkImplementation& impl = hm[head]; |
| if (!impl.LibrariesDone) { |
| impl.LibrariesDone = true; |
| this->ComputeLinkImplementationLibraries(config, impl, head); |
| } |
| return &impl; |
| } |
| |
| bool cmGeneratorTarget::IsNullImpliedByLinkLibraries( |
| const std::string& p) const |
| { |
| return this->LinkImplicitNullProperties.find(p) != |
| this->LinkImplicitNullProperties.end(); |
| } |
| |
| void cmGeneratorTarget::ComputeLinkImplementationLibraries( |
| const std::string& config, cmOptionalLinkImplementation& impl, |
| cmGeneratorTarget const* head) const |
| { |
| cmStringRange entryRange = this->Target->GetLinkImplementationEntries(); |
| cmBacktraceRange btRange = this->Target->GetLinkImplementationBacktraces(); |
| cmBacktraceRange::const_iterator btIt = btRange.begin(); |
| // Collect libraries directly linked in this configuration. |
| for (cmStringRange::const_iterator le = entryRange.begin(), |
| end = entryRange.end(); |
| le != end; ++le, ++btIt) { |
| std::vector<std::string> llibs; |
| cmGeneratorExpressionDAGChecker dagChecker(this, "LINK_LIBRARIES", nullptr, |
| nullptr); |
| cmGeneratorExpression ge(*btIt); |
| std::unique_ptr<cmCompiledGeneratorExpression> const cge = ge.Parse(*le); |
| std::string const& evaluated = |
| cge->Evaluate(this->LocalGenerator, config, false, head, &dagChecker); |
| cmSystemTools::ExpandListArgument(evaluated, llibs); |
| if (cge->GetHadHeadSensitiveCondition()) { |
| impl.HadHeadSensitiveCondition = true; |
| } |
| |
| for (std::string const& lib : llibs) { |
| // Skip entries that resolve to the target itself or are empty. |
| std::string name = this->CheckCMP0004(lib); |
| if (name == this->GetName() || name.empty()) { |
| if (name == this->GetName()) { |
| bool noMessage = false; |
| MessageType messageType = MessageType::FATAL_ERROR; |
| std::ostringstream e; |
| switch (this->GetPolicyStatusCMP0038()) { |
| case cmPolicies::WARN: { |
| e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0038) << "\n"; |
| messageType = MessageType::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->GetName() << "\" links to itself."; |
| this->LocalGenerator->GetCMakeInstance()->IssueMessage( |
| messageType, e.str(), this->GetBacktrace()); |
| if (messageType == MessageType::FATAL_ERROR) { |
| return; |
| } |
| } |
| } |
| continue; |
| } |
| |
| // The entry is meant for this configuration. |
| impl.Libraries.emplace_back(this->ResolveLinkItem(name, *btIt), |
| evaluated != *le); |
| } |
| |
| std::set<std::string> const& seenProps = cge->GetSeenTargetProperties(); |
| for (std::string const& sp : seenProps) { |
| if (!this->GetProperty(sp)) { |
| this->LinkImplicitNullProperties.insert(sp); |
| } |
| } |
| cge->GetMaxLanguageStandard(this, this->MaxLanguageStandards); |
| } |
| |
| // Get the list of configurations considered to be DEBUG. |
| std::vector<std::string> debugConfigs = |
| this->Makefile->GetCMakeInstance()->GetDebugConfigs(); |
| |
| cmTargetLinkLibraryType linkType = |
| CMP0003_ComputeLinkType(config, debugConfigs); |
| cmTarget::LinkLibraryVectorType const& oldllibs = |
| this->Target->GetOriginalLinkLibraries(); |
| for (cmTarget::LibraryID const& oldllib : oldllibs) { |
| if (oldllib.second != GENERAL_LibraryType && oldllib.second != linkType) { |
| std::string name = this->CheckCMP0004(oldllib.first); |
| if (name == this->GetName() || name.empty()) { |
| continue; |
| } |
| // Support OLD behavior for CMP0003. |
| impl.WrongConfigLibraries.push_back( |
| this->ResolveLinkItem(name, cmListFileBacktrace())); |
| } |
| } |
| } |
| |
| cmGeneratorTarget::TargetOrString cmGeneratorTarget::ResolveTargetReference( |
| std::string const& name) const |
| { |
| cmLocalGenerator const* lg = this->LocalGenerator; |
| std::string const* lookupName = &name; |
| |
| // When target_link_libraries() is called with a LHS target that is |
| // not created in the calling directory it adds a directory id suffix |
| // that we can use to look up the calling directory. It is that scope |
| // in which the item name is meaningful. This case is relatively rare |
| // so we allocate a separate string only when the directory id is present. |
| std::string::size_type pos = name.find(CMAKE_DIRECTORY_ID_SEP); |
| std::string plainName; |
| if (pos != std::string::npos) { |
| // We will look up the plain name without the directory id suffix. |
| plainName = name.substr(0, pos); |
| |
| // We will look up in the scope of the directory id. |
| // If we do not recognize the id then leave the original |
| // syntax in place to produce an indicative error later. |
| cmDirectoryId const dirId = |
| name.substr(pos + sizeof(CMAKE_DIRECTORY_ID_SEP) - 1); |
| if (cmLocalGenerator const* otherLG = |
| this->GlobalGenerator->FindLocalGenerator(dirId)) { |
| lg = otherLG; |
| lookupName = &plainName; |
| } |
| } |
| |
| TargetOrString resolved; |
| |
| if (cmGeneratorTarget* tgt = lg->FindGeneratorTargetToUse(*lookupName)) { |
| resolved.Target = tgt; |
| } else if (lookupName == &plainName) { |
| resolved.String = std::move(plainName); |
| } else { |
| resolved.String = name; |
| } |
| |
| return resolved; |
| } |
| |
| cmLinkItem cmGeneratorTarget::ResolveLinkItem( |
| std::string const& name, cmListFileBacktrace const& bt) const |
| { |
| TargetOrString resolved = this->ResolveTargetReference(name); |
| |
| if (!resolved.Target) { |
| return cmLinkItem(resolved.String, bt); |
| } |
| |
| // Skip targets that will not really be linked. This is probably a |
| // name conflict between an external library and an executable |
| // within the project. |
| if (resolved.Target->GetType() == cmStateEnums::EXECUTABLE && |
| !resolved.Target->IsExecutableWithExports()) { |
| return cmLinkItem(resolved.Target->GetName(), bt); |
| } |
| |
| return cmLinkItem(resolved.Target, bt); |
| } |
| |
| std::string cmGeneratorTarget::GetPDBDirectory(const std::string& config) const |
| { |
| if (OutputInfo const* info = this->GetOutputInfo(config)) { |
| // Return the directory in which the target will be built. |
| return info->PdbDir; |
| } |
| return ""; |
| } |
| |
| bool cmGeneratorTarget::HasImplibGNUtoMS(std::string const& config) const |
| { |
| return this->HasImportLibrary(config) && this->GetPropertyAsBool("GNUtoMS"); |
| } |
| |
| bool cmGeneratorTarget::GetImplibGNUtoMS(std::string const& config, |
| std::string const& gnuName, |
| std::string& out, |
| const char* newExt) const |
| { |
| if (this->HasImplibGNUtoMS(config) && gnuName.size() > 6 && |
| gnuName.substr(gnuName.size() - 6) == ".dll.a") { |
| out = gnuName.substr(0, gnuName.size() - 6); |
| out += newExt ? newExt : ".lib"; |
| return true; |
| } |
| return false; |
| } |
| |
| bool cmGeneratorTarget::IsExecutableWithExports() const |
| { |
| return (this->GetType() == cmStateEnums::EXECUTABLE && |
| this->GetPropertyAsBool("ENABLE_EXPORTS")); |
| } |
| |
| bool cmGeneratorTarget::HasImportLibrary(std::string const& config) const |
| { |
| return (this->IsDLLPlatform() && |
| (this->GetType() == cmStateEnums::SHARED_LIBRARY || |
| this->IsExecutableWithExports()) && |
| // Assemblies which have only managed code do not have |
| // import libraries. |
| this->GetManagedType(config) != ManagedType::Managed); |
| } |
| |
| std::string cmGeneratorTarget::GetSupportDirectory() const |
| { |
| std::string dir = this->LocalGenerator->GetCurrentBinaryDirectory(); |
| dir += "/CMakeFiles"; |
| dir += "/"; |
| dir += this->GetName(); |
| #if defined(__VMS) |
| dir += "_dir"; |
| #else |
| dir += ".dir"; |
| #endif |
| return dir; |
| } |
| |
| bool cmGeneratorTarget::IsLinkable() const |
| { |
| return (this->GetType() == cmStateEnums::STATIC_LIBRARY || |
| this->GetType() == cmStateEnums::SHARED_LIBRARY || |
| this->GetType() == cmStateEnums::MODULE_LIBRARY || |
| this->GetType() == cmStateEnums::UNKNOWN_LIBRARY || |
| this->GetType() == cmStateEnums::OBJECT_LIBRARY || |
| this->GetType() == cmStateEnums::INTERFACE_LIBRARY || |
| this->IsExecutableWithExports()); |
| } |
| |
| bool cmGeneratorTarget::IsFrameworkOnApple() const |
| { |
| return ((this->GetType() == cmStateEnums::SHARED_LIBRARY || |
| this->GetType() == cmStateEnums::STATIC_LIBRARY) && |
| this->Makefile->IsOn("APPLE") && |
| this->GetPropertyAsBool("FRAMEWORK")); |
| } |
| |
| bool cmGeneratorTarget::IsAppBundleOnApple() const |
| { |
| return (this->GetType() == cmStateEnums::EXECUTABLE && |
| this->Makefile->IsOn("APPLE") && |
| this->GetPropertyAsBool("MACOSX_BUNDLE")); |
| } |
| |
| bool cmGeneratorTarget::IsXCTestOnApple() const |
| { |
| return (this->IsCFBundleOnApple() && this->GetPropertyAsBool("XCTEST")); |
| } |
| |
| bool cmGeneratorTarget::IsCFBundleOnApple() const |
| { |
| return (this->GetType() == cmStateEnums::MODULE_LIBRARY && |
| this->Makefile->IsOn("APPLE") && this->GetPropertyAsBool("BUNDLE")); |
| } |
| |
| cmGeneratorTarget::ManagedType cmGeneratorTarget::CheckManagedType( |
| std::string const& propval) const |
| { |
| // The type of the managed assembly (mixed unmanaged C++ and C++/CLI, |
| // or only C++/CLI) does only depend on whether the property is an empty |
| // string or contains any value at all. In Visual Studio generators |
| // this propval is prepended with /clr[:] which results in: |
| // |
| // 1. propval does not exist: no /clr flag, unmanaged target, has import |
| // lib |
| // 2. empty propval: add /clr as flag, mixed unmanaged/managed |
| // target, has import lib |
| // 3. any value (safe,pure): add /clr:[propval] as flag, target with |
| // managed code only, no import lib |
| return propval.empty() ? ManagedType::Mixed : ManagedType::Managed; |
| } |
| |
| cmGeneratorTarget::ManagedType cmGeneratorTarget::GetManagedType( |
| const std::string& config) const |
| { |
| // Only libraries and executables can be managed targets. |
| if (this->GetType() > cmStateEnums::SHARED_LIBRARY) { |
| return ManagedType::Undefined; |
| } |
| |
| if (this->GetType() == cmStateEnums::STATIC_LIBRARY) { |
| return ManagedType::Native; |
| } |
| |
| // Check imported target. |
| if (this->IsImported()) { |
| if (cmGeneratorTarget::ImportInfo const* info = |
| this->GetImportInfo(config)) { |
| return info->Managed; |
| } |
| return ManagedType::Undefined; |
| } |
| |
| // Check for explicitly set clr target property. |
| if (auto* clr = this->GetProperty("COMMON_LANGUAGE_RUNTIME")) { |
| return this->CheckManagedType(clr); |
| } |
| |
| // C# targets are always managed. This language specific check |
| // is added to avoid that the COMMON_LANGUAGE_RUNTIME target property |
| // has to be set manually for C# targets. |
| return this->IsCSharpOnly() ? ManagedType::Managed : ManagedType::Native; |
| } |