| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying | |
| file LICENSE.rst or https://cmake.org/licensing for details. */ | |
| #include "cmGeneratorTarget.h" | |
| #include <algorithm> | |
| #include <array> | |
| #include <cassert> | |
| #include <cstddef> | |
| #include <cstdio> | |
| #include <cstring> | |
| #include <ostream> | |
| #include <string> | |
| #include <unordered_set> | |
| #include <utility> | |
| #include <cm/memory> | |
| #include <cm/optional> | |
| #include <cm/string_view> | |
| #include <cmext/algorithm> | |
| #include <cmext/string_view> | |
| #include "cmsys/String.h" | |
| #include "cmAlgorithms.h" | |
| #include "cmComputeLinkInformation.h" // IWYU pragma: keep | |
| #include "cmCryptoHash.h" | |
| #include "cmCxxModuleMetadata.h" | |
| #include "cmCxxModuleUsageEffects.h" | |
| #include "cmExperimental.h" | |
| #include "cmFileSet.h" | |
| #include "cmFileSetMetadata.h" | |
| #include "cmFileTimes.h" | |
| #include "cmGenExContext.h" | |
| #include "cmGeneratedFileStream.h" | |
| #include "cmGeneratorExpression.h" | |
| #include "cmGeneratorExpressionDAGChecker.h" | |
| #include "cmGeneratorFileSet.h" | |
| #include "cmGeneratorFileSets.h" | |
| #include "cmGeneratorOptions.h" | |
| #include "cmGlobalGenerator.h" | |
| #include "cmLinkLineDeviceComputer.h" | |
| #include "cmList.h" | |
| #include "cmLocalGenerator.h" | |
| #include "cmMakefile.h" | |
| #include "cmMessageType.h" | |
| #include "cmOutputConverter.h" | |
| #include "cmPropertyMap.h" | |
| #include "cmRulePlaceholderExpander.h" | |
| #include "cmSourceFile.h" | |
| #include "cmSourceFileLocationKind.h" | |
| #include "cmStandardLevel.h" | |
| #include "cmStandardLevelResolver.h" | |
| #include "cmState.h" | |
| #include "cmStateTypes.h" | |
| #include "cmStringAlgorithms.h" | |
| #include "cmSystemTools.h" | |
| #include "cmTarget.h" | |
| #include "cmTargetLinkLibraryType.h" | |
| #include "cmTargetPropertyComputer.h" | |
| #include "cmTargetTraceDependencies.h" | |
| #include "cmake.h" | |
| namespace { | |
| using UseTo = cmGeneratorTarget::UseTo; | |
| } | |
| template <> | |
| cmValue cmTargetPropertyComputer::GetSources<cmGeneratorTarget>( | |
| cmGeneratorTarget const* tgt) | |
| { | |
| return tgt->GetSourcesProperty(); | |
| } | |
| template <> | |
| std::string const& | |
| cmTargetPropertyComputer::ImportedLocation<cmGeneratorTarget>( | |
| cmGeneratorTarget const* tgt, std::string const& config) | |
| { | |
| return tgt->GetLocation(config); | |
| } | |
| static void CreatePropertyGeneratorExpressions( | |
| cmake& cmakeInstance, cmBTStringRange entries, | |
| std::vector<std::unique_ptr<cm::TargetPropertyEntry>>& items, | |
| bool evaluateForBuildsystem = false) | |
| { | |
| for (auto const& entry : entries) { | |
| items.emplace_back(cm::TargetPropertyEntry::Create( | |
| cmakeInstance, entry, evaluateForBuildsystem)); | |
| } | |
| } | |
| cmGeneratorTarget::cmGeneratorTarget(cmTarget* t, cmLocalGenerator* lg) | |
| : Target(t) | |
| , Makefile(t->GetMakefile()) | |
| , LocalGenerator(lg) | |
| , GlobalGenerator(lg->GetGlobalGenerator()) | |
| , FileSets(cm::make_unique<cmGeneratorFileSets>(this, lg)) | |
| { | |
| this->GlobalGenerator->ComputeTargetObjectDirectory(this); | |
| CreatePropertyGeneratorExpressions(*lg->GetCMakeInstance(), | |
| t->GetIncludeDirectoriesEntries(), | |
| this->IncludeDirectoriesEntries); | |
| CreatePropertyGeneratorExpressions(*lg->GetCMakeInstance(), | |
| t->GetCompileOptionsEntries(), | |
| this->CompileOptionsEntries); | |
| CreatePropertyGeneratorExpressions(*lg->GetCMakeInstance(), | |
| t->GetCompileFeaturesEntries(), | |
| this->CompileFeaturesEntries); | |
| CreatePropertyGeneratorExpressions(*lg->GetCMakeInstance(), | |
| t->GetCompileDefinitionsEntries(), | |
| this->CompileDefinitionsEntries); | |
| CreatePropertyGeneratorExpressions(*lg->GetCMakeInstance(), | |
| t->GetCompileWrappersEntries(), | |
| this->CompileWrappersEntries); | |
| CreatePropertyGeneratorExpressions(*lg->GetCMakeInstance(), | |
| t->GetLinkOptionsEntries(), | |
| this->LinkOptionsEntries); | |
| CreatePropertyGeneratorExpressions(*lg->GetCMakeInstance(), | |
| t->GetLinkDirectoriesEntries(), | |
| this->LinkDirectoriesEntries); | |
| CreatePropertyGeneratorExpressions(*lg->GetCMakeInstance(), | |
| t->GetLinkWrappersEntries(), | |
| this->LinkWrappersEntries); | |
| CreatePropertyGeneratorExpressions(*lg->GetCMakeInstance(), | |
| t->GetPrecompileHeadersEntries(), | |
| this->PrecompileHeadersEntries); | |
| CreatePropertyGeneratorExpressions( | |
| *lg->GetCMakeInstance(), t->GetSourceEntries(), this->SourceEntries, true); | |
| this->PolicyMap = t->GetPolicyMap(); | |
| // Get hard-coded linker language | |
| if (this->Target->GetProperty("HAS_CXX")) { | |
| this->LinkerLanguage = "CXX"; | |
| } else { | |
| this->LinkerLanguage = this->Target->GetSafeProperty("LINKER_LANGUAGE"); | |
| } | |
| auto configs = | |
| this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); | |
| std::string build_db_languages[] = { "CXX" }; | |
| for (auto const& language : build_db_languages) { | |
| for (auto const& config : configs) { | |
| auto bdb_path = this->BuildDatabasePath(language, config); | |
| if (!bdb_path.empty()) { | |
| this->Makefile->GetOrCreateGeneratedSource(bdb_path); | |
| this->GetGlobalGenerator()->AddBuildDatabaseFile(language, config, | |
| bdb_path); | |
| } | |
| } | |
| } | |
| } | |
| cmGeneratorTarget::~cmGeneratorTarget() = default; | |
| cmValue cmGeneratorTarget::GetSourcesProperty() const | |
| { | |
| std::vector<std::string> values; | |
| for (auto const& se : this->SourceEntries) { | |
| values.push_back(se->GetInput()); | |
| } | |
| static std::string value; | |
| value = cmList::to_string(values); | |
| return cmValue(value); | |
| } | |
| cmGlobalGenerator* cmGeneratorTarget::GetGlobalGenerator() const | |
| { | |
| return this->GetLocalGenerator()->GetGlobalGenerator(); | |
| } | |
| cmLocalGenerator* cmGeneratorTarget::GetLocalGenerator() const | |
| { | |
| return this->LocalGenerator; | |
| } | |
| cm::TargetType cmGeneratorTarget::GetType() const | |
| { | |
| return this->Target->GetType(); | |
| } | |
| std::string const& cmGeneratorTarget::GetName() const | |
| { | |
| return this->Target->GetName(); | |
| } | |
| std::string cmGeneratorTarget::GetFamilyName() const | |
| { | |
| if (!this->IsImported() && !this->IsSynthetic()) { | |
| return this->Target->GetTemplateName(); | |
| } | |
| cmCryptoHash hasher(cmCryptoHash::AlgoSHA3_512); | |
| constexpr size_t HASH_TRUNCATION = 12; | |
| auto dirhash = | |
| hasher.HashString(this->GetLocalGenerator()->GetCurrentBinaryDirectory()); | |
| auto targetIdent = hasher.HashString(cmStrCat("@d_", dirhash)); | |
| return cmStrCat(this->Target->GetTemplateName(), '@', | |
| targetIdent.substr(0, HASH_TRUNCATION)); | |
| } | |
| std::string cmGeneratorTarget::GetExportName() const | |
| { | |
| cmValue exportName = this->GetProperty("EXPORT_NAME"); | |
| if (cmNonempty(exportName)) { | |
| if (!cmGeneratorExpression::IsValidTargetName(*exportName)) { | |
| cmSystemTools::Error(cmStrCat("EXPORT_NAME property \"", *exportName, | |
| "\" for \"", this->GetName(), | |
| "\": is not valid.")); | |
| return ""; | |
| } | |
| return *exportName; | |
| } | |
| return this->GetName(); | |
| } | |
| std::string cmGeneratorTarget::GetFilesystemExportName() const | |
| { | |
| auto fs_safe = this->GetExportName(); | |
| // First escape any `_` characters to avoid collisions. | |
| cmSystemTools::ReplaceString(fs_safe, "_", "__"); | |
| // Escape other characters that are not generally filesystem-safe. | |
| cmSystemTools::ReplaceString(fs_safe, ":", "_c"); | |
| return fs_safe; | |
| } | |
| cmValue cmGeneratorTarget::GetProperty(std::string const& prop) const | |
| { | |
| if (cmValue result = | |
| cmTargetPropertyComputer::GetProperty(this, prop, *this->Makefile)) { | |
| return result; | |
| } | |
| if (cmSystemTools::GetFatalErrorOccurred()) { | |
| return nullptr; | |
| } | |
| return this->Target->GetProperty(prop); | |
| } | |
| std::string const& cmGeneratorTarget::GetSafeProperty( | |
| std::string const& prop) const | |
| { | |
| return this->GetProperty(prop); | |
| } | |
| char const* cmGeneratorTarget::GetOutputTargetType( | |
| cmStateEnums::ArtifactType artifact) const | |
| { | |
| if (this->IsFrameworkOnApple() || this->GetGlobalGenerator()->IsXcode()) { | |
| // import file (i.e. .tbd file) is always in same location as library | |
| artifact = cmStateEnums::RuntimeBinaryArtifact; | |
| } | |
| switch (this->GetType()) { | |
| case cm::TargetType::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 { | |
| switch (artifact) { | |
| case cmStateEnums::RuntimeBinaryArtifact: | |
| // For non-DLL platforms shared libraries are treated as | |
| // library targets. | |
| return "LIBRARY"; | |
| case cmStateEnums::ImportLibraryArtifact: | |
| // Library import libraries are treated as archive targets. | |
| return "ARCHIVE"; | |
| } | |
| } | |
| break; | |
| case cm::TargetType::STATIC_LIBRARY: | |
| // Static libraries are always treated as archive targets. | |
| return "ARCHIVE"; | |
| case cm::TargetType::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 cm::TargetType::OBJECT_LIBRARY: | |
| // Object libraries are always treated as object targets. | |
| return "OBJECT"; | |
| case cm::TargetType::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( | |
| std::string const& config, cmStateEnums::ArtifactType artifact) const | |
| { | |
| // Lookup/compute/cache the output name for this configuration. | |
| OutputNameKey key(config, artifact); | |
| auto 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(cmStrCat(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 (cmValue outNameProp = this->GetProperty(p)) { | |
| outName = *outNameProp; | |
| break; | |
| } | |
| } | |
| if (outName.empty()) { | |
| outName = this->GetName(); | |
| } | |
| // Now evaluate genex and update the previously-prepared map entry. | |
| i->second = | |
| cmGeneratorExpression::Evaluate(outName, 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, | |
| cmStrCat("Target '", this->GetName(), | |
| "' OUTPUT_NAME depends on itself."), | |
| this->GetBacktrace()); | |
| } | |
| return i->second; | |
| } | |
| std::string cmGeneratorTarget::GetFilePrefix( | |
| std::string const& config, cmStateEnums::ArtifactType artifact) const | |
| { | |
| if (this->IsImported()) { | |
| cmValue prefix = this->GetFilePrefixInternal(config, artifact); | |
| return prefix ? *prefix : std::string(); | |
| } | |
| return this->GetFullNameInternalComponents(config, artifact).prefix; | |
| } | |
| std::string cmGeneratorTarget::GetFileSuffix( | |
| std::string const& config, cmStateEnums::ArtifactType artifact) const | |
| { | |
| if (this->IsImported()) { | |
| cmValue suffix = this->GetFileSuffixInternal(config, artifact); | |
| return suffix ? *suffix : std::string(); | |
| } | |
| return this->GetFullNameInternalComponents(config, artifact).suffix; | |
| } | |
| std::string cmGeneratorTarget::GetFilePostfix(std::string const& config) const | |
| { | |
| cmValue postfix = nullptr; | |
| std::string frameworkPostfix; | |
| if (!config.empty()) { | |
| std::string configProp = | |
| cmStrCat(cmSystemTools::UpperCase(config), "_POSTFIX"); | |
| postfix = this->GetProperty(configProp); | |
| // Mac application bundles and frameworks have no regular postfix like | |
| // libraries do. | |
| if (!this->IsImported() && postfix && | |
| (this->IsAppBundleOnApple() || this->IsFrameworkOnApple())) { | |
| postfix = nullptr; | |
| } | |
| // Frameworks created by multi config generators can have a special | |
| // framework postfix. | |
| frameworkPostfix = this->GetFrameworkMultiConfigPostfix(config); | |
| if (!frameworkPostfix.empty()) { | |
| postfix = cmValue(frameworkPostfix); | |
| } | |
| } | |
| return postfix ? *postfix : std::string(); | |
| } | |
| std::string cmGeneratorTarget::GetFrameworkMultiConfigPostfix( | |
| std::string const& config) const | |
| { | |
| cmValue postfix = nullptr; | |
| if (!config.empty()) { | |
| std::string configProp = cmStrCat("FRAMEWORK_MULTI_CONFIG_POSTFIX_", | |
| cmSystemTools::UpperCase(config)); | |
| postfix = this->GetProperty(configProp); | |
| if (!this->IsImported() && postfix && | |
| (this->IsFrameworkOnApple() && | |
| !this->GetGlobalGenerator()->IsMultiConfig())) { | |
| postfix = nullptr; | |
| } | |
| } | |
| return postfix ? *postfix : std::string(); | |
| } | |
| cmValue cmGeneratorTarget::GetFilePrefixInternal( | |
| std::string const& config, cmStateEnums::ArtifactType artifact, | |
| std::string const& language) const | |
| { | |
| // no prefix for non-main target types. | |
| if (this->GetType() != cm::TargetType::STATIC_LIBRARY && | |
| this->GetType() != cm::TargetType::SHARED_LIBRARY && | |
| this->GetType() != cm::TargetType::MODULE_LIBRARY && | |
| this->GetType() != cm::TargetType::EXECUTABLE) { | |
| return nullptr; | |
| } | |
| bool const isImportedLibraryArtifact = | |
| (artifact == cmStateEnums::ImportLibraryArtifact); | |
| // Return an empty prefix for the import library if this platform | |
| // does not support import libraries. | |
| if (isImportedLibraryArtifact && !this->NeedImportLibraryName(config)) { | |
| return nullptr; | |
| } | |
| // The implib option is only allowed for shared libraries, module | |
| // libraries, and executables. | |
| if (this->GetType() != cm::TargetType::SHARED_LIBRARY && | |
| this->GetType() != cm::TargetType::MODULE_LIBRARY && | |
| this->GetType() != cm::TargetType::EXECUTABLE) { | |
| artifact = cmStateEnums::RuntimeBinaryArtifact; | |
| } | |
| // Compute prefix value. | |
| cmValue targetPrefix = | |
| (isImportedLibraryArtifact ? this->GetProperty("IMPORT_PREFIX") | |
| : this->GetProperty("PREFIX")); | |
| if (!targetPrefix) { | |
| char const* prefixVar = this->Target->GetPrefixVariableInternal(artifact); | |
| if (!language.empty() && cmNonempty(prefixVar)) { | |
| std::string langPrefix = cmStrCat(prefixVar, '_', language); | |
| targetPrefix = this->Makefile->GetDefinition(langPrefix); | |
| } | |
| // if there is no prefix on the target nor specific language | |
| // use the cmake definition. | |
| if (!targetPrefix && prefixVar) { | |
| targetPrefix = this->Makefile->GetDefinition(prefixVar); | |
| } | |
| } | |
| return targetPrefix; | |
| } | |
| cmValue cmGeneratorTarget::GetFileSuffixInternal( | |
| std::string const& config, cmStateEnums::ArtifactType artifact, | |
| std::string const& language) const | |
| { | |
| // no suffix for non-main target types. | |
| if (this->GetType() != cm::TargetType::STATIC_LIBRARY && | |
| this->GetType() != cm::TargetType::SHARED_LIBRARY && | |
| this->GetType() != cm::TargetType::MODULE_LIBRARY && | |
| this->GetType() != cm::TargetType::EXECUTABLE) { | |
| return nullptr; | |
| } | |
| bool const isImportedLibraryArtifact = | |
| (artifact == cmStateEnums::ImportLibraryArtifact); | |
| // Return an empty suffix for the import library if this platform | |
| // does not support import libraries. | |
| if (isImportedLibraryArtifact && !this->NeedImportLibraryName(config)) { | |
| return nullptr; | |
| } | |
| // The implib option is only allowed for shared libraries, module | |
| // libraries, and executables. | |
| if (this->GetType() != cm::TargetType::SHARED_LIBRARY && | |
| this->GetType() != cm::TargetType::MODULE_LIBRARY && | |
| this->GetType() != cm::TargetType::EXECUTABLE) { | |
| artifact = cmStateEnums::RuntimeBinaryArtifact; | |
| } | |
| // Compute suffix value. | |
| cmValue targetSuffix = | |
| (isImportedLibraryArtifact ? this->GetProperty("IMPORT_SUFFIX") | |
| : this->GetProperty("SUFFIX")); | |
| if (!targetSuffix) { | |
| char const* suffixVar = this->Target->GetSuffixVariableInternal(artifact); | |
| if (!language.empty() && cmNonempty(suffixVar)) { | |
| std::string langSuffix = cmStrCat(suffixVar, '_', language); | |
| targetSuffix = this->Makefile->GetDefinition(langSuffix); | |
| } | |
| // if there is no suffix on the target nor specific language | |
| // use the cmake definition. | |
| if (!targetSuffix && suffixVar) { | |
| targetSuffix = this->Makefile->GetDefinition(suffixVar); | |
| } | |
| } | |
| return targetSuffix; | |
| } | |
| void cmGeneratorTarget::ClearSourcesCache() | |
| { | |
| this->AllConfigSources.clear(); | |
| this->AllConfigCompileLanguages.clear(); | |
| this->KindedSourcesMap.clear(); | |
| this->SourcesAreContextDependent = Tribool::Indeterminate; | |
| this->Objects.clear(); | |
| this->VisitedConfigsForObjects.clear(); | |
| this->LinkImplClosureForLinkMap.clear(); | |
| this->LinkImplClosureForUsageMap.clear(); | |
| this->LinkImplMap.clear(); | |
| this->LinkImplUsageRequirementsOnlyMap.clear(); | |
| this->LinkWrappersCache.clear(); | |
| this->IncludeDirectoriesCache.clear(); | |
| this->CompileOptionsCache.clear(); | |
| this->CompileDefinitionsCache.clear(); | |
| this->CompileWrappersCache.clear(); | |
| this->CustomTransitiveBuildPropertiesMap.clear(); | |
| this->CustomTransitiveInterfacePropertiesMap.clear(); | |
| this->PrecompileHeadersCache.clear(); | |
| this->LinkOptionsCache.clear(); | |
| this->LinkDirectoriesCache.clear(); | |
| this->RuntimeBinaryFullNameCache.clear(); | |
| this->ImportLibraryFullNameCache.clear(); | |
| } | |
| void cmGeneratorTarget::ClearLinkInterfaceCache() | |
| { | |
| this->LinkInterfaceMap.clear(); | |
| this->LinkInterfaceUsageRequirementsOnlyMap.clear(); | |
| } | |
| void cmGeneratorTarget::AddSourceCommon(std::string const& src, bool before) | |
| { | |
| this->SourceEntries.insert( | |
| before ? this->SourceEntries.begin() : this->SourceEntries.end(), | |
| TargetPropertyEntry::Create( | |
| *this->LocalGenerator->GetCMakeInstance(), | |
| BT<std::string>(src, this->Makefile->GetBacktrace()), true)); | |
| this->ClearSourcesCache(); | |
| } | |
| cmSourceFile* cmGeneratorTarget::AddSource(std::string const& src, bool before) | |
| { | |
| auto* sf = this->Target->AddSource(src, before); | |
| this->AddSourceCommon(src, before); | |
| return sf; | |
| } | |
| void cmGeneratorTarget::AddTracedSources(std::vector<std::string> const& srcs) | |
| { | |
| this->Target->AddTracedSources(srcs); | |
| if (!srcs.empty()) { | |
| this->AddSourceCommon(cmJoin(srcs, ";")); | |
| } | |
| } | |
| void cmGeneratorTarget::AddIncludeDirectory(std::string const& src, | |
| bool before) | |
| { | |
| this->Target->InsertInclude( | |
| BT<std::string>(src, this->Makefile->GetBacktrace()), before); | |
| this->IncludeDirectoriesEntries.insert( | |
| before ? this->IncludeDirectoriesEntries.begin() | |
| : this->IncludeDirectoriesEntries.end(), | |
| TargetPropertyEntry::Create( | |
| *this->Makefile->GetCMakeInstance(), | |
| BT<std::string>(src, this->Makefile->GetBacktrace()), true)); | |
| } | |
| void cmGeneratorTarget::AddSystemIncludeDirectory(std::string const& inc, | |
| std::string const& lang) | |
| { | |
| std::string config_upper; | |
| auto const& configs = | |
| this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); | |
| for (auto const& config : configs) { | |
| std::string inc_with_config = inc; | |
| if (!config.empty()) { | |
| cmSystemTools::ReplaceString(inc_with_config, "$<CONFIG>", config); | |
| config_upper = cmSystemTools::UpperCase(config); | |
| } | |
| auto const& key = cmStrCat(config_upper, '/', lang); | |
| this->Target->AddSystemIncludeDirectories({ inc_with_config }); | |
| if (this->SystemIncludesCache.find(key) == | |
| this->SystemIncludesCache.end()) { | |
| this->AddSystemIncludeCacheKey(key, config, lang); | |
| } | |
| this->SystemIncludesCache[key].emplace_back(inc_with_config); | |
| // SystemIncludesCache should be sorted so that binary search can be used | |
| std::sort(this->SystemIncludesCache[key].begin(), | |
| this->SystemIncludesCache[key].end()); | |
| } | |
| } | |
| std::vector<cmSourceFile*> const* cmGeneratorTarget::GetSourceDepends( | |
| cmSourceFile const* sf) const | |
| { | |
| auto i = this->SourceDepends.find(sf); | |
| if (i != this->SourceDepends.end()) { | |
| return &i->second.Depends; | |
| } | |
| return nullptr; | |
| } | |
| namespace { | |
| void handleSystemIncludesDep(cmLocalGenerator const* lg, | |
| cmGeneratorTarget const* depTgt, | |
| std::string const& config, | |
| cmGeneratorTarget const* headTarget, | |
| cmGeneratorExpressionDAGChecker* dagChecker, | |
| cmList& result, bool excludeImported, | |
| std::string const& language) | |
| { | |
| if (cmValue dirs = | |
| depTgt->GetProperty("INTERFACE_SYSTEM_INCLUDE_DIRECTORIES")) { | |
| result.append(cmGeneratorExpression::Evaluate( | |
| *dirs, lg, config, headTarget, dagChecker, depTgt, language)); | |
| } | |
| if (!depTgt->GetPropertyAsBool("SYSTEM")) { | |
| return; | |
| } | |
| if (depTgt->IsImported()) { | |
| if (excludeImported) { | |
| return; | |
| } | |
| if (depTgt->GetPropertyAsBool("IMPORTED_NO_SYSTEM")) { | |
| return; | |
| } | |
| } | |
| if (cmValue dirs = depTgt->GetProperty("INTERFACE_INCLUDE_DIRECTORIES")) { | |
| result.append(cmGeneratorExpression::Evaluate( | |
| *dirs, lg, config, headTarget, dagChecker, depTgt, language)); | |
| } | |
| if (depTgt->Target->IsFrameworkOnApple() || | |
| depTgt->IsImportedFrameworkFolderOnApple(config)) { | |
| if (auto fwDescriptor = depTgt->GetGlobalGenerator()->SplitFrameworkPath( | |
| depTgt->GetLocation(config))) { | |
| result.push_back(fwDescriptor->Directory); | |
| result.push_back(fwDescriptor->GetFrameworkPath()); | |
| } | |
| } | |
| } | |
| } | |
| /* 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, std::string const& config) const | |
| { | |
| IMPLEMENT_VISIT(SourceKindObjectSource); | |
| if (this->VisitedConfigsForObjects.count(config)) { | |
| return; | |
| } | |
| for (cmSourceFile const* it : data) { | |
| this->Objects[it]; | |
| } | |
| this->LocalGenerator->ComputeObjectFilenames(this->Objects, config, this); | |
| this->VisitedConfigsForObjects.insert(config); | |
| } | |
| void cmGeneratorTarget::ComputeObjectMapping() const | |
| { | |
| auto const& configs = | |
| this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); | |
| std::set<std::string> configSet(configs.begin(), configs.end()); | |
| if (configSet == this->VisitedConfigsForObjects) { | |
| return; | |
| } | |
| for (std::string const& c : configs) { | |
| std::vector<cmSourceFile const*> sourceFiles; | |
| this->GetObjectSources(sourceFiles, c); | |
| } | |
| } | |
| cmValue cmGeneratorTarget::GetFeature(std::string const& feature, | |
| std::string const& config) const | |
| { | |
| if (!config.empty()) { | |
| std::string featureConfig = | |
| cmStrCat(feature, '_', cmSystemTools::UpperCase(config)); | |
| if (cmValue value = this->GetProperty(featureConfig)) { | |
| return value; | |
| } | |
| } | |
| if (cmValue value = this->GetProperty(feature)) { | |
| return value; | |
| } | |
| return this->LocalGenerator->GetFeature(feature, config); | |
| } | |
| std::string cmGeneratorTarget::GetLinkerTypeProperty( | |
| std::string const& lang, std::string const& config) const | |
| { | |
| std::string propName{ "LINKER_TYPE" }; | |
| auto linkerType = this->GetProperty(propName); | |
| if (!linkerType.IsEmpty()) { | |
| cm::GenEx::Context context(this->LocalGenerator, config, lang); | |
| cmGeneratorExpressionDAGChecker dagChecker{ | |
| this, propName, nullptr, nullptr, context, | |
| }; | |
| auto ltype = cmGeneratorExpression::Evaluate( | |
| *linkerType, context.LG, context.Config, this, &dagChecker, this, | |
| context.Language); | |
| if (this->IsDeviceLink()) { | |
| cmList list{ ltype }; | |
| auto const DL_BEGIN = "<DEVICE_LINK>"_s; | |
| auto const DL_END = "</DEVICE_LINK>"_s; | |
| cm::erase_if(list, [&](std::string const& item) { | |
| return item == DL_BEGIN || item == DL_END; | |
| }); | |
| return list.to_string(); | |
| } | |
| return ltype; | |
| } | |
| return std::string{}; | |
| } | |
| char const* cmGeneratorTarget::GetLinkPIEProperty( | |
| std::string const& config) const | |
| { | |
| static std::string PICValue; | |
| PICValue = this->GetLinkInterfaceDependentStringAsBoolProperty( | |
| "POSITION_INDEPENDENT_CODE", config); | |
| if (PICValue == "(unset)") { | |
| // POSITION_INDEPENDENT_CODE is not set | |
| return nullptr; | |
| } | |
| auto status = this->GetPolicyStatusCMP0083(); | |
| return (status != cmPolicies::WARN && status != cmPolicies::OLD) | |
| ? PICValue.c_str() | |
| : nullptr; | |
| } | |
| bool cmGeneratorTarget::IsIPOEnabled(std::string const& lang, | |
| std::string const& config) const | |
| { | |
| cmValue feature = this->GetFeature("INTERPROCEDURAL_OPTIMIZATION", config); | |
| if (!feature.IsOn()) { | |
| // 'INTERPROCEDURAL_OPTIMIZATION' is off, no need to check policies | |
| return false; | |
| } | |
| if (lang != "C" && lang != "CXX" && lang != "CUDA" && lang != "Fortran") { | |
| // We do not define IPO behavior for other languages. | |
| return false; | |
| } | |
| if (lang == "CUDA") { | |
| // CUDA IPO requires both CUDA_ARCHITECTURES and CUDA_SEPARABLE_COMPILATION | |
| if (cmIsOff(this->GetSafeProperty("CUDA_ARCHITECTURES")) || | |
| cmIsOff(this->GetSafeProperty("CUDA_SEPARABLE_COMPILATION"))) { | |
| return false; | |
| } | |
| } | |
| cmPolicies::PolicyStatus cmp0069 = this->GetPolicyStatusCMP0069(); | |
| if (cmp0069 == cmPolicies::OLD || cmp0069 == cmPolicies::WARN) { | |
| if (this->Makefile->IsOn( | |
| cmStrCat("_CMAKE_", lang, "_IPO_LEGACY_BEHAVIOR"))) { | |
| return true; | |
| } | |
| if (this->PolicyReportedCMP0069) { | |
| // problem is already reported, no need to issue a message | |
| return false; | |
| } | |
| bool const in_try_compile = | |
| this->LocalGenerator->GetCMakeInstance()->GetIsInTryCompile(); | |
| if (cmp0069 == cmPolicies::WARN && !in_try_compile) { | |
| this->Makefile->IssuePolicyWarning( | |
| cmPolicies::CMP0069, {}, | |
| cmStrCat("INTERPROCEDURAL_OPTIMIZATION property " | |
| "will be ignored for target '"_s, | |
| this->GetName(), "'."_s), | |
| this->GetBacktrace()); | |
| this->PolicyReportedCMP0069 = true; | |
| } | |
| return false; | |
| } | |
| // Note: check consistency with messages from CheckIPOSupported | |
| char const* message = nullptr; | |
| if (!this->Makefile->IsOn( | |
| cmStrCat("_CMAKE_", lang, "_IPO_SUPPORTED_BY_CMAKE"))) { | |
| message = "CMake doesn't support IPO for current compiler"; | |
| } else if (!this->Makefile->IsOn(cmStrCat( | |
| "_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; | |
| } | |
| std::string const& cmGeneratorTarget::GetObjectName( | |
| cmSourceFile const* file) const | |
| { | |
| this->ComputeObjectMapping(); | |
| auto const useShortPaths = this->GetUseShortObjectNames() | |
| ? cmObjectLocations::UseShortPath::Yes | |
| : cmObjectLocations::UseShortPath::No; | |
| return this->Objects[file].GetPath(useShortPaths); | |
| } | |
| char const* cmGeneratorTarget::GetCustomObjectExtension() const | |
| { | |
| struct compiler_mode | |
| { | |
| std::string variable; | |
| std::string extension; | |
| }; | |
| static std::array<compiler_mode, 4> const modes{ | |
| { { "CUDA_PTX_COMPILATION", ".ptx" }, | |
| { "CUDA_CUBIN_COMPILATION", ".cubin" }, | |
| { "CUDA_FATBIN_COMPILATION", ".fatbin" }, | |
| { "CUDA_OPTIX_COMPILATION", ".optixir" } } | |
| }; | |
| std::string const& compiler = | |
| this->Makefile->GetSafeDefinition("CMAKE_CUDA_COMPILER_ID"); | |
| if (!compiler.empty()) { | |
| for (auto const& m : modes) { | |
| bool const has_extension = this->GetPropertyAsBool(m.variable); | |
| if (has_extension) { | |
| return m.extension.c_str(); | |
| } | |
| } | |
| } | |
| return nullptr; | |
| } | |
| void cmGeneratorTarget::AddExplicitObjectName(cmSourceFile const* sf) | |
| { | |
| this->ExplicitObjectName.insert(sf); | |
| } | |
| bool cmGeneratorTarget::HasExplicitObjectName(cmSourceFile const* file) const | |
| { | |
| this->ComputeObjectMapping(); | |
| auto it = this->ExplicitObjectName.find(file); | |
| return it != this->ExplicitObjectName.end(); | |
| } | |
| BTs<std::string> const* cmGeneratorTarget::GetLanguageStandardProperty( | |
| std::string const& lang, std::string const& config) const | |
| { | |
| std::string key = cmStrCat(cmSystemTools::UpperCase(config), '-', lang); | |
| auto langStandardIter = this->LanguageStandardMap.find(key); | |
| if (langStandardIter != this->LanguageStandardMap.end()) { | |
| return &langStandardIter->second; | |
| } | |
| if (BTs<std::string> const* languageStandard = | |
| this->Target->GetLanguageStandardProperty( | |
| cmStandardLevelResolver::GetStandardPropertyName(lang))) { | |
| return languageStandard; | |
| } | |
| if (this->IsSYCLAppendMode(lang)) { | |
| return this->GetLanguageStandardProperty("CXX", config); | |
| } | |
| return nullptr; | |
| } | |
| cmValue cmGeneratorTarget::GetLanguageStandard(std::string const& lang, | |
| std::string const& config) const | |
| { | |
| BTs<std::string> const* languageStandard = | |
| this->GetLanguageStandardProperty(lang, config); | |
| if (languageStandard) { | |
| return cmValue(languageStandard->Value); | |
| } | |
| return nullptr; | |
| } | |
| cmValue cmGeneratorTarget::GetPropertyWithPairedLanguageSupport( | |
| std::string const& lang, char const* suffix) const | |
| { | |
| std::string const property = std::string(suffix) == "_STANDARD_REQUIRED" | |
| ? cmStrCat(cmStandardLevelResolver::GetStandardPropertyName(lang), | |
| "_REQUIRED") | |
| : cmStrCat(lang, suffix); | |
| cmValue propertyValue = this->Target->GetProperty(property); | |
| if (!propertyValue) { | |
| // Check if we should use the value set by another language. | |
| if (lang == "OBJC") { | |
| propertyValue = this->GetPropertyWithPairedLanguageSupport("C", suffix); | |
| } else if (lang == "OBJCXX" || lang == "CUDA" || lang == "HIP" || | |
| this->IsSYCLAppendMode(lang)) { | |
| propertyValue = | |
| this->GetPropertyWithPairedLanguageSupport("CXX", suffix); | |
| } | |
| } | |
| return propertyValue; | |
| } | |
| cmValue cmGeneratorTarget::GetLanguageExtensions(std::string const& lang) const | |
| { | |
| return this->GetPropertyWithPairedLanguageSupport(lang, "_EXTENSIONS"); | |
| } | |
| bool cmGeneratorTarget::GetLanguageStandardRequired( | |
| std::string const& lang) const | |
| { | |
| return this->GetPropertyWithPairedLanguageSupport(lang, "_STANDARD_REQUIRED") | |
| .IsOn(); | |
| } | |
| cmGeneratorTarget::SYCLExtensionMode cmGeneratorTarget::GetSYCLExtensionMode() | |
| const | |
| { | |
| cmValue mode = this->GetProperty("SYCL_EXTENSION_MODE"); | |
| if (mode.IsEmpty()) { | |
| return SYCLExtensionMode::Append; | |
| } | |
| std::string normalizedMode = cmSystemTools::UpperCase(*mode); | |
| if (normalizedMode == "APPEND") { | |
| return SYCLExtensionMode::Append; | |
| } | |
| if (normalizedMode == "REPLACE") { | |
| return SYCLExtensionMode::Replace; | |
| } | |
| this->LocalGenerator->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| cmStrCat("Target \"", this->GetName(), | |
| "\" has an invalid SYCL_EXTENSION_MODE value \"", *mode, | |
| "\". Expected APPEND or REPLACE.")); | |
| return SYCLExtensionMode::Append; | |
| } | |
| bool cmGeneratorTarget::IsSYCLAppendMode(std::string const& lang) const | |
| { | |
| return lang == "SYCL" && | |
| this->GetSYCLExtensionMode() == SYCLExtensionMode::Append; | |
| } | |
| void cmGeneratorTarget::GetModuleDefinitionSources( | |
| std::vector<cmSourceFile const*>& data, std::string const& config) const | |
| { | |
| IMPLEMENT_VISIT(SourceKindModuleDefinition); | |
| } | |
| void cmGeneratorTarget::GetHeaderSources( | |
| std::vector<cmSourceFile const*>& data, std::string const& config) const | |
| { | |
| IMPLEMENT_VISIT(SourceKindHeader); | |
| } | |
| void cmGeneratorTarget::GetCxxModuleSources( | |
| std::vector<cmSourceFile const*>& data, std::string const& config) const | |
| { | |
| IMPLEMENT_VISIT(SourceKindCxxModuleSource); | |
| } | |
| void cmGeneratorTarget::GetExtraSources(std::vector<cmSourceFile const*>& data, | |
| std::string const& config) const | |
| { | |
| IMPLEMENT_VISIT(SourceKindExtra); | |
| } | |
| void cmGeneratorTarget::GetCustomCommands( | |
| std::vector<cmSourceFile const*>& data, std::string const& config) const | |
| { | |
| IMPLEMENT_VISIT(SourceKindCustomCommand); | |
| } | |
| void cmGeneratorTarget::GetExternalObjects( | |
| std::vector<cmSourceFile const*>& data, std::string const& config) const | |
| { | |
| IMPLEMENT_VISIT(SourceKindExternalObject); | |
| } | |
| void cmGeneratorTarget::GetManifests(std::vector<cmSourceFile const*>& data, | |
| std::string const& config) const | |
| { | |
| IMPLEMENT_VISIT(SourceKindManifest); | |
| } | |
| void cmGeneratorTarget::GetRustMainCrateRoot( | |
| std::vector<cmSourceFile const*>& data, std::string const& config) const | |
| { | |
| IMPLEMENT_VISIT(SourceKindRustMainCrateRoot); | |
| } | |
| cmSourceFile const* cmGeneratorTarget::GetRustMainCrateRoot( | |
| std::string const& config) const | |
| { | |
| std::vector<cmSourceFile const*> files; | |
| GetRustMainCrateRoot(files, config); | |
| if (files.empty()) { | |
| return nullptr; | |
| } | |
| assert(files.size() == 1); | |
| return files[0]; | |
| } | |
| std::set<cmLinkItem> const& cmGeneratorTarget::GetUtilityItems() const | |
| { | |
| if (!this->UtilityItemsDone) { | |
| this->UtilityItemsDone = true; | |
| std::set<BT<std::pair<std::string, bool>>> const& utilities = | |
| this->GetUtilities(); | |
| for (BT<std::pair<std::string, bool>> const& i : utilities) { | |
| if (cmGeneratorTarget* gt = | |
| this->LocalGenerator->FindGeneratorTargetToUse(i.Value.first)) { | |
| this->UtilityItems.insert(cmLinkItem(gt, i.Value.second, i.Backtrace)); | |
| } else { | |
| this->UtilityItems.insert( | |
| cmLinkItem(i.Value.first, i.Value.second, i.Backtrace)); | |
| } | |
| } | |
| if (cmGeneratorTarget const* reuseTarget = this->GetPchReuseTarget()) { | |
| this->UtilityItems.insert( | |
| cmLinkItem(reuseTarget, false, cmListFileBacktrace())); | |
| } | |
| } | |
| return this->UtilityItems; | |
| } | |
| std::string const& cmGeneratorTarget::GetLocation( | |
| std::string const& 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; | |
| } | |
| cm::optional<std::string> cmGeneratorTarget::MaybeGetLocation( | |
| std::string const& config) const | |
| { | |
| cm::optional<std::string> location; | |
| if (cmGeneratorTarget::ImportInfo const* imp = this->GetImportInfo(config)) { | |
| if (!imp->Location.empty()) { | |
| location = imp->Location; | |
| } | |
| } else { | |
| location = this->GetFullPath(config, cmStateEnums::RuntimeBinaryArtifact); | |
| } | |
| return location; | |
| } | |
| 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(); | |
| } | |
| void cmGeneratorTarget::AppendCustomCommandSideEffects( | |
| std::set<cmGeneratorTarget const*>& sideEffects) const | |
| { | |
| if (!this->GetPreBuildCommands().empty() || | |
| !this->GetPreLinkCommands().empty() || | |
| !this->GetPostBuildCommands().empty()) { | |
| sideEffects.insert(this); | |
| } else { | |
| for (auto const& source : this->GetAllConfigSources()) { | |
| if (source.Source->GetCustomCommand()) { | |
| sideEffects.insert(this); | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| void cmGeneratorTarget::AppendLanguageSideEffects( | |
| std::map<std::string, std::set<cmGeneratorTarget const*>>& sideEffects) const | |
| { | |
| static std::set<cm::string_view> const LANGS_WITH_NO_SIDE_EFFECTS = { | |
| "C"_s, "CXX"_s, "OBJC"_s, "OBJCXX"_s, "ASM"_s, "CUDA"_s, "HIP"_s | |
| }; | |
| for (auto const& lang : this->GetAllConfigCompileLanguages()) { | |
| if (!LANGS_WITH_NO_SIDE_EFFECTS.count(lang)) { | |
| sideEffects[lang].insert(this); | |
| } | |
| } | |
| } | |
| bool cmGeneratorTarget::IsInBuildSystem() const | |
| { | |
| if (this->IsImported()) { | |
| return false; | |
| } | |
| switch (this->Target->GetType()) { | |
| case cm::TargetType::EXECUTABLE: | |
| case cm::TargetType::STATIC_LIBRARY: | |
| case cm::TargetType::SHARED_LIBRARY: | |
| case cm::TargetType::MODULE_LIBRARY: | |
| case cm::TargetType::OBJECT_LIBRARY: | |
| case cm::TargetType::UTILITY: | |
| case cm::TargetType::GLOBAL_TARGET: | |
| return true; | |
| case cm::TargetType::INTERFACE_LIBRARY: | |
| // An INTERFACE library is in the build system if it has SOURCES | |
| // or C++ module filesets. | |
| if (!this->SourceEntries.empty() || | |
| !this->GetFileSets(cm::FileSetMetadata::HEADERS).empty() || | |
| !this->GetFileSets(cm::FileSetMetadata::CXX_MODULES).empty()) { | |
| return true; | |
| } | |
| break; | |
| case cm::TargetType::UNKNOWN_LIBRARY: | |
| break; | |
| } | |
| return false; | |
| } | |
| bool cmGeneratorTarget::IsNormal() const | |
| { | |
| return this->Target->IsNormal(); | |
| } | |
| bool cmGeneratorTarget::IsRuntimeBinary() const | |
| { | |
| return this->Target->IsRuntimeBinary(); | |
| } | |
| bool cmGeneratorTarget::IsSynthetic() const | |
| { | |
| return this->Target->IsSynthetic(); | |
| } | |
| bool cmGeneratorTarget::IsImported() const | |
| { | |
| return this->Target->IsImported(); | |
| } | |
| bool cmGeneratorTarget::IsImportedGloballyVisible() const | |
| { | |
| return this->Target->IsImportedGloballyVisible(); | |
| } | |
| bool cmGeneratorTarget::IsSymbolic() const | |
| { | |
| return this->Target->IsSymbolic(); | |
| } | |
| bool cmGeneratorTarget::IsForeign() const | |
| { | |
| return this->Target->IsForeign(); | |
| } | |
| bool cmGeneratorTarget::CanCompileSources() const | |
| { | |
| return this->Target->CanCompileSources(); | |
| } | |
| bool cmGeneratorTarget::HasKnownRuntimeArtifactLocation( | |
| std::string const& config) const | |
| { | |
| if (!this->IsRuntimeBinary()) { | |
| return false; | |
| } | |
| if (!this->IsImported()) { | |
| return true; | |
| } | |
| ImportInfo const* info = this->GetImportInfo(config); | |
| return info && !info->Location.empty(); | |
| } | |
| std::string const& cmGeneratorTarget::GetLocationForBuild() const | |
| { | |
| static std::string location; | |
| if (this->IsImported()) { | |
| location = this->Target->ImportedGetFullPath( | |
| "", cmStateEnums::RuntimeBinaryArtifact); | |
| return location; | |
| } | |
| // Now handle the deprecated build-time configuration location. | |
| std::string const noConfig; | |
| location = this->GetDirectory(noConfig); | |
| cmValue cfgid = this->Makefile->GetDefinition("CMAKE_CFG_INTDIR"); | |
| if (cfgid && (*cfgid != ".")) { | |
| location = cmStrCat(std::move(location), '/', *cfgid); | |
| } | |
| if (this->IsAppBundleOnApple()) { | |
| std::string macdir = this->BuildBundleDirectory("", "", FullLevel); | |
| if (!macdir.empty()) { | |
| location = cmStrCat(std::move(location), '/', macdir); | |
| } | |
| } | |
| location = | |
| cmStrCat(std::move(location), '/', | |
| this->GetFullName("", cmStateEnums::RuntimeBinaryArtifact)); | |
| return location; | |
| } | |
| void cmGeneratorTarget::AddSystemIncludeCacheKey( | |
| std::string const& key, std::string const& config, | |
| std::string const& language) const | |
| { | |
| cm::GenEx::Context context(this->LocalGenerator, config, language); | |
| cmGeneratorExpressionDAGChecker dagChecker{ | |
| this, "SYSTEM_INCLUDE_DIRECTORIES", nullptr, nullptr, context, | |
| }; | |
| bool excludeImported = this->GetPropertyAsBool("NO_SYSTEM_FROM_IMPORTED"); | |
| cmList result; | |
| for (std::string const& it : this->Target->GetSystemIncludeDirectories()) { | |
| result.append( | |
| cmGeneratorExpression::Evaluate(it, context.LG, context.Config, this, | |
| &dagChecker, nullptr, context.Language)); | |
| } | |
| std::vector<cmGeneratorTarget const*> const& deps = | |
| this->GetLinkImplementationClosure(config, UseTo::Compile); | |
| for (cmGeneratorTarget const* dep : deps) { | |
| handleSystemIncludesDep(context.LG, dep, context.Config, this, &dagChecker, | |
| result, excludeImported, context.Language); | |
| } | |
| cmLinkImplementation const* impl = | |
| this->GetLinkImplementation(config, UseTo::Compile); | |
| if (impl) { | |
| auto runtimeEntries = impl->LanguageRuntimeLibraries.find(language); | |
| if (runtimeEntries != impl->LanguageRuntimeLibraries.end()) { | |
| for (auto const& lib : runtimeEntries->second) { | |
| if (lib.Target) { | |
| handleSystemIncludesDep(context.LG, lib.Target, context.Config, this, | |
| &dagChecker, result, excludeImported, | |
| context.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()); | |
| SystemIncludesCache.emplace(key, result); | |
| } | |
| bool cmGeneratorTarget::IsSystemIncludeDirectory( | |
| std::string const& dir, std::string const& config, | |
| std::string const& language) const | |
| { | |
| std::string config_upper; | |
| if (!config.empty()) { | |
| config_upper = cmSystemTools::UpperCase(config); | |
| } | |
| std::string key = cmStrCat(config_upper, '/', language); | |
| auto iter = this->SystemIncludesCache.find(key); | |
| if (iter == this->SystemIncludesCache.end()) { | |
| this->AddSystemIncludeCacheKey(key, config, language); | |
| iter = this->SystemIncludesCache.find(key); | |
| } | |
| return std::binary_search(iter->second.begin(), iter->second.end(), dir); | |
| } | |
| bool cmGeneratorTarget::GetPropertyAsBool(std::string const& prop) const | |
| { | |
| return this->Target->GetPropertyAsBool(prop); | |
| } | |
| std::string cmGeneratorTarget::GetCompilePDBName( | |
| std::string const& config) const | |
| { | |
| if (cmGeneratorTarget const* reuseTarget = this->GetPchReuseTarget()) { | |
| if (reuseTarget != this) { | |
| return reuseTarget->GetCompilePDBName(config); | |
| } | |
| } | |
| // Check for a per-configuration output directory target property. | |
| std::string configUpper = cmSystemTools::UpperCase(config); | |
| std::string configProp = cmStrCat("COMPILE_PDB_NAME_", configUpper); | |
| cmValue config_name = this->GetProperty(configProp); | |
| if (cmNonempty(config_name)) { | |
| std::string pdbName = cmGeneratorExpression::Evaluate( | |
| *config_name, this->LocalGenerator, config, this); | |
| NameComponents const& components = GetFullNameInternalComponents( | |
| config, cmStateEnums::RuntimeBinaryArtifact); | |
| return cmStrCat(components.prefix, pdbName, ".pdb"); | |
| } | |
| cmValue name = this->GetProperty("COMPILE_PDB_NAME"); | |
| if (cmNonempty(name)) { | |
| std::string pdbName = cmGeneratorExpression::Evaluate( | |
| *name, this->LocalGenerator, config, this); | |
| NameComponents const& components = GetFullNameInternalComponents( | |
| config, cmStateEnums::RuntimeBinaryArtifact); | |
| return cmStrCat(components.prefix, pdbName, ".pdb"); | |
| } | |
| // If the target is PCH-reused or PCH-reuses, we need a stable name for the | |
| // PDB file so that reusing targets can construct a stable name for it. | |
| cmGeneratorTarget const* reuseTarget = this->GetPchReuseTarget(); | |
| bool const hasReuse = reuseTarget && reuseTarget != this; | |
| if (this->PchReused || hasReuse) { | |
| NameComponents const& components = GetFullNameInternalComponents( | |
| config, cmStateEnums::RuntimeBinaryArtifact); | |
| return cmStrCat(components.prefix, this->GetName(), ".pdb"); | |
| } | |
| return std::string{}; | |
| } | |
| std::string cmGeneratorTarget::GetCompilePDBPath( | |
| std::string const& 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(std::string const& config) const | |
| { | |
| // soname is supported only for shared libraries and modules, | |
| // and then only when the platform supports an soname flag. | |
| return ((this->GetType() == cm::TargetType::SHARED_LIBRARY) && | |
| !this->GetPropertyAsBool("NO_SONAME") && | |
| (this->Makefile->GetSONameFlag(this->GetLinkerLanguage(config)) || | |
| this->IsArchivedAIXSharedLibrary())); | |
| } | |
| bool cmGeneratorTarget::NeedRelinkBeforeInstall( | |
| std::string const& config) const | |
| { | |
| // Only executables and shared libraries can have an rpath and may | |
| // need relinking. | |
| if (this->GetType() != cm::TargetType::EXECUTABLE && | |
| this->GetType() != cm::TargetType::SHARED_LIBRARY && | |
| this->GetType() != cm::TargetType::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 = | |
| cmStrCat("CMAKE_SHARED_LIBRARY_RUNTIME_", ll, "_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(config); | |
| bool is_ninja = this->LocalGenerator->GetGlobalGenerator()->IsNinja(); | |
| if (have_rpath && is_ninja) { | |
| cmake* cm = this->LocalGenerator->GetCMakeInstance(); | |
| cm->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| cmStrCat("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 or XCOFF-based platform. The " | |
| "CMAKE_BUILD_WITH_INSTALL_RPATH variable may be set to avoid " | |
| "this relinking step."), | |
| this->GetBacktrace()); | |
| } | |
| return have_rpath; | |
| } | |
| bool cmGeneratorTarget::IsChrpathUsed(std::string const& config) const | |
| { | |
| // Only certain target types have an rpath. | |
| if (!(this->GetType() == cm::TargetType::SHARED_LIBRARY || | |
| this->GetType() == cm::TargetType::MODULE_LIBRARY || | |
| this->GetType() == cm::TargetType::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; | |
| } | |
| // Enable if the rpath flag uses a separator and the target uses | |
| // binaries we know how to edit. | |
| std::string ll = this->GetLinkerLanguage(config); | |
| if (!ll.empty()) { | |
| std::string sepVar = | |
| cmStrCat("CMAKE_SHARED_LIBRARY_RUNTIME_", ll, "_FLAG_SEP"); | |
| cmValue sep = this->Makefile->GetDefinition(sepVar); | |
| if (cmNonempty(sep)) { | |
| // TODO: Add binary format check to ABI detection and get rid of | |
| // CMAKE_EXECUTABLE_FORMAT. | |
| if (cmValue fmt = | |
| this->Makefile->GetDefinition("CMAKE_EXECUTABLE_FORMAT")) { | |
| if (*fmt == "ELF") { | |
| return true; | |
| } | |
| #if defined(CMake_USE_XCOFF_PARSER) | |
| if (*fmt == "XCOFF") { | |
| return true; | |
| } | |
| #endif | |
| } | |
| } | |
| } | |
| return false; | |
| } | |
| bool cmGeneratorTarget::IsImportedSharedLibWithoutSOName( | |
| std::string const& config) const | |
| { | |
| if (this->IsImported() && | |
| this->GetType() == cm::TargetType::SHARED_LIBRARY) { | |
| if (cmGeneratorTarget::ImportInfo const* info = | |
| this->GetImportInfo(config)) { | |
| return info->NoSOName; | |
| } | |
| } | |
| return false; | |
| } | |
| bool cmGeneratorTarget::HasMacOSXRpathInstallNameDir( | |
| std::string const& config) const | |
| { | |
| TargetPtrToBoolMap& cache = this->MacOSXRpathInstallNameDirCache[config]; | |
| auto const lookup = cache.find(this->Target); | |
| if (lookup != cache.cend()) { | |
| return lookup->second; | |
| } | |
| bool const result = this->DetermineHasMacOSXRpathInstallNameDir(config); | |
| cache[this->Target] = result; | |
| return result; | |
| } | |
| bool cmGeneratorTarget::DetermineHasMacOSXRpathInstallNameDir( | |
| std::string const& config) const | |
| { | |
| bool install_name_is_rpath = false; | |
| bool macosx_rpath = false; | |
| if (!this->IsImported()) { | |
| if (this->GetType() != cm::TargetType::SHARED_LIBRARY) { | |
| return false; | |
| } | |
| cmValue install_name = this->GetProperty("INSTALL_NAME_DIR"); | |
| bool use_install_name = this->MacOSXUseInstallNameDir(); | |
| if (install_name && use_install_name && *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 (cmHasLiteralPrefix(info->SOName, "@rpath/")) { | |
| 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")) { | |
| cmake* cm = this->LocalGenerator->GetCMakeInstance(); | |
| cm->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| cmStrCat("Attempting to use ", | |
| macosx_rpath ? "MACOSX_RPATH"_s : "@rpath"_s, | |
| " without CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG being set." | |
| " This could be because you are using a Mac OS X version" | |
| " less than 10.5 or because CMake's platform configuration is" | |
| " corrupt."), | |
| 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; | |
| } | |
| cmValue macosx_rpath_str = this->GetProperty("MACOSX_RPATH"); | |
| if (macosx_rpath_str) { | |
| return this->GetPropertyAsBool("MACOSX_RPATH"); | |
| } | |
| return true; | |
| } | |
| bool cmGeneratorTarget::MacOSXUseInstallNameDir() const | |
| { | |
| cmValue build_with_install_name = | |
| this->GetProperty("BUILD_WITH_INSTALL_NAME_DIR"); | |
| if (build_with_install_name) { | |
| return build_with_install_name.IsOn(); | |
| } | |
| 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( | |
| std::string const& config, cmStateEnums::ArtifactType artifact) 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 (this->IsFrameworkOnApple()) { | |
| auto fwDescriptor = this->GetGlobalGenerator()->SplitFrameworkPath( | |
| info->SOName, cmGlobalGenerator::FrameworkFormat::Strict); | |
| if (fwDescriptor) { | |
| return fwDescriptor->GetVersionedName(); | |
| } | |
| } | |
| if (cmHasLiteralPrefix(info->SOName, "@rpath/")) { | |
| return info->SOName.substr(cmStrLen("@rpath/")); | |
| } | |
| return info->SOName; | |
| } | |
| return ""; | |
| } | |
| // Compute the soname that will be built. | |
| return artifact == cmStateEnums::RuntimeBinaryArtifact | |
| ? this->GetLibraryNames(config).SharedObject | |
| : this->GetLibraryNames(config).ImportLibrary; | |
| } | |
| namespace { | |
| bool shouldAddFullLevel(cmGeneratorTarget::BundleDirectoryLevel level) | |
| { | |
| return level == cmGeneratorTarget::FullLevel; | |
| } | |
| bool shouldAddContentLevel(cmGeneratorTarget::BundleDirectoryLevel level) | |
| { | |
| return level == cmGeneratorTarget::ContentLevel || shouldAddFullLevel(level); | |
| } | |
| } | |
| std::string cmGeneratorTarget::GetAppBundleDirectory( | |
| std::string const& config, BundleDirectoryLevel level) const | |
| { | |
| std::string fpath = cmStrCat( | |
| this->GetFullName(config, cmStateEnums::RuntimeBinaryArtifact), '.'); | |
| cmValue ext = this->GetProperty("BUNDLE_EXTENSION"); | |
| fpath += (ext ? *ext : "app"); | |
| 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(); | |
| } | |
| bool cmGeneratorTarget::IsWin32Executable(std::string const& config) const | |
| { | |
| return cmIsOn(cmGeneratorExpression::Evaluate( | |
| this->GetSafeProperty("WIN32_EXECUTABLE"), this->LocalGenerator, config)); | |
| } | |
| std::string cmGeneratorTarget::GetCFBundleDirectory( | |
| std::string const& config, BundleDirectoryLevel level) const | |
| { | |
| std::string fpath = cmStrCat( | |
| this->GetOutputName(config, cmStateEnums::RuntimeBinaryArtifact), '.'); | |
| std::string ext; | |
| if (cmValue p = this->GetProperty("BUNDLE_EXTENSION")) { | |
| ext = *p; | |
| } else { | |
| 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( | |
| std::string const& config, BundleDirectoryLevel level) const | |
| { | |
| std::string fpath = cmStrCat( | |
| this->GetOutputName(config, cmStateEnums::RuntimeBinaryArtifact), '.'); | |
| cmValue ext = this->GetProperty("BUNDLE_EXTENSION"); | |
| fpath += (ext ? *ext : "framework"); | |
| if (shouldAddFullLevel(level) && | |
| !this->Makefile->PlatformIsAppleEmbedded()) { | |
| fpath = | |
| cmStrCat(std::move(fpath), "/Versions/", this->GetFrameworkVersion()); | |
| } | |
| return fpath; | |
| } | |
| std::string cmGeneratorTarget::GetFullName( | |
| std::string const& config, cmStateEnums::ArtifactType artifact) const | |
| { | |
| if (this->IsImported()) { | |
| return this->GetFullNameImported(config, artifact); | |
| } | |
| return this->GetFullNameInternal(config, artifact); | |
| } | |
| std::string cmGeneratorTarget::GetInstallNameDirForBuildTree( | |
| std::string const& 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()) { | |
| std::string installPrefix = | |
| this->Makefile->GetSafeDefinition("CMAKE_INSTALL_PREFIX"); | |
| return this->GetInstallNameDirForInstallTree(config, installPrefix); | |
| } | |
| // 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( | |
| std::string const& config, std::string const& installPrefix) const | |
| { | |
| if (this->Makefile->IsOn("CMAKE_PLATFORM_HAS_INSTALLNAME")) { | |
| std::string dir; | |
| cmValue install_name_dir = this->GetProperty("INSTALL_NAME_DIR"); | |
| if (this->CanGenerateInstallNameDir(INSTALL_NAME_FOR_INSTALL)) { | |
| if (cmNonempty(install_name_dir)) { | |
| dir = *install_name_dir; | |
| cmGeneratorExpression::ReplaceInstallPrefix(dir, installPrefix); | |
| dir = | |
| cmGeneratorExpression::Evaluate(dir, this->LocalGenerator, config); | |
| if (!dir.empty()) { | |
| dir = cmStrCat(std::move(dir), '/'); | |
| } | |
| } | |
| } | |
| if (!install_name_dir) { | |
| if (this->MacOSXRpathInstallNameDirDefault()) { | |
| dir = "@rpath/"; | |
| } | |
| } | |
| return dir; | |
| } | |
| return ""; | |
| } | |
| cmListFileBacktrace cmGeneratorTarget::GetBacktrace() const | |
| { | |
| return this->Target->GetBacktrace(); | |
| } | |
| std::set<BT<std::pair<std::string, bool>>> const& | |
| cmGeneratorTarget::GetUtilities() const | |
| { | |
| return this->Target->GetUtilities(); | |
| } | |
| bool cmGeneratorTarget::HaveWellDefinedOutputFiles() const | |
| { | |
| return this->GetType() == cm::TargetType::STATIC_LIBRARY || | |
| this->GetType() == cm::TargetType::SHARED_LIBRARY || | |
| this->GetType() == cm::TargetType::MODULE_LIBRARY || | |
| this->GetType() == cm::TargetType::OBJECT_LIBRARY || | |
| this->GetType() == cm::TargetType::EXECUTABLE; | |
| } | |
| std::string const* cmGeneratorTarget::GetExportMacro() const | |
| { | |
| // Define the symbol for targets that export symbols. | |
| if (this->GetType() == cm::TargetType::SHARED_LIBRARY || | |
| this->GetType() == cm::TargetType::MODULE_LIBRARY || | |
| this->IsExecutableWithExports()) { | |
| if (cmValue custom_export_name = this->GetProperty("DEFINE_SYMBOL")) { | |
| this->ExportMacro = *custom_export_name; | |
| } else { | |
| std::string in = cmStrCat(this->GetName(), "_EXPORTS"); | |
| this->ExportMacro = cmSystemTools::MakeCidentifier(in); | |
| } | |
| return &this->ExportMacro; | |
| } | |
| return nullptr; | |
| } | |
| cmList const& cmGeneratorTarget::GetSharedLibraryCompileDefs( | |
| std::string const& config) const | |
| { | |
| { | |
| auto it = this->SharedLibraryCompileDefs.find(config); | |
| if (it != this->SharedLibraryCompileDefs.end()) { | |
| return it->second; | |
| } | |
| } | |
| auto emplaceResult = | |
| this->SharedLibraryCompileDefs.emplace(config, cmList{}); | |
| auto& defs = emplaceResult.first->second; | |
| if (this->GetType() != cm::TargetType::SHARED_LIBRARY && | |
| this->GetType() != cm::TargetType::MODULE_LIBRARY) { | |
| return defs; | |
| } | |
| if (this->GetPolicyStatusCMP0203() != cmPolicies::NEW) { | |
| return defs; | |
| } | |
| auto linkerLang = this->GetLinkerLanguage(config); | |
| auto definitionVar = | |
| cmStrCat("CMAKE_", linkerLang, "_SHARED_LIBRARY_COMPILE_DEFINITIONS"); | |
| defs = this->Makefile->GetSafeDefinition(definitionVar); | |
| return defs; | |
| } | |
| cmGeneratorTarget::NameComponents const& | |
| cmGeneratorTarget::GetFullNameComponents( | |
| std::string const& config, cmStateEnums::ArtifactType artifact) const | |
| { | |
| return this->GetFullNameInternalComponents(config, artifact); | |
| } | |
| std::string cmGeneratorTarget::BuildBundleDirectory( | |
| std::string const& base, std::string const& 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( | |
| std::string const& config, cmStateEnums::ArtifactType artifact) const | |
| { | |
| // Start with the output directory for the target. | |
| std::string fpath = cmStrCat(this->GetDirectory(config, artifact), '/'); | |
| 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; | |
| } | |
| cmValue targetFolder = this->GetProperty("FOLDER"); | |
| if (targetFolder) { | |
| effectiveFolder += *targetFolder; | |
| } | |
| return effectiveFolder; | |
| } | |
| cmGeneratorTarget::CompileInfo const* cmGeneratorTarget::GetCompileInfo( | |
| std::string const& config) const | |
| { | |
| // There is no compile information for imported targets. | |
| if (this->IsImported()) { | |
| return nullptr; | |
| } | |
| if (this->GetType() > cm::TargetType::OBJECT_LIBRARY) { | |
| std::string msg = cmStrCat("cmTarget::GetCompileInfo called for ", | |
| this->GetName(), " which has type ", | |
| 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); | |
| } | |
| auto 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() != cm::TargetType::SHARED_LIBRARY && | |
| this->GetType() != cm::TargetType::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); | |
| } | |
| auto 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(CMAKE_BOOTSTRAP) | |
| info.DefFileGenerated = | |
| info.WindowsExportAllSymbols || info.Sources.size() > 1; | |
| #else | |
| // Our __create_def helper is not available during CMake bootstrap. | |
| info.DefFileGenerated = false; | |
| #endif | |
| if (info.DefFileGenerated) { | |
| info.DefFile = | |
| this->GetObjectDirectory(config) /* has slash */ + "exports.def"; | |
| } else if (!info.Sources.empty()) { | |
| info.DefFile = info.Sources.front()->GetFullPath(); | |
| } | |
| } | |
| bool cmGeneratorTarget::IsAIX() const | |
| { | |
| return this->Target->IsAIX(); | |
| } | |
| bool cmGeneratorTarget::IsApple() const | |
| { | |
| return this->Target->IsApple(); | |
| } | |
| bool cmGeneratorTarget::IsDLLPlatform() const | |
| { | |
| return this->Target->IsDLLPlatform(); | |
| } | |
| void cmGeneratorTarget::GetAutoUicOptions(std::vector<std::string>& result, | |
| std::string const& config) const | |
| { | |
| char const* prop = | |
| this->GetLinkInterfaceDependentStringProperty("AUTOUIC_OPTIONS", config); | |
| if (!prop) { | |
| return; | |
| } | |
| cm::GenEx::Context context(this->LocalGenerator, config); | |
| cmGeneratorExpressionDAGChecker dagChecker{ | |
| this, "AUTOUIC_OPTIONS", nullptr, nullptr, context, | |
| }; | |
| cmExpandList(cmGeneratorExpression::Evaluate( | |
| prop, context.LG, context.Config, this, &dagChecker), | |
| result); | |
| } | |
| 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() == cm::TargetType::GLOBAL_TARGET) { | |
| return; | |
| } | |
| // Use a helper object to trace the dependencies. | |
| cmTargetTraceDependencies tracer(this); | |
| tracer.Trace(); | |
| } | |
| std::string cmGeneratorTarget::GetCompilePDBDirectory( | |
| std::string const& config) const | |
| { | |
| if (CompileInfo const* info = this->GetCompileInfo(config)) { | |
| return info->CompilePdbDir; | |
| } | |
| return ""; | |
| } | |
| std::vector<std::string> cmGeneratorTarget::GetAppleArchs( | |
| std::string const& config, cm::optional<std::string> lang) const | |
| { | |
| cmList archList; | |
| if (!this->IsApple()) { | |
| return std::move(archList.data()); | |
| } | |
| cmValue archs = nullptr; | |
| if (!config.empty()) { | |
| std::string defVarName = | |
| cmStrCat("OSX_ARCHITECTURES_", cmSystemTools::UpperCase(config)); | |
| archs = this->GetProperty(defVarName); | |
| } | |
| if (!archs) { | |
| archs = this->GetProperty("OSX_ARCHITECTURES"); | |
| } | |
| if (archs) { | |
| archList.assign(*archs); | |
| } | |
| if (archList.empty() && | |
| // Fall back to a default architecture if no compiler target is set. | |
| (!lang || | |
| this->Makefile | |
| ->GetDefinition(cmStrCat("CMAKE_", *lang, "_COMPILER_TARGET")) | |
| .IsEmpty())) { | |
| archList.assign( | |
| this->Makefile->GetDefinition("_CMAKE_APPLE_ARCHS_DEFAULT")); | |
| } | |
| return std::move(archList.data()); | |
| } | |
| namespace { | |
| bool IsSupportedClassifiedFlagsLanguage(std::string const& lang) | |
| { | |
| return lang == "CXX"_s || lang == "SYCL"_s; | |
| } | |
| bool CanUseCompilerLauncher(std::string const& lang) | |
| { | |
| // Also found in `cmCommonTargetGenerator::GetCompilerLauncher`. | |
| return lang == "C"_s || lang == "CXX"_s || lang == "Fortran"_s || | |
| lang == "CUDA"_s || lang == "HIP"_s || lang == "ISPC"_s || | |
| lang == "SYCL"_s || lang == "OBJC"_s || lang == "OBJCXX"_s || | |
| lang == "Rust"_s; | |
| } | |
| // FIXME: return a vector of `cm::string_view` instead to avoid lots of tiny | |
| // allocations. | |
| std::vector<std::string> SplitFlags(std::string const& flags) | |
| { | |
| std::vector<std::string> options; | |
| #ifdef _WIN32 | |
| cmSystemTools::ParseWindowsCommandLine(flags.c_str(), options); | |
| #else | |
| cmSystemTools::ParseUnixCommandLine(flags.c_str(), options); | |
| #endif | |
| return options; | |
| } | |
| } | |
| cmGeneratorTarget::ClassifiedFlags | |
| cmGeneratorTarget::GetClassifiedFlagsForSource(cmSourceFile const* sf, | |
| std::string const& config) | |
| { | |
| auto& sourceFlagsCache = this->Configs[config].SourceFlags; | |
| auto cacheEntry = sourceFlagsCache.lower_bound(sf); | |
| if (cacheEntry != sourceFlagsCache.end() && cacheEntry->first == sf) { | |
| return cacheEntry->second; | |
| } | |
| ClassifiedFlags flags; | |
| std::string const lang = this->GetSourceFileLanguage(sf, config); | |
| if (!IsSupportedClassifiedFlagsLanguage(lang)) { | |
| return flags; | |
| } | |
| auto* const lg = this->GetLocalGenerator(); | |
| auto const* const mf = this->Makefile; | |
| cmGeneratorFileSet const* const fileSet = | |
| this->GetFileSetForSource(config, sf); | |
| // Compute the compiler launcher flags. | |
| if (CanUseCompilerLauncher(lang)) { | |
| // Compiler launchers are all execution flags and should not be relevant to | |
| // the actual compilation. | |
| FlagClassification cls = FlagClassification::ExecutionFlag; | |
| FlagKind kind = FlagKind::NotAFlag; | |
| std::string const clauncher_prop = cmStrCat(lang, "_COMPILER_LAUNCHER"); | |
| cmValue clauncher = this->GetProperty(clauncher_prop); | |
| std::string const evaluatedClauncher = cmGeneratorExpression::Evaluate( | |
| *clauncher, lg, config, this, nullptr, this, lang); | |
| for (auto const& flag : SplitFlags(evaluatedClauncher)) { | |
| flags.emplace_back(cls, kind, flag); | |
| } | |
| } | |
| ClassifiedFlags define_flags; | |
| ClassifiedFlags include_flags; | |
| ClassifiedFlags compile_flags; | |
| SourceVariables sfVars = this->GetSourceVariables(sf, config); | |
| // Compute language flags. | |
| { | |
| FlagClassification cls = FlagClassification::BaselineFlag; | |
| FlagKind kind = FlagKind::Compile; | |
| std::string mfFlags; | |
| // Explicitly add the explicit language flag before any other flag | |
| // so user flags can override it. | |
| this->AddExplicitLanguageFlags(mfFlags, *sf, config); | |
| for (auto const& flag : SplitFlags(mfFlags)) { | |
| flags.emplace_back(cls, kind, flag); | |
| } | |
| } | |
| std::unordered_map<std::string, std::string> pchSources; | |
| std::string filterArch; | |
| { | |
| std::vector<std::string> pchArchs = this->GetPchArchs(config, lang); | |
| for (std::string const& arch : pchArchs) { | |
| std::string const pchSource = this->GetPchSource(config, lang, arch); | |
| if (pchSource == sf->GetFullPath()) { | |
| filterArch = arch; | |
| } | |
| if (!pchSource.empty()) { | |
| pchSources.insert(std::make_pair(pchSource, arch)); | |
| } | |
| } | |
| } | |
| // File set specific flags | |
| if (fileSet) { | |
| // include flags | |
| { | |
| auto fsIncludes = fileSet->BelongsTo(this) | |
| ? fileSet->GetIncludeDirectories(config, lang) | |
| : fileSet->GetInterfaceIncludeDirectories(config, lang); | |
| if (!fsIncludes.empty()) { | |
| std::vector<std::string> includes; | |
| lg->AppendIncludeDirectories(includes, cm::remove_BT(fsIncludes), *sf); | |
| auto includeFlags = | |
| lg->GetIncludeFlags(includes, this, lang, config, false); | |
| for (auto&& flag : SplitFlags(includeFlags)) { | |
| include_flags.emplace_back(FlagClassification::PrivateFlag, | |
| FlagKind::Include, std::move(flag)); | |
| } | |
| } | |
| } | |
| } | |
| // source specific flags | |
| { | |
| // include flags | |
| if (cmValue srcIncludes = sf->GetProperty("INCLUDE_DIRECTORIES")) { | |
| std::vector<std::string> includes; | |
| lg->AppendIncludeDirectories(includes, srcIncludes, *sf); | |
| auto includeFlags = | |
| lg->GetIncludeFlags(includes, this, lang, config, false); | |
| for (auto&& flag : SplitFlags(includeFlags)) { | |
| include_flags.emplace_back(FlagClassification::PrivateFlag, | |
| FlagKind::Include, std::move(flag)); | |
| } | |
| } | |
| } | |
| // Compute target-wide flags. | |
| { | |
| FlagClassification cls = FlagClassification::BaselineFlag; | |
| // Compile flags | |
| { | |
| FlagKind kind = FlagKind::Compile; | |
| std::string targetFlags; | |
| lg->GetTargetCompileFlags(this, config, lang, targetFlags, filterArch); | |
| for (auto&& flag : SplitFlags(targetFlags)) { | |
| compile_flags.emplace_back(cls, kind, std::move(flag)); | |
| } | |
| } | |
| // Define flags | |
| { | |
| FlagKind kind = FlagKind::Definition; | |
| std::set<std::string> defines; | |
| lg->GetTargetDefines(this, config, lang, defines); | |
| std::string defineFlags; | |
| lg->JoinDefines(defines, defineFlags, lang); | |
| for (auto&& flag : SplitFlags(defineFlags)) { | |
| define_flags.emplace_back(cls, kind, std::move(flag)); | |
| } | |
| } | |
| // Include flags | |
| { | |
| FlagKind kind = FlagKind::Include; | |
| std::vector<std::string> includes; | |
| lg->GetIncludeDirectories(includes, this, lang, config); | |
| auto includeFlags = | |
| lg->GetIncludeFlags(includes, this, lang, config, false); | |
| for (auto&& flag : SplitFlags(includeFlags)) { | |
| include_flags.emplace_back(cls, kind, std::move(flag)); | |
| } | |
| } | |
| } | |
| std::string const COMPILE_OPTIONS("COMPILE_OPTIONS"); | |
| cmGeneratorExpressionInterpreter genexInterpreter(lg, config, this, lang); | |
| // Source-specific flags. | |
| { | |
| // Define flags | |
| { | |
| if (cmValue srcDefines = sf->GetProperty("COMPILE_DEFINITIONS")) { | |
| std::set<std::string> defines; | |
| lg->AppendDefines(defines, srcDefines); | |
| std::string defineFlags; | |
| lg->JoinDefines(defines, defineFlags, lang); | |
| for (auto&& flag : SplitFlags(defineFlags)) { | |
| define_flags.emplace_back(FlagClassification::PrivateFlag, | |
| FlagKind::Definition, std::move(flag)); | |
| } | |
| } | |
| } | |
| // Compile flags. | |
| { | |
| std::string const COMPILE_FLAGS("COMPILE_FLAGS"); | |
| FlagClassification cls = FlagClassification::PrivateFlag; | |
| FlagKind kind = FlagKind::Compile; | |
| std::string sourceFlags; | |
| if (cmValue cflags = sf->GetProperty(COMPILE_FLAGS)) { | |
| lg->AppendFlags(sourceFlags, | |
| genexInterpreter.Evaluate(*cflags, COMPILE_FLAGS)); | |
| } | |
| if (cmValue coptions = sf->GetProperty(COMPILE_OPTIONS)) { | |
| lg->AppendCompileOptions( | |
| sourceFlags, genexInterpreter.Evaluate(*coptions, COMPILE_OPTIONS)); | |
| } | |
| for (auto&& flag : SplitFlags(sourceFlags)) { | |
| compile_flags.emplace_back(cls, kind, std::move(flag)); | |
| } | |
| } | |
| } | |
| // File set specific flags | |
| if (fileSet) { | |
| // Define flags | |
| { | |
| auto fsDefines = fileSet->BelongsTo(this) | |
| ? fileSet->GetCompileDefinitions(config, lang) | |
| : fileSet->GetInterfaceCompileDefinitions(config, lang); | |
| if (!fsDefines.empty()) { | |
| std::set<std::string> defines; | |
| lg->AppendDefines(defines, fsDefines); | |
| std::string defineFlags; | |
| lg->JoinDefines(defines, defineFlags, lang); | |
| for (auto&& flag : SplitFlags(defineFlags)) { | |
| define_flags.emplace_back(FlagClassification::PrivateFlag, | |
| FlagKind::Definition, std::move(flag)); | |
| } | |
| } | |
| } | |
| // Compile flags. | |
| { | |
| auto options = fileSet->BelongsTo(this) | |
| ? fileSet->GetCompileOptions(config, lang) | |
| : fileSet->GetInterfaceCompileOptions(config, lang); | |
| if (!options.empty()) { | |
| std::string compileFlags; | |
| lg->AppendCompileOptions(compileFlags, cm::remove_BT(options)); | |
| for (auto&& flag : SplitFlags(compileFlags)) { | |
| compile_flags.emplace_back(FlagClassification::PrivateFlag, | |
| FlagKind::Compile, std::move(flag)); | |
| } | |
| } | |
| } | |
| } | |
| // Dependency tracking flags. | |
| { | |
| if (!sfVars.DependencyFlags.empty()) { | |
| cmRulePlaceholderExpander::RuleVariables vars; | |
| auto rulePlaceholderExpander = lg->CreateRulePlaceholderExpander(); | |
| vars.DependencyFile = sfVars.DependencyFile.c_str(); | |
| vars.DependencyTarget = sfVars.DependencyTarget.c_str(); | |
| std::string depfileFlags = sfVars.DependencyFlags; | |
| rulePlaceholderExpander->ExpandRuleVariables(lg, depfileFlags, vars); | |
| for (auto&& flag : SplitFlags(depfileFlags)) { | |
| compile_flags.emplace_back(FlagClassification::LocationFlag, | |
| FlagKind::BuildSystem, std::move(flag)); | |
| } | |
| } | |
| } | |
| // Precompiled headers. | |
| { | |
| FlagClassification cls = FlagClassification::PrivateFlag; | |
| FlagKind kind = FlagKind::Compile; | |
| std::string pchFlags; | |
| // Add precompile headers compile options. | |
| if (!((fileSet && sf->GetProperty("SKIP_PRECOMPILE_HEADERS")) || | |
| sf->GetProperty("SKIP_PRECOMPILE_HEADERS"))) { | |
| if (!pchSources.empty()) { | |
| std::string pchOptions; | |
| auto pchIt = pchSources.find(sf->GetFullPath()); | |
| if (pchIt != pchSources.end()) { | |
| pchOptions = | |
| this->GetPchCreateCompileOptions(config, lang, pchIt->second); | |
| } else { | |
| pchOptions = this->GetPchUseCompileOptions(config, lang); | |
| } | |
| this->LocalGenerator->AppendCompileOptions( | |
| pchFlags, genexInterpreter.Evaluate(pchOptions, COMPILE_OPTIONS)); | |
| } | |
| } | |
| for (auto&& flag : SplitFlags(pchFlags)) { | |
| compile_flags.emplace_back(cls, kind, std::move(flag)); | |
| } | |
| } | |
| // C++ module flags. | |
| bool useBmiTemplate = false; | |
| if (lang == "CXX"_s) { | |
| FlagClassification cls = FlagClassification::LocationFlag; | |
| FlagKind kind = FlagKind::BuildSystem; | |
| std::string bmiFlags; | |
| auto const* fs = this->GetFileSetForSource(config, sf); | |
| if (fs && fs->GetType() == cm::FileSetMetadata::CXX_MODULES) { | |
| if (lang != "CXX"_s) { | |
| mf->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| cmStrCat( | |
| "Target \"", this->Target->GetName(), "\" contains the source\n ", | |
| sf->GetFullPath(), "\nin a file set of type \"", fs->GetType(), | |
| R"(" but the source is not classified as a "CXX" source.)")); | |
| } | |
| if (!this->Target->IsNormal()) { | |
| if (!mf->GetDefinition("CMAKE_CXX_COMPILE_BMI").IsEmpty()) { | |
| useBmiTemplate = true; | |
| } else { | |
| std::string flag = | |
| mf->GetSafeDefinition("CMAKE_CXX_MODULE_BMI_ONLY_FLAG"); | |
| cmRulePlaceholderExpander::RuleVariables compileObjectVars; | |
| compileObjectVars.Object = sfVars.ObjectFileDir.c_str(); | |
| auto rulePlaceholderExpander = lg->CreateRulePlaceholderExpander(); | |
| rulePlaceholderExpander->ExpandRuleVariables(lg, flag, | |
| compileObjectVars); | |
| lg->AppendCompileOptions(bmiFlags, flag); | |
| } | |
| } | |
| } | |
| for (auto&& flag : SplitFlags(bmiFlags)) { | |
| compile_flags.emplace_back(cls, kind, std::move(flag)); | |
| } | |
| } | |
| cmRulePlaceholderExpander::RuleVariables vars; | |
| vars.CMTargetName = this->GetName().c_str(); | |
| vars.CMTargetType = cmState::GetTargetTypeName(this->GetType()).c_str(); | |
| vars.Language = lang.c_str(); | |
| auto const sfPath = this->LocalGenerator->ConvertToOutputFormat( | |
| sf->GetFullPath(), cmOutputConverter::SHELL); | |
| // Compute the base compiler command line. We'll find placeholders and | |
| // replace them with arguments later in this function. | |
| { | |
| FlagClassification cls = FlagClassification::ExecutionFlag; | |
| FlagKind kind = FlagKind::NotAFlag; | |
| std::string const cmdVar = | |
| cmStrCat("CMAKE_", lang, "_COMPILE_", useBmiTemplate ? "BMI" : "OBJECT"); | |
| std::string const& compileCmd = mf->GetRequiredDefinition(cmdVar); | |
| cmList compileCmds(compileCmd); // FIXME: which command to use? | |
| std::string& cmd = compileCmds[0]; | |
| auto rulePlaceholderExpander = lg->CreateRulePlaceholderExpander(); | |
| static std::string const PlaceholderDefines = "__CMAKE_DEFINES"; | |
| static std::string const PlaceholderIncludes = "__CMAKE_INCLUDES"; | |
| static std::string const PlaceholderFlags = "__CMAKE_FLAGS"; | |
| static std::string const PlaceholderSource = "__CMAKE_SOURCE"; | |
| vars.Defines = PlaceholderDefines.c_str(); | |
| vars.Includes = PlaceholderIncludes.c_str(); | |
| vars.TargetPDB = sfVars.TargetPDB.c_str(); | |
| vars.TargetCompilePDB = sfVars.TargetCompilePDB.c_str(); | |
| vars.Object = sfVars.ObjectFileDir.c_str(); | |
| vars.ObjectDir = sfVars.ObjectDir.c_str(); | |
| vars.ObjectFileDir = sfVars.ObjectFileDir.c_str(); | |
| vars.TargetSupportDir = sfVars.TargetSupportDir.c_str(); | |
| vars.Flags = PlaceholderFlags.c_str(); | |
| vars.DependencyFile = sfVars.DependencyFile.c_str(); | |
| vars.DependencyTarget = sfVars.DependencyTarget.c_str(); | |
| vars.Source = PlaceholderSource.c_str(); | |
| rulePlaceholderExpander->ExpandRuleVariables(lg, cmd, vars); | |
| for (auto&& flag : SplitFlags(cmd)) { | |
| if (flag == PlaceholderDefines) { | |
| flags.insert(flags.end(), define_flags.begin(), define_flags.end()); | |
| } else if (flag == PlaceholderIncludes) { | |
| flags.insert(flags.end(), include_flags.begin(), include_flags.end()); | |
| } else if (flag == PlaceholderFlags) { | |
| flags.insert(flags.end(), compile_flags.begin(), compile_flags.end()); | |
| } else if (flag == PlaceholderSource) { | |
| flags.emplace_back(FlagClassification::LocationFlag, | |
| FlagKind::BuildSystem, sfPath); | |
| } else { | |
| flags.emplace_back(cls, kind, std::move(flag)); | |
| // All remaining flags here are build system flags. | |
| kind = FlagKind::BuildSystem; | |
| } | |
| } | |
| } | |
| cacheEntry = sourceFlagsCache.emplace_hint(cacheEntry, sf, std::move(flags)); | |
| return cacheEntry->second; | |
| } | |
| cmGeneratorTarget::SourceVariables cmGeneratorTarget::GetSourceVariables( | |
| cmSourceFile const* sf, std::string const& config) | |
| { | |
| SourceVariables vars; | |
| auto const language = this->GetSourceFileLanguage(sf, config); | |
| auto const targetType = this->GetType(); | |
| auto const* const lg = this->GetLocalGenerator(); | |
| auto const* const gg = this->GetGlobalGenerator(); | |
| auto const* const mf = this->Makefile; | |
| // PDB settings. | |
| { | |
| if (mf->GetDefinition("MSVC_C_ARCHITECTURE_ID") || | |
| mf->GetDefinition("MSVC_CXX_ARCHITECTURE_ID") || | |
| mf->GetDefinition("MSVC_CUDA_ARCHITECTURE_ID")) { | |
| std::string pdbPath; | |
| std::string compilePdbPath; | |
| if (targetType <= cm::TargetType::OBJECT_LIBRARY) { | |
| compilePdbPath = this->GetCompilePDBPath(config); | |
| if (compilePdbPath.empty()) { | |
| // Match VS default: `$(IntDir)vc$(PlatformToolsetVersion).pdb`. | |
| // A trailing slash tells the toolchain to add its default file name. | |
| compilePdbPath = this->GetSupportDirectory(); | |
| if (gg->IsMultiConfig()) { | |
| compilePdbPath = cmStrCat(std::move(compilePdbPath), '/', config); | |
| } | |
| compilePdbPath += '/'; | |
| if (targetType == cm::TargetType::STATIC_LIBRARY) { | |
| // Match VS default for static libs: `$(IntDir)$(ProjectName).pdb`. | |
| compilePdbPath = | |
| cmStrCat(std::move(compilePdbPath), this->GetName(), ".pdb"); | |
| } | |
| } | |
| } | |
| if (targetType == cm::TargetType::EXECUTABLE || | |
| targetType == cm::TargetType::STATIC_LIBRARY || | |
| targetType == cm::TargetType::SHARED_LIBRARY || | |
| targetType == cm::TargetType::MODULE_LIBRARY) { | |
| pdbPath = cmStrCat(this->GetPDBDirectory(config), '/', | |
| this->GetPDBName(config)); | |
| } | |
| vars.TargetPDB = lg->ConvertToOutputFormat( | |
| gg->ConvertToOutputPath(std::move(pdbPath)), cmOutputConverter::SHELL); | |
| vars.TargetCompilePDB = lg->ConvertToOutputFormat( | |
| gg->ConvertToOutputPath(std::move(compilePdbPath)), | |
| cmOutputConverter::SHELL); | |
| } | |
| } | |
| // Object settings. | |
| { | |
| std::string const targetSupportDir = lg->MaybeRelativeToTopBinDir( | |
| gg->ConvertToOutputPath(this->GetCMFSupportDirectory())); | |
| std::string const objectDir = gg->ConvertToOutputPath( | |
| cmStrCat(this->GetSupportDirectory(), gg->GetConfigDirectory(config))); | |
| std::string const objectFileName = this->GetObjectName(sf); | |
| std::string const objectFilePath = | |
| cmStrCat(objectDir, '/', objectFileName); | |
| vars.TargetSupportDir = | |
| lg->ConvertToOutputFormat(targetSupportDir, cmOutputConverter::SHELL); | |
| vars.ObjectDir = | |
| lg->ConvertToOutputFormat(objectDir, cmOutputConverter::SHELL); | |
| vars.ObjectFileDir = | |
| lg->ConvertToOutputFormat(objectFilePath, cmOutputConverter::SHELL); | |
| // Dependency settings. | |
| { | |
| std::string const depfileFormatName = | |
| cmStrCat("CMAKE_", language, "_DEPFILE_FORMAT"); | |
| std::string const depfileFormat = | |
| mf->GetSafeDefinition(depfileFormatName); | |
| if (depfileFormat != "msvc"_s) { | |
| std::string const flagsName = | |
| cmStrCat("CMAKE_DEPFILE_FLAGS_", language); | |
| std::string const depfileFlags = mf->GetSafeDefinition(flagsName); | |
| if (!depfileFlags.empty()) { | |
| bool replaceExt = false; | |
| if (!language.empty()) { | |
| std::string const repVar = | |
| cmStrCat("CMAKE_", language, "_DEPFILE_EXTENSION_REPLACE"); | |
| replaceExt = mf->IsOn(repVar); | |
| } | |
| std::string const depfilePath = cmStrCat( | |
| objectDir, '/', | |
| replaceExt | |
| ? cmSystemTools::GetFilenameWithoutLastExtension(objectFileName) | |
| : objectFileName, | |
| ".d"); | |
| vars.DependencyFlags = depfileFlags; | |
| vars.DependencyTarget = vars.ObjectFileDir; | |
| vars.DependencyFile = | |
| lg->ConvertToOutputFormat(depfilePath, cmOutputConverter::SHELL); | |
| } | |
| } | |
| } | |
| } | |
| return vars; | |
| } | |
| void cmGeneratorTarget::AddExplicitLanguageFlags( | |
| std::string& flags, cmSourceFile const& sf, std::string const& config) const | |
| { | |
| auto const fileSetLanguage = | |
| this->FileSets->GetLanguageForSource(config, &sf); | |
| std::string const effectiveLanguage = | |
| this->GetSourceFileLanguage(&sf, config); | |
| if (fileSetLanguage || | |
| (!effectiveLanguage.empty() && effectiveLanguage != sf.GetLanguage())) { | |
| this->LocalGenerator->AppendFeatureOptions(flags, effectiveLanguage, | |
| "EXPLICIT_LANGUAGE"); | |
| return; | |
| } | |
| cmValue lang = sf.GetProperty("LANGUAGE"); | |
| if (!lang) { | |
| return; | |
| } | |
| switch (this->GetPolicyStatusCMP0119()) { | |
| case cmPolicies::WARN: | |
| CM_FALLTHROUGH; | |
| case cmPolicies::OLD: | |
| // The OLD behavior is to not add explicit language flags. | |
| return; | |
| case cmPolicies::NEW: | |
| // The NEW behavior is to add explicit language flags. | |
| break; | |
| } | |
| this->LocalGenerator->AppendFeatureOptions(flags, *lang, | |
| "EXPLICIT_LANGUAGE"); | |
| } | |
| void cmGeneratorTarget::AddSYCLDeviceTargetFlags(cmBuildStep compileOrLink, | |
| std::string const& config, | |
| std::string& flags) const | |
| { | |
| if (compileOrLink == cmBuildStep::Link && !this->IsDeviceLink() && | |
| requireDeviceLinking(*this, *this->LocalGenerator, config, "SYCL")) { | |
| return; | |
| } | |
| std::string targets = this->GetSafeProperty("SYCL_DEVICE_TARGETS"); | |
| auto const& compiler = | |
| this->Makefile->GetSafeDefinition("CMAKE_SYCL_COMPILER_ID"); | |
| bool const acpp = compiler == "AdaptiveCpp"; | |
| auto const& baseline = | |
| this->Makefile->GetSafeDefinition("_CMAKE_SYCL_DEVICE_TARGETS"); | |
| if (targets.empty()) { | |
| targets = baseline; | |
| } | |
| bool multipass = false; | |
| if (acpp) { | |
| static std::unordered_set<std::string> const multipassFlows{ | |
| "cuda", "cuda.integrated-multipass", "cuda.explicit-multipass", | |
| "hip", "hip.integrated-multipass", "hip.explicit-multipass" | |
| }; | |
| for (auto const& entry : cmList{ targets }) { | |
| std::string const flow = cmSystemTools::LowerCase( | |
| cmTrimWhitespace(entry.substr(0, entry.find(':')))); | |
| if (multipassFlows.find(flow) != multipassFlows.end()) { | |
| multipass = true; | |
| } | |
| } | |
| } | |
| if (!targets.empty() && !cmIsOff(targets)) { | |
| std::string option; | |
| if (acpp) { | |
| option = cmStrCat("--acpp-targets=", targets); | |
| } else if (compiler == "IntelLLVM" || compiler == "DPClang") { | |
| option = cmStrCat("-fsycl-targets=", cmJoin(cmList{ targets }, ",")); | |
| } else if (compiler == "Clang") { | |
| option = cmStrCat("--offload-targets=", cmJoin(cmList{ targets }, ",")); | |
| } else { | |
| this->LocalGenerator->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| cmStrCat("SYCL_DEVICE_TARGETS is not supported by the ", compiler, | |
| " compiler.")); | |
| return; | |
| } | |
| flags = cmStrCat(std::move(flags), ' ', | |
| this->LocalGenerator->EscapeForShell(option)); | |
| } | |
| bool const callerSelectsLanguage = | |
| acpp && !targets.empty() && cmIsOff(targets); | |
| if (compileOrLink == cmBuildStep::Compile && !multipass && | |
| !callerSelectsLanguage) { | |
| flags += " -x c++"; | |
| } | |
| } | |
| void cmGeneratorTarget::AddCUDAArchitectureFlags(cmBuildStep compileOrLink, | |
| std::string const& config, | |
| std::string& flags) const | |
| { | |
| std::string arch = this->GetSafeProperty("CUDA_ARCHITECTURES"); | |
| if (arch.empty()) { | |
| switch (this->GetPolicyStatusCMP0104()) { | |
| case cmPolicies::WARN: | |
| if (!this->LocalGenerator->GetCMakeInstance()->GetIsInTryCompile()) { | |
| this->Makefile->IssuePolicyWarning( | |
| cmPolicies::CMP0104, {}, | |
| cmStrCat("CUDA_ARCHITECTURES is empty for target \""_s, | |
| this->GetName(), "\"."_s)); | |
| } | |
| CM_FALLTHROUGH; | |
| case cmPolicies::OLD: | |
| break; | |
| default: | |
| this->Makefile->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| cmStrCat("CUDA_ARCHITECTURES is empty for target \"", | |
| this->GetName(), "\".")); | |
| } | |
| } | |
| // If CUDA_ARCHITECTURES is false we don't add any architectures. | |
| if (cmIsOff(arch)) { | |
| return; | |
| } | |
| this->AddCUDAArchitectureFlagsImpl(compileOrLink, config, "CUDA", | |
| std::move(arch), flags); | |
| } | |
| void cmGeneratorTarget::AddCUDAArchitectureFlagsImpl(cmBuildStep compileOrLink, | |
| std::string const& config, | |
| std::string const& lang, | |
| std::string arch, | |
| std::string& flags) const | |
| { | |
| std::string const& compiler = this->Makefile->GetSafeDefinition( | |
| cmStrCat("CMAKE_", lang, "_COMPILER_ID")); | |
| bool const ipoEnabled = this->IsIPOEnabled(lang, config); | |
| // Check for special modes: `all`, `all-major`. | |
| if (arch == "all" || arch == "all-major") { | |
| if (compiler == "NVIDIA" && | |
| cmSystemTools::VersionCompare(cmSystemTools::OP_GREATER_EQUAL, | |
| this->Makefile->GetDefinition(cmStrCat( | |
| "CMAKE_", lang, "_COMPILER_VERSION")), | |
| "11.5")) { | |
| flags = cmStrCat(std::move(flags), " -arch=", arch); | |
| return; | |
| } | |
| if (arch == "all") { | |
| arch = *this->Makefile->GetDefinition( | |
| cmStrCat("CMAKE_", lang, "_ARCHITECTURES_ALL")); | |
| } else if (arch == "all-major") { | |
| arch = *this->Makefile->GetDefinition( | |
| cmStrCat("CMAKE_", lang, "_ARCHITECTURES_ALL_MAJOR")); | |
| } | |
| } else if (arch == "native") { | |
| cmValue native = this->Makefile->GetDefinition( | |
| cmStrCat("CMAKE_", lang, "_ARCHITECTURES_NATIVE")); | |
| if (native.IsEmpty()) { | |
| this->Makefile->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| cmStrCat(lang, | |
| "_ARCHITECTURES is set to \"native\", but no NVIDIA GPU was " | |
| "detected.")); | |
| } | |
| if (compiler == "NVIDIA" && | |
| cmSystemTools::VersionCompare(cmSystemTools::OP_GREATER_EQUAL, | |
| this->Makefile->GetDefinition(cmStrCat( | |
| "CMAKE_", lang, "_COMPILER_VERSION")), | |
| "11.6")) { | |
| flags = cmStrCat(std::move(flags), " -arch=", arch); | |
| return; | |
| } | |
| arch = *native; | |
| } | |
| struct CudaArchitecture | |
| { | |
| std::string name; | |
| bool real{ true }; | |
| bool virtual_{ true }; | |
| }; | |
| std::vector<CudaArchitecture> architectures; | |
| { | |
| cmList options(arch); | |
| for (auto& option : options) { | |
| CudaArchitecture architecture; | |
| // Architecture name is up to the first specifier. | |
| std::size_t pos = option.find_first_of('-'); | |
| architecture.name = option.substr(0, pos); | |
| if (pos != std::string::npos) { | |
| cm::string_view specifier{ option.c_str() + pos + 1, | |
| option.length() - pos - 1 }; | |
| if (specifier == "real") { | |
| architecture.real = true; | |
| architecture.virtual_ = false; | |
| } else if (specifier == "virtual") { | |
| architecture.real = false; | |
| architecture.virtual_ = true; | |
| } else { | |
| this->Makefile->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| cmStrCat("Unknown CUDA architecture specifier \"", specifier, | |
| "\".")); | |
| } | |
| } | |
| architectures.emplace_back(architecture); | |
| } | |
| } | |
| if (compiler == "NVIDIA") { | |
| if (ipoEnabled && compileOrLink == cmBuildStep::Link) { | |
| if (cmValue cudaIPOFlags = this->Makefile->GetDefinition( | |
| cmStrCat("CMAKE_", lang, "_LINK_OPTIONS_IPO"))) { | |
| flags += *cudaIPOFlags; | |
| } | |
| } | |
| for (CudaArchitecture& architecture : architectures) { | |
| flags = cmStrCat(std::move(flags), " \"--generate-code=arch=compute_", | |
| architecture.name, ",code=["); | |
| if (architecture.virtual_) { | |
| flags = cmStrCat(std::move(flags), "compute_", architecture.name); | |
| if (ipoEnabled || architecture.real) { | |
| flags += ','; | |
| } | |
| } | |
| if (ipoEnabled) { | |
| if (compileOrLink == cmBuildStep::Compile) { | |
| flags = cmStrCat(std::move(flags), "lto_", architecture.name); | |
| } else if (compileOrLink == cmBuildStep::Link) { | |
| flags = cmStrCat(std::move(flags), "sm_", architecture.name); | |
| } | |
| } else if (architecture.real) { | |
| flags = cmStrCat(std::move(flags), "sm_", architecture.name); | |
| } | |
| flags += "]\""; | |
| } | |
| } else if (compiler == "Clang" && compileOrLink == cmBuildStep::Compile) { | |
| if (cmSystemTools::VersionCompare(cmSystemTools::OP_GREATER_EQUAL, | |
| this->Makefile->GetDefinition(cmStrCat( | |
| "CMAKE_", lang, "_COMPILER_VERSION")), | |
| "20.0")) { | |
| bool const separableCompilation = | |
| cmIsOn(this->GetSafeProperty("CUDA_SEPARABLE_COMPILATION")); | |
| for (CudaArchitecture& architecture : architectures) { | |
| flags = | |
| cmStrCat(std::move(flags), " --offload-arch=sm_", architecture.name); | |
| if (!architecture.real) { | |
| this->Makefile->IssueMessage( | |
| MessageType::WARNING, | |
| "Clang doesn't support disabling CUDA real code generation."); | |
| } | |
| if (architecture.virtual_ && separableCompilation && | |
| !this->Makefile->GetCMakeInstance()->GetIsInTryCompile()) { | |
| this->Makefile->IssueMessage( | |
| MessageType::WARNING, | |
| "Clang 20+ CUDA offloading model doesn't support virtual archs " | |
| "when CUDA_SEPARABLE_COMPILATION is enabled."); | |
| } else if (architecture.virtual_) { | |
| flags = cmStrCat(std::move(flags), " --cuda-include-ptx=sm_", | |
| architecture.name); | |
| } | |
| } | |
| } else { | |
| for (CudaArchitecture& architecture : architectures) { | |
| flags = cmStrCat(std::move(flags), " --cuda-gpu-arch=sm_", | |
| architecture.name); | |
| if (!architecture.real) { | |
| this->Makefile->IssueMessage( | |
| MessageType::WARNING, | |
| "Clang doesn't support disabling CUDA real code generation."); | |
| } | |
| if (!architecture.virtual_) { | |
| flags = cmStrCat(std::move(flags), " --no-cuda-include-ptx=sm_", | |
| architecture.name); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| void cmGeneratorTarget::AddISPCTargetFlags(std::string& flags) const | |
| { | |
| std::string const& arch = this->GetSafeProperty("ISPC_INSTRUCTION_SETS"); | |
| // If ISPC_TARGET is false we don't add any architectures. | |
| if (cmIsOff(arch)) { | |
| return; | |
| } | |
| std::string const& compiler = | |
| this->Makefile->GetSafeDefinition("CMAKE_ISPC_COMPILER_ID"); | |
| if (compiler == "Intel") { | |
| cmList targets(arch); | |
| if (!targets.empty()) { | |
| flags = | |
| cmStrCat(std::move(flags), " --target=", cmWrap("", targets, "", ",")); | |
| } | |
| } | |
| } | |
| void cmGeneratorTarget::AddHIPArchitectureFlags(cmBuildStep compileOrLink, | |
| std::string const& config, | |
| std::string& flags) const | |
| { | |
| std::string arch = this->GetSafeProperty("HIP_ARCHITECTURES"); | |
| if (arch.empty()) { | |
| this->Makefile->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| cmStrCat("HIP_ARCHITECTURES is empty for target \"", this->GetName(), | |
| "\".")); | |
| } | |
| // If HIP_ARCHITECTURES is false we don't add any architectures. | |
| if (cmIsOff(arch)) { | |
| return; | |
| } | |
| if (this->Makefile->GetSafeDefinition("CMAKE_HIP_PLATFORM") == "nvidia") { | |
| this->AddCUDAArchitectureFlagsImpl(compileOrLink, config, "HIP", | |
| std::move(arch), flags); | |
| return; | |
| } | |
| // For spirv platform, chipStar handles targeting via the hip package | |
| if (this->Makefile->GetSafeDefinition("CMAKE_HIP_PLATFORM") == "spirv") { | |
| return; | |
| } | |
| cmList options(arch); | |
| for (std::string& option : options) { | |
| flags = cmStrCat(std::move(flags), " --offload-arch=", option); | |
| } | |
| } | |
| void cmGeneratorTarget::AddRustTargetFlags(std::string& flags) const | |
| { | |
| cmValue const edition = this->GetProperty("Rust_EDITION"); | |
| if (edition && !edition->empty()) { | |
| flags = cmStrCat(std::move(flags), " --edition=", *edition); | |
| } | |
| } | |
| void cmGeneratorTarget::AddSwiftTargetFlags(std::string& flags) const | |
| { | |
| if (cmValue version = GetProperty("Swift_LANGUAGE_VERSION")) { | |
| if (cmSystemTools::VersionCompare( | |
| cmSystemTools::OP_GREATER_EQUAL, | |
| this->Makefile->GetDefinition("CMAKE_Swift_COMPILER_VERSION"), | |
| "4.2")) { | |
| flags = cmStrCat(std::move(flags), " -swift-version ", *version); | |
| } | |
| } | |
| if (!this->GetGlobalGenerator()->IsXcode() && | |
| cmSystemTools::VersionCompare( | |
| cmSystemTools::OP_GREATER_EQUAL, | |
| this->Makefile->GetDefinition("CMAKE_Swift_COMPILER_VERSION"), | |
| "5.8")) { | |
| // Note: The Xcode generator sets the `SWIFT_PACKAGE_NAME` BuildSettings | |
| // attribute | |
| std::string const packageName = this->GetSwiftPackageName(); | |
| if (!packageName.empty()) { | |
| std::string const packageFlag = | |
| this->Makefile->GetSafeDefinition("CMAKE_Swift_PACKAGE_NAME_FLAG"); | |
| // Add the package name to the flags | |
| flags = cmStrCat(std::move(flags), ' ', packageFlag, ' ', packageName); | |
| } | |
| } | |
| } | |
| void cmGeneratorTarget::AddCUDAToolkitFlags(std::string& flags) const | |
| { | |
| std::string const& compiler = | |
| this->Makefile->GetSafeDefinition("CMAKE_CUDA_COMPILER_ID"); | |
| if (compiler == "Clang") { | |
| // Pass CUDA toolkit explicitly to Clang. | |
| // Clang's searching for the system CUDA toolkit isn't very good and it's | |
| // expected the user will explicitly pass the toolkit path. | |
| // This also avoids Clang having to search for the toolkit on every | |
| // invocation. | |
| std::string toolkitRoot = | |
| this->Makefile->GetSafeDefinition("CMAKE_CUDA_COMPILER_LIBRARY_ROOT"); | |
| if (!toolkitRoot.empty()) { | |
| flags = cmStrCat(std::move(flags), " --cuda-path=", | |
| this->LocalGenerator->ConvertToOutputFormat( | |
| toolkitRoot, cmOutputConverter::SHELL)); | |
| } | |
| } | |
| } | |
| //---------------------------------------------------------------------------- | |
| 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 cm::TargetType::STATIC_LIBRARY: { | |
| std::string var = cmStrCat("CMAKE_", lang, "_CREATE_STATIC_LIBRARY"); | |
| return this->GetFeatureSpecificLinkRuleVariable(var, lang, config); | |
| } | |
| case cm::TargetType::SHARED_LIBRARY: | |
| if (this->IsArchivedAIXSharedLibrary()) { | |
| return cmStrCat("CMAKE_", lang, "_CREATE_SHARED_LIBRARY_ARCHIVE"); | |
| } | |
| return cmStrCat("CMAKE_", lang, "_CREATE_SHARED_LIBRARY"); | |
| case cm::TargetType::MODULE_LIBRARY: | |
| return cmStrCat("CMAKE_", lang, "_CREATE_SHARED_MODULE"); | |
| case cm::TargetType::EXECUTABLE: | |
| if (this->IsExecutableWithExports()) { | |
| std::string linkExeWithExports = | |
| cmStrCat("CMAKE_", lang, "_LINK_EXECUTABLE_WITH_EXPORTS"); | |
| if (this->Makefile->IsDefinitionSet(linkExeWithExports)) { | |
| return linkExeWithExports; | |
| } | |
| } | |
| return cmStrCat("CMAKE_", lang, "_LINK_EXECUTABLE"); | |
| default: | |
| break; | |
| } | |
| return {}; | |
| } | |
| //---------------------------------------------------------------------------- | |
| std::string cmGeneratorTarget::GetClangTidyExportFixesDirectory( | |
| std::string const& lang) const | |
| { | |
| cmValue val = | |
| this->GetProperty(cmStrCat(lang, "_CLANG_TIDY_EXPORT_FIXES_DIR")); | |
| if (!cmNonempty(val)) { | |
| return {}; | |
| } | |
| std::string path = *val; | |
| if (!cmSystemTools::FileIsFullPath(path)) { | |
| path = cmStrCat(this->LocalGenerator->GetCurrentBinaryDirectory(), '/', | |
| std::move(path)); | |
| } | |
| return cmSystemTools::CollapseFullPath(path); | |
| } | |
| struct CycleWatcher | |
| { | |
| CycleWatcher(bool& flag) | |
| : Flag(flag) | |
| { | |
| this->Flag = true; | |
| } | |
| ~CycleWatcher() { this->Flag = false; } | |
| bool& Flag; | |
| }; | |
| cmGeneratorTarget const* cmGeneratorTarget::GetPchReuseTarget() const | |
| { | |
| if (this->ComputingPchReuse) { | |
| // TODO: Get the full cycle. | |
| if (!this->PchReuseCycleDetected) { | |
| this->Makefile->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| cmStrCat("Circular PCH reuse target involving '", this->GetName(), | |
| '\'')); | |
| } | |
| this->PchReuseCycleDetected = true; | |
| return nullptr; | |
| } | |
| CycleWatcher watch(this->ComputingPchReuse); | |
| (void)watch; | |
| cmValue pchReuseFrom = this->GetProperty("PRECOMPILE_HEADERS_REUSE_FROM"); | |
| if (!pchReuseFrom) { | |
| return nullptr; | |
| } | |
| cmGeneratorTarget const* generatorTarget = | |
| this->GetGlobalGenerator()->FindGeneratorTarget(*pchReuseFrom); | |
| if (!generatorTarget) { | |
| this->Makefile->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| cmStrCat( | |
| "Target \"", *pchReuseFrom, "\" for the \"", this->GetName(), | |
| R"(" target's "PRECOMPILE_HEADERS_REUSE_FROM" property does not exist.)")); | |
| } | |
| if (this->GetProperty("PRECOMPILE_HEADERS").IsOn()) { | |
| this->Makefile->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| cmStrCat("PRECOMPILE_HEADERS property is already set on target (\"", | |
| this->GetName(), "\")\n")); | |
| } | |
| if (generatorTarget) { | |
| if (generatorTarget->GetPropertyAsBool("DISABLE_PRECOMPILE_HEADERS")) { | |
| this->Makefile->IssueMessage( | |
| MessageType::WARNING, | |
| cmStrCat( | |
| "Target \"", *pchReuseFrom, "\" for the \"", this->GetName(), | |
| R"(" target's "PRECOMPILE_HEADERS_REUSE_FROM" property has set "DISABLE_PRECOMPILE_HEADERS"; ignoring.)")); | |
| return nullptr; | |
| } | |
| if (auto const* recurseReuseTarget = | |
| generatorTarget->GetPchReuseTarget()) { | |
| return recurseReuseTarget; | |
| } | |
| } | |
| return generatorTarget; | |
| } | |
| cmGeneratorTarget* cmGeneratorTarget::GetPchReuseTarget() | |
| { | |
| if (this->ComputingPchReuse) { | |
| // TODO: Get the full cycle. | |
| if (!this->PchReuseCycleDetected) { | |
| this->Makefile->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| cmStrCat("Circular PCH reuse target involving '", this->GetName(), | |
| '\'')); | |
| } | |
| this->PchReuseCycleDetected = true; | |
| return nullptr; | |
| } | |
| CycleWatcher watch(this->ComputingPchReuse); | |
| (void)watch; | |
| cmValue pchReuseFrom = this->GetProperty("PRECOMPILE_HEADERS_REUSE_FROM"); | |
| if (!pchReuseFrom) { | |
| return nullptr; | |
| } | |
| cmGeneratorTarget* generatorTarget = | |
| this->GetGlobalGenerator()->FindGeneratorTarget(*pchReuseFrom); | |
| if (!generatorTarget) { | |
| this->Makefile->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| cmStrCat( | |
| "Target \"", *pchReuseFrom, "\" for the \"", this->GetName(), | |
| R"(" target's "PRECOMPILE_HEADERS_REUSE_FROM" property does not exist.)")); | |
| } | |
| if (this->GetProperty("PRECOMPILE_HEADERS").IsOn()) { | |
| this->Makefile->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| cmStrCat("PRECOMPILE_HEADERS property is already set on target (\"", | |
| this->GetName(), "\")\n")); | |
| } | |
| if (generatorTarget) { | |
| if (generatorTarget->GetPropertyAsBool("DISABLE_PRECOMPILE_HEADERS")) { | |
| this->Makefile->IssueMessage( | |
| MessageType::WARNING, | |
| cmStrCat( | |
| "Target \"", *pchReuseFrom, "\" for the \"", this->GetName(), | |
| R"(" target's "PRECOMPILE_HEADERS_REUSE_FROM" property has set "DISABLE_PRECOMPILE_HEADERS"; ignoring.)")); | |
| return nullptr; | |
| } | |
| if (auto* recurseReuseTarget = generatorTarget->GetPchReuseTarget()) { | |
| return recurseReuseTarget; | |
| } | |
| } | |
| return generatorTarget; | |
| } | |
| std::vector<std::string> cmGeneratorTarget::GetPchArchs( | |
| std::string const& config, std::string const& lang) const | |
| { | |
| std::vector<std::string> pchArchs; | |
| if (!this->GetGlobalGenerator()->IsXcode()) { | |
| pchArchs = this->GetAppleArchs(config, lang); | |
| } | |
| if (pchArchs.size() < 2) { | |
| // We do not need per-arch PCH files when building for one architecture. | |
| pchArchs = { {} }; | |
| } | |
| return pchArchs; | |
| } | |
| std::string cmGeneratorTarget::GetPchHeader(std::string const& config, | |
| std::string const& language, | |
| std::string const& arch) const | |
| { | |
| if (language != "C" && language != "CXX" && language != "OBJC" && | |
| language != "OBJCXX") { | |
| return std::string(); | |
| } | |
| if (this->GetPropertyAsBool("DISABLE_PRECOMPILE_HEADERS")) { | |
| return std::string(); | |
| } | |
| cmGeneratorTarget const* generatorTarget = this; | |
| cmGeneratorTarget const* reuseTarget = this->GetPchReuseTarget(); | |
| bool const haveReuseTarget = reuseTarget && reuseTarget != this; | |
| if (reuseTarget) { | |
| generatorTarget = reuseTarget; | |
| } | |
| auto const inserted = this->PchHeaders.insert( | |
| std::make_pair(cmStrCat(language, config, arch), "")); | |
| if (inserted.second) { | |
| std::vector<BT<std::string>> const headers = | |
| this->GetPrecompileHeaders(config, language); | |
| if (headers.empty() && !haveReuseTarget) { | |
| return std::string(); | |
| } | |
| std::string& filename = inserted.first->second; | |
| std::map<std::string, std::string> const languageToExtension = { | |
| { "C", ".h" }, | |
| { "CXX", ".hxx" }, | |
| { "OBJC", ".objc.h" }, | |
| { "OBJCXX", ".objcxx.hxx" } | |
| }; | |
| filename = generatorTarget->GetCMFSupportDirectory(); | |
| if (this->GetGlobalGenerator()->IsMultiConfig()) { | |
| filename = cmStrCat(std::move(filename), '/', config); | |
| } | |
| // This is acceptable as its the source file, won't have a rename/hash | |
| filename = cmStrCat(std::move(filename), "/cmake_pch", | |
| arch.empty() ? "" : cmStrCat('_', arch), | |
| languageToExtension.at(language)); | |
| std::string const filename_tmp = cmStrCat(filename, ".tmp"); | |
| if (!haveReuseTarget) { | |
| // Marking the generated header as a system header suppresses the | |
| // diagnostics of every header it includes. Skip it when the target | |
| // asks for warnings from its precompiled headers. | |
| cmValue pchSystemHeaderPragma = this->GetPropertyAsBool("PCH_WARNINGS") | |
| ? nullptr | |
| : this->Makefile->GetDefinition("CMAKE_PCH_SYSTEM_HEADER_PRAGMA"); | |
| cmValue pchPrologue = | |
| this->Makefile->GetDefinition("CMAKE_PCH_PROLOGUE"); | |
| cmValue pchEpilogue = | |
| this->Makefile->GetDefinition("CMAKE_PCH_EPILOGUE"); | |
| std::string firstHeaderOnDisk; | |
| { | |
| cmGeneratedFileStream file( | |
| filename_tmp, false, | |
| this->GetGlobalGenerator()->GetMakefileEncoding()); | |
| file << "/* generated by CMake */\n\n"; | |
| if (pchSystemHeaderPragma) { | |
| file << *pchSystemHeaderPragma << "\n"; | |
| } | |
| if (pchPrologue) { | |
| file << *pchPrologue << "\n"; | |
| } | |
| if (this->GetGlobalGenerator()->IsXcode()) { | |
| file << "#ifndef CMAKE_SKIP_PRECOMPILE_HEADERS\n"; | |
| } | |
| if (language == "CXX" && !this->GetGlobalGenerator()->IsXcode()) { | |
| file << "#ifdef __cplusplus\n"; | |
| } | |
| for (auto const& header_bt : headers) { | |
| if (header_bt.Value.empty()) { | |
| continue; | |
| } | |
| if (header_bt.Value[0] == '<' || header_bt.Value[0] == '\"') { | |
| file << "#include " << header_bt.Value << "\n"; | |
| } else { | |
| file << "#include \"" << header_bt.Value << "\"\n"; | |
| } | |
| if (cmSystemTools::FileExists(header_bt.Value) && | |
| firstHeaderOnDisk.empty()) { | |
| firstHeaderOnDisk = header_bt.Value; | |
| } | |
| } | |
| if (language == "CXX" && !this->GetGlobalGenerator()->IsXcode()) { | |
| file << "#endif // __cplusplus\n"; | |
| } | |
| if (this->GetGlobalGenerator()->IsXcode()) { | |
| file << "#endif // CMAKE_SKIP_PRECOMPILE_HEADERS\n"; | |
| } | |
| if (pchEpilogue) { | |
| file << *pchEpilogue << "\n"; | |
| } | |
| } | |
| // Give the generated header the timestamp of a header from the project | |
| // to help ccache. Do this only when creating it for the first time: | |
| // if its content later changes, e.g. because PCH_WARNINGS was toggled, | |
| // an older timestamp would make build systems consider the already | |
| // precompiled header up to date. | |
| if (!firstHeaderOnDisk.empty() && !cmSystemTools::FileExists(filename)) { | |
| cmFileTimes::Copy(firstHeaderOnDisk, filename_tmp); | |
| } | |
| cmSystemTools::MoveFileIfDifferent(filename_tmp, filename); | |
| } | |
| } | |
| return inserted.first->second; | |
| } | |
| std::string cmGeneratorTarget::GetPchSource(std::string const& config, | |
| std::string const& language, | |
| std::string const& arch) const | |
| { | |
| if (language != "C" && language != "CXX" && language != "OBJC" && | |
| language != "OBJCXX") { | |
| return std::string(); | |
| } | |
| auto const inserted = this->PchSources.insert( | |
| std::make_pair(cmStrCat(language, config, arch), "")); | |
| if (inserted.second) { | |
| std::string const pchHeader = this->GetPchHeader(config, language, arch); | |
| if (pchHeader.empty()) { | |
| return std::string(); | |
| } | |
| std::string& filename = inserted.first->second; | |
| cmGeneratorTarget const* generatorTarget = this; | |
| cmGeneratorTarget const* reuseTarget = this->GetPchReuseTarget(); | |
| bool const haveReuseTarget = reuseTarget && reuseTarget != this; | |
| if (reuseTarget) { | |
| generatorTarget = reuseTarget; | |
| } | |
| filename = | |
| cmStrCat(generatorTarget->GetCMFSupportDirectory(), "/cmake_pch"); | |
| // For GCC the source extension will be transformed into .h[xx].gch | |
| if (!this->Makefile->IsOn("CMAKE_LINK_PCH")) { | |
| std::map<std::string, std::string> const languageToExtension = { | |
| { "C", ".h.c" }, | |
| { "CXX", ".hxx.cxx" }, | |
| { "OBJC", ".objc.h.m" }, | |
| { "OBJCXX", ".objcxx.hxx.mm" } | |
| }; | |
| filename = | |
| cmStrCat(std::move(filename), arch.empty() ? "" : cmStrCat('_', arch), | |
| languageToExtension.at(language)); | |
| } else { | |
| std::map<std::string, std::string> const languageToExtension = { | |
| { "C", ".c" }, { "CXX", ".cxx" }, { "OBJC", ".m" }, { "OBJCXX", ".mm" } | |
| }; | |
| filename = | |
| cmStrCat(std::move(filename), arch.empty() ? "" : cmStrCat('_', arch), | |
| languageToExtension.at(language)); | |
| } | |
| std::string const filename_tmp = cmStrCat(filename, ".tmp"); | |
| if (!haveReuseTarget) { | |
| { | |
| cmGeneratedFileStream file(filename_tmp); | |
| file << "/* generated by CMake */\n"; | |
| } | |
| cmFileTimes::Copy(pchHeader, filename_tmp); | |
| cmSystemTools::MoveFileIfDifferent(filename_tmp, filename); | |
| } | |
| } | |
| return inserted.first->second; | |
| } | |
| std::string cmGeneratorTarget::GetPchFileObject(std::string const& config, | |
| std::string const& language, | |
| std::string const& arch) | |
| { | |
| if (language != "C" && language != "CXX" && language != "OBJC" && | |
| language != "OBJCXX") { | |
| return std::string(); | |
| } | |
| auto const inserted = this->PchObjectFiles.insert( | |
| std::make_pair(cmStrCat(language, config, arch), "")); | |
| if (inserted.second) { | |
| std::string const pchSource = this->GetPchSource(config, language, arch); | |
| if (pchSource.empty()) { | |
| return std::string(); | |
| } | |
| std::string& filename = inserted.first->second; | |
| auto* pchSf = this->Makefile->GetOrCreateSource( | |
| pchSource, false, cmSourceFileLocationKind::Known); | |
| pchSf->SetSpecialSourceType(cmSourceFile::SpecialSourceType::PchSource); | |
| pchSf->ResolveFullPath(); | |
| filename = cmStrCat(this->GetObjectDirectory(config), '/', | |
| this->GetObjectName(pchSf)); | |
| } | |
| return inserted.first->second; | |
| } | |
| std::string cmGeneratorTarget::GetPchFile(std::string const& config, | |
| std::string const& language, | |
| std::string const& arch) | |
| { | |
| auto const inserted = this->PchFiles.insert( | |
| std::make_pair(cmStrCat(language, config, arch), "")); | |
| if (inserted.second) { | |
| std::string& pchFile = inserted.first->second; | |
| std::string const pchExtension = | |
| this->Makefile->GetSafeDefinition("CMAKE_PCH_EXTENSION"); | |
| if (this->Makefile->IsOn("CMAKE_LINK_PCH")) { | |
| auto replaceExtension = [](std::string const& str, | |
| std::string const& ext) -> std::string { | |
| auto dot_pos = str.rfind('.'); | |
| std::string result; | |
| if (dot_pos != std::string::npos) { | |
| result = str.substr(0, dot_pos); | |
| } | |
| result += ext; | |
| return result; | |
| }; | |
| cmGeneratorTarget* generatorTarget = this; | |
| cmGeneratorTarget* reuseTarget = this->GetPchReuseTarget(); | |
| if (reuseTarget) { | |
| generatorTarget = reuseTarget; | |
| } | |
| std::string const pchFileObject = | |
| generatorTarget->GetPchFileObject(config, language, arch); | |
| if (!pchExtension.empty()) { | |
| pchFile = replaceExtension(pchFileObject, pchExtension); | |
| } | |
| } else { | |
| if (this->GetUseShortObjectNames() && !pchExtension.empty()) { | |
| auto pchSource = this->GetPchSource(config, language, arch); | |
| auto* pchSf = this->Makefile->GetOrCreateSource( | |
| pchSource, false, cmSourceFileLocationKind::Known); | |
| pchSf->SetSpecialSourceType( | |
| cmSourceFile::SpecialSourceType::PchSource); | |
| pchSf->ResolveFullPath(); | |
| std::string cfgSubdir; | |
| if (this->GetGlobalGenerator()->IsMultiConfig()) { | |
| cfgSubdir = cmStrCat(config, '/'); | |
| } | |
| pchFile = cmStrCat( | |
| this->GetSupportDirectory(), '/', cfgSubdir, | |
| this->LocalGenerator->GetShortObjectFileName(*pchSf), pchExtension); | |
| } else { | |
| pchFile = | |
| cmStrCat(this->GetPchHeader(config, language, arch), pchExtension); | |
| } | |
| } | |
| } | |
| return inserted.first->second; | |
| } | |
| std::string cmGeneratorTarget::GetPchCreateCompileOptions( | |
| std::string const& config, std::string const& language, | |
| std::string const& arch) | |
| { | |
| auto const inserted = this->PchCreateCompileOptions.insert( | |
| std::make_pair(cmStrCat(language, config, arch), "")); | |
| if (inserted.second) { | |
| std::string& createOptionList = inserted.first->second; | |
| if (this->GetPropertyAsBool("PCH_WARN_INVALID")) { | |
| createOptionList = this->Makefile->GetSafeDefinition( | |
| cmStrCat("CMAKE_", language, "_COMPILE_OPTIONS_INVALID_PCH")); | |
| } | |
| if (this->GetPropertyAsBool("PCH_INSTANTIATE_TEMPLATES")) { | |
| std::string varName = cmStrCat( | |
| "CMAKE_", language, "_COMPILE_OPTIONS_INSTANTIATE_TEMPLATES_PCH"); | |
| std::string instantiateOption = | |
| this->Makefile->GetSafeDefinition(varName); | |
| if (!instantiateOption.empty()) { | |
| createOptionList = | |
| cmStrCat(std::move(createOptionList), ';', instantiateOption); | |
| } | |
| } | |
| std::string const createOptVar = | |
| cmStrCat("CMAKE_", language, "_COMPILE_OPTIONS_CREATE_PCH"); | |
| createOptionList = | |
| cmStrCat(std::move(createOptionList), ';', | |
| this->Makefile->GetSafeDefinition(createOptVar)); | |
| std::string const pchHeader = this->GetPchHeader(config, language, arch); | |
| std::string const pchFile = this->GetPchFile(config, language, arch); | |
| if (GlobalGenerator->IsFastbuild()) { | |
| // Account for potential spaces in a shell-friendly way. | |
| cmSystemTools::ReplaceString(createOptionList, "<PCH_HEADER>", | |
| cmStrCat('"', pchHeader, '"')); | |
| cmSystemTools::ReplaceString(createOptionList, "<PCH_FILE>", | |
| cmStrCat('"', pchFile, '"')); | |
| } else { | |
| cmSystemTools::ReplaceString(createOptionList, "<PCH_HEADER>", | |
| pchHeader); | |
| cmSystemTools::ReplaceString(createOptionList, "<PCH_FILE>", pchFile); | |
| } | |
| } | |
| return inserted.first->second; | |
| } | |
| std::string cmGeneratorTarget::GetPchUseCompileOptions( | |
| std::string const& config, std::string const& language, | |
| std::string const& arch) | |
| { | |
| auto const inserted = this->PchUseCompileOptions.insert( | |
| std::make_pair(cmStrCat(language, config, arch), "")); | |
| if (inserted.second) { | |
| std::string& useOptionList = inserted.first->second; | |
| if (this->GetPropertyAsBool("PCH_WARN_INVALID")) { | |
| useOptionList = this->Makefile->GetSafeDefinition( | |
| cmStrCat("CMAKE_", language, "_COMPILE_OPTIONS_INVALID_PCH")); | |
| } | |
| std::string const useOptVar = | |
| cmStrCat(language, "_COMPILE_OPTIONS_USE_PCH"); | |
| std::string const& useOptionListProperty = | |
| this->GetSafeProperty(useOptVar); | |
| useOptionList = cmStrCat( | |
| std::move(useOptionList), ';', | |
| useOptionListProperty.empty() | |
| ? this->Makefile->GetSafeDefinition(cmStrCat("CMAKE_", useOptVar)) | |
| : useOptionListProperty); | |
| std::string const pchHeader = this->GetPchHeader(config, language, arch); | |
| std::string const pchFile = this->GetPchFile(config, language, arch); | |
| if (GlobalGenerator->IsFastbuild()) { | |
| // Account for potential spaces in a shell-friendly way. | |
| cmSystemTools::ReplaceString(useOptionList, "<PCH_HEADER>", | |
| cmStrCat('"', pchHeader, '"')); | |
| cmSystemTools::ReplaceString(useOptionList, "<PCH_FILE>", | |
| cmStrCat('"', pchFile, '"')); | |
| } else { | |
| cmSystemTools::ReplaceString(useOptionList, "<PCH_HEADER>", pchHeader); | |
| cmSystemTools::ReplaceString(useOptionList, "<PCH_FILE>", pchFile); | |
| } | |
| } | |
| return inserted.first->second; | |
| } | |
| void cmGeneratorTarget::AddSourceFileToUnityBatch( | |
| std::string const& sourceFilename) | |
| { | |
| this->UnityBatchedSourceFiles.insert(sourceFilename); | |
| } | |
| bool cmGeneratorTarget::IsSourceFilePartOfUnityBatch( | |
| std::string const& sourceFilename) const | |
| { | |
| if (!this->GetPropertyAsBool("UNITY_BUILD")) { | |
| return false; | |
| } | |
| return this->UnityBatchedSourceFiles.find(sourceFilename) != | |
| this->UnityBatchedSourceFiles.end(); | |
| } | |
| void cmGeneratorTarget::ComputeTargetManifest(std::string const& config) const | |
| { | |
| if (this->IsImported()) { | |
| return; | |
| } | |
| cmGlobalGenerator* gg = this->LocalGenerator->GetGlobalGenerator(); | |
| // Get the names. | |
| cmGeneratorTarget::Names targetNames; | |
| if (this->GetType() == cm::TargetType::EXECUTABLE) { | |
| targetNames = this->GetExecutableNames(config); | |
| } else if (this->GetType() == cm::TargetType::STATIC_LIBRARY || | |
| this->GetType() == cm::TargetType::SHARED_LIBRARY || | |
| this->GetType() == cm::TargetType::MODULE_LIBRARY) { | |
| targetNames = this->GetLibraryNames(config); | |
| } else { | |
| return; | |
| } | |
| // Get the directory. | |
| std::string dir = | |
| this->GetDirectory(config, cmStateEnums::RuntimeBinaryArtifact); | |
| // Add each name. | |
| std::string f; | |
| if (!targetNames.Output.empty()) { | |
| f = cmStrCat(dir, '/', targetNames.Output); | |
| gg->AddToManifest(f); | |
| } | |
| if (!targetNames.SharedObject.empty()) { | |
| f = cmStrCat(dir, '/', targetNames.SharedObject); | |
| gg->AddToManifest(f); | |
| } | |
| if (!targetNames.Real.empty()) { | |
| f = cmStrCat(dir, '/', targetNames.Real); | |
| gg->AddToManifest(f); | |
| } | |
| if (!targetNames.PDB.empty()) { | |
| f = cmStrCat(dir, '/', targetNames.PDB); | |
| gg->AddToManifest(f); | |
| } | |
| dir = this->GetDirectory(config, cmStateEnums::ImportLibraryArtifact); | |
| if (!targetNames.ImportOutput.empty()) { | |
| f = cmStrCat(dir, '/', targetNames.ImportOutput); | |
| gg->AddToManifest(f); | |
| } | |
| if (!targetNames.ImportLibrary.empty()) { | |
| f = cmStrCat(dir, '/', targetNames.ImportLibrary); | |
| gg->AddToManifest(f); | |
| } | |
| if (!targetNames.ImportReal.empty()) { | |
| f = cmStrCat(dir, '/', targetNames.ImportReal); | |
| gg->AddToManifest(f); | |
| } | |
| } | |
| cm::optional<cmStandardLevel> cmGeneratorTarget::GetExplicitStandardLevel( | |
| std::string const& lang, std::string const& config) const | |
| { | |
| cm::optional<cmStandardLevel> level; | |
| std::string key = cmStrCat(cmSystemTools::UpperCase(config), '-', lang); | |
| auto i = this->ExplicitStandardLevel.find(key); | |
| if (i != this->ExplicitStandardLevel.end()) { | |
| level = i->second; | |
| } | |
| return level; | |
| } | |
| void cmGeneratorTarget::UpdateExplicitStandardLevel(std::string const& lang, | |
| std::string const& config, | |
| cmStandardLevel level) | |
| { | |
| auto e = this->ExplicitStandardLevel.emplace( | |
| cmStrCat(cmSystemTools::UpperCase(config), '-', lang), level); | |
| if (!e.second && e.first->second < level) { | |
| e.first->second = level; | |
| } | |
| } | |
| bool cmGeneratorTarget::ComputeCompileFeatures(std::string const& config) | |
| { | |
| cmStandardLevelResolver standardResolver(this->Makefile); | |
| for (std::string const& lang : | |
| this->Makefile->GetState()->GetEnabledLanguages()) { | |
| if (cmValue languageStd = this->GetLanguageStandard(lang, config)) { | |
| if (cm::optional<cmStandardLevel> langLevel = | |
| standardResolver.LanguageStandardLevel(lang, *languageStd)) { | |
| this->UpdateExplicitStandardLevel(lang, config, *langLevel); | |
| } | |
| } | |
| } | |
| // Compute the language standard based on the compile features. | |
| std::vector<BT<std::string>> features = this->GetCompileFeatures(config); | |
| for (BT<std::string> const& f : features) { | |
| std::string lang; | |
| if (!standardResolver.CompileFeatureKnown(this->Target->GetName(), f.Value, | |
| lang, nullptr)) { | |
| return false; | |
| } | |
| std::string key = cmStrCat(cmSystemTools::UpperCase(config), '-', lang); | |
| cmValue currentLanguageStandard = this->GetLanguageStandard(lang, config); | |
| cm::optional<cmStandardLevel> featureLevel; | |
| std::string newRequiredStandard; | |
| if (!standardResolver.GetNewRequiredStandard( | |
| this->Target->GetName(), f.Value, currentLanguageStandard, | |
| featureLevel, newRequiredStandard)) { | |
| return false; | |
| } | |
| if (featureLevel) { | |
| this->UpdateExplicitStandardLevel(lang, config, *featureLevel); | |
| } | |
| if (!newRequiredStandard.empty()) { | |
| BTs<std::string>& languageStandardProperty = | |
| this->LanguageStandardMap[key]; | |
| if (languageStandardProperty.Value != newRequiredStandard) { | |
| languageStandardProperty.Value = newRequiredStandard; | |
| languageStandardProperty.Backtraces.clear(); | |
| } | |
| languageStandardProperty.Backtraces.emplace_back(f.Backtrace); | |
| } | |
| } | |
| return true; | |
| } | |
| bool cmGeneratorTarget::ComputeCompileFeatures( | |
| std::string const& config, std::set<LanguagePair> const& languagePairs) | |
| { | |
| cmStandardLevelResolver standardResolver(this->Makefile); | |
| for (auto const& language : languagePairs) { | |
| if (language.first == "SYCL") { | |
| if (!this->IsSYCLAppendMode(language.first)) { | |
| continue; | |
| } | |
| std::string key = | |
| cmStrCat(cmSystemTools::UpperCase(config), '-', language.first); | |
| BTs<std::string> const* currentStandard = nullptr; | |
| auto current = this->LanguageStandardMap.find(key); | |
| if (current != this->LanguageStandardMap.end()) { | |
| currentStandard = ¤t->second; | |
| } else { | |
| currentStandard = this->Target->GetLanguageStandardProperty( | |
| cmStandardLevelResolver::GetStandardPropertyName(language.first)); | |
| } | |
| BTs<std::string> const* pairedStandard = | |
| this->GetLanguageStandardProperty(language.second, config); | |
| BTs<std::string> defaultStandard; | |
| if (!pairedStandard && !currentStandard) { | |
| if (cmValue value = this->Makefile->GetDefinition( | |
| cmStrCat("CMAKE_", language.second, "_STANDARD_DEFAULT"))) { | |
| defaultStandard = BTs<std::string>(*value); | |
| pairedStandard = &defaultStandard; | |
| } | |
| if (cmValue value = | |
| this->Makefile->GetDefinition("CMAKE_SYCL_STANDARD_DEFAULT")) { | |
| if (!pairedStandard || | |
| standardResolver.IsLaterStandard(language.first, *value, | |
| pairedStandard->Value)) { | |
| defaultStandard = BTs<std::string>(*value); | |
| pairedStandard = &defaultStandard; | |
| } | |
| } | |
| } | |
| if (pairedStandard && | |
| (!currentStandard || | |
| standardResolver.IsLaterStandard( | |
| language.first, pairedStandard->Value, currentStandard->Value))) { | |
| this->LanguageStandardMap[key] = *pairedStandard; | |
| } | |
| continue; | |
| } | |
| BTs<std::string> const* generatorTargetLanguageStandard = | |
| this->GetLanguageStandardProperty(language.first, config); | |
| if (!generatorTargetLanguageStandard) { | |
| // If the standard isn't explicitly set we copy it over from the | |
| // specified paired language. | |
| std::string key = | |
| cmStrCat(cmSystemTools::UpperCase(config), '-', language.first); | |
| BTs<std::string> const* standardToCopy = | |
| this->GetLanguageStandardProperty(language.second, config); | |
| if (standardToCopy) { | |
| this->LanguageStandardMap[key] = *standardToCopy; | |
| generatorTargetLanguageStandard = &this->LanguageStandardMap[key]; | |
| } else { | |
| cmValue defaultStandard = this->Makefile->GetDefinition( | |
| cmStrCat("CMAKE_", language.second, "_STANDARD_DEFAULT")); | |
| if (defaultStandard) { | |
| this->LanguageStandardMap[key] = BTs<std::string>(*defaultStandard); | |
| generatorTargetLanguageStandard = &this->LanguageStandardMap[key]; | |
| } | |
| } | |
| // Custom updates for the CUDA standard. | |
| if (generatorTargetLanguageStandard && (language.first == "CUDA")) { | |
| if (generatorTargetLanguageStandard->Value == "98") { | |
| this->LanguageStandardMap[key].Value = "03"; | |
| } | |
| } | |
| } | |
| } | |
| 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(std::string const& 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( | |
| std::string const& config, cmStateEnums::ArtifactType artifact, | |
| bool realname) const | |
| { | |
| std::string fpath = cmStrCat(this->GetDirectory(config, artifact), '/'); | |
| if (this->IsAppBundleOnApple()) { | |
| fpath = | |
| cmStrCat(this->BuildBundleDirectory(fpath, config, FullLevel), '/'); | |
| } | |
| // 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: | |
| if (realname) { | |
| fpath += | |
| this->NormalGetRealName(config, cmStateEnums::ImportLibraryArtifact); | |
| } else { | |
| fpath += | |
| this->GetFullName(config, cmStateEnums::ImportLibraryArtifact); | |
| } | |
| break; | |
| } | |
| return fpath; | |
| } | |
| std::string cmGeneratorTarget::NormalGetRealName( | |
| std::string const& config, cmStateEnums::ArtifactType artifact) 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 = cmStrCat("NormalGetRealName called on imported target: ", | |
| this->GetName()); | |
| this->LocalGenerator->IssueMessage(MessageType::INTERNAL_ERROR, msg); | |
| } | |
| Names names = this->GetType() == cm::TargetType::EXECUTABLE | |
| ? this->GetExecutableNames(config) | |
| : this->GetLibraryNames(config); | |
| // Compute the real name that will be built. | |
| return artifact == cmStateEnums::RuntimeBinaryArtifact ? names.Real | |
| : names.ImportReal; | |
| } | |
| cmGeneratorTarget::Names cmGeneratorTarget::GetLibraryNames( | |
| std::string const& config) const | |
| { | |
| cmGeneratorTarget::Names targetNames; | |
| // 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 = | |
| cmStrCat("GetLibraryNames called on imported target: ", this->GetName()); | |
| this->LocalGenerator->IssueMessage(MessageType::INTERNAL_ERROR, msg); | |
| } | |
| // Check for library version properties. | |
| cmValue version = this->GetProperty("VERSION"); | |
| cmValue 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. | |
| NameComponents const& components = this->GetFullNameInternalComponents( | |
| config, cmStateEnums::RuntimeBinaryArtifact); | |
| // The library name. | |
| targetNames.Base = components.base; | |
| targetNames.Output = | |
| cmStrCat(components.prefix, targetNames.Base, components.suffix); | |
| if (this->IsFrameworkOnApple()) { | |
| targetNames.Real = components.prefix; | |
| if (!this->Makefile->PlatformIsAppleEmbedded()) { | |
| targetNames.Real = cmStrCat(std::move(targetNames.Real), "Versions/", | |
| this->GetFrameworkVersion(), '/'); | |
| } | |
| targetNames.Real = cmStrCat(std::move(targetNames.Real), targetNames.Base, | |
| components.suffix); | |
| targetNames.SharedObject = targetNames.Real; | |
| } else if (this->IsArchivedAIXSharedLibrary()) { | |
| targetNames.SharedObject = | |
| cmStrCat(components.prefix, targetNames.Base, ".so"); | |
| if (soversion) { | |
| targetNames.SharedObject = | |
| cmStrCat(std::move(targetNames.SharedObject), '.', *soversion); | |
| } | |
| targetNames.Real = targetNames.Output; | |
| } else { | |
| // The library's soname. | |
| targetNames.SharedObject = this->ComputeVersionedName( | |
| components.prefix, targetNames.Base, components.suffix, | |
| targetNames.Output, soversion); | |
| // The library's real name on disk. | |
| targetNames.Real = this->ComputeVersionedName( | |
| components.prefix, targetNames.Base, components.suffix, | |
| targetNames.Output, version); | |
| } | |
| // The import library names. | |
| if (this->GetType() == cm::TargetType::SHARED_LIBRARY || | |
| this->GetType() == cm::TargetType::MODULE_LIBRARY) { | |
| NameComponents const& importComponents = | |
| this->GetFullNameInternalComponents(config, | |
| cmStateEnums::ImportLibraryArtifact); | |
| targetNames.ImportOutput = cmStrCat( | |
| importComponents.prefix, importComponents.base, importComponents.suffix); | |
| if (this->IsFrameworkOnApple() && this->IsSharedLibraryWithExports()) { | |
| targetNames.ImportReal = components.prefix; | |
| if (!this->Makefile->PlatformIsAppleEmbedded()) { | |
| targetNames.ImportReal = | |
| cmStrCat(std::move(targetNames.ImportReal), "Versions/", | |
| this->GetFrameworkVersion(), '/'); | |
| } | |
| targetNames.ImportReal = | |
| cmStrCat(std::move(targetNames.ImportReal), importComponents.base, | |
| importComponents.suffix); | |
| targetNames.ImportLibrary = targetNames.ImportOutput; | |
| } else { | |
| // The import library's soname. | |
| targetNames.ImportLibrary = this->ComputeVersionedName( | |
| importComponents.prefix, importComponents.base, | |
| importComponents.suffix, targetNames.ImportOutput, soversion); | |
| // The import library's real name on disk. | |
| targetNames.ImportReal = this->ComputeVersionedName( | |
| importComponents.prefix, importComponents.base, | |
| importComponents.suffix, targetNames.ImportOutput, version); | |
| } | |
| } | |
| // The program database file name. | |
| targetNames.PDB = this->GetPDBName(config); | |
| return targetNames; | |
| } | |
| cmGeneratorTarget::Names cmGeneratorTarget::GetExecutableNames( | |
| std::string const& config) const | |
| { | |
| cmGeneratorTarget::Names targetNames; | |
| // 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 = cmStrCat( | |
| "GetExecutableNames called on imported target: ", 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__) | |
| cmValue version; | |
| #else | |
| // Check for executable version properties. | |
| cmValue version = this->GetProperty("VERSION"); | |
| if (this->GetType() != cm::TargetType::EXECUTABLE || | |
| this->Makefile->IsOn("XCODE")) { | |
| version = nullptr; | |
| } | |
| #endif | |
| // Get the components of the executable name. | |
| NameComponents const& components = this->GetFullNameInternalComponents( | |
| config, cmStateEnums::RuntimeBinaryArtifact); | |
| // The executable name. | |
| targetNames.Base = components.base; | |
| if (this->IsArchivedAIXSharedLibrary()) { | |
| targetNames.Output = components.prefix + targetNames.Base; | |
| } else { | |
| targetNames.Output = | |
| cmStrCat(components.prefix, targetNames.Base, components.suffix); | |
| } | |
| // The executable's real name on disk. | |
| #if defined(__CYGWIN__) | |
| targetNames.Real = components.prefix + targetNames.Base; | |
| #else | |
| targetNames.Real = targetNames.Output; | |
| #endif | |
| if (version) { | |
| targetNames.Real = cmStrCat(std::move(targetNames.Real), '-', *version); | |
| } | |
| #if defined(__CYGWIN__) | |
| targetNames.Real += components.suffix; | |
| #endif | |
| // The import library name. | |
| targetNames.ImportLibrary = | |
| this->GetFullNameInternal(config, cmStateEnums::ImportLibraryArtifact); | |
| targetNames.ImportReal = targetNames.ImportLibrary; | |
| targetNames.ImportOutput = targetNames.ImportLibrary; | |
| // The program database file name. | |
| targetNames.PDB = this->GetPDBName(config); | |
| return targetNames; | |
| } | |
| std::string cmGeneratorTarget::GetFullNameInternal( | |
| std::string const& config, cmStateEnums::ArtifactType artifact) const | |
| { | |
| NameComponents const& components = | |
| this->GetFullNameInternalComponents(config, artifact); | |
| return cmStrCat(components.prefix, components.base, components.suffix); | |
| } | |
| std::string cmGeneratorTarget::ImportedGetLocation( | |
| std::string const& config) const | |
| { | |
| assert(this->IsImported()); | |
| return this->Target->ImportedGetFullPath( | |
| config, cmStateEnums::RuntimeBinaryArtifact); | |
| } | |
| std::string cmGeneratorTarget::GetFullNameImported( | |
| std::string const& config, cmStateEnums::ArtifactType artifact) const | |
| { | |
| return cmSystemTools::GetFilenameName( | |
| this->Target->ImportedGetFullPath(config, artifact)); | |
| } | |
| cmGeneratorTarget::NameComponents const& | |
| cmGeneratorTarget::GetFullNameInternalComponents( | |
| std::string const& config, cmStateEnums::ArtifactType artifact) const | |
| { | |
| assert(artifact == cmStateEnums::RuntimeBinaryArtifact || | |
| artifact == cmStateEnums::ImportLibraryArtifact); | |
| FullNameCache& cache = artifact == cmStateEnums::RuntimeBinaryArtifact | |
| ? RuntimeBinaryFullNameCache | |
| : ImportLibraryFullNameCache; | |
| auto search = cache.find(config); | |
| if (search != cache.end()) { | |
| return search->second; | |
| } | |
| // Use just the target name for non-main target types. | |
| if (this->GetType() != cm::TargetType::STATIC_LIBRARY && | |
| this->GetType() != cm::TargetType::SHARED_LIBRARY && | |
| this->GetType() != cm::TargetType::MODULE_LIBRARY && | |
| this->GetType() != cm::TargetType::EXECUTABLE) { | |
| NameComponents components; | |
| components.base = this->GetName(); | |
| return cache.emplace(config, std::move(components)).first->second; | |
| } | |
| bool const isImportedLibraryArtifact = | |
| (artifact == cmStateEnums::ImportLibraryArtifact); | |
| // Return an empty name for the import library if this platform | |
| // does not support import libraries. | |
| if (isImportedLibraryArtifact && !this->NeedImportLibraryName(config)) { | |
| return cache.emplace(config, NameComponents()).first->second; | |
| } | |
| NameComponents parts; | |
| std::string& outPrefix = parts.prefix; | |
| std::string& outBase = parts.base; | |
| std::string& outSuffix = parts.suffix; | |
| // retrieve prefix and suffix | |
| std::string ll = this->GetLinkerLanguage(config); | |
| cmValue targetPrefix = this->GetFilePrefixInternal(config, artifact, ll); | |
| cmValue targetSuffix = this->GetFileSuffixInternal(config, artifact, ll); | |
| // The implib option is only allowed for shared libraries, module | |
| // libraries, and executables. | |
| if (this->GetType() != cm::TargetType::SHARED_LIBRARY && | |
| this->GetType() != cm::TargetType::MODULE_LIBRARY && | |
| this->GetType() != cm::TargetType::EXECUTABLE) { | |
| artifact = cmStateEnums::RuntimeBinaryArtifact; | |
| } | |
| // Compute the full name for main target types. | |
| std::string configPostfix = this->GetFilePostfix(config); | |
| // frameworks have directory prefix | |
| std::string fw_prefix; | |
| if (this->IsFrameworkOnApple()) { | |
| fw_prefix = | |
| cmStrCat(this->GetFrameworkDirectory(config, ContentLevel), '/'); | |
| targetPrefix = cmValue(fw_prefix); | |
| if (!isImportedLibraryArtifact) { | |
| // no suffix | |
| targetSuffix = nullptr; | |
| } | |
| } | |
| if (this->IsCFBundleOnApple()) { | |
| fw_prefix = cmStrCat(this->GetCFBundleDirectory(config, FullLevel), '/'); | |
| targetPrefix = cmValue(fw_prefix); | |
| 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. | |
| // When using Xcode, the postfix should be part of the suffix rather than | |
| // the base, because the suffix ends up being used in Xcode's | |
| // EXECUTABLE_SUFFIX attribute. | |
| if (this->IsFrameworkOnApple() && this->GetGlobalGenerator()->IsXcode()) { | |
| configPostfix += *targetSuffix; | |
| targetSuffix = cmValue(configPostfix); | |
| } else { | |
| outBase += configPostfix; | |
| } | |
| // Name shared libraries with their version number on some platforms. | |
| if (cmValue soversion = this->GetProperty("SOVERSION")) { | |
| cmValue dllProp; | |
| if (this->IsDLLPlatform()) { | |
| dllProp = this->GetProperty("DLL_NAME_WITH_SOVERSION"); | |
| } | |
| if (this->GetType() == cm::TargetType::SHARED_LIBRARY && | |
| !isImportedLibraryArtifact && | |
| (dllProp.IsOn() || | |
| (!dllProp.IsSet() && | |
| this->Makefile->IsOn("CMAKE_SHARED_LIBRARY_NAME_WITH_VERSION")))) { | |
| outBase = cmStrCat(std::move(outBase), '-', *soversion); | |
| } | |
| } | |
| // Append the suffix. | |
| outSuffix = targetSuffix ? *targetSuffix : ""; | |
| return cache.emplace(config, std::move(parts)).first->second; | |
| } | |
| std::string cmGeneratorTarget::GetLinkerLanguage( | |
| std::string const& config) const | |
| { | |
| return this->GetLinkClosure(config)->LinkerLanguage; | |
| } | |
| std::string cmGeneratorTarget::GetLinkerTool(std::string const& config) const | |
| { | |
| return this->GetLinkerTool(this->GetLinkerLanguage(config), config); | |
| } | |
| std::string cmGeneratorTarget::GetLinkerTool(std::string const& lang, | |
| std::string const& config) const | |
| { | |
| auto linkMode = cmStrCat( | |
| "CMAKE_", lang, this->IsDeviceLink() ? "_DEVICE_" : "_", "LINK_MODE"); | |
| auto mode = this->Makefile->GetDefinition(linkMode); | |
| if (!mode || mode != "LINKER"_s) { | |
| // On the Visual Studio generators the project links via the platform | |
| // toolset, so languages without a LINKER-mode toolchain (e.g. ASM_NASM) | |
| // must not force a linker; defer to the toolset's default. | |
| if (this->GetGlobalGenerator()->IsVisualStudio()) { | |
| return std::string{}; | |
| } | |
| return this->Makefile->GetDefinition("CMAKE_LINKER"); | |
| } | |
| auto linkerType = this->GetLinkerTypeProperty(lang, config); | |
| if (linkerType.empty()) { | |
| linkerType = "DEFAULT"; | |
| } | |
| auto usingLinker = | |
| cmStrCat("CMAKE_", lang, "_USING_", this->IsDeviceLink() ? "DEVICE_" : "", | |
| "LINKER_", linkerType); | |
| auto linkerTool = this->Makefile->GetDefinition(usingLinker); | |
| if (!linkerTool) { | |
| if (this->GetGlobalGenerator()->IsVisualStudio() && | |
| linkerType == "DEFAULT"_s) { | |
| return std::string{}; | |
| } | |
| // fall-back to generic definition | |
| linkerTool = this->Makefile->GetDefinition("CMAKE_LINKER"); | |
| if (linkerType != "DEFAULT"_s) { | |
| auto isCMakeLinkerType = [](std::string const& type) -> bool { | |
| return std::all_of(type.cbegin(), type.cend(), cmsysString_isupper); | |
| }; | |
| if (isCMakeLinkerType(linkerType)) { | |
| this->LocalGenerator->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| cmStrCat("LINKER_TYPE '", linkerType, | |
| "' is unknown or not supported by this toolchain.")); | |
| } else { | |
| this->LocalGenerator->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| cmStrCat("LINKER_TYPE '", linkerType, | |
| "' is unknown. Did you forget to define the '", usingLinker, | |
| "' variable?")); | |
| } | |
| } | |
| } | |
| return linkerTool; | |
| } | |
| bool cmGeneratorTarget::LinkerEnforcesNoAllowShLibUndefined( | |
| std::string const& config) const | |
| { | |
| // FIXME(#25486): Account for the LINKER_TYPE target property. | |
| // Also factor out the hard-coded list below into a platform | |
| // information table based on the linker id. | |
| std::string ll = this->GetLinkerLanguage(config); | |
| std::string linkerIdVar = cmStrCat("CMAKE_", ll, "_COMPILER_LINKER_ID"); | |
| cmValue linkerId = this->Makefile->GetDefinition(linkerIdVar); | |
| // The GNU bfd-based linker may enforce '--no-allow-shlib-undefined' | |
| // recursively by default. The Solaris linker has similar behavior. | |
| return linkerId && (*linkerId == "GNU" || *linkerId == "Solaris"); | |
| } | |
| std::string cmGeneratorTarget::GetPDBOutputName( | |
| std::string const& config) const | |
| { | |
| // Lookup/compute/cache the pdb output name for this configuration. | |
| auto i = this->PdbOutputNameMap.find(config); | |
| if (i == this->PdbOutputNameMap.end()) { | |
| // Add empty name in map to detect potential recursion. | |
| PdbOutputNameMapType::value_type entry(config, ""); | |
| i = this->PdbOutputNameMap.insert(entry).first; | |
| // Compute output name. | |
| 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"); | |
| std::string outName; | |
| for (std::string const& p : props) { | |
| if (cmValue outNameProp = this->GetProperty(p)) { | |
| outName = *outNameProp; | |
| break; | |
| } | |
| } | |
| // Now evaluate genex and update the previously-prepared map entry. | |
| if (outName.empty()) { | |
| i->second = cmStrCat( | |
| this->GetOutputName(config, cmStateEnums::RuntimeBinaryArtifact), | |
| this->GetPolicyStatusCMP0202() != cmPolicies::NEW | |
| ? this->GetFilePostfix(config) | |
| : ""); | |
| } else { | |
| i->second = | |
| cmGeneratorExpression::Evaluate(outName, 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, | |
| cmStrCat("Target '", this->GetName(), "' PDB_NAME depends on itself."), | |
| this->GetBacktrace()); | |
| } | |
| return i->second; | |
| } | |
| std::string cmGeneratorTarget::GetPDBName(std::string const& config) const | |
| { | |
| NameComponents const& parts = this->GetFullNameInternalComponents( | |
| config, cmStateEnums::RuntimeBinaryArtifact); | |
| std::string base = this->GetPDBOutputName(config); | |
| return cmStrCat(parts.prefix, base, | |
| this->GetPolicyStatusCMP0202() == cmPolicies::NEW | |
| ? this->GetFilePostfix(config) | |
| : "", | |
| ".pdb"); | |
| } | |
| std::string cmGeneratorTarget::GetObjectDirectory( | |
| std::string const& config) const | |
| { | |
| std::string obj_dir = | |
| this->GlobalGenerator->ExpandCFGIntDir(this->ObjectDirectory, config); | |
| #if defined(__APPLE__) | |
| // 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 | |
| { | |
| this->GetTargetObjectNames( | |
| config, [](cmSourceFile const&) -> bool { return true; }, objects); | |
| } | |
| void cmGeneratorTarget::GetTargetObjectNames( | |
| std::string const& config, std::function<bool(cmSourceFile const&)> filter, | |
| std::vector<std::string>& objects) const | |
| { | |
| this->GetTargetObjectLocations( | |
| config, filter, | |
| [&objects](cmObjectLocation const& buildLoc, cmObjectLocation const&) { | |
| objects.push_back(buildLoc.GetPath()); | |
| }); | |
| } | |
| void cmGeneratorTarget::GetTargetObjectLocations( | |
| std::string const& config, | |
| std::function<void(cmObjectLocation const&, cmObjectLocation const&)> cb) | |
| const | |
| { | |
| this->GetTargetObjectLocations( | |
| config, [](cmSourceFile const&) -> bool { return true; }, cb); | |
| } | |
| void cmGeneratorTarget::GetTargetObjectLocations( | |
| std::string const& config, std::function<bool(cmSourceFile const&)> filter, | |
| std::function<void(cmObjectLocation const&, cmObjectLocation const&)> cb) | |
| const | |
| { | |
| std::vector<cmSourceFile const*> objectSources; | |
| this->GetObjectSources(objectSources, config); | |
| std::map<cmSourceFile const*, cmObjectLocations> mapping; | |
| for (cmSourceFile const* sf : objectSources) { | |
| mapping[sf]; | |
| } | |
| this->LocalGenerator->ComputeObjectFilenames(mapping, config, this); | |
| auto const buildUseShortPaths = this->GetUseShortObjectNames() | |
| ? cmObjectLocations::UseShortPath::Yes | |
| : cmObjectLocations::UseShortPath::No; | |
| auto const installUseShortPaths = this->GetUseShortObjectNamesForInstall(); | |
| for (cmSourceFile const* src : objectSources) { | |
| if (filter(*src)) { | |
| // Find the object file name corresponding to this source file. | |
| auto map_it = mapping.find(src); | |
| auto const& buildLoc = map_it->second.GetLocation(buildUseShortPaths); | |
| auto const& installLoc = | |
| map_it->second.GetInstallLocation(installUseShortPaths, config); | |
| // It must exist because we populated the mapping just above. | |
| assert(!buildLoc.GetPath().empty()); | |
| assert(!installLoc.GetPath().empty()); | |
| cb(buildLoc, installLoc); | |
| } | |
| } | |
| // We need to compute the relative path from the root of | |
| // of the object directory to handle subdirectory paths | |
| std::string rootObjectDir = this->GetObjectDirectory(config); | |
| rootObjectDir = cmSystemTools::CollapseFullPath(rootObjectDir); | |
| auto ispcObjects = this->GetGeneratedISPCObjects(config); | |
| for (auto const& output : ispcObjects) { | |
| if (filter(*output.first)) { | |
| auto relativePathFromObjectDir = | |
| output.second.substr(rootObjectDir.size()); | |
| cmObjectLocation ispcLoc(relativePathFromObjectDir); | |
| // FIXME: apply short path to this object if needed. | |
| cb(ispcLoc, ispcLoc); | |
| } | |
| } | |
| } | |
| 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(cmSourceFile const* sf) const | |
| { | |
| struct SourceFileFlags flags; | |
| this->ConstructSourceFileFlags(); | |
| auto 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 (cmValue location = sf->GetProperty("MACOSX_PACKAGE_LOCATION")) { | |
| flags.MacFolder = location->c_str(); | |
| bool const stripResources = | |
| this->GlobalGenerator->ShouldStripResourcePath(this->Makefile); | |
| if (*location == "Resources") { | |
| flags.Type = cmGeneratorTarget::SourceFileTypeResource; | |
| if (stripResources) { | |
| flags.MacFolder = ""; | |
| } | |
| } else if (cmHasLiteralPrefix(*location, "Resources/")) { | |
| flags.Type = cmGeneratorTarget::SourceFileTypeDeepResource; | |
| if (stripResources) { | |
| flags.MacFolder += cmStrLen("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 (cmValue files = this->GetProperty("PUBLIC_HEADER")) { | |
| cmList relFiles{ *files }; | |
| for (auto 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 (cmValue files = this->GetProperty("PRIVATE_HEADER")) { | |
| cmList relFiles{ *files }; | |
| for (auto 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 (cmValue files = this->GetProperty("RESOURCE")) { | |
| cmList relFiles{ *files }; | |
| for (auto 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; | |
| } | |
| } | |
| } | |
| } | |
| bool cmGeneratorTarget::SetDeviceLink(bool deviceLink) | |
| { | |
| bool previous = this->DeviceLink; | |
| this->DeviceLink = deviceLink; | |
| return previous; | |
| } | |
| void cmGeneratorTarget::GetTargetVersion(int& major, int& minor) const | |
| { | |
| int patch; | |
| this->GetTargetVersion("VERSION", major, minor, patch); | |
| } | |
| void cmGeneratorTarget::GetTargetVersionFallback( | |
| std::string const& property, std::string const& fallback_property, | |
| int& major, int& minor, int& patch) const | |
| { | |
| if (this->GetProperty(property)) { | |
| this->GetTargetVersion(property, major, minor, patch); | |
| } else { | |
| this->GetTargetVersion(fallback_property, major, minor, patch); | |
| } | |
| } | |
| void cmGeneratorTarget::GetTargetVersion(std::string const& property, | |
| int& major, int& minor, | |
| int& patch) const | |
| { | |
| // Set the default values. | |
| major = 0; | |
| minor = 0; | |
| patch = 0; | |
| assert(this->GetType() != cm::TargetType::INTERFACE_LIBRARY); | |
| if (cmValue version = this->GetProperty(property)) { | |
| // 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->c_str(), "%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::GetRuntimeLinkLibrary( | |
| std::string const& lang, std::string const& config) const | |
| { | |
| // This is activated by the presence of a default selection whether or | |
| // not it is overridden by a property. | |
| cmValue runtimeLibraryDefault = this->Makefile->GetDefinition( | |
| cmStrCat("CMAKE_", lang, "_RUNTIME_LIBRARY_DEFAULT")); | |
| if (!cmNonempty(runtimeLibraryDefault)) { | |
| return std::string(); | |
| } | |
| cmValue runtimeLibraryValue = | |
| this->Target->GetProperty(cmStrCat(lang, "_RUNTIME_LIBRARY")); | |
| if (!runtimeLibraryValue) { | |
| runtimeLibraryValue = runtimeLibraryDefault; | |
| } | |
| return cmSystemTools::UpperCase(cmGeneratorExpression::Evaluate( | |
| *runtimeLibraryValue, this->LocalGenerator, config, this)); | |
| } | |
| 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; | |
| } | |
| bool cmGeneratorTarget::IsFortranBuildingIntrinsicModules() const | |
| { | |
| // ATTENTION Before 4.0 the property name was misspelled. | |
| // Check the correct name first and than the old name. | |
| if (cmValue prop = this->GetProperty("Fortran_BUILDING_INTRINSIC_MODULES")) { | |
| return prop.IsOn(); | |
| } | |
| if (cmValue prop = | |
| this->GetProperty("Fortran_BUILDING_INSTRINSIC_MODULES")) { | |
| return prop.IsOn(); | |
| } | |
| return false; | |
| } | |
| std::string cmGeneratorTarget::CreateFortranModuleDirectory( | |
| std::string const& working_dir) const | |
| { | |
| std::string mod_dir; | |
| std::string target_mod_dir; | |
| if (cmValue 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; | |
| } | |
| } | |
| cmValue 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 = cmStrCat(this->LocalGenerator->GetCurrentBinaryDirectory(), | |
| '/', target_mod_dir); | |
| } | |
| // Make sure the module output directory exists. | |
| cmSystemTools::MakeDirectory(mod_dir); | |
| } | |
| return mod_dir; | |
| } | |
| void cmGeneratorTarget::AddISPCGeneratedHeader(std::string const& header, | |
| std::string const& config) | |
| { | |
| std::string config_upper; | |
| if (!config.empty()) { | |
| config_upper = cmSystemTools::UpperCase(config); | |
| } | |
| auto iter = this->ISPCGeneratedHeaders.find(config_upper); | |
| if (iter == this->ISPCGeneratedHeaders.end()) { | |
| std::vector<std::string> headers; | |
| headers.emplace_back(header); | |
| this->ISPCGeneratedHeaders.insert({ config_upper, headers }); | |
| } else { | |
| iter->second.emplace_back(header); | |
| } | |
| } | |
| std::vector<std::string> cmGeneratorTarget::GetGeneratedISPCHeaders( | |
| std::string const& config) const | |
| { | |
| std::string config_upper; | |
| if (!config.empty()) { | |
| config_upper = cmSystemTools::UpperCase(config); | |
| } | |
| auto iter = this->ISPCGeneratedHeaders.find(config_upper); | |
| if (iter == this->ISPCGeneratedHeaders.end()) { | |
| return std::vector<std::string>{}; | |
| } | |
| return iter->second; | |
| } | |
| void cmGeneratorTarget::AddISPCGeneratedObject( | |
| std::vector<std::pair<cmSourceFile const*, std::string>>&& objs, | |
| std::string const& config) | |
| { | |
| std::string config_upper; | |
| if (!config.empty()) { | |
| config_upper = cmSystemTools::UpperCase(config); | |
| } | |
| auto iter = this->ISPCGeneratedObjects.find(config_upper); | |
| if (iter == this->ISPCGeneratedObjects.end()) { | |
| this->ISPCGeneratedObjects.insert({ config_upper, objs }); | |
| } else { | |
| iter->second.insert(iter->second.end(), objs.begin(), objs.end()); | |
| } | |
| } | |
| std::vector<std::pair<cmSourceFile const*, std::string>> | |
| cmGeneratorTarget::GetGeneratedISPCObjects(std::string const& config) const | |
| { | |
| std::string config_upper; | |
| if (!config.empty()) { | |
| config_upper = cmSystemTools::UpperCase(config); | |
| } | |
| auto iter = this->ISPCGeneratedObjects.find(config_upper); | |
| if (iter == this->ISPCGeneratedObjects.end()) { | |
| return std::vector<std::pair<cmSourceFile const*, std::string>>{}; | |
| } | |
| return iter->second; | |
| } | |
| std::string cmGeneratorTarget::GetFrameworkVersion() const | |
| { | |
| assert(this->GetType() != cm::TargetType::INTERFACE_LIBRARY); | |
| if (cmValue fversion = this->GetProperty("FRAMEWORK_VERSION")) { | |
| return *fversion; | |
| } | |
| if (cmValue tversion = this->GetProperty("VERSION")) { | |
| return *tversion; | |
| } | |
| return "A"; | |
| } | |
| std::string cmGeneratorTarget::ComputeVersionedName(std::string const& prefix, | |
| std::string const& base, | |
| std::string const& suffix, | |
| std::string const& name, | |
| cmValue version) const | |
| { | |
| std::string vName = this->IsApple() ? (prefix + base) : name; | |
| if (version) { | |
| vName = cmStrCat(std::move(vName), '.', *version); | |
| } | |
| if (this->IsApple()) { | |
| vName += suffix; | |
| } | |
| return vName; | |
| } | |
| std::vector<std::string> cmGeneratorTarget::GetPropertyKeys() const | |
| { | |
| return this->Target->GetDirectProperties().GetKeys(); | |
| } | |
| void cmGeneratorTarget::ReportPropertyOrigin( | |
| std::string const& p, std::string const& result, std::string const& report, | |
| std::string const& compatibilityType) const | |
| { | |
| cmList debugProperties{ this->Target->GetMakefile()->GetDefinition( | |
| "CMAKE_DEBUG_TARGET_PROPERTIES") }; | |
| bool debugOrigin = !this->DebugCompatiblePropertiesDone[p] && | |
| cm::contains(debugProperties, p); | |
| this->DebugCompatiblePropertiesDone[p] = true; | |
| if (!debugOrigin) { | |
| return; | |
| } | |
| std::string areport = | |
| cmStrCat(compatibilityType, " of property \"", p, "\" for target \"", | |
| this->GetName(), "\" (result: \"", result, "\"):\n", report); | |
| this->LocalGenerator->GetCMakeInstance()->IssueMessage(MessageType::LOG, | |
| areport); | |
| } | |
| std::string cmGeneratorTarget::GetDirectory( | |
| std::string const& config, cmStateEnums::ArtifactType artifact) const | |
| { | |
| if (this->IsImported()) { | |
| auto fullPath = this->Target->ImportedGetFullPath(config, artifact); | |
| if (this->IsFrameworkOnApple()) { | |
| auto fwDescriptor = this->GetGlobalGenerator()->SplitFrameworkPath( | |
| fullPath, cmGlobalGenerator::FrameworkFormat::Strict); | |
| if (fwDescriptor) { | |
| return fwDescriptor->Directory; | |
| } | |
| } | |
| // Return the directory from which the target is imported. | |
| return cmSystemTools::GetFilenamePath(fullPath); | |
| } | |
| 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( | |
| std::string const& config, cmStateEnums::ArtifactType artifact) const | |
| { | |
| std::string dir; | |
| return this->ComputeOutputDir(config, artifact, dir); | |
| } | |
| cmGeneratorTarget::OutputInfo const* cmGeneratorTarget::GetOutputInfo( | |
| std::string const& config) const | |
| { | |
| // There is no output information for imported targets. | |
| if (this->IsImported()) { | |
| return nullptr; | |
| } | |
| // Synthetic targets don't have output. | |
| if (this->IsSynthetic()) { | |
| return nullptr; | |
| } | |
| // Only libraries and executables have well-defined output files. | |
| if (!this->HaveWellDefinedOutputFiles()) { | |
| std::string msg = cmStrCat("cmGeneratorTarget::GetOutputInfo called for ", | |
| this->GetName(), " which has type ", | |
| 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); | |
| } | |
| auto 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, | |
| cmStrCat("Target '", this->GetName(), | |
| "' OUTPUT_DIRECTORY depends on itself."), | |
| this->GetBacktrace()); | |
| return nullptr; | |
| } | |
| return &i->second; | |
| } | |
| bool cmGeneratorTarget::ComputeOutputDir(std::string const& 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); | |
| std::string propertyName; | |
| if (!targetTypeName.empty()) { | |
| propertyName = cmStrCat(targetTypeName, "_OUTPUT_DIRECTORY"); | |
| } | |
| // Check for a per-configuration output directory target property. | |
| std::string configUpper = cmSystemTools::UpperCase(conf); | |
| std::string configProp; | |
| if (!targetTypeName.empty()) { | |
| configProp = cmStrCat(targetTypeName, "_OUTPUT_DIRECTORY_", configUpper); | |
| } | |
| // Select an output directory. | |
| if (cmValue config_outdir = this->GetProperty(configProp)) { | |
| // Use the user-specified per-configuration output directory. | |
| out = cmGeneratorExpression::Evaluate(*config_outdir, this->LocalGenerator, | |
| config, this); | |
| // Skip per-configuration subdirectory. | |
| conf.clear(); | |
| } else if (cmValue outdir = this->GetProperty(propertyName)) { | |
| // Use the user-specified output directory. | |
| out = cmGeneratorExpression::Evaluate(*outdir, this->LocalGenerator, | |
| config, this); | |
| // Skip per-configuration subdirectory if the value contained a | |
| // generator expression. | |
| if (out != *outdir) { | |
| conf.clear(); | |
| } | |
| } else if (this->GetType() == cm::TargetType::EXECUTABLE) { | |
| // Lookup the output path for executables. | |
| out = this->Makefile->GetSafeDefinition("EXECUTABLE_OUTPUT_PATH"); | |
| } else if (this->GetType() == cm::TargetType::STATIC_LIBRARY || | |
| this->GetType() == cm::TargetType::SHARED_LIBRARY || | |
| this->GetType() == cm::TargetType::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(std::string const& kind, | |
| std::string const& config, | |
| std::string& out) const | |
| { | |
| // Look for a target property defining the target output directory | |
| // based on the target type. | |
| std::string propertyName; | |
| if (!kind.empty()) { | |
| propertyName = cmStrCat(kind, "_OUTPUT_DIRECTORY"); | |
| } | |
| std::string conf = config; | |
| // Check for a per-configuration output directory target property. | |
| std::string configUpper = cmSystemTools::UpperCase(conf); | |
| std::string configProp; | |
| if (!kind.empty()) { | |
| configProp = cmStrCat(kind, "_OUTPUT_DIRECTORY_", configUpper); | |
| } | |
| // Select an output directory. | |
| if (cmValue config_outdir = this->GetProperty(configProp)) { | |
| // Use the user-specified per-configuration output directory. | |
| out = cmGeneratorExpression::Evaluate(*config_outdir, this->LocalGenerator, | |
| config); | |
| // Skip per-configuration subdirectory. | |
| conf.clear(); | |
| } else if (cmValue outdir = this->GetProperty(propertyName)) { | |
| // Use the user-specified output directory. | |
| out = | |
| cmGeneratorExpression::Evaluate(*outdir, this->LocalGenerator, config); | |
| // Skip per-configuration subdirectory if the value contained a | |
| // generator expression. | |
| if (out != *outdir) { | |
| conf.clear(); | |
| } | |
| } | |
| if (out.empty()) { | |
| cmGeneratorTarget const* reuseTarget = this->GetPchReuseTarget(); | |
| bool const hasReuse = reuseTarget && reuseTarget != this; | |
| // Compiler-generated PDB output always needed for REUSE_FROM. | |
| if (kind == "COMPILE_PDB"_s && (this->PchReused || hasReuse)) { | |
| out = this->GetSupportDirectory(); | |
| } else { | |
| 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(std::string const& config) const | |
| { | |
| std::string install_rpath; | |
| this->GetInstallRPATH(config, install_rpath); | |
| return !install_rpath.empty() && | |
| !this->Makefile->IsOn("CMAKE_SKIP_INSTALL_RPATH"); | |
| } | |
| bool cmGeneratorTarget::GetBuildRPATH(std::string const& config, | |
| std::string& rpath) const | |
| { | |
| return this->GetRPATH(config, "BUILD_RPATH", rpath); | |
| } | |
| bool cmGeneratorTarget::GetInstallRPATH(std::string const& config, | |
| std::string& rpath) const | |
| { | |
| return this->GetRPATH(config, "INSTALL_RPATH", rpath); | |
| } | |
| bool cmGeneratorTarget::GetRPATH(std::string const& config, | |
| std::string const& prop, | |
| std::string& rpath) const | |
| { | |
| cmValue value = this->GetProperty(prop); | |
| if (!value) { | |
| return false; | |
| } | |
| rpath = | |
| cmGeneratorExpression::Evaluate(*value, this->LocalGenerator, config); | |
| return true; | |
| } | |
| cmGeneratorTarget::ImportInfo const* cmGeneratorTarget::GetImportInfo( | |
| std::string const& 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"; | |
| } | |
| auto 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() == cm::TargetType::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; | |
| cmValue loc = nullptr; | |
| cmValue imp = nullptr; | |
| std::string suffix; | |
| if (!this->Target->GetMappedConfig(desired_config, loc, imp, suffix)) { | |
| return; | |
| } | |
| // Get the link interface. | |
| { | |
| // Use the INTERFACE_LINK_LIBRARIES special representation directly | |
| // to get backtraces. | |
| cmBTStringRange entries = this->Target->GetLinkInterfaceEntries(); | |
| if (!entries.empty()) { | |
| info.LibrariesProp = "INTERFACE_LINK_LIBRARIES"; | |
| for (BT<std::string> const& entry : entries) { | |
| info.Libraries.emplace_back(entry); | |
| } | |
| } else if (this->GetType() != cm::TargetType::INTERFACE_LIBRARY) { | |
| std::string linkProp = | |
| cmStrCat("IMPORTED_LINK_INTERFACE_LIBRARIES", suffix); | |
| cmValue propertyLibs = this->GetProperty(linkProp); | |
| if (!propertyLibs) { | |
| linkProp = "IMPORTED_LINK_INTERFACE_LIBRARIES"; | |
| propertyLibs = this->GetProperty(linkProp); | |
| } | |
| if (propertyLibs) { | |
| info.LibrariesProp = linkProp; | |
| info.Libraries.emplace_back(*propertyLibs); | |
| } | |
| } | |
| } | |
| for (BT<std::string> const& entry : | |
| this->Target->GetLinkInterfaceDirectEntries()) { | |
| info.LibrariesHeadInclude.emplace_back(entry); | |
| } | |
| for (BT<std::string> const& entry : | |
| this->Target->GetLinkInterfaceDirectExcludeEntries()) { | |
| info.LibrariesHeadExclude.emplace_back(entry); | |
| } | |
| if (this->GetType() == cm::TargetType::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 = cmStrCat("IMPORTED_LOCATION", suffix); | |
| if (cmValue config_location = this->GetProperty(impProp)) { | |
| info.Location = *config_location; | |
| } else if (cmValue location = this->GetProperty("IMPORTED_LOCATION")) { | |
| info.Location = *location; | |
| } | |
| } | |
| // Get the soname. | |
| if (this->GetType() == cm::TargetType::SHARED_LIBRARY) { | |
| std::string soProp = cmStrCat("IMPORTED_SONAME", suffix); | |
| if (cmValue config_soname = this->GetProperty(soProp)) { | |
| info.SOName = *config_soname; | |
| } else if (cmValue soname = this->GetProperty("IMPORTED_SONAME")) { | |
| info.SOName = *soname; | |
| } | |
| } | |
| // Get the "no-soname" mark. | |
| if (this->GetType() == cm::TargetType::SHARED_LIBRARY) { | |
| std::string soProp = cmStrCat("IMPORTED_NO_SONAME", suffix); | |
| if (cmValue config_no_soname = this->GetProperty(soProp)) { | |
| info.NoSOName = config_no_soname.IsOn(); | |
| } else if (cmValue no_soname = this->GetProperty("IMPORTED_NO_SONAME")) { | |
| info.NoSOName = no_soname.IsOn(); | |
| } | |
| } | |
| // Get the import library. | |
| if (imp) { | |
| info.ImportLibrary = *imp; | |
| } else if (this->GetType() == cm::TargetType::SHARED_LIBRARY || | |
| this->IsExecutableWithExports()) { | |
| std::string impProp = cmStrCat("IMPORTED_IMPLIB", suffix); | |
| if (cmValue config_implib = this->GetProperty(impProp)) { | |
| info.ImportLibrary = *config_implib; | |
| } else if (cmValue implib = this->GetProperty("IMPORTED_IMPLIB")) { | |
| info.ImportLibrary = *implib; | |
| } | |
| } | |
| // Get the link dependencies. | |
| { | |
| std::string linkProp = | |
| cmStrCat("IMPORTED_LINK_DEPENDENT_LIBRARIES", suffix); | |
| if (cmValue config_libs = this->GetProperty(linkProp)) { | |
| info.SharedDeps = *config_libs; | |
| } else if (cmValue libs = | |
| this->GetProperty("IMPORTED_LINK_DEPENDENT_LIBRARIES")) { | |
| info.SharedDeps = *libs; | |
| } | |
| } | |
| // Get the link languages. | |
| if (this->LinkLanguagePropagatesToDependents()) { | |
| std::string linkProp = | |
| cmStrCat("IMPORTED_LINK_INTERFACE_LANGUAGES", suffix); | |
| if (cmValue config_libs = this->GetProperty(linkProp)) { | |
| info.Languages = *config_libs; | |
| } else if (cmValue 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 (cmValue pc = this->GetProperty(linkProp + suffix)) { | |
| info.Managed = this->CheckManagedType(*pc); | |
| } else if (cmValue p = this->GetProperty(linkProp)) { | |
| info.Managed = this->CheckManagedType(*p); | |
| } | |
| } | |
| // Get the cyclic repetition count. | |
| if (this->GetType() == cm::TargetType::STATIC_LIBRARY) { | |
| std::string linkProp = | |
| cmStrCat("IMPORTED_LINK_INTERFACE_MULTIPLICITY", suffix); | |
| if (cmValue config_reps = this->GetProperty(linkProp)) { | |
| sscanf(config_reps->c_str(), "%u", &info.Multiplicity); | |
| } else if (cmValue reps = | |
| this->GetProperty("IMPORTED_LINK_INTERFACE_MULTIPLICITY")) { | |
| sscanf(reps->c_str(), "%u", &info.Multiplicity); | |
| } | |
| } | |
| } | |
| bool cmGeneratorTarget::GetConfigCommonSourceFilesForXcode( | |
| std::vector<cmSourceFile*>& files) const | |
| { | |
| std::vector<std::string> const& configs = | |
| this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); | |
| auto it = configs.begin(); | |
| std::string const& 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; | |
| char const* sep = ""; | |
| for (cmSourceFile* f : files) { | |
| firstConfigFiles = | |
| cmStrCat(std::move(firstConfigFiles), sep, f->ResolveFullPath()); | |
| sep = "\n "; | |
| } | |
| std::string thisConfigFiles; | |
| sep = ""; | |
| for (cmSourceFile* f : configFiles) { | |
| thisConfigFiles = | |
| cmStrCat(std::move(thisConfigFiles), sep, f->ResolveFullPath()); | |
| sep = "\n "; | |
| } | |
| /* clang-format off */ | |
| std::string e = cmStrCat("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); | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| void cmGeneratorTarget::GetObjectLibrariesInSources( | |
| std::vector<BT<cmGeneratorTarget*>>& objlibs) const | |
| { | |
| // FIXME: This searches SOURCES for TARGET_OBJECTS for backwards | |
| // compatibility with the OLD behavior of CMP0026 since this | |
| // could be called at configure time. CMP0026 has been removed, | |
| // so this should now be called only at generate time. | |
| // Therefore we should be able to improve the implementation | |
| // with generate-time information. | |
| cmBTStringRange rng = this->Target->GetSourceEntries(); | |
| for (auto const& entry : rng) { | |
| cmList files{ entry.Value }; | |
| for (auto 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.emplace_back(objLib, entry.Backtrace); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| 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.resize(pos + 1); | |
| } | |
| if (lib != item) { | |
| cmake* cm = this->LocalGenerator->GetCMakeInstance(); | |
| cm->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| cmStrCat("Target \"", this->GetName(), "\" links to item \"", item, | |
| "\" which has leading or trailing whitespace. " | |
| "This is now an error according to policy CMP0004."), | |
| this->GetBacktrace()); | |
| } | |
| return lib; | |
| } | |
| bool cmGeneratorTarget::IsDeprecated() const | |
| { | |
| cmValue deprecation = this->GetProperty("DEPRECATION"); | |
| return cmNonempty(deprecation); | |
| } | |
| std::string cmGeneratorTarget::GetDeprecation() const | |
| { | |
| // find DEPRECATION property | |
| if (cmValue deprecation = this->GetProperty("DEPRECATION")) { | |
| return *deprecation; | |
| } | |
| return std::string(); | |
| } | |
| void cmGeneratorTarget::GetLanguages(std::set<std::string>& languages, | |
| std::string const& config) const | |
| { | |
| // Targets that do not compile anything have no languages. | |
| if (!this->CanCompileSources()) { | |
| return; | |
| } | |
| std::vector<cmSourceFile*> sourceFiles; | |
| this->GetSourceFiles(sourceFiles, config); | |
| for (cmSourceFile* src : sourceFiles) { | |
| std::string const lang = this->GetSourceFileLanguage(src, config); | |
| if (!lang.empty()) { | |
| languages.insert(lang); | |
| } | |
| } | |
| std::set<cmGeneratorTarget const*> objectLibraries = | |
| this->GetSourceObjectLibraries(config); | |
| for (cmGeneratorTarget const* objLib : objectLibraries) { | |
| objLib->GetLanguages(languages, config); | |
| } | |
| } | |
| std::set<cmGeneratorTarget const*> cmGeneratorTarget::GetSourceObjectLibraries( | |
| std::string const& config) const | |
| { | |
| std::set<cmGeneratorTarget const*> objectLibraries; | |
| std::vector<cmSourceFile const*> externalObjects; | |
| this->GetExternalObjects(externalObjects, config); | |
| for (cmSourceFile const* extObj : externalObjects) { | |
| std::string objLib = extObj->GetObjectLibrary(); | |
| if (cmGeneratorTarget* tgt = | |
| this->LocalGenerator->FindGeneratorTargetToUse(objLib)) { | |
| objectLibraries.insert(tgt); | |
| } | |
| } | |
| return objectLibraries; | |
| } | |
| bool cmGeneratorTarget::IsLanguageUsed(std::string const& language, | |
| std::string const& config) const | |
| { | |
| std::set<std::string> languages; | |
| this->GetLanguages(languages, config); | |
| return languages.count(language); | |
| } | |
| bool cmGeneratorTarget::IsCSharpOnly() const | |
| { | |
| // Only certain target types may compile CSharp. | |
| if (this->GetType() != cm::TargetType::SHARED_LIBRARY && | |
| this->GetType() != cm::TargetType::STATIC_LIBRARY && | |
| this->GetType() != cm::TargetType::EXECUTABLE) { | |
| return false; | |
| } | |
| std::set<std::string> languages = this->GetAllConfigCompileLanguages(); | |
| // Consider an explicit linker language property, but *not* the | |
| // computed linker language that may depend on linked targets. | |
| cmValue linkLang = this->GetProperty("LINKER_LANGUAGE"); | |
| if (cmNonempty(linkLang)) { | |
| languages.insert(*linkLang); | |
| } | |
| return languages.size() == 1 && languages.count("CSharp") > 0; | |
| } | |
| bool cmGeneratorTarget::IsDotNetSdkTarget() const | |
| { | |
| return !this->GetProperty("DOTNET_SDK").IsEmpty(); | |
| } | |
| void cmGeneratorTarget::ComputeLinkImplementationLanguages( | |
| std::string const& 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(std::string const& config) const | |
| { | |
| if (this->GetPropertyAsBool("SKIP_BUILD_RPATH")) { | |
| return false; | |
| } | |
| std::string build_rpath; | |
| if (this->GetBuildRPATH(config, build_rpath)) { | |
| return true; | |
| } | |
| if (cmLinkImplementationLibraries const* impl = | |
| this->GetLinkImplementationLibraries(config, UseTo::Link)) { | |
| return !impl->Libraries.empty(); | |
| } | |
| return false; | |
| } | |
| bool cmGeneratorTarget::IsNullImpliedByLinkLibraries( | |
| std::string const& p) const | |
| { | |
| return cm::contains(this->LinkImplicitNullProperties, p); | |
| } | |
| namespace { | |
| bool CreateCxxStdlibTarget(cmMakefile* makefile, cmLocalGenerator* lg, | |
| std::vector<std::string> const& configs) | |
| { | |
| #ifndef CMAKE_BOOTSTRAP | |
| static cm::optional<cmCxxModuleMetadata> metadata; | |
| // Load metadata only when we need to create a target | |
| if (!metadata) { | |
| auto errorMessage = | |
| makefile->GetDefinition("CMAKE_CXX_COMPILER_IMPORT_STD_ERROR_MESSAGE"); | |
| if (!errorMessage.IsEmpty()) { | |
| auto* stdlibTgt = makefile->AddImportedTarget( | |
| "@cmake_cxx_std", cm::TargetType::INTERFACE_LIBRARY, | |
| cm::ImportedTargetScope::Global); | |
| stdlibTgt->SetProperty("CXX_IMPORT_ERROR_MESSAGE", *errorMessage); | |
| stdlibTgt->SetProperty("CXX_IMPORT_ERROR_MODULES", "std;std.compat"); | |
| auto gt = cm::make_unique<cmGeneratorTarget>(stdlibTgt, lg); | |
| lg->AddImportedGeneratorTarget(gt.get()); | |
| lg->AddOwnedImportedGeneratorTarget(std::move(gt)); | |
| return true; | |
| } | |
| auto metadataPath = | |
| makefile->GetDefinition("CMAKE_CXX_STDLIB_MODULES_JSON"); | |
| if (metadataPath.IsEmpty()) { | |
| auto* stdlibTgt = makefile->AddImportedTarget( | |
| "@cmake_cxx_std", cm::TargetType::INTERFACE_LIBRARY, | |
| cm::ImportedTargetScope::Global); | |
| stdlibTgt->SetProperty( | |
| "CXX_IMPORT_ERROR_MESSAGE", | |
| R"(CMAKE_CXX_STDLIB_MODULES_JSON set to empty string)"); | |
| stdlibTgt->SetProperty("CXX_IMPORT_ERROR_MODULES", "std;std.compat"); | |
| auto gt = cm::make_unique<cmGeneratorTarget>(stdlibTgt, lg); | |
| lg->AddImportedGeneratorTarget(gt.get()); | |
| lg->AddOwnedImportedGeneratorTarget(std::move(gt)); | |
| return true; | |
| } | |
| auto parseResult = cmCxxModuleMetadata::LoadFromFile(*metadataPath); | |
| if (!parseResult) { | |
| makefile->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| cmStrCat("Failed to load C++ standard library modules metadata " | |
| "from \"", | |
| *metadataPath, "\": ", parseResult.Error)); | |
| return false; | |
| } | |
| metadata = std::move(*parseResult.Meta); | |
| } | |
| auto* stdlibTgt = makefile->AddImportedTarget( | |
| "@cmake_cxx_std", cm::TargetType::INTERFACE_LIBRARY, | |
| cm::ImportedTargetScope::Global); | |
| cmCxxModuleMetadata::PopulateTarget(*stdlibTgt, *metadata, configs); | |
| cmStandardLevelResolver standardResolver(makefile); | |
| standardResolver.AddRequiredTargetFeature(stdlibTgt, "cxx_std_20"); | |
| auto gt = cm::make_unique<cmGeneratorTarget>(stdlibTgt, lg); | |
| for (auto const& config : configs) { | |
| gt->ComputeCompileFeatures(config); | |
| } | |
| auto compilerId = makefile->GetSafeDefinition("CMAKE_CXX_COMPILER_ID"); | |
| if (compilerId == "MSVC") { | |
| stdlibTgt->SetCxxModuleNeedsInterfaceObjects(true); | |
| } else if (cmHasLiteralSuffix(compilerId, "Clang")) { | |
| stdlibTgt->AddCxxModuleSynthCompileOption( | |
| BT<std::string>("-Wno-reserved-module-identifier")); | |
| } | |
| lg->AddImportedGeneratorTarget(gt.get()); | |
| lg->AddOwnedImportedGeneratorTarget(std::move(gt)); | |
| #endif // CMAKE_BOOTSTRAP | |
| return true; | |
| } | |
| } // namespace | |
| bool cmGeneratorTarget::ApplyCXXStdTarget() | |
| { | |
| std::vector<std::string> const& configs = | |
| this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); | |
| bool needsStd = false; | |
| std::vector<cm::string_view> stdConfigs; | |
| if (this->Target->IsImported()) { | |
| needsStd = this->HaveInterfaceCxx20ModuleSources(); | |
| } else { | |
| // Only configurations compiling sources that need C++ module scanning | |
| // need the implicit standard library module dependency. | |
| for (std::string const& config : configs) { | |
| if (this->NeedCxxDyndep(config) != CxxModuleSupport::Enabled) { | |
| continue; | |
| } | |
| std::vector<SourceAndKind> const& sources = | |
| this->GetKindedSources(config).Sources; | |
| bool const needsStdForConfig = std::any_of( | |
| sources.begin(), sources.end(), | |
| [this, &config](SourceAndKind const& s) { | |
| if (s.Kind != SourceKindObjectSource && | |
| s.Kind != SourceKindCxxModuleSource) { | |
| return false; | |
| } | |
| cmSourceFile const* sf = s.Source.Value; | |
| std::string const lang = this->GetSourceFileLanguage(sf, config); | |
| return lang == "CXX" && this->NeedDyndepForSource(lang, config, sf); | |
| }); | |
| if (needsStdForConfig) { | |
| needsStd = true; | |
| stdConfigs.push_back(config); | |
| } | |
| } | |
| } | |
| if (!needsStd) { | |
| return true; | |
| } | |
| // Create the single, unreferenceable import std target if it doesn't | |
| // already exist. BMI compatibility handles per-consumer standard level | |
| // differences by creating synthetic targets as needed. | |
| if (!this->Makefile->FindTargetToUse("@cmake_cxx_std") && | |
| !CreateCxxStdlibTarget(this->Makefile, this->LocalGenerator, configs)) { | |
| return false; | |
| } | |
| if (this->Target->IsImported()) { | |
| this->Target->AppendProperty("IMPORTED_CXX_MODULES_LINK_LIBRARIES", | |
| "@cmake_cxx_std"); | |
| } else { | |
| std::string stdTarget = "@cmake_cxx_std"; | |
| if (stdConfigs.size() != configs.size()) { | |
| stdTarget = cmStrCat("$<$<CONFIG:", cmJoin(stdConfigs, ","), | |
| ">:", std::move(stdTarget), '>'); | |
| } | |
| this->Target->AppendProperty( | |
| "LINK_LIBRARIES", cmStrCat("$<BUILD_LOCAL_INTERFACE:", stdTarget, '>')); | |
| } | |
| return true; | |
| } | |
| bool cmGeneratorTarget::HasCxxImportModuleErrors() const | |
| { | |
| return this->Target->GetProperty("CXX_IMPORT_ERROR_MODULES") != nullptr; | |
| } | |
| cmCxxModuleUsageEffects const& cmGeneratorTarget::GetCxxModuleUsageEffects( | |
| std::string const& config) const | |
| { | |
| auto iter = this->CxxModuleUsageEffects.find(config); | |
| if (iter == this->CxxModuleUsageEffects.end()) { | |
| auto result = this->CxxModuleUsageEffects.emplace( | |
| std::pair<std::string, cmCxxModuleUsageEffects>{ | |
| config, cmCxxModuleUsageEffects(this, config) }); | |
| return result.first->second; | |
| } | |
| return iter->second; | |
| } | |
| cmGeneratorTarget const* cmGeneratorTarget::GetTargetForCxxModules( | |
| std::string const& config, cmGeneratorTarget const& bmiConsumer) const | |
| { | |
| auto const& consumingUsage = bmiConsumer.GetCxxModuleUsageEffects(config); | |
| auto const& owningUsage = this->GetCxxModuleUsageEffects(config); | |
| if (consumingUsage.GetHash() == owningUsage.GetHash()) { | |
| if (this->IsImported()) { | |
| return this->GetCxxSyntheticTarget(config, *this); | |
| } | |
| return this; | |
| } | |
| return this->GetCxxSyntheticTarget(config, bmiConsumer); | |
| } | |
| cmGeneratorTarget const* cmGeneratorTarget::GetCxxSyntheticTarget( | |
| std::string const& config, cmGeneratorTarget const& bmiConsumer) const | |
| { | |
| auto const& usageHash = | |
| bmiConsumer.GetCxxModuleUsageEffects(config).GetHash(); | |
| auto cached = this->SynthCxxTargets.find(usageHash); | |
| if (cached != this->SynthCxxTargets.end()) { | |
| return cached->second; | |
| } | |
| auto targetNameBase = this->GetName(); | |
| if (this->IsImported()) { | |
| cmSystemTools::ReplaceString(targetNameBase, "::", "__"); | |
| } | |
| auto const targetName = | |
| cmStrCat(targetNameBase, "@synth_", this->SynthCxxTargets.size()); | |
| std::vector<std::string> allConfigs = | |
| this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); | |
| auto const* model = this->Target; | |
| auto* mf = this->Makefile; | |
| auto* lg = this->GetLocalGenerator(); | |
| auto* tgt = | |
| mf->AddSynthesizedTarget(cm::TargetType::INTERFACE_LIBRARY, targetName); | |
| if (model->CxxModuleNeedsInterfaceObjects()) { | |
| tgt->SetCxxModuleNeedsInterfaceObjects(true); | |
| } | |
| // Copy relevant information from the existing target. | |
| // Copy policies to the target. | |
| tgt->CopyPolicyStatuses(model); | |
| // Copy file sets. | |
| { | |
| for (auto const* gfs : | |
| this->GetInterfaceFileSets(cm::FileSetMetadata::CXX_MODULES)) { | |
| auto* newFs = | |
| tgt | |
| ->GetOrCreateFileSet(gfs->GetName(), gfs->GetType(), | |
| cm::FileSetMetadata::Visibility::Public) | |
| .first; | |
| newFs->CopyEntries(gfs->GetFileSet()); | |
| } | |
| } | |
| // Copy properties which effect consumer compatibility. | |
| // CopyUsageEffects now uses the consumer's full (own + transitive) | |
| // compile features and options. | |
| tgt->CopyUsageEffects(&bmiConsumer, config); | |
| // These internal options belong to the provider, not the consumer, and must | |
| // survive replacement of the provider's ordinary compile options. | |
| for (auto const& option : model->GetCxxModuleSynthCompileOptions()) { | |
| tgt->InsertCompileOption(option); | |
| } | |
| auto const& preprocessorCompileOptions = | |
| this->GetCxxModuleUsageEffects(config).GetPreprocessorCompileOptions(); | |
| for (auto it = preprocessorCompileOptions.rbegin(); | |
| it != preprocessorCompileOptions.rend(); ++it) { | |
| tgt->InsertCompileOption(*it, true); | |
| } | |
| // Copy properties which don't effect consumer compatibility | |
| tgt->CopyCxxModulesEntries(model); | |
| // Copy other properties which may affect the C++ module BMI generation. | |
| tgt->CopyCxxModulesProperties(model); | |
| tgt->AddLinkLibrary(*mf, cmStrCat("$<COMPILE_ONLY:", model->GetName(), '>'), | |
| GENERAL_LibraryType); | |
| // Create the generator target and attach it to the local generator. | |
| auto gtp = cm::make_unique<cmGeneratorTarget>(tgt, lg); | |
| auto* syntheticTarget = gtp.get(); | |
| // See `localGen->ComputeTargetCompileFeatures()` call in | |
| // `cmGlobalGenerator::Compute` for where non-synthetic targets resolve | |
| // this. | |
| for (auto const& innerConfig : allConfigs) { | |
| gtp->ComputeCompileFeatures(innerConfig); | |
| } | |
| lg->AddGeneratorTarget(std::move(gtp)); | |
| this->SynthCxxTargets[usageHash] = syntheticTarget; | |
| for (auto const& innerConfig : allConfigs) { | |
| if (!syntheticTarget->DiscoverSyntheticTargets(innerConfig, | |
| &bmiConsumer)) { | |
| return nullptr; | |
| } | |
| } | |
| return syntheticTarget; | |
| } | |
| bool cmGeneratorTarget::DiscoverSyntheticTargets( | |
| std::string const& config, cmGeneratorTarget const* bmiConsumer) | |
| { | |
| auto& configInfo = this->Configs[config]; | |
| cmOptionalLinkImplementation impl; | |
| this->ComputeLinkImplementationLibraries(config, impl, UseTo::Link); | |
| if (!bmiConsumer) { | |
| bmiConsumer = this; | |
| } | |
| auto& SyntheticDeps = configInfo.SyntheticDeps; | |
| for (auto const& entry : impl.Libraries) { | |
| auto const* gt = entry.Target; | |
| if (!gt || !gt->HaveInterfaceCxx20ModuleSources()) { | |
| continue; | |
| } | |
| // Visual Studio generators do not currently support BMI-only compilation, | |
| // so they can't generate synthetic targets. For consuming native modules, | |
| // skip so that the native target is used directly. For imported modules, | |
| // create the synth target anyway and diagnose later, in the VS generator. | |
| if (!gt->IsImported() && this->GlobalGenerator->IsVisualStudio()) { | |
| continue; | |
| } | |
| auto const* dep = gt->GetTargetForCxxModules(config, *bmiConsumer); | |
| if (!dep) { | |
| return false; | |
| } | |
| if (dep->IsSynthetic()) { | |
| SyntheticDeps[gt].insert(dep); | |
| } | |
| } | |
| return true; | |
| } | |
| cmGeneratorTarget::SyntheticDepsMap const& cmGeneratorTarget::GetSyntheticDeps( | |
| std::string const& config) const | |
| { | |
| return this->Configs[config].SyntheticDeps; | |
| } | |
| bool cmGeneratorTarget::HasPackageReferences() const | |
| { | |
| return this->IsInBuildSystem() && | |
| !this->GetProperty("VS_PACKAGE_REFERENCES")->empty(); | |
| } | |
| std::vector<std::string> cmGeneratorTarget::GetPackageReferences() const | |
| { | |
| cmList packageReferences; | |
| if (this->IsInBuildSystem()) { | |
| if (cmValue vsPackageReferences = | |
| this->GetProperty("VS_PACKAGE_REFERENCES")) { | |
| packageReferences.assign(*vsPackageReferences); | |
| } | |
| } | |
| return std::move(packageReferences.data()); | |
| } | |
| std::string cmGeneratorTarget::GetPDBDirectory(std::string const& 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, | |
| char const* newExt) const | |
| { | |
| if (this->HasImplibGNUtoMS(config) && gnuName.size() > 6 && | |
| gnuName.substr(gnuName.size() - 6) == ".dll.a") { | |
| out = cmStrCat(cm::string_view(gnuName).substr(0, gnuName.size() - 6), | |
| newExt ? newExt : ".lib"); | |
| return true; | |
| } | |
| return false; | |
| } | |
| bool cmGeneratorTarget::HasContextDependentSources() const | |
| { | |
| return this->SourcesAreContextDependent == Tribool::True; | |
| } | |
| bool cmGeneratorTarget::IsExecutableWithExports() const | |
| { | |
| return this->Target->IsExecutableWithExports(); | |
| } | |
| bool cmGeneratorTarget::IsSharedLibraryWithExports() const | |
| { | |
| return this->Target->IsSharedLibraryWithExports(); | |
| } | |
| bool cmGeneratorTarget::HasImportLibrary(std::string const& config) const | |
| { | |
| bool generate_Stubs = true; | |
| if (this->GetGlobalGenerator()->IsXcode()) { | |
| // take care of CMAKE_XCODE_ATTRIBUTE_GENERATE_TEXT_BASED_STUBS variable | |
| // as well as XCODE_ATTRIBUTE_GENERATE_TEXT_BASED_STUBS property | |
| if (cmValue propGenStubs = | |
| this->GetProperty("XCODE_ATTRIBUTE_GENERATE_TEXT_BASED_STUBS")) { | |
| generate_Stubs = propGenStubs == "YES"; | |
| } else if (cmValue varGenStubs = this->Makefile->GetDefinition( | |
| "CMAKE_XCODE_ATTRIBUTE_GENERATE_TEXT_BASED_STUBS")) { | |
| generate_Stubs = varGenStubs == "YES"; | |
| } | |
| } | |
| return (this->IsDLLPlatform() && | |
| (this->GetType() == cm::TargetType::SHARED_LIBRARY || | |
| this->IsExecutableWithExports()) && | |
| // Assemblies which have only managed code do not have | |
| // import libraries. | |
| this->GetManagedType(config) != ManagedType::Managed) || | |
| (this->IsAIX() && this->IsExecutableWithExports()) || | |
| (this->Makefile->PlatformSupportsAppleTextStubs() && | |
| this->IsSharedLibraryWithExports() && generate_Stubs); | |
| } | |
| bool cmGeneratorTarget::NeedImportLibraryName(std::string const& config) const | |
| { | |
| return this->HasImportLibrary(config) || | |
| // On DLL platforms we always generate the import library name | |
| // just in case the sources have export markup. | |
| (this->IsDLLPlatform() && | |
| (this->GetType() == cm::TargetType::EXECUTABLE || | |
| this->GetType() == cm::TargetType::MODULE_LIBRARY)); | |
| } | |
| bool cmGeneratorTarget::GetUseShortObjectNames( | |
| cmStateEnums::IntermediateDirKind kind) const | |
| { | |
| return this->LocalGenerator->UseShortObjectNames(kind); | |
| } | |
| cmObjectLocations::UseShortPath | |
| cmGeneratorTarget::GetUseShortObjectNamesForInstall() const | |
| { | |
| auto prop = this->Target->GetProperty("INSTALL_OBJECT_NAME_STRATEGY"); | |
| if (prop == "SHORT"_s) { | |
| return cmObjectLocations::UseShortPath::Yes; | |
| } | |
| if (prop == "FULL"_s) { | |
| return cmObjectLocations::UseShortPath::No; | |
| } | |
| if (prop.IsSet()) { | |
| this->Makefile->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| cmStrCat("Property INSTALL_OBJECT_NAME_STRATEGY of target \"", | |
| this->GetName(), "\" set to the unsupported strategy ", prop)); | |
| } | |
| return cmObjectLocations::UseShortPath::No; | |
| } | |
| std::string cmGeneratorTarget::GetSupportDirectory( | |
| cmStateEnums::IntermediateDirKind kind) const | |
| { | |
| cmLocalGenerator* lg = this->GetLocalGenerator(); | |
| auto targetDir = cmStrCat(lg->GetObjectOutputRoot(kind), '/', | |
| lg->GetTargetDirectory(this, kind)); | |
| #ifndef CMAKE_BOOTSTRAP | |
| auto& tdr = | |
| this->GetGlobalGenerator()->RegisterTargetDirectory(this, targetDir); | |
| if (tdr.CollidesWith && !tdr.Warned) { | |
| this->Makefile->IssueMessage( | |
| MessageType::WARNING, | |
| cmStrCat("The '", tdr.CollidesWith->GetName(), "' and '", | |
| this->GetName(), | |
| "' targets share an intermediate directory\n ", targetDir, | |
| "\nwhich may cause problems with the build graph. This project " | |
| "is not compatible with the `SHORT` target intermediate " | |
| "directory strategy. Possible remedies include: moving the " | |
| "target into different directories or renaming a target.")); | |
| tdr.Warned = true; | |
| } | |
| #endif | |
| return targetDir; | |
| } | |
| std::string cmGeneratorTarget::GetCMFSupportDirectory( | |
| cmStateEnums::IntermediateDirKind kind) const | |
| { | |
| cmLocalGenerator* lg = this->GetLocalGenerator(); | |
| if (!lg->AlwaysUsesCMFPaths()) { | |
| return cmStrCat(lg->GetCurrentBinaryDirectory(), "/CMakeFiles/", | |
| lg->GetTargetDirectory(this, kind)); | |
| } | |
| return cmStrCat(lg->GetObjectOutputRoot(kind), '/', | |
| lg->GetTargetDirectory(this, kind)); | |
| } | |
| bool cmGeneratorTarget::IsLinkable() const | |
| { | |
| return (this->GetType() == cm::TargetType::STATIC_LIBRARY || | |
| this->GetType() == cm::TargetType::SHARED_LIBRARY || | |
| this->GetType() == cm::TargetType::MODULE_LIBRARY || | |
| this->GetType() == cm::TargetType::UNKNOWN_LIBRARY || | |
| this->GetType() == cm::TargetType::OBJECT_LIBRARY || | |
| this->GetType() == cm::TargetType::INTERFACE_LIBRARY || | |
| this->IsExecutableWithExports()); | |
| } | |
| bool cmGeneratorTarget::HasLinkDependencyFile(std::string const& config) const | |
| { | |
| if (this->GetType() != cm::TargetType::EXECUTABLE && | |
| this->GetType() != cm::TargetType::SHARED_LIBRARY && | |
| this->GetType() != cm::TargetType::MODULE_LIBRARY) { | |
| return false; | |
| } | |
| if (this->Target->GetProperty("LINK_DEPENDS_NO_SHARED").IsOn()) { | |
| // Do not use the linker dependency file because it includes shared | |
| // libraries as well | |
| return false; | |
| } | |
| std::string const depsUseLinker{ "CMAKE_LINK_DEPENDS_USE_LINKER" }; | |
| auto linkLanguage = this->GetLinkerLanguage(config); | |
| std::string const langDepsUseLinker{ cmStrCat("CMAKE_", linkLanguage, | |
| "_LINK_DEPENDS_USE_LINKER") }; | |
| return (!this->Makefile->IsDefinitionSet(depsUseLinker) || | |
| this->Makefile->IsOn(depsUseLinker)) && | |
| this->Makefile->IsOn(langDepsUseLinker); | |
| } | |
| bool cmGeneratorTarget::IsFrameworkOnApple() const | |
| { | |
| return this->Target->IsFrameworkOnApple(); | |
| } | |
| bool cmGeneratorTarget::IsArchivedAIXSharedLibrary() const | |
| { | |
| return this->Target->IsArchivedAIXSharedLibrary(); | |
| } | |
| bool cmGeneratorTarget::IsImportedFrameworkFolderOnApple( | |
| std::string const& config) const | |
| { | |
| if (this->IsApple() && this->IsImported() && | |
| (this->GetType() == cm::TargetType::STATIC_LIBRARY || | |
| this->GetType() == cm::TargetType::SHARED_LIBRARY || | |
| this->GetType() == cm::TargetType::UNKNOWN_LIBRARY)) { | |
| std::string cfg = config; | |
| if (cfg.empty() && this->GetGlobalGenerator()->IsXcode()) { | |
| // FIXME(#25515): Remove the need for this workaround. | |
| // The Xcode generator queries include directories without any | |
| // specific configuration. Pick one in case this target does | |
| // not set either IMPORTED_LOCATION or IMPORTED_CONFIGURATIONS. | |
| cfg = | |
| this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig)[0]; | |
| } | |
| return cmSystemTools::IsPathToFramework(this->GetLocation(cfg)); | |
| } | |
| return false; | |
| } | |
| bool cmGeneratorTarget::IsAppBundleOnApple() const | |
| { | |
| return this->Target->IsAppBundleOnApple(); | |
| } | |
| bool cmGeneratorTarget::IsXCTestOnApple() const | |
| { | |
| return (this->IsCFBundleOnApple() && this->GetPropertyAsBool("XCTEST")); | |
| } | |
| bool cmGeneratorTarget::IsCFBundleOnApple() const | |
| { | |
| return (this->GetType() == cm::TargetType::MODULE_LIBRARY && | |
| this->IsApple() && 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. netcore propval: add /clr:netcore as flag, mixed | |
| // unmanaged/managed target, has import lib. | |
| // 4. any value (safe,pure): add /clr:[propval] as flag, target with | |
| // managed code only, no import lib | |
| if (propval.empty() || propval == "netcore") { | |
| return ManagedType::Mixed; | |
| } | |
| return ManagedType::Managed; | |
| } | |
| cmGeneratorTarget::ManagedType cmGeneratorTarget::GetManagedType( | |
| std::string const& config) const | |
| { | |
| // Only libraries and executables can be managed targets. | |
| if (this->GetType() > cm::TargetType::SHARED_LIBRARY) { | |
| return ManagedType::Undefined; | |
| } | |
| if (this->GetType() == cm::TargetType::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 (cmValue 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; | |
| } | |
| std::string cmGeneratorTarget::GetImportedXcFrameworkPath( | |
| std::string const& config) const | |
| { | |
| if (!(this->IsApple() && this->IsImported() && | |
| (this->GetType() == cm::TargetType::SHARED_LIBRARY || | |
| this->GetType() == cm::TargetType::STATIC_LIBRARY || | |
| this->GetType() == cm::TargetType::UNKNOWN_LIBRARY))) { | |
| return {}; | |
| } | |
| std::string desiredConfig = config; | |
| if (config.empty()) { | |
| desiredConfig = "NOCONFIG"; | |
| } | |
| std::string result; | |
| cmValue loc = nullptr; | |
| cmValue imp = nullptr; | |
| std::string suffix; | |
| if (this->Target->GetMappedConfig(desiredConfig, loc, imp, suffix)) { | |
| if (loc) { | |
| result = *loc; | |
| } else { | |
| std::string impProp = cmStrCat("IMPORTED_LOCATION", suffix); | |
| if (cmValue configLocation = this->GetProperty(impProp)) { | |
| result = *configLocation; | |
| } else if (cmValue location = this->GetProperty("IMPORTED_LOCATION")) { | |
| result = *location; | |
| } | |
| } | |
| if (cmSystemTools::IsPathToXcFramework(result)) { | |
| return result; | |
| } | |
| } | |
| return {}; | |
| } | |
| bool cmGeneratorTarget::HaveFortranSources(std::string const& config) const | |
| { | |
| auto sources = this->GetSourceFiles(config); | |
| bool const have_direct = std::any_of( | |
| sources.begin(), sources.end(), | |
| [this, &config](BT<cmSourceFile*> const& sf) -> bool { | |
| return this->GetSourceFileLanguage(sf.Value, config) == "Fortran"_s; | |
| }); | |
| bool have_via_target_objects = false; | |
| if (!have_direct) { | |
| auto const sourceObjectLibraries = this->GetSourceObjectLibraries(config); | |
| have_via_target_objects = | |
| std::any_of(sourceObjectLibraries.begin(), sourceObjectLibraries.end(), | |
| [&config](cmGeneratorTarget const* tgt) -> bool { | |
| return tgt->HaveFortranSources(config); | |
| }); | |
| } | |
| return have_direct || have_via_target_objects; | |
| } | |
| bool cmGeneratorTarget::HaveFortranSources() const | |
| { | |
| bool const have_direct = | |
| this->GetAllConfigCompileLanguages().count("Fortran") != 0; | |
| bool have_via_target_objects = false; | |
| if (!have_direct) { | |
| std::vector<std::string> configs = | |
| this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); | |
| for (auto const& config : configs) { | |
| auto const sourceObjectLibraries = | |
| this->GetSourceObjectLibraries(config); | |
| have_via_target_objects = | |
| std::any_of(sourceObjectLibraries.begin(), sourceObjectLibraries.end(), | |
| [&config](cmGeneratorTarget const* tgt) -> bool { | |
| return tgt->HaveFortranSources(config); | |
| }); | |
| if (have_via_target_objects) { | |
| break; | |
| } | |
| } | |
| } | |
| return have_direct || have_via_target_objects; | |
| } | |
| bool cmGeneratorTarget::HaveInterfaceCxx20ModuleSources() const | |
| { | |
| return !this->GetInterfaceFileSets(cm::FileSetMetadata::CXX_MODULES).empty(); | |
| } | |
| bool cmGeneratorTarget::HaveCxx20ModuleSources() const | |
| { | |
| return !this->GetFileSets(cm::FileSetMetadata::CXX_MODULES).empty() || | |
| !this->GetInterfaceFileSets(cm::FileSetMetadata::CXX_MODULES).empty(); | |
| } | |
| cmGeneratorTarget::Cxx20SupportLevel cmGeneratorTarget::HaveCxxModuleSupport( | |
| std::string const& config) const | |
| { | |
| auto const* state = this->Makefile->GetState(); | |
| if (!state->GetLanguageEnabled("CXX")) { | |
| return Cxx20SupportLevel::MissingCxx; | |
| } | |
| cmValue standardDefault = | |
| this->Makefile->GetDefinition("CMAKE_CXX_STANDARD_DEFAULT"); | |
| if (!standardDefault || standardDefault->empty()) { | |
| // We do not know any meaningful C++ standard levels for this compiler. | |
| return Cxx20SupportLevel::NoCxx20; | |
| } | |
| cmStandardLevelResolver standardResolver(this->Makefile); | |
| cmStandardLevel const cxxStd20 = | |
| *standardResolver.LanguageStandardLevel("CXX", "20"); | |
| cm::optional<cmStandardLevel> explicitLevel = | |
| this->GetExplicitStandardLevel("CXX", config); | |
| if (!explicitLevel || *explicitLevel < cxxStd20) { | |
| return Cxx20SupportLevel::NoCxx20; | |
| } | |
| cmValue scandepRule = | |
| this->Makefile->GetDefinition("CMAKE_CXX_SCANDEP_SOURCE"); | |
| if (!scandepRule) { | |
| return Cxx20SupportLevel::MissingRule; | |
| } | |
| return Cxx20SupportLevel::Supported; | |
| } | |
| void cmGeneratorTarget::CheckCxxModuleStatus(std::string const& config) const | |
| { | |
| bool haveScannableSources = false; | |
| // Check for `CXX_MODULE*` file sets and a lack of support. | |
| if (this->HaveCxx20ModuleSources()) { | |
| haveScannableSources = true; | |
| } | |
| if (!haveScannableSources) { | |
| // Check to see if there are regular sources that have requested scanning. | |
| auto sources = this->GetSourceFiles(config); | |
| for (auto const& source : sources) { | |
| auto const* sf = source.Value; | |
| auto const lang = this->GetSourceFileLanguage(sf, config); | |
| if (lang != "CXX"_s) { | |
| continue; | |
| } | |
| // Ignore sources which do not need dyndep. | |
| if (this->NeedDyndepForSource(lang, config, sf)) { | |
| haveScannableSources = true; | |
| } | |
| } | |
| } | |
| // If there isn't anything scannable, ignore it. | |
| if (!haveScannableSources) { | |
| return; | |
| } | |
| // If the generator doesn't support modules at all, error that we have | |
| // sources that require the support. | |
| if (!this->GetGlobalGenerator()->CheckCxxModuleSupport( | |
| cmGlobalGenerator::CxxModuleSupportQuery::Expected)) { | |
| this->Makefile->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| cmStrCat("The target named \"", this->GetName(), | |
| "\" has C++ sources that may use modules, but modules are not " | |
| "supported by this generator:\n ", | |
| this->GetGlobalGenerator()->GetName(), | |
| "\n" | |
| "Modules are supported only by Ninja, Ninja Multi-Config, " | |
| "and Visual Studio generators for VS 17.4 and newer. " | |
| "See the cmake-cxxmodules(7) manual for details. " | |
| "Use the CMAKE_CXX_SCAN_FOR_MODULES variable to enable or " | |
| "disable scanning.")); | |
| return; | |
| } | |
| switch (this->HaveCxxModuleSupport(config)) { | |
| case cmGeneratorTarget::Cxx20SupportLevel::MissingCxx: | |
| this->Makefile->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| cmStrCat("The target named \"", this->GetName(), | |
| "\" has C++ sources that use modules, but the \"CXX\" " | |
| "language has not been enabled.")); | |
| break; | |
| case cmGeneratorTarget::Cxx20SupportLevel::NoCxx20: { | |
| cmStandardLevelResolver standardResolver(this->Makefile); | |
| auto effStandard = | |
| standardResolver.GetEffectiveStandard(this, "CXX", config); | |
| if (effStandard.empty()) { | |
| effStandard = "; no C++ standard found"; | |
| } else { | |
| effStandard = | |
| cmStrCat("; found \"cxx_std_", std::move(effStandard), '"'); | |
| } | |
| this->Makefile->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| cmStrCat( | |
| "The target named \"", this->GetName(), | |
| "\" has C++ sources that use modules, but does not include " | |
| "\"cxx_std_20\" (or newer) among its `target_compile_features`", | |
| effStandard, '.')); | |
| } break; | |
| case cmGeneratorTarget::Cxx20SupportLevel::MissingRule: { | |
| this->Makefile->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| cmStrCat("The target named \"", this->GetName(), | |
| "\" has C++ sources that may use modules, but the compiler " | |
| "does not provide a way to discover the import graph " | |
| "dependencies. See the cmake-cxxmodules(7) manual for " | |
| "details. Use the CMAKE_CXX_SCAN_FOR_MODULES variable to " | |
| "enable or disable scanning.")); | |
| } break; | |
| case cmGeneratorTarget::Cxx20SupportLevel::Supported: | |
| // All is well. | |
| break; | |
| } | |
| } | |
| bool cmGeneratorTarget::NeedCxxModuleSupport(std::string const& lang, | |
| std::string const& config) const | |
| { | |
| if (lang != "CXX"_s) { | |
| return false; | |
| } | |
| return this->HaveCxxModuleSupport(config) == Cxx20SupportLevel::Supported && | |
| this->GetGlobalGenerator()->CheckCxxModuleSupport( | |
| cmGlobalGenerator::CxxModuleSupportQuery::Inspect); | |
| } | |
| bool cmGeneratorTarget::NeedDyndep(std::string const& lang, | |
| std::string const& config) const | |
| { | |
| return lang == "Fortran"_s || this->NeedCxxModuleSupport(lang, config); | |
| } | |
| bool cmGeneratorTarget::NeedDyndepForSource(std::string const& lang, | |
| std::string const& config, | |
| cmSourceFile const* sf) const | |
| { | |
| // Fortran always needs to be scanned. | |
| if (lang == "Fortran"_s) { | |
| return true; | |
| } | |
| // Only C++ code needs scanned otherwise. | |
| if (lang != "CXX"_s) { | |
| return false; | |
| } | |
| // Any file in `CXX_MODULES` file sets need scanned (it being `CXX` is | |
| // enforced elsewhere). | |
| auto const* fs = this->GetFileSetForSource(config, sf); | |
| if (fs && fs->GetType() == cm::FileSetMetadata::CXX_MODULES) { | |
| return true; | |
| } | |
| auto targetDyndep = this->NeedCxxDyndep(config); | |
| if (targetDyndep == CxxModuleSupport::Unavailable) { | |
| return false; | |
| } | |
| if (fs) { | |
| auto const fsProp = fs->GetProperty("CXX_SCAN_FOR_MODULES"); | |
| if (fsProp.IsSet()) { | |
| return fsProp.IsOn(); | |
| } | |
| } | |
| auto const sfProp = sf->GetProperty("CXX_SCAN_FOR_MODULES"); | |
| if (sfProp.IsSet()) { | |
| return sfProp.IsOn(); | |
| } | |
| return targetDyndep == CxxModuleSupport::Enabled; | |
| } | |
| cmGeneratorTarget::CxxModuleSupport cmGeneratorTarget::NeedCxxDyndep( | |
| std::string const& config) const | |
| { | |
| bool haveRule = false; | |
| switch (this->HaveCxxModuleSupport(config)) { | |
| case Cxx20SupportLevel::MissingCxx: | |
| case Cxx20SupportLevel::NoCxx20: | |
| return CxxModuleSupport::Unavailable; | |
| case Cxx20SupportLevel::MissingRule: | |
| break; | |
| case Cxx20SupportLevel::Supported: | |
| haveRule = true; | |
| break; | |
| } | |
| bool haveGeneratorSupport = | |
| this->GetGlobalGenerator()->CheckCxxModuleSupport( | |
| cmGlobalGenerator::CxxModuleSupportQuery::Inspect); | |
| auto const tgtProp = this->GetProperty("CXX_SCAN_FOR_MODULES"); | |
| if (tgtProp.IsSet()) { | |
| return tgtProp.IsOn() ? CxxModuleSupport::Enabled | |
| : CxxModuleSupport::Disabled; | |
| } | |
| CxxModuleSupport policyAnswer = CxxModuleSupport::Unavailable; | |
| switch (this->GetPolicyStatusCMP0155()) { | |
| case cmPolicies::WARN: | |
| case cmPolicies::OLD: | |
| // The OLD behavior is to not scan the source. | |
| policyAnswer = CxxModuleSupport::Disabled; | |
| break; | |
| case cmPolicies::NEW: | |
| // The NEW behavior is to scan the source if the compiler supports | |
| // scanning and the generator supports it. | |
| if (haveRule && haveGeneratorSupport) { | |
| policyAnswer = CxxModuleSupport::Enabled; | |
| } else { | |
| policyAnswer = CxxModuleSupport::Disabled; | |
| } | |
| break; | |
| } | |
| return policyAnswer; | |
| } | |
| bool cmGeneratorTarget::HasFileSets() const | |
| { | |
| return !this->FileSets->Empty(); | |
| } | |
| std::vector<cmGeneratorFileSet const*> const& | |
| cmGeneratorTarget::GetAllFileSets() const | |
| { | |
| return this->FileSets->GetAllFileSets(); | |
| } | |
| std::vector<cmGeneratorFileSet const*> const& cmGeneratorTarget::GetFileSets( | |
| cm::string_view type) const | |
| { | |
| return this->FileSets->GetFileSets(type); | |
| } | |
| std::vector<cmGeneratorFileSet const*> const& | |
| cmGeneratorTarget::GetInterfaceFileSets(cm::string_view type) const | |
| { | |
| return this->FileSets->GetInterfaceFileSets(type); | |
| } | |
| cmGeneratorFileSet const* cmGeneratorTarget::GetFileSet( | |
| std::string const& name) const | |
| { | |
| return this->FileSets->GetFileSet(name); | |
| } | |
| cmGeneratorFileSet const* cmGeneratorTarget::GetFileSetForSource( | |
| std::string const& config, cmSourceFile const* sf) const | |
| { | |
| return this->FileSets->GetFileSetForSource(config, sf); | |
| } | |
| std::string cmGeneratorTarget::GetSourceFileLanguage( | |
| cmSourceFile const* source, std::string const& config) const | |
| { | |
| std::string language = source->GetLanguage(); | |
| if (auto const overrideLanguage = | |
| this->FileSets->GetLanguageForSource(config, source)) { | |
| language = *overrideLanguage; | |
| } | |
| return language == "CXX"_s && this->FileSets->HasSyclHeaders(config) | |
| ? "SYCL" | |
| : language; | |
| } | |
| std::string cmGeneratorTarget::BuildDatabasePath( | |
| std::string const& lang, std::string const& config) const | |
| { | |
| // Check to see if the target wants it. | |
| if (!this->GetPropertyAsBool("EXPORT_BUILD_DATABASE")) { | |
| return {}; | |
| } | |
| if (!cmExperimental::HasSupportEnabled( | |
| *this->Makefile, cmExperimental::Feature::ExportBuildDatabase)) { | |
| return {}; | |
| } | |
| // Check to see if the generator supports it. | |
| if (!this->GetGlobalGenerator()->SupportsBuildDatabase()) { | |
| return {}; | |
| } | |
| if (this->GetGlobalGenerator()->IsMultiConfig()) { | |
| return cmStrCat(this->GetSupportDirectory(), '/', config, '/', lang, | |
| "_build_database.json"); | |
| } | |
| return cmStrCat(this->GetSupportDirectory(), '/', lang, | |
| "_build_database.json"); | |
| } | |
| std::string cmGeneratorTarget::GetSwiftPackageName() const | |
| { | |
| std::string packageName; | |
| if (cmValue projectName = this->GetProperty("Swift_PACKAGE_NAME")) { | |
| packageName = *projectName; | |
| } else if (this->GetPolicyStatusCMP0216() == cmPolicies::NEW) { | |
| packageName = this->Makefile->GetSafeDefinition("PROJECT_NAME"); | |
| } | |
| return packageName; | |
| } | |
| std::string cmGeneratorTarget::GetSwiftModuleName() const | |
| { | |
| if (cmValue name = this->GetProperty("Swift_MODULE_NAME")) { | |
| return *name; | |
| } | |
| // Hyphens are not valid in Swift module identifiers. | |
| std::string name = this->GetName(); | |
| std::replace(name.begin(), name.end(), '-', '_'); | |
| return name; | |
| } | |
| std::string cmGeneratorTarget::GetSwiftModuleFileName() const | |
| { | |
| std::string moduleFilename = this->GetPropertyOrDefault( | |
| "Swift_MODULE", this->GetSwiftModuleName() + ".swiftmodule"); | |
| if (this->GetPolicyStatusCMP0195() == cmPolicies::NEW) { | |
| if (cmValue moduleTriple = | |
| this->Makefile->GetDefinition("CMAKE_Swift_MODULE_TRIPLE")) { | |
| moduleFilename = cmStrCat(std::move(moduleFilename), '/', *moduleTriple, | |
| ".swiftmodule"); | |
| } | |
| } | |
| return moduleFilename; | |
| } | |
| std::string cmGeneratorTarget::GetSwiftModuleDirectory( | |
| std::string const& config) const | |
| { | |
| // This is like the *_OUTPUT_DIRECTORY properties except that we don't have a | |
| // separate per-configuration target property. | |
| // | |
| // The property expands generator expressions. Multi-config generators append | |
| // a per-configuration subdirectory to the specified directory unless a | |
| // generator expression is used. | |
| bool appendConfigDir = true; | |
| std::string moduleDirectory; | |
| if (cmValue value = this->GetProperty("Swift_MODULE_DIRECTORY")) { | |
| moduleDirectory = cmGeneratorExpression::Evaluate( | |
| *value, this->LocalGenerator, config, this); | |
| appendConfigDir = *value == moduleDirectory; | |
| } | |
| if (moduleDirectory.empty()) { | |
| moduleDirectory = this->LocalGenerator->GetCurrentBinaryDirectory(); | |
| } | |
| if (appendConfigDir) { | |
| this->LocalGenerator->GetGlobalGenerator()->AppendDirectoryForConfig( | |
| "/", config, "", moduleDirectory); | |
| } | |
| return moduleDirectory; | |
| } | |
| std::string cmGeneratorTarget::GetSwiftModulePath( | |
| std::string const& config) const | |
| { | |
| return cmStrCat(this->GetSwiftModuleDirectory(config), '/', | |
| this->GetSwiftModuleFileName()); | |
| } | |
| std::string cmGeneratorTarget::GetPropertyOrDefault( | |
| std::string const& property, std::string defaultValue) const | |
| { | |
| if (cmValue name = this->GetProperty(property)) { | |
| return *name; | |
| } | |
| return defaultValue; | |
| } |