1 /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
2 file Copyright.txt or https://cmake.org/licensing for details. */
3 #include "cmGeneratorTarget.h"
15 #include <unordered_set>
19 #include <cm/optional>
20 #include <cm/string_view>
21 #include <cmext/algorithm>
22 #include <cmext/string_view>
24 #include "cmsys/RegularExpression.hxx"
26 #include "cmAlgorithms.h"
27 #include "cmComputeLinkInformation.h"
28 #include "cmCustomCommandGenerator.h"
29 #include "cmFileSet.h"
30 #include "cmFileTimes.h"
31 #include "cmGeneratedFileStream.h"
32 #include "cmGeneratorExpression.h"
33 #include "cmGeneratorExpressionContext.h"
34 #include "cmGeneratorExpressionDAGChecker.h"
35 #include "cmGeneratorExpressionNode.h"
36 #include "cmGlobalGenerator.h"
37 #include "cmLocalGenerator.h"
38 #include "cmMakefile.h"
39 #include "cmMessageType.h"
40 #include "cmOutputConverter.h"
41 #include "cmPropertyMap.h"
43 #include "cmSourceFile.h"
44 #include "cmSourceFileLocation.h"
45 #include "cmSourceFileLocationKind.h"
46 #include "cmSourceGroup.h"
47 #include "cmStandardLevelResolver.h"
49 #include "cmStringAlgorithms.h"
50 #include "cmSystemTools.h"
52 #include "cmTargetLinkLibraryType.h"
53 #include "cmTargetPropertyComputer.h"
57 using LinkInterfaceFor = cmGeneratorTarget::LinkInterfaceFor;
59 const cmsys::RegularExpression FrameworkRegularExpression(
60 "^(.*/)?([^/]*)\\.framework/(.*)$");
61 const std::string kINTERFACE_LINK_LIBRARIES = "INTERFACE_LINK_LIBRARIES";
62 const std::string kINTERFACE_LINK_LIBRARIES_DIRECT =
63 "INTERFACE_LINK_LIBRARIES_DIRECT";
64 const std::string kINTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE =
65 "INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE";
69 cmValue cmTargetPropertyComputer::GetSources<cmGeneratorTarget>(
70 cmGeneratorTarget const* tgt, cmMakefile const& /* mf */)
72 return tgt->GetSourcesProperty();
77 cmTargetPropertyComputer::ComputeLocationForBuild<cmGeneratorTarget>(
78 cmGeneratorTarget const* tgt)
80 return tgt->GetLocation("");
85 cmTargetPropertyComputer::ComputeLocation<cmGeneratorTarget>(
86 cmGeneratorTarget const* tgt, const std::string& config)
88 return tgt->GetLocation(config);
91 class cmGeneratorTarget::TargetPropertyEntry
94 static cmLinkImplItem NoLinkImplItem;
97 TargetPropertyEntry(cmLinkImplItem const& item)
101 virtual ~TargetPropertyEntry() = default;
103 virtual const std::string& Evaluate(
104 cmLocalGenerator* lg, const std::string& config,
105 cmGeneratorTarget const* headTarget,
106 cmGeneratorExpressionDAGChecker* dagChecker,
107 std::string const& language) const = 0;
109 virtual cmListFileBacktrace GetBacktrace() const = 0;
110 virtual std::string const& GetInput() const = 0;
111 virtual bool GetHadContextSensitiveCondition() const { return false; }
113 cmLinkImplItem const& LinkImplItem;
115 cmLinkImplItem cmGeneratorTarget::TargetPropertyEntry::NoLinkImplItem;
117 class TargetPropertyEntryGenex : public cmGeneratorTarget::TargetPropertyEntry
120 TargetPropertyEntryGenex(std::unique_ptr<cmCompiledGeneratorExpression> cge,
121 cmLinkImplItem const& item = NoLinkImplItem)
122 : cmGeneratorTarget::TargetPropertyEntry(item)
127 const std::string& Evaluate(cmLocalGenerator* lg, const std::string& config,
128 cmGeneratorTarget const* headTarget,
129 cmGeneratorExpressionDAGChecker* dagChecker,
130 std::string const& language) const override
132 return this->ge->Evaluate(lg, config, headTarget, dagChecker, nullptr,
136 cmListFileBacktrace GetBacktrace() const override
138 return this->ge->GetBacktrace();
141 std::string const& GetInput() const override { return this->ge->GetInput(); }
143 bool GetHadContextSensitiveCondition() const override
145 return this->ge->GetHadContextSensitiveCondition();
149 const std::unique_ptr<cmCompiledGeneratorExpression> ge;
152 class TargetPropertyEntryString : public cmGeneratorTarget::TargetPropertyEntry
155 TargetPropertyEntryString(BT<std::string> propertyValue,
156 cmLinkImplItem const& item = NoLinkImplItem)
157 : cmGeneratorTarget::TargetPropertyEntry(item)
158 , PropertyValue(std::move(propertyValue))
162 const std::string& Evaluate(cmLocalGenerator*, const std::string&,
163 cmGeneratorTarget const*,
164 cmGeneratorExpressionDAGChecker*,
165 std::string const&) const override
167 return this->PropertyValue.Value;
170 cmListFileBacktrace GetBacktrace() const override
172 return this->PropertyValue.Backtrace;
174 std::string const& GetInput() const override
176 return this->PropertyValue.Value;
180 BT<std::string> PropertyValue;
183 class TargetPropertyEntryFileSet
184 : public cmGeneratorTarget::TargetPropertyEntry
187 TargetPropertyEntryFileSet(
188 std::vector<std::string> dirs, bool contextSensitiveDirs,
189 std::unique_ptr<cmCompiledGeneratorExpression> entryCge,
190 const cmFileSet* fileSet, cmLinkImplItem const& item = NoLinkImplItem)
191 : cmGeneratorTarget::TargetPropertyEntry(item)
192 , BaseDirs(std::move(dirs))
193 , ContextSensitiveDirs(contextSensitiveDirs)
194 , EntryCge(std::move(entryCge))
199 const std::string& Evaluate(cmLocalGenerator* lg, const std::string& config,
200 cmGeneratorTarget const* headTarget,
201 cmGeneratorExpressionDAGChecker* dagChecker,
202 std::string const& /*lang*/) const override
204 std::map<std::string, std::vector<std::string>> filesPerDir;
205 this->FileSet->EvaluateFileEntry(this->BaseDirs, filesPerDir,
206 this->EntryCge, lg, config, headTarget,
209 std::vector<std::string> files;
210 for (auto const& it : filesPerDir) {
211 files.insert(files.end(), it.second.begin(), it.second.end());
214 static std::string filesStr;
215 filesStr = cmJoin(files, ";");
219 cmListFileBacktrace GetBacktrace() const override
221 return this->EntryCge->GetBacktrace();
224 std::string const& GetInput() const override
226 return this->EntryCge->GetInput();
229 bool GetHadContextSensitiveCondition() const override
231 return this->ContextSensitiveDirs ||
232 this->EntryCge->GetHadContextSensitiveCondition();
236 const std::vector<std::string> BaseDirs;
237 const bool ContextSensitiveDirs;
238 const std::unique_ptr<cmCompiledGeneratorExpression> EntryCge;
239 const cmFileSet* FileSet;
244 TargetPropertyEntry> static CreateTargetPropertyEntry(const BT<std::
248 evaluateForBuildsystem =
251 if (cmGeneratorExpression::Find(propertyValue.Value) != std::string::npos) {
252 cmGeneratorExpression ge(propertyValue.Backtrace);
253 std::unique_ptr<cmCompiledGeneratorExpression> cge =
254 ge.Parse(propertyValue.Value);
255 cge->SetEvaluateForBuildsystem(evaluateForBuildsystem);
256 return std::unique_ptr<cmGeneratorTarget::TargetPropertyEntry>(
257 cm::make_unique<TargetPropertyEntryGenex>(std::move(cge)));
260 return std::unique_ptr<cmGeneratorTarget::TargetPropertyEntry>(
261 cm::make_unique<TargetPropertyEntryString>(propertyValue));
264 static void CreatePropertyGeneratorExpressions(
265 cmBTStringRange entries,
266 std::vector<std::unique_ptr<cmGeneratorTarget::TargetPropertyEntry>>& items,
267 bool evaluateForBuildsystem = false)
269 for (auto const& entry : entries) {
270 items.push_back(CreateTargetPropertyEntry(entry, evaluateForBuildsystem));
275 // Represent a target property entry after evaluating generator expressions
276 // and splitting up lists.
277 struct EvaluatedTargetPropertyEntry
279 EvaluatedTargetPropertyEntry(cmLinkImplItem const& item,
280 cmListFileBacktrace bt)
282 , Backtrace(std::move(bt))
287 EvaluatedTargetPropertyEntry(EvaluatedTargetPropertyEntry&&) = default;
288 EvaluatedTargetPropertyEntry(EvaluatedTargetPropertyEntry const&) = delete;
289 EvaluatedTargetPropertyEntry& operator=(EvaluatedTargetPropertyEntry&&) =
291 EvaluatedTargetPropertyEntry& operator=(
292 EvaluatedTargetPropertyEntry const&) = delete;
294 cmLinkImplItem const& LinkImplItem;
295 cmListFileBacktrace Backtrace;
296 std::vector<std::string> Values;
297 bool ContextDependent = false;
300 EvaluatedTargetPropertyEntry EvaluateTargetPropertyEntry(
301 cmGeneratorTarget const* thisTarget, std::string const& config,
302 std::string const& lang, cmGeneratorExpressionDAGChecker* dagChecker,
303 cmGeneratorTarget::TargetPropertyEntry& entry)
305 EvaluatedTargetPropertyEntry ee(entry.LinkImplItem, entry.GetBacktrace());
306 cmExpandList(entry.Evaluate(thisTarget->GetLocalGenerator(), config,
307 thisTarget, dagChecker, lang),
309 if (entry.GetHadContextSensitiveCondition()) {
310 ee.ContextDependent = true;
315 struct EvaluatedTargetPropertyEntries
317 std::vector<EvaluatedTargetPropertyEntry> Entries;
318 bool HadContextSensitiveCondition = false;
321 EvaluatedTargetPropertyEntries EvaluateTargetPropertyEntries(
322 cmGeneratorTarget const* thisTarget, std::string const& config,
323 std::string const& lang, cmGeneratorExpressionDAGChecker* dagChecker,
324 std::vector<std::unique_ptr<cmGeneratorTarget::TargetPropertyEntry>> const&
327 EvaluatedTargetPropertyEntries out;
328 out.Entries.reserve(in.size());
329 for (auto const& entry : in) {
330 out.Entries.emplace_back(EvaluateTargetPropertyEntry(
331 thisTarget, config, lang, dagChecker, *entry));
337 cmGeneratorTarget::cmGeneratorTarget(cmTarget* t, cmLocalGenerator* lg)
340 this->Makefile = this->Target->GetMakefile();
341 this->LocalGenerator = lg;
342 this->GlobalGenerator = this->LocalGenerator->GetGlobalGenerator();
344 this->GlobalGenerator->ComputeTargetObjectDirectory(this);
346 CreatePropertyGeneratorExpressions(t->GetIncludeDirectoriesEntries(),
347 this->IncludeDirectoriesEntries);
349 CreatePropertyGeneratorExpressions(t->GetCompileOptionsEntries(),
350 this->CompileOptionsEntries);
352 CreatePropertyGeneratorExpressions(t->GetCompileFeaturesEntries(),
353 this->CompileFeaturesEntries);
355 CreatePropertyGeneratorExpressions(t->GetCompileDefinitionsEntries(),
356 this->CompileDefinitionsEntries);
358 CreatePropertyGeneratorExpressions(t->GetLinkOptionsEntries(),
359 this->LinkOptionsEntries);
361 CreatePropertyGeneratorExpressions(t->GetLinkDirectoriesEntries(),
362 this->LinkDirectoriesEntries);
364 CreatePropertyGeneratorExpressions(t->GetPrecompileHeadersEntries(),
365 this->PrecompileHeadersEntries);
367 CreatePropertyGeneratorExpressions(t->GetSourceEntries(),
368 this->SourceEntries, true);
370 this->PolicyMap = t->GetPolicyMap();
372 // Get hard-coded linker language
373 if (this->Target->GetProperty("HAS_CXX")) {
374 this->LinkerLanguage = "CXX";
376 this->LinkerLanguage = this->Target->GetSafeProperty("LINKER_LANGUAGE");
380 cmGeneratorTarget::~cmGeneratorTarget() = default;
382 cmValue cmGeneratorTarget::GetSourcesProperty() const
384 std::vector<std::string> values;
385 for (auto const& se : this->SourceEntries) {
386 values.push_back(se->GetInput());
388 static std::string value;
390 value = cmJoin(values, ";");
391 return cmValue(value);
394 cmGlobalGenerator* cmGeneratorTarget::GetGlobalGenerator() const
396 return this->GetLocalGenerator()->GetGlobalGenerator();
399 cmLocalGenerator* cmGeneratorTarget::GetLocalGenerator() const
401 return this->LocalGenerator;
404 cmStateEnums::TargetType cmGeneratorTarget::GetType() const
406 return this->Target->GetType();
409 const std::string& cmGeneratorTarget::GetName() const
411 return this->Target->GetName();
414 std::string cmGeneratorTarget::GetExportName() const
416 cmValue exportName = this->GetProperty("EXPORT_NAME");
418 if (cmNonempty(exportName)) {
419 if (!cmGeneratorExpression::IsValidTargetName(*exportName)) {
420 std::ostringstream e;
421 e << "EXPORT_NAME property \"" << *exportName << "\" for \""
422 << this->GetName() << "\": is not valid.";
423 cmSystemTools::Error(e.str());
428 return this->GetName();
431 cmValue cmGeneratorTarget::GetProperty(const std::string& prop) const
434 cmTargetPropertyComputer::GetProperty(this, prop, *this->Makefile)) {
437 if (cmSystemTools::GetFatalErrorOccurred()) {
440 return this->Target->GetProperty(prop);
443 std::string const& cmGeneratorTarget::GetSafeProperty(
444 std::string const& prop) const
446 return this->GetProperty(prop);
449 const char* cmGeneratorTarget::GetOutputTargetType(
450 cmStateEnums::ArtifactType artifact) const
452 switch (this->GetType()) {
453 case cmStateEnums::SHARED_LIBRARY:
454 if (this->IsDLLPlatform()) {
456 case cmStateEnums::RuntimeBinaryArtifact:
457 // A DLL shared library is treated as a runtime target.
459 case cmStateEnums::ImportLibraryArtifact:
460 // A DLL import library is treated as an archive target.
464 // For non-DLL platforms shared libraries are treated as
469 case cmStateEnums::STATIC_LIBRARY:
470 // Static libraries are always treated as archive targets.
472 case cmStateEnums::MODULE_LIBRARY:
474 case cmStateEnums::RuntimeBinaryArtifact:
475 // Module libraries are always treated as library targets.
477 case cmStateEnums::ImportLibraryArtifact:
478 // Module import libraries are treated as archive targets.
482 case cmStateEnums::OBJECT_LIBRARY:
483 // Object libraries are always treated as object targets.
485 case cmStateEnums::EXECUTABLE:
487 case cmStateEnums::RuntimeBinaryArtifact:
488 // Executables are always treated as runtime targets.
490 case cmStateEnums::ImportLibraryArtifact:
491 // Executable import libraries are treated as archive targets.
501 std::string cmGeneratorTarget::GetOutputName(
502 const std::string& config, cmStateEnums::ArtifactType artifact) const
504 // Lookup/compute/cache the output name for this configuration.
505 OutputNameKey key(config, artifact);
506 auto i = this->OutputNameMap.find(key);
507 if (i == this->OutputNameMap.end()) {
508 // Add empty name in map to detect potential recursion.
509 OutputNameMapType::value_type entry(key, "");
510 i = this->OutputNameMap.insert(entry).first;
512 // Compute output name.
513 std::vector<std::string> props;
514 std::string type = this->GetOutputTargetType(artifact);
515 std::string configUpper = cmSystemTools::UpperCase(config);
516 if (!type.empty() && !configUpper.empty()) {
517 // <ARCHIVE|LIBRARY|RUNTIME>_OUTPUT_NAME_<CONFIG>
518 props.push_back(type + "_OUTPUT_NAME_" + configUpper);
521 // <ARCHIVE|LIBRARY|RUNTIME>_OUTPUT_NAME
522 props.push_back(type + "_OUTPUT_NAME");
524 if (!configUpper.empty()) {
525 // OUTPUT_NAME_<CONFIG>
526 props.push_back("OUTPUT_NAME_" + configUpper);
527 // <CONFIG>_OUTPUT_NAME
528 props.push_back(configUpper + "_OUTPUT_NAME");
531 props.emplace_back("OUTPUT_NAME");
534 for (std::string const& p : props) {
535 if (cmValue outNameProp = this->GetProperty(p)) {
536 outName = *outNameProp;
541 if (outName.empty()) {
542 outName = this->GetName();
545 // Now evaluate genex and update the previously-prepared map entry.
547 cmGeneratorExpression::Evaluate(outName, this->LocalGenerator, config);
548 } else if (i->second.empty()) {
549 // An empty map entry indicates we have been called recursively
550 // from the above block.
551 this->LocalGenerator->GetCMakeInstance()->IssueMessage(
552 MessageType::FATAL_ERROR,
553 "Target '" + this->GetName() + "' OUTPUT_NAME depends on itself.",
554 this->GetBacktrace());
559 std::string cmGeneratorTarget::GetFilePrefix(
560 const std::string& config, cmStateEnums::ArtifactType artifact) const
562 if (this->IsImported()) {
563 cmValue prefix = this->GetFilePrefixInternal(config, artifact);
564 return prefix ? *prefix : std::string();
570 this->GetFullNameInternal(config, artifact, prefix, base, suffix);
573 std::string cmGeneratorTarget::GetFileSuffix(
574 const std::string& config, cmStateEnums::ArtifactType artifact) const
576 if (this->IsImported()) {
577 cmValue suffix = this->GetFileSuffixInternal(config, artifact);
578 return suffix ? *suffix : std::string();
584 this->GetFullNameInternal(config, artifact, prefix, base, suffix);
588 std::string cmGeneratorTarget::GetFilePostfix(const std::string& config) const
590 cmValue postfix = nullptr;
591 std::string frameworkPostfix;
592 if (!config.empty()) {
593 std::string configProp =
594 cmStrCat(cmSystemTools::UpperCase(config), "_POSTFIX");
595 postfix = this->GetProperty(configProp);
597 // Mac application bundles and frameworks have no regular postfix like
599 if (!this->IsImported() && postfix &&
600 (this->IsAppBundleOnApple() || this->IsFrameworkOnApple())) {
604 // Frameworks created by multi config generators can have a special
605 // framework postfix.
606 frameworkPostfix = this->GetFrameworkMultiConfigPostfix(config);
607 if (!frameworkPostfix.empty()) {
608 postfix = cmValue(frameworkPostfix);
611 return postfix ? *postfix : std::string();
614 std::string cmGeneratorTarget::GetFrameworkMultiConfigPostfix(
615 const std::string& config) const
617 cmValue postfix = nullptr;
618 if (!config.empty()) {
619 std::string configProp = cmStrCat("FRAMEWORK_MULTI_CONFIG_POSTFIX_",
620 cmSystemTools::UpperCase(config));
621 postfix = this->GetProperty(configProp);
623 if (!this->IsImported() && postfix &&
624 (this->IsFrameworkOnApple() &&
625 !this->GetGlobalGenerator()->IsMultiConfig())) {
629 return postfix ? *postfix : std::string();
632 cmValue cmGeneratorTarget::GetFilePrefixInternal(
633 std::string const& config, cmStateEnums::ArtifactType artifact,
634 const std::string& language) const
636 // no prefix for non-main target types.
637 if (this->GetType() != cmStateEnums::STATIC_LIBRARY &&
638 this->GetType() != cmStateEnums::SHARED_LIBRARY &&
639 this->GetType() != cmStateEnums::MODULE_LIBRARY &&
640 this->GetType() != cmStateEnums::EXECUTABLE) {
644 const bool isImportedLibraryArtifact =
645 (artifact == cmStateEnums::ImportLibraryArtifact);
647 // Return an empty prefix for the import library if this platform
648 // does not support import libraries.
649 if (isImportedLibraryArtifact && !this->NeedImportLibraryName(config)) {
653 // The implib option is only allowed for shared libraries, module
654 // libraries, and executables.
655 if (this->GetType() != cmStateEnums::SHARED_LIBRARY &&
656 this->GetType() != cmStateEnums::MODULE_LIBRARY &&
657 this->GetType() != cmStateEnums::EXECUTABLE) {
658 artifact = cmStateEnums::RuntimeBinaryArtifact;
661 // Compute prefix value.
662 cmValue targetPrefix =
663 (isImportedLibraryArtifact ? this->GetProperty("IMPORT_PREFIX")
664 : this->GetProperty("PREFIX"));
667 const char* prefixVar = this->Target->GetPrefixVariableInternal(artifact);
668 if (!language.empty() && cmNonempty(prefixVar)) {
669 std::string langPrefix = cmStrCat(prefixVar, "_", language);
670 targetPrefix = this->Makefile->GetDefinition(langPrefix);
673 // if there is no prefix on the target nor specific language
674 // use the cmake definition.
675 if (!targetPrefix && prefixVar) {
676 targetPrefix = this->Makefile->GetDefinition(prefixVar);
683 cmValue cmGeneratorTarget::GetFileSuffixInternal(
684 std::string const& config, cmStateEnums::ArtifactType artifact,
685 const std::string& language) const
687 // no suffix for non-main target types.
688 if (this->GetType() != cmStateEnums::STATIC_LIBRARY &&
689 this->GetType() != cmStateEnums::SHARED_LIBRARY &&
690 this->GetType() != cmStateEnums::MODULE_LIBRARY &&
691 this->GetType() != cmStateEnums::EXECUTABLE) {
695 const bool isImportedLibraryArtifact =
696 (artifact == cmStateEnums::ImportLibraryArtifact);
698 // Return an empty suffix for the import library if this platform
699 // does not support import libraries.
700 if (isImportedLibraryArtifact && !this->NeedImportLibraryName(config)) {
704 // The implib option is only allowed for shared libraries, module
705 // libraries, and executables.
706 if (this->GetType() != cmStateEnums::SHARED_LIBRARY &&
707 this->GetType() != cmStateEnums::MODULE_LIBRARY &&
708 this->GetType() != cmStateEnums::EXECUTABLE) {
709 artifact = cmStateEnums::RuntimeBinaryArtifact;
712 // Compute suffix value.
713 cmValue targetSuffix =
714 (isImportedLibraryArtifact ? this->GetProperty("IMPORT_SUFFIX")
715 : this->GetProperty("SUFFIX"));
718 const char* suffixVar = this->Target->GetSuffixVariableInternal(artifact);
719 if (!language.empty() && cmNonempty(suffixVar)) {
720 std::string langSuffix = cmStrCat(suffixVar, "_", language);
721 targetSuffix = this->Makefile->GetDefinition(langSuffix);
724 // if there is no suffix on the target nor specific language
725 // use the cmake definition.
726 if (!targetSuffix && suffixVar) {
727 targetSuffix = this->Makefile->GetDefinition(suffixVar);
734 void cmGeneratorTarget::ClearSourcesCache()
736 this->AllConfigSources.clear();
737 this->KindedSourcesMap.clear();
738 this->SourcesAreContextDependent = Tribool::Indeterminate;
739 this->Objects.clear();
740 this->VisitedConfigsForObjects.clear();
741 this->LinkImplMap.clear();
742 this->LinkImplUsageRequirementsOnlyMap.clear();
745 void cmGeneratorTarget::ClearLinkInterfaceCache()
747 this->LinkInterfaceMap.clear();
748 this->LinkInterfaceUsageRequirementsOnlyMap.clear();
751 void cmGeneratorTarget::AddSourceCommon(const std::string& src, bool before)
753 this->SourceEntries.insert(
754 before ? this->SourceEntries.begin() : this->SourceEntries.end(),
755 CreateTargetPropertyEntry(
756 BT<std::string>(src, this->Makefile->GetBacktrace()), true));
757 this->ClearSourcesCache();
760 void cmGeneratorTarget::AddSource(const std::string& src, bool before)
762 this->Target->AddSource(src, before);
763 this->AddSourceCommon(src, before);
766 void cmGeneratorTarget::AddTracedSources(std::vector<std::string> const& srcs)
768 this->Target->AddTracedSources(srcs);
770 this->AddSourceCommon(cmJoin(srcs, ";"));
774 void cmGeneratorTarget::AddIncludeDirectory(const std::string& src,
777 this->Target->InsertInclude(
778 BT<std::string>(src, this->Makefile->GetBacktrace()), before);
779 this->IncludeDirectoriesEntries.insert(
780 before ? this->IncludeDirectoriesEntries.begin()
781 : this->IncludeDirectoriesEntries.end(),
782 CreateTargetPropertyEntry(
783 BT<std::string>(src, this->Makefile->GetBacktrace()), true));
786 std::vector<cmSourceFile*> const* cmGeneratorTarget::GetSourceDepends(
787 cmSourceFile const* sf) const
789 auto i = this->SourceDepends.find(sf);
790 if (i != this->SourceDepends.end()) {
791 return &i->second.Depends;
797 void handleSystemIncludesDep(cmLocalGenerator* lg,
798 cmGeneratorTarget const* depTgt,
799 const std::string& config,
800 cmGeneratorTarget const* headTarget,
801 cmGeneratorExpressionDAGChecker* dagChecker,
802 std::vector<std::string>& result,
803 bool excludeImported, std::string const& language)
806 depTgt->GetProperty("INTERFACE_SYSTEM_INCLUDE_DIRECTORIES")) {
807 cmExpandList(cmGeneratorExpression::Evaluate(*dirs, lg, config, headTarget,
808 dagChecker, depTgt, language),
811 if (!depTgt->GetPropertyAsBool("SYSTEM")) {
814 if (depTgt->IsImported()) {
815 if (excludeImported) {
818 if (depTgt->GetPropertyAsBool("IMPORTED_NO_SYSTEM")) {
823 if (cmValue dirs = depTgt->GetProperty("INTERFACE_INCLUDE_DIRECTORIES")) {
824 cmExpandList(cmGeneratorExpression::Evaluate(*dirs, lg, config, headTarget,
825 dagChecker, depTgt, language),
831 /* clang-format off */
832 #define IMPLEMENT_VISIT(KIND) \
834 KindedSources const& kinded = this->GetKindedSources(config); \
835 for (SourceAndKind const& s : kinded.Sources) { \
836 if (s.Kind == KIND) { \
837 data.push_back(s.Source.Value); \
841 /* clang-format on */
843 void cmGeneratorTarget::GetObjectSources(
844 std::vector<cmSourceFile const*>& data, const std::string& config) const
846 IMPLEMENT_VISIT(SourceKindObjectSource);
848 if (this->VisitedConfigsForObjects.count(config)) {
852 for (cmSourceFile const* it : data) {
856 this->LocalGenerator->ComputeObjectFilenames(this->Objects, this);
857 this->VisitedConfigsForObjects.insert(config);
860 void cmGeneratorTarget::ComputeObjectMapping()
862 auto const& configs =
863 this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
864 std::set<std::string> configSet(configs.begin(), configs.end());
865 if (configSet == this->VisitedConfigsForObjects) {
869 for (std::string const& c : configs) {
870 std::vector<cmSourceFile const*> sourceFiles;
871 this->GetObjectSources(sourceFiles, c);
875 cmValue cmGeneratorTarget::GetFeature(const std::string& feature,
876 const std::string& config) const
878 if (!config.empty()) {
879 std::string featureConfig =
880 cmStrCat(feature, '_', cmSystemTools::UpperCase(config));
881 if (cmValue value = this->GetProperty(featureConfig)) {
885 if (cmValue value = this->GetProperty(feature)) {
888 return this->LocalGenerator->GetFeature(feature, config);
891 const char* cmGeneratorTarget::GetLinkPIEProperty(
892 const std::string& config) const
894 static std::string PICValue;
896 PICValue = this->GetLinkInterfaceDependentStringAsBoolProperty(
897 "POSITION_INDEPENDENT_CODE", config);
899 if (PICValue == "(unset)") {
900 // POSITION_INDEPENDENT_CODE is not set
904 auto status = this->GetPolicyStatusCMP0083();
905 return (status != cmPolicies::WARN && status != cmPolicies::OLD)
910 bool cmGeneratorTarget::IsIPOEnabled(std::string const& lang,
911 std::string const& config) const
913 cmValue feature = this->GetFeature("INTERPROCEDURAL_OPTIMIZATION", config);
915 if (!cmIsOn(feature)) {
916 // 'INTERPROCEDURAL_OPTIMIZATION' is off, no need to check policies
920 if (lang != "C" && lang != "CXX" && lang != "CUDA" && lang != "Fortran") {
921 // We do not define IPO behavior for other languages.
925 if (lang == "CUDA") {
926 // CUDA IPO requires both CUDA_ARCHITECTURES and CUDA_SEPARABLE_COMPILATION
927 if (cmIsOff(this->GetSafeProperty("CUDA_ARCHITECTURES")) ||
928 cmIsOff(this->GetSafeProperty("CUDA_SEPARABLE_COMPILATION"))) {
933 cmPolicies::PolicyStatus cmp0069 = this->GetPolicyStatusCMP0069();
935 if (cmp0069 == cmPolicies::OLD || cmp0069 == cmPolicies::WARN) {
936 if (this->Makefile->IsOn("_CMAKE_" + lang + "_IPO_LEGACY_BEHAVIOR")) {
939 if (this->PolicyReportedCMP0069) {
940 // problem is already reported, no need to issue a message
943 const bool in_try_compile =
944 this->LocalGenerator->GetCMakeInstance()->GetIsInTryCompile();
945 if (cmp0069 == cmPolicies::WARN && !in_try_compile) {
946 std::ostringstream w;
947 w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0069) << "\n";
948 w << "INTERPROCEDURAL_OPTIMIZATION property will be ignored for target "
949 << "'" << this->GetName() << "'.";
950 this->LocalGenerator->GetCMakeInstance()->IssueMessage(
951 MessageType::AUTHOR_WARNING, w.str(), this->GetBacktrace());
953 this->PolicyReportedCMP0069 = true;
958 // Note: check consistency with messages from CheckIPOSupported
959 const char* message = nullptr;
960 if (!this->Makefile->IsOn("_CMAKE_" + lang + "_IPO_SUPPORTED_BY_CMAKE")) {
961 message = "CMake doesn't support IPO for current compiler";
962 } else if (!this->Makefile->IsOn("_CMAKE_" + lang +
963 "_IPO_MAY_BE_SUPPORTED_BY_COMPILER")) {
964 message = "Compiler doesn't support IPO";
965 } else if (!this->GlobalGenerator->IsIPOSupported()) {
966 message = "CMake doesn't support IPO for current generator";
970 // No error/warning messages
974 if (this->PolicyReportedCMP0069) {
975 // problem is already reported, no need to issue a message
979 this->PolicyReportedCMP0069 = true;
981 this->LocalGenerator->GetCMakeInstance()->IssueMessage(
982 MessageType::FATAL_ERROR, message, this->GetBacktrace());
986 const std::string& cmGeneratorTarget::GetObjectName(cmSourceFile const* file)
988 this->ComputeObjectMapping();
989 return this->Objects[file];
992 const char* cmGeneratorTarget::GetCustomObjectExtension() const
994 static std::string extension;
995 const bool has_ptx_extension =
996 this->GetPropertyAsBool("CUDA_PTX_COMPILATION");
997 if (has_ptx_extension) {
999 return extension.c_str();
1004 void cmGeneratorTarget::AddExplicitObjectName(cmSourceFile const* sf)
1006 this->ExplicitObjectName.insert(sf);
1009 bool cmGeneratorTarget::HasExplicitObjectName(cmSourceFile const* file) const
1011 const_cast<cmGeneratorTarget*>(this)->ComputeObjectMapping();
1012 auto it = this->ExplicitObjectName.find(file);
1013 return it != this->ExplicitObjectName.end();
1016 BTs<std::string> const* cmGeneratorTarget::GetLanguageStandardProperty(
1017 std::string const& lang, std::string const& config) const
1019 std::string key = cmStrCat(cmSystemTools::UpperCase(config), '-', lang);
1020 auto langStandardIter = this->LanguageStandardMap.find(key);
1021 if (langStandardIter != this->LanguageStandardMap.end()) {
1022 return &langStandardIter->second;
1025 return this->Target->GetLanguageStandardProperty(
1026 cmStrCat(lang, "_STANDARD"));
1029 cmValue cmGeneratorTarget::GetLanguageStandard(std::string const& lang,
1030 std::string const& config) const
1032 BTs<std::string> const* languageStandard =
1033 this->GetLanguageStandardProperty(lang, config);
1035 if (languageStandard) {
1036 return cmValue(languageStandard->Value);
1042 cmValue cmGeneratorTarget::GetPropertyWithPairedLanguageSupport(
1043 std::string const& lang, const char* suffix) const
1045 cmValue propertyValue = this->Target->GetProperty(cmStrCat(lang, suffix));
1046 if (!propertyValue) {
1047 // Check if we should use the value set by another language.
1048 if (lang == "OBJC") {
1049 propertyValue = this->GetPropertyWithPairedLanguageSupport("C", suffix);
1050 } else if (lang == "OBJCXX" || lang == "CUDA" || lang == "HIP") {
1052 this->GetPropertyWithPairedLanguageSupport("CXX", suffix);
1055 return propertyValue;
1058 cmValue cmGeneratorTarget::GetLanguageExtensions(std::string const& lang) const
1060 return this->GetPropertyWithPairedLanguageSupport(lang, "_EXTENSIONS");
1063 bool cmGeneratorTarget::GetLanguageStandardRequired(
1064 std::string const& lang) const
1067 this->GetPropertyWithPairedLanguageSupport(lang, "_STANDARD_REQUIRED"));
1070 void cmGeneratorTarget::GetModuleDefinitionSources(
1071 std::vector<cmSourceFile const*>& data, const std::string& config) const
1073 IMPLEMENT_VISIT(SourceKindModuleDefinition);
1076 void cmGeneratorTarget::GetHeaderSources(
1077 std::vector<cmSourceFile const*>& data, const std::string& config) const
1079 IMPLEMENT_VISIT(SourceKindHeader);
1082 void cmGeneratorTarget::GetExtraSources(std::vector<cmSourceFile const*>& data,
1083 const std::string& config) const
1085 IMPLEMENT_VISIT(SourceKindExtra);
1088 void cmGeneratorTarget::GetCustomCommands(
1089 std::vector<cmSourceFile const*>& data, const std::string& config) const
1091 IMPLEMENT_VISIT(SourceKindCustomCommand);
1094 void cmGeneratorTarget::GetExternalObjects(
1095 std::vector<cmSourceFile const*>& data, const std::string& config) const
1097 IMPLEMENT_VISIT(SourceKindExternalObject);
1100 void cmGeneratorTarget::GetManifests(std::vector<cmSourceFile const*>& data,
1101 const std::string& config) const
1103 IMPLEMENT_VISIT(SourceKindManifest);
1106 std::set<cmLinkItem> const& cmGeneratorTarget::GetUtilityItems() const
1108 if (!this->UtilityItemsDone) {
1109 this->UtilityItemsDone = true;
1110 std::set<BT<std::pair<std::string, bool>>> const& utilities =
1111 this->GetUtilities();
1112 for (BT<std::pair<std::string, bool>> const& i : utilities) {
1113 if (cmGeneratorTarget* gt =
1114 this->LocalGenerator->FindGeneratorTargetToUse(i.Value.first)) {
1115 this->UtilityItems.insert(cmLinkItem(gt, i.Value.second, i.Backtrace));
1117 this->UtilityItems.insert(
1118 cmLinkItem(i.Value.first, i.Value.second, i.Backtrace));
1122 return this->UtilityItems;
1125 const std::string& cmGeneratorTarget::GetLocation(
1126 const std::string& config) const
1128 static std::string location;
1129 if (this->IsImported()) {
1130 location = this->Target->ImportedGetFullPath(
1131 config, cmStateEnums::RuntimeBinaryArtifact);
1133 location = this->GetFullPath(config, cmStateEnums::RuntimeBinaryArtifact);
1138 cm::optional<std::string> cmGeneratorTarget::MaybeGetLocation(
1139 std::string const& config) const
1141 cm::optional<std::string> location;
1142 if (cmGeneratorTarget::ImportInfo const* imp = this->GetImportInfo(config)) {
1143 if (!imp->Location.empty()) {
1144 location = imp->Location;
1147 location = this->GetFullPath(config, cmStateEnums::RuntimeBinaryArtifact);
1152 std::vector<cmCustomCommand> const& cmGeneratorTarget::GetPreBuildCommands()
1155 return this->Target->GetPreBuildCommands();
1158 std::vector<cmCustomCommand> const& cmGeneratorTarget::GetPreLinkCommands()
1161 return this->Target->GetPreLinkCommands();
1164 std::vector<cmCustomCommand> const& cmGeneratorTarget::GetPostBuildCommands()
1167 return this->Target->GetPostBuildCommands();
1170 void cmGeneratorTarget::AppendCustomCommandSideEffects(
1171 std::set<cmGeneratorTarget const*>& sideEffects) const
1173 if (!this->GetPreBuildCommands().empty() ||
1174 !this->GetPreLinkCommands().empty() ||
1175 !this->GetPostBuildCommands().empty()) {
1176 sideEffects.insert(this);
1178 for (auto const& source : this->GetAllConfigSources()) {
1179 if (source.Source->GetCustomCommand() != nullptr) {
1180 sideEffects.insert(this);
1187 void cmGeneratorTarget::AppendLanguageSideEffects(
1188 std::map<std::string, std::set<cmGeneratorTarget const*>>& sideEffects) const
1190 static const std::set<cm::string_view> LANGS_WITH_NO_SIDE_EFFECTS = {
1191 "C"_s, "CXX"_s, "OBJC"_s, "OBJCXX"_s, "ASM"_s, "CUDA"_s, "HIP"_s
1194 for (auto const& lang : this->GetAllConfigCompileLanguages()) {
1195 if (!LANGS_WITH_NO_SIDE_EFFECTS.count(lang)) {
1196 sideEffects[lang].insert(this);
1201 bool cmGeneratorTarget::IsInBuildSystem() const
1203 if (this->IsImported()) {
1206 switch (this->Target->GetType()) {
1207 case cmStateEnums::EXECUTABLE:
1208 case cmStateEnums::STATIC_LIBRARY:
1209 case cmStateEnums::SHARED_LIBRARY:
1210 case cmStateEnums::MODULE_LIBRARY:
1211 case cmStateEnums::OBJECT_LIBRARY:
1212 case cmStateEnums::UTILITY:
1213 case cmStateEnums::GLOBAL_TARGET:
1215 case cmStateEnums::INTERFACE_LIBRARY:
1216 // An INTERFACE library is in the build system if it has SOURCES or
1218 if (!this->SourceEntries.empty() ||
1219 !this->Target->GetHeaderSetsEntries().empty()) {
1223 case cmStateEnums::UNKNOWN_LIBRARY:
1229 bool cmGeneratorTarget::IsImported() const
1231 return this->Target->IsImported();
1234 bool cmGeneratorTarget::IsImportedGloballyVisible() const
1236 return this->Target->IsImportedGloballyVisible();
1239 bool cmGeneratorTarget::CanCompileSources() const
1241 return this->Target->CanCompileSources();
1244 const std::string& cmGeneratorTarget::GetLocationForBuild() const
1246 static std::string location;
1247 if (this->IsImported()) {
1248 location = this->Target->ImportedGetFullPath(
1249 "", cmStateEnums::RuntimeBinaryArtifact);
1253 // Now handle the deprecated build-time configuration location.
1254 std::string const noConfig;
1255 location = this->GetDirectory(noConfig);
1256 cmValue cfgid = this->Makefile->GetDefinition("CMAKE_CFG_INTDIR");
1257 if (cfgid && (*cfgid != ".")) {
1262 if (this->IsAppBundleOnApple()) {
1263 std::string macdir = this->BuildBundleDirectory("", "", FullLevel);
1264 if (!macdir.empty()) {
1270 location += this->GetFullName("", cmStateEnums::RuntimeBinaryArtifact);
1274 bool cmGeneratorTarget::IsSystemIncludeDirectory(
1275 const std::string& dir, const std::string& config,
1276 const std::string& language) const
1278 assert(this->GetType() != cmStateEnums::INTERFACE_LIBRARY);
1279 std::string config_upper;
1280 if (!config.empty()) {
1281 config_upper = cmSystemTools::UpperCase(config);
1284 std::string key = cmStrCat(config_upper, "/", language);
1285 auto iter = this->SystemIncludesCache.find(key);
1287 if (iter == this->SystemIncludesCache.end()) {
1288 cmGeneratorExpressionDAGChecker dagChecker(
1289 this, "SYSTEM_INCLUDE_DIRECTORIES", nullptr, nullptr);
1291 bool excludeImported = this->GetPropertyAsBool("NO_SYSTEM_FROM_IMPORTED");
1293 std::vector<std::string> result;
1294 for (std::string const& it : this->Target->GetSystemIncludeDirectories()) {
1295 cmExpandList(cmGeneratorExpression::Evaluate(it, this->LocalGenerator,
1296 config, this, &dagChecker,
1301 std::vector<cmGeneratorTarget const*> const& deps =
1302 this->GetLinkImplementationClosure(config);
1303 for (cmGeneratorTarget const* dep : deps) {
1304 handleSystemIncludesDep(this->LocalGenerator, dep, config, this,
1305 &dagChecker, result, excludeImported, language);
1308 cmLinkImplementation const* impl =
1309 this->GetLinkImplementation(config, LinkInterfaceFor::Usage);
1310 if (impl != nullptr) {
1311 auto runtimeEntries = impl->LanguageRuntimeLibraries.find(language);
1312 if (runtimeEntries != impl->LanguageRuntimeLibraries.end()) {
1313 for (auto const& lib : runtimeEntries->second) {
1315 handleSystemIncludesDep(this->LocalGenerator, lib.Target, config,
1316 this, &dagChecker, result, excludeImported,
1323 std::for_each(result.begin(), result.end(),
1324 cmSystemTools::ConvertToUnixSlashes);
1325 std::sort(result.begin(), result.end());
1326 result.erase(std::unique(result.begin(), result.end()), result.end());
1328 iter = this->SystemIncludesCache.emplace(key, result).first;
1331 return std::binary_search(iter->second.begin(), iter->second.end(), dir);
1334 bool cmGeneratorTarget::GetPropertyAsBool(const std::string& prop) const
1336 return this->Target->GetPropertyAsBool(prop);
1339 bool cmGeneratorTarget::MaybeHaveInterfaceProperty(
1340 std::string const& prop, cmGeneratorExpressionContext* context,
1341 LinkInterfaceFor interfaceFor) const
1343 std::string const key = prop + '@' + context->Config;
1344 auto i = this->MaybeInterfacePropertyExists.find(key);
1345 if (i == this->MaybeInterfacePropertyExists.end()) {
1346 // Insert an entry now in case there is a cycle.
1347 i = this->MaybeInterfacePropertyExists.emplace(key, false).first;
1348 bool& maybeInterfaceProp = i->second;
1350 // If this target itself has a non-empty property value, we are done.
1351 maybeInterfaceProp = cmNonempty(this->GetProperty(prop));
1353 // Otherwise, recurse to interface dependencies.
1354 if (!maybeInterfaceProp) {
1355 cmGeneratorTarget const* headTarget =
1356 context->HeadTarget ? context->HeadTarget : this;
1357 if (cmLinkInterfaceLibraries const* iface =
1358 this->GetLinkInterfaceLibraries(context->Config, headTarget,
1360 if (iface->HadHeadSensitiveCondition) {
1361 // With a different head target we may get to a library with
1362 // this interface property.
1363 maybeInterfaceProp = true;
1365 // The transitive interface libraries do not depend on the
1366 // head target, so we can follow them.
1367 for (cmLinkItem const& lib : iface->Libraries) {
1369 lib.Target->MaybeHaveInterfaceProperty(prop, context,
1371 maybeInterfaceProp = true;
1382 std::string cmGeneratorTarget::EvaluateInterfaceProperty(
1383 std::string const& prop, cmGeneratorExpressionContext* context,
1384 cmGeneratorExpressionDAGChecker* dagCheckerParent,
1385 LinkInterfaceFor interfaceFor) const
1389 // If the property does not appear transitively at all, we are done.
1390 if (!this->MaybeHaveInterfaceProperty(prop, context, interfaceFor)) {
1394 // Evaluate $<TARGET_PROPERTY:this,prop> as if it were compiled. This is
1395 // a subset of TargetPropertyNode::Evaluate without stringify/parse steps
1396 // but sufficient for transitive interface properties.
1397 cmGeneratorExpressionDAGChecker dagChecker(context->Backtrace, this, prop,
1398 nullptr, dagCheckerParent);
1399 switch (dagChecker.Check()) {
1400 case cmGeneratorExpressionDAGChecker::SELF_REFERENCE:
1401 dagChecker.ReportError(
1402 context, "$<TARGET_PROPERTY:" + this->GetName() + "," + prop + ">");
1404 case cmGeneratorExpressionDAGChecker::CYCLIC_REFERENCE:
1405 // No error. We just skip cyclic references.
1406 case cmGeneratorExpressionDAGChecker::ALREADY_SEEN:
1407 // No error. We have already seen this transitive property.
1409 case cmGeneratorExpressionDAGChecker::DAG:
1413 cmGeneratorTarget const* headTarget =
1414 context->HeadTarget ? context->HeadTarget : this;
1416 if (cmValue p = this->GetProperty(prop)) {
1417 result = cmGeneratorExpressionNode::EvaluateDependentExpression(
1418 *p, context->LG, context, headTarget, &dagChecker, this);
1421 if (cmLinkInterfaceLibraries const* iface = this->GetLinkInterfaceLibraries(
1422 context->Config, headTarget, interfaceFor)) {
1423 context->HadContextSensitiveCondition =
1424 context->HadContextSensitiveCondition ||
1425 iface->HadContextSensitiveCondition;
1426 for (cmLinkItem const& lib : iface->Libraries) {
1427 // Broken code can have a target in its own link interface.
1428 // Don't follow such link interface entries so as not to create a
1429 // self-referencing loop.
1430 if (lib.Target && lib.Target != this) {
1431 // Pretend $<TARGET_PROPERTY:lib.Target,prop> appeared in the
1432 // above property and hand-evaluate it as if it were compiled.
1433 // Create a context as cmCompiledGeneratorExpression::Evaluate does.
1434 cmGeneratorExpressionContext libContext(
1435 context->LG, context->Config, context->Quiet, headTarget, this,
1436 context->EvaluateForBuildsystem, context->Backtrace,
1438 std::string libResult = cmGeneratorExpression::StripEmptyListElements(
1439 lib.Target->EvaluateInterfaceProperty(prop, &libContext, &dagChecker,
1441 if (!libResult.empty()) {
1442 if (result.empty()) {
1443 result = std::move(libResult);
1445 result.reserve(result.size() + 1 + libResult.size());
1447 result += libResult;
1450 context->HadContextSensitiveCondition =
1451 context->HadContextSensitiveCondition ||
1452 libContext.HadContextSensitiveCondition;
1453 context->HadHeadSensitiveCondition =
1454 context->HadHeadSensitiveCondition ||
1455 libContext.HadHeadSensitiveCondition;
1465 enum class IncludeDirectoryFallBack
1471 std::string AddLangSpecificInterfaceIncludeDirectories(
1472 const cmGeneratorTarget* root, const cmGeneratorTarget* target,
1473 const std::string& lang, const std::string& config,
1474 const std::string& propertyName, IncludeDirectoryFallBack mode,
1475 cmGeneratorExpressionDAGChecker* context)
1477 cmGeneratorExpressionDAGChecker dag{ target->GetBacktrace(), target,
1478 propertyName, nullptr, context };
1479 switch (dag.Check()) {
1480 case cmGeneratorExpressionDAGChecker::SELF_REFERENCE:
1482 nullptr, "$<TARGET_PROPERTY:" + target->GetName() + ",propertyName");
1484 case cmGeneratorExpressionDAGChecker::CYCLIC_REFERENCE:
1485 // No error. We just skip cyclic references.
1486 case cmGeneratorExpressionDAGChecker::ALREADY_SEEN:
1487 // No error. We have already seen this transitive property.
1489 case cmGeneratorExpressionDAGChecker::DAG:
1493 std::string directories;
1494 if (const auto* interface = target->GetLinkInterfaceLibraries(
1495 config, root, LinkInterfaceFor::Usage)) {
1496 for (const cmLinkItem& library : interface->Libraries) {
1497 if (const cmGeneratorTarget* dependency = library.Target) {
1498 if (cm::contains(dependency->GetAllConfigCompileLanguages(), lang)) {
1499 auto* lg = dependency->GetLocalGenerator();
1500 std::string value = dependency->GetSafeProperty(propertyName);
1501 if (value.empty()) {
1502 if (mode == IncludeDirectoryFallBack::BINARY) {
1503 value = lg->GetCurrentBinaryDirectory();
1504 } else if (mode == IncludeDirectoryFallBack::OBJECT) {
1505 value = cmStrCat(lg->GetCurrentBinaryDirectory(), '/',
1506 lg->GetTargetDirectory(dependency));
1510 if (!directories.empty()) {
1513 directories += value;
1521 void AddLangSpecificImplicitIncludeDirectories(
1522 const cmGeneratorTarget* target, const std::string& lang,
1523 const std::string& config, const std::string& propertyName,
1524 IncludeDirectoryFallBack mode, EvaluatedTargetPropertyEntries& entries)
1526 if (const auto* libraries = target->GetLinkImplementationLibraries(
1527 config, LinkInterfaceFor::Usage)) {
1528 cmGeneratorExpressionDAGChecker dag{ target->GetBacktrace(), target,
1529 propertyName, nullptr, nullptr };
1531 for (const cmLinkImplItem& library : libraries->Libraries) {
1532 if (const cmGeneratorTarget* dependency = library.Target) {
1533 if (!dependency->IsInBuildSystem()) {
1536 if (cm::contains(dependency->GetAllConfigCompileLanguages(), lang)) {
1537 auto* lg = dependency->GetLocalGenerator();
1538 EvaluatedTargetPropertyEntry entry{ library, library.Backtrace };
1540 if (cmValue val = dependency->GetProperty(propertyName)) {
1541 entry.Values.emplace_back(*val);
1543 if (mode == IncludeDirectoryFallBack::BINARY) {
1544 entry.Values.emplace_back(lg->GetCurrentBinaryDirectory());
1545 } else if (mode == IncludeDirectoryFallBack::OBJECT) {
1546 entry.Values.emplace_back(
1547 dependency->GetObjectDirectory(config));
1552 AddLangSpecificInterfaceIncludeDirectories(
1553 target, dependency, lang, config, propertyName, mode, &dag),
1555 entries.Entries.emplace_back(std::move(entry));
1562 void addInterfaceEntry(cmGeneratorTarget const* headTarget,
1563 std::string const& config, std::string const& prop,
1564 std::string const& lang,
1565 cmGeneratorExpressionDAGChecker* dagChecker,
1566 EvaluatedTargetPropertyEntries& entries,
1567 LinkInterfaceFor interfaceFor,
1568 std::vector<cmLinkImplItem> const& libraries)
1570 for (cmLinkImplItem const& lib : libraries) {
1572 EvaluatedTargetPropertyEntry ee(lib, lib.Backtrace);
1573 // Pretend $<TARGET_PROPERTY:lib.Target,prop> appeared in our
1574 // caller's property and hand-evaluate it as if it were compiled.
1575 // Create a context as cmCompiledGeneratorExpression::Evaluate does.
1576 cmGeneratorExpressionContext context(
1577 headTarget->GetLocalGenerator(), config, false, headTarget, headTarget,
1578 true, lib.Backtrace, lang);
1579 cmExpandList(lib.Target->EvaluateInterfaceProperty(
1580 prop, &context, dagChecker, interfaceFor),
1582 ee.ContextDependent = context.HadContextSensitiveCondition;
1583 entries.Entries.emplace_back(std::move(ee));
1588 // IncludeRuntimeInterface is used to break the cycle in computing
1589 // the necessary transitive dependencies of targets that can occur
1590 // now that we have implicit language runtime targets.
1592 // To determine the set of languages that a target has we need to iterate
1593 // all the sources which includes transitive INTERFACE sources.
1594 // Therefore we can't determine what language runtimes are needed
1595 // for a target until after all sources are computed.
1597 // Therefore while computing the applicable INTERFACE_SOURCES we
1598 // must ignore anything in LanguageRuntimeLibraries or we would
1599 // create a cycle ( INTERFACE_SOURCES requires LanguageRuntimeLibraries,
1600 // LanguageRuntimeLibraries requires INTERFACE_SOURCES).
1602 enum class IncludeRuntimeInterface
1607 void AddInterfaceEntries(
1608 cmGeneratorTarget const* headTarget, std::string const& config,
1609 std::string const& prop, std::string const& lang,
1610 cmGeneratorExpressionDAGChecker* dagChecker,
1611 EvaluatedTargetPropertyEntries& entries,
1612 IncludeRuntimeInterface searchRuntime,
1613 LinkInterfaceFor interfaceFor = LinkInterfaceFor::Usage)
1615 if (searchRuntime == IncludeRuntimeInterface::Yes) {
1616 if (cmLinkImplementation const* impl =
1617 headTarget->GetLinkImplementation(config, interfaceFor)) {
1618 entries.HadContextSensitiveCondition =
1619 impl->HadContextSensitiveCondition;
1621 auto runtimeLibIt = impl->LanguageRuntimeLibraries.find(lang);
1622 if (runtimeLibIt != impl->LanguageRuntimeLibraries.end()) {
1623 addInterfaceEntry(headTarget, config, prop, lang, dagChecker, entries,
1624 interfaceFor, runtimeLibIt->second);
1626 addInterfaceEntry(headTarget, config, prop, lang, dagChecker, entries,
1627 interfaceFor, impl->Libraries);
1630 if (cmLinkImplementationLibraries const* impl =
1631 headTarget->GetLinkImplementationLibraries(config, interfaceFor)) {
1632 entries.HadContextSensitiveCondition =
1633 impl->HadContextSensitiveCondition;
1634 addInterfaceEntry(headTarget, config, prop, lang, dagChecker, entries,
1635 interfaceFor, impl->Libraries);
1640 void AddObjectEntries(cmGeneratorTarget const* headTarget,
1641 std::string const& config,
1642 cmGeneratorExpressionDAGChecker* dagChecker,
1643 EvaluatedTargetPropertyEntries& entries)
1645 if (cmLinkImplementationLibraries const* impl =
1646 headTarget->GetLinkImplementationLibraries(config,
1647 LinkInterfaceFor::Usage)) {
1648 entries.HadContextSensitiveCondition = impl->HadContextSensitiveCondition;
1649 for (cmLinkImplItem const& lib : impl->Libraries) {
1651 lib.Target->GetType() == cmStateEnums::OBJECT_LIBRARY) {
1652 std::string uniqueName =
1653 headTarget->GetGlobalGenerator()->IndexGeneratorTargetUniquely(
1655 std::string genex = "$<TARGET_OBJECTS:" + std::move(uniqueName) + ">";
1656 cmGeneratorExpression ge(lib.Backtrace);
1657 std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(genex);
1658 cge->SetEvaluateForBuildsystem(true);
1660 EvaluatedTargetPropertyEntry ee(lib, lib.Backtrace);
1661 cmExpandList(cge->Evaluate(headTarget->GetLocalGenerator(), config,
1662 headTarget, dagChecker),
1664 if (cge->GetHadContextSensitiveCondition()) {
1665 ee.ContextDependent = true;
1667 entries.Entries.emplace_back(std::move(ee));
1673 void addFileSetEntry(cmGeneratorTarget const* headTarget,
1674 std::string const& config,
1675 cmGeneratorExpressionDAGChecker* dagChecker,
1676 cmFileSet const* fileSet,
1677 EvaluatedTargetPropertyEntries& entries)
1679 auto dirCges = fileSet->CompileDirectoryEntries();
1680 auto dirs = fileSet->EvaluateDirectoryEntries(
1681 dirCges, headTarget->GetLocalGenerator(), config, headTarget, dagChecker);
1682 bool contextSensitiveDirs = false;
1683 for (auto const& dirCge : dirCges) {
1684 if (dirCge->GetHadContextSensitiveCondition()) {
1685 contextSensitiveDirs = true;
1689 cmake* cm = headTarget->GetLocalGenerator()->GetCMakeInstance();
1690 for (auto& entryCge : fileSet->CompileFileEntries()) {
1691 TargetPropertyEntryFileSet tpe(dirs, contextSensitiveDirs,
1692 std::move(entryCge), fileSet);
1693 entries.Entries.emplace_back(
1694 EvaluateTargetPropertyEntry(headTarget, config, "", dagChecker, tpe));
1695 for (auto const& file : entries.Entries.back().Values) {
1696 auto* sf = headTarget->Makefile->GetOrCreateSource(file);
1697 if (fileSet->GetType() == "HEADERS"_s) {
1698 sf->SetProperty("HEADER_FILE_ONLY", "TRUE");
1701 #ifndef CMAKE_BOOTSTRAP
1704 auto path = sf->ResolveFullPath(&e, &w);
1706 cm->IssueMessage(MessageType::AUTHOR_WARNING, w,
1707 headTarget->GetBacktrace());
1711 cm->IssueMessage(MessageType::FATAL_ERROR, e,
1712 headTarget->GetBacktrace());
1717 for (auto const& sg : headTarget->Makefile->GetSourceGroups()) {
1718 if (sg.MatchChildrenFiles(path)) {
1724 if (fileSet->GetType() == "HEADERS"_s ||
1725 fileSet->GetType() == "CXX_MODULE_HEADER_UNITS"_s) {
1726 headTarget->Makefile->GetOrCreateSourceGroup("Header Files")
1727 ->AddGroupFile(path);
1735 void AddFileSetEntries(cmGeneratorTarget const* headTarget,
1736 std::string const& config,
1737 cmGeneratorExpressionDAGChecker* dagChecker,
1738 EvaluatedTargetPropertyEntries& entries)
1740 for (auto const& entry : headTarget->Target->GetHeaderSetsEntries()) {
1741 for (auto const& name : cmExpandedList(entry.Value)) {
1742 auto const* headerSet = headTarget->Target->GetFileSet(name);
1743 addFileSetEntry(headTarget, config, dagChecker, headerSet, entries);
1746 for (auto const& entry : headTarget->Target->GetCxxModuleSetsEntries()) {
1747 for (auto const& name : cmExpandedList(entry.Value)) {
1748 auto const* cxxModuleSet = headTarget->Target->GetFileSet(name);
1749 addFileSetEntry(headTarget, config, dagChecker, cxxModuleSet, entries);
1752 for (auto const& entry :
1753 headTarget->Target->GetCxxModuleHeaderSetsEntries()) {
1754 for (auto const& name : cmExpandedList(entry.Value)) {
1755 auto const* cxxModuleHeaderSet = headTarget->Target->GetFileSet(name);
1756 addFileSetEntry(headTarget, config, dagChecker, cxxModuleHeaderSet,
1762 bool processSources(cmGeneratorTarget const* tgt,
1763 EvaluatedTargetPropertyEntries& entries,
1764 std::vector<BT<std::string>>& srcs,
1765 std::unordered_set<std::string>& uniqueSrcs,
1768 cmMakefile* mf = tgt->Target->GetMakefile();
1770 bool contextDependent = entries.HadContextSensitiveCondition;
1772 for (EvaluatedTargetPropertyEntry& entry : entries.Entries) {
1773 if (entry.ContextDependent) {
1774 contextDependent = true;
1777 cmLinkImplItem const& item = entry.LinkImplItem;
1778 std::string const& targetName = item.AsStr();
1780 for (std::string& src : entry.Values) {
1781 cmSourceFile* sf = mf->GetOrCreateSource(src);
1784 std::string fullPath = sf->ResolveFullPath(&e, &w);
1785 cmake* cm = tgt->GetLocalGenerator()->GetCMakeInstance();
1787 cm->IssueMessage(MessageType::AUTHOR_WARNING, w, tgt->GetBacktrace());
1789 if (fullPath.empty()) {
1791 cm->IssueMessage(MessageType::FATAL_ERROR, e, tgt->GetBacktrace());
1793 return contextDependent;
1796 if (!targetName.empty() && !cmSystemTools::FileIsFullPath(src)) {
1797 std::ostringstream err;
1798 if (!targetName.empty()) {
1799 err << "Target \"" << targetName
1800 << "\" contains relative path in its INTERFACE_SOURCES:\n \""
1803 err << "Found relative path while evaluating sources of \""
1804 << tgt->GetName() << "\":\n \"" << src << "\"\n";
1806 tgt->GetLocalGenerator()->IssueMessage(MessageType::FATAL_ERROR,
1808 return contextDependent;
1812 std::string usedSources;
1813 for (std::string const& src : entry.Values) {
1814 if (uniqueSrcs.insert(src).second) {
1815 srcs.emplace_back(src, entry.Backtrace);
1817 usedSources += " * " + src + "\n";
1821 if (!usedSources.empty()) {
1822 tgt->GetLocalGenerator()->GetCMakeInstance()->IssueMessage(
1824 std::string("Used sources for target ") + tgt->GetName() + ":\n" +
1829 return contextDependent;
1833 std::vector<BT<std::string>> cmGeneratorTarget::GetSourceFilePaths(
1834 std::string const& config) const
1836 std::vector<BT<std::string>> files;
1838 if (!this->LocalGenerator->GetGlobalGenerator()->GetConfigureDoneCMP0026()) {
1839 // At configure-time, this method can be called as part of getting the
1840 // LOCATION property or to export() a file to be include()d. However
1841 // there is no cmGeneratorTarget at configure-time, so search the SOURCES
1842 // for TARGET_OBJECTS instead for backwards compatibility with OLD
1843 // behavior of CMP0024 and CMP0026 only.
1845 cmBTStringRange sourceEntries = this->Target->GetSourceEntries();
1846 for (auto const& entry : sourceEntries) {
1847 std::vector<std::string> items = cmExpandedList(entry.Value);
1848 for (std::string const& item : items) {
1849 if (cmHasLiteralPrefix(item, "$<TARGET_OBJECTS:") &&
1850 item.back() == '>') {
1853 files.emplace_back(item);
1859 std::vector<std::string> debugProperties;
1860 this->Makefile->GetDefExpandList("CMAKE_DEBUG_TARGET_PROPERTIES",
1864 !this->DebugSourcesDone && cm::contains(debugProperties, "SOURCES");
1866 if (this->LocalGenerator->GetGlobalGenerator()->GetConfigureDoneCMP0026()) {
1867 this->DebugSourcesDone = true;
1870 cmGeneratorExpressionDAGChecker dagChecker(this, "SOURCES", nullptr,
1873 EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries(
1874 this, config, std::string(), &dagChecker, this->SourceEntries);
1876 std::unordered_set<std::string> uniqueSrcs;
1877 bool contextDependentDirectSources =
1878 processSources(this, entries, files, uniqueSrcs, debugSources);
1880 // Collect INTERFACE_SOURCES of all direct link-dependencies.
1881 EvaluatedTargetPropertyEntries linkInterfaceSourcesEntries;
1882 AddInterfaceEntries(this, config, "INTERFACE_SOURCES", std::string(),
1883 &dagChecker, linkInterfaceSourcesEntries,
1884 IncludeRuntimeInterface::No, LinkInterfaceFor::Usage);
1885 std::vector<std::string>::size_type numFilesBefore = files.size();
1886 bool contextDependentInterfaceSources = processSources(
1887 this, linkInterfaceSourcesEntries, files, uniqueSrcs, debugSources);
1889 // Collect TARGET_OBJECTS of direct object link-dependencies.
1890 bool contextDependentObjects = false;
1891 std::vector<std::string>::size_type numFilesBefore2 = files.size();
1892 if (this->GetType() != cmStateEnums::OBJECT_LIBRARY) {
1893 EvaluatedTargetPropertyEntries linkObjectsEntries;
1894 AddObjectEntries(this, config, &dagChecker, linkObjectsEntries);
1895 contextDependentObjects = processSources(this, linkObjectsEntries, files,
1896 uniqueSrcs, debugSources);
1899 // Collect this target's file sets.
1900 std::vector<std::string>::size_type numFilesBefore3 = files.size();
1901 EvaluatedTargetPropertyEntries fileSetEntries;
1902 AddFileSetEntries(this, config, &dagChecker, fileSetEntries);
1903 bool contextDependentFileSets =
1904 processSources(this, fileSetEntries, files, uniqueSrcs, debugSources);
1906 // Determine if sources are context-dependent or not.
1907 if (!contextDependentDirectSources &&
1908 !(contextDependentInterfaceSources && numFilesBefore < files.size()) &&
1909 !(contextDependentObjects && numFilesBefore2 < files.size()) &&
1910 !(contextDependentFileSets && numFilesBefore3 < files.size())) {
1911 this->SourcesAreContextDependent = Tribool::False;
1913 this->SourcesAreContextDependent = Tribool::True;
1919 void cmGeneratorTarget::GetSourceFiles(std::vector<cmSourceFile*>& files,
1920 const std::string& config) const
1922 std::vector<BT<cmSourceFile*>> tmp = this->GetSourceFiles(config);
1923 files.reserve(tmp.size());
1924 for (BT<cmSourceFile*>& v : tmp) {
1925 files.push_back(v.Value);
1929 std::vector<BT<cmSourceFile*>> cmGeneratorTarget::GetSourceFiles(
1930 std::string const& config) const
1932 std::vector<BT<cmSourceFile*>> files;
1933 if (!this->GlobalGenerator->GetConfigureDoneCMP0026()) {
1934 // Since we are still configuring not all sources may exist yet,
1935 // so we need to avoid full source classification because that
1936 // requires the absolute paths to all sources to be determined.
1937 // Since this is only for compatibility with old policies that
1938 // projects should not depend on anymore, just compute the files
1939 // without memoizing them.
1940 std::vector<BT<std::string>> srcs = this->GetSourceFilePaths(config);
1941 std::set<cmSourceFile*> emitted;
1942 for (BT<std::string> const& s : srcs) {
1943 cmSourceFile* sf = this->Makefile->GetOrCreateSource(s.Value);
1944 if (emitted.insert(sf).second) {
1945 files.emplace_back(sf, s.Backtrace);
1951 KindedSources const& kinded = this->GetKindedSources(config);
1952 files.reserve(kinded.Sources.size());
1953 for (SourceAndKind const& si : kinded.Sources) {
1954 files.push_back(si.Source);
1959 void cmGeneratorTarget::GetSourceFilesWithoutObjectLibraries(
1960 std::vector<cmSourceFile*>& files, const std::string& config) const
1962 std::vector<BT<cmSourceFile*>> tmp =
1963 this->GetSourceFilesWithoutObjectLibraries(config);
1964 files.reserve(tmp.size());
1965 for (BT<cmSourceFile*>& v : tmp) {
1966 files.push_back(v.Value);
1970 std::vector<BT<cmSourceFile*>>
1971 cmGeneratorTarget::GetSourceFilesWithoutObjectLibraries(
1972 std::string const& config) const
1974 std::vector<BT<cmSourceFile*>> files;
1975 KindedSources const& kinded = this->GetKindedSources(config);
1976 files.reserve(kinded.Sources.size());
1977 for (SourceAndKind const& si : kinded.Sources) {
1978 if (si.Source.Value->GetObjectLibrary().empty()) {
1979 files.push_back(si.Source);
1985 cmGeneratorTarget::KindedSources const& cmGeneratorTarget::GetKindedSources(
1986 std::string const& config) const
1988 // If we already processed one configuration and found no dependency
1989 // on configuration then always use the one result.
1990 if (this->SourcesAreContextDependent == Tribool::False) {
1991 return this->KindedSourcesMap.begin()->second;
1994 // Lookup any existing link implementation for this configuration.
1995 std::string const key = cmSystemTools::UpperCase(config);
1996 auto it = this->KindedSourcesMap.find(key);
1997 if (it != this->KindedSourcesMap.end()) {
1998 if (!it->second.Initialized) {
1999 std::ostringstream e;
2000 e << "The SOURCES of \"" << this->GetName()
2001 << "\" use a generator expression that depends on the "
2002 "SOURCES themselves.";
2003 this->GlobalGenerator->GetCMakeInstance()->IssueMessage(
2004 MessageType::FATAL_ERROR, e.str(), this->GetBacktrace());
2005 static KindedSources empty;
2011 // Add an entry to the map for this configuration.
2012 KindedSources& files = this->KindedSourcesMap[key];
2013 this->ComputeKindedSources(files, config);
2014 files.Initialized = true;
2018 void cmGeneratorTarget::ComputeKindedSources(KindedSources& files,
2019 std::string const& config) const
2021 // Get the source file paths by string.
2022 std::vector<BT<std::string>> srcs = this->GetSourceFilePaths(config);
2024 cmsys::RegularExpression header_regex(CM_HEADER_REGEX);
2025 std::vector<cmSourceFile*> badObjLib;
2027 std::set<cmSourceFile*> emitted;
2028 for (BT<std::string> const& s : srcs) {
2029 // Create each source at most once.
2030 cmSourceFile* sf = this->Makefile->GetOrCreateSource(s.Value);
2031 if (!emitted.insert(sf).second) {
2035 // Compute the kind (classification) of this source file.
2037 std::string ext = cmSystemTools::LowerCase(sf->GetExtension());
2038 if (sf->GetCustomCommand()) {
2039 kind = SourceKindCustomCommand;
2040 } else if (this->Target->GetType() == cmStateEnums::UTILITY ||
2041 this->Target->GetType() == cmStateEnums::INTERFACE_LIBRARY
2042 // XXX(clang-tidy): https://bugs.llvm.org/show_bug.cgi?id=44165
2043 // NOLINTNEXTLINE(bugprone-branch-clone)
2045 kind = SourceKindExtra;
2046 } else if (this->IsSourceFilePartOfUnityBatch(sf->ResolveFullPath())) {
2047 kind = SourceKindUnityBatched;
2048 // XXX(clang-tidy): https://bugs.llvm.org/show_bug.cgi?id=44165
2049 // NOLINTNEXTLINE(bugprone-branch-clone)
2050 } else if (sf->GetPropertyAsBool("HEADER_FILE_ONLY")) {
2051 kind = SourceKindHeader;
2052 } else if (sf->GetPropertyAsBool("EXTERNAL_OBJECT")) {
2053 kind = SourceKindExternalObject;
2054 } else if (!sf->GetOrDetermineLanguage().empty()) {
2055 kind = SourceKindObjectSource;
2056 } else if (ext == "def") {
2057 kind = SourceKindModuleDefinition;
2058 if (this->GetType() == cmStateEnums::OBJECT_LIBRARY) {
2059 badObjLib.push_back(sf);
2061 } else if (ext == "idl") {
2062 kind = SourceKindIDL;
2063 if (this->GetType() == cmStateEnums::OBJECT_LIBRARY) {
2064 badObjLib.push_back(sf);
2066 } else if (ext == "resx") {
2067 kind = SourceKindResx;
2068 } else if (ext == "appxmanifest") {
2069 kind = SourceKindAppManifest;
2070 } else if (ext == "manifest") {
2071 if (sf->GetPropertyAsBool("VS_DEPLOYMENT_CONTENT")) {
2072 kind = SourceKindExtra;
2074 kind = SourceKindManifest;
2076 } else if (ext == "pfx") {
2077 kind = SourceKindCertificate;
2078 } else if (ext == "xaml") {
2079 kind = SourceKindXaml;
2080 } else if (header_regex.find(sf->ResolveFullPath())) {
2081 kind = SourceKindHeader;
2083 kind = SourceKindExtra;
2086 // Save this classified source file in the result vector.
2087 files.Sources.push_back({ BT<cmSourceFile*>(sf, s.Backtrace), kind });
2090 if (!badObjLib.empty()) {
2091 std::ostringstream e;
2092 e << "OBJECT library \"" << this->GetName() << "\" contains:\n";
2093 for (cmSourceFile* i : badObjLib) {
2094 e << " " << i->GetLocation().GetName() << "\n";
2096 e << "but may contain only sources that compile, header files, and "
2097 "other files that would not affect linking of a normal library.";
2098 this->GlobalGenerator->GetCMakeInstance()->IssueMessage(
2099 MessageType::FATAL_ERROR, e.str(), this->GetBacktrace());
2103 std::vector<cmGeneratorTarget::AllConfigSource> const&
2104 cmGeneratorTarget::GetAllConfigSources() const
2106 if (this->AllConfigSources.empty()) {
2107 this->ComputeAllConfigSources();
2109 return this->AllConfigSources;
2112 void cmGeneratorTarget::ComputeAllConfigSources() const
2114 std::vector<std::string> configs =
2115 this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
2117 std::map<cmSourceFile const*, size_t> index;
2119 for (size_t ci = 0; ci < configs.size(); ++ci) {
2120 KindedSources const& sources = this->GetKindedSources(configs[ci]);
2121 for (SourceAndKind const& src : sources.Sources) {
2122 auto mi = index.find(src.Source.Value);
2123 if (mi == index.end()) {
2124 AllConfigSource acs;
2125 acs.Source = src.Source.Value;
2126 acs.Kind = src.Kind;
2127 this->AllConfigSources.push_back(std::move(acs));
2128 std::map<cmSourceFile const*, size_t>::value_type entry(
2129 src.Source.Value, this->AllConfigSources.size() - 1);
2130 mi = index.insert(entry).first;
2132 this->AllConfigSources[mi->second].Configs.push_back(ci);
2137 std::vector<cmGeneratorTarget::AllConfigSource>
2138 cmGeneratorTarget::GetAllConfigSources(SourceKind kind) const
2140 std::vector<AllConfigSource> result;
2141 for (AllConfigSource const& source : this->GetAllConfigSources()) {
2142 if (source.Kind == kind) {
2143 result.push_back(source);
2149 std::set<std::string> cmGeneratorTarget::GetAllConfigCompileLanguages() const
2151 std::set<std::string> languages;
2152 std::vector<AllConfigSource> const& sources = this->GetAllConfigSources();
2153 for (AllConfigSource const& si : sources) {
2154 std::string const& lang = si.Source->GetOrDetermineLanguage();
2155 if (!lang.empty()) {
2156 languages.emplace(lang);
2162 std::string cmGeneratorTarget::GetCompilePDBName(
2163 const std::string& config) const
2168 this->GetFullNameInternal(config, cmStateEnums::RuntimeBinaryArtifact,
2169 prefix, base, suffix);
2171 // Check for a per-configuration output directory target property.
2172 std::string configUpper = cmSystemTools::UpperCase(config);
2173 std::string configProp = cmStrCat("COMPILE_PDB_NAME_", configUpper);
2174 cmValue config_name = this->GetProperty(configProp);
2175 if (cmNonempty(config_name)) {
2176 return prefix + *config_name + ".pdb";
2179 cmValue name = this->GetProperty("COMPILE_PDB_NAME");
2180 if (cmNonempty(name)) {
2181 return prefix + *name + ".pdb";
2187 std::string cmGeneratorTarget::GetCompilePDBPath(
2188 const std::string& config) const
2190 std::string dir = this->GetCompilePDBDirectory(config);
2191 std::string name = this->GetCompilePDBName(config);
2192 if (dir.empty() && !name.empty() && this->HaveWellDefinedOutputFiles()) {
2193 dir = this->GetPDBDirectory(config);
2201 bool cmGeneratorTarget::HasSOName(const std::string& config) const
2203 // soname is supported only for shared libraries and modules,
2204 // and then only when the platform supports an soname flag.
2205 return ((this->GetType() == cmStateEnums::SHARED_LIBRARY) &&
2206 !this->GetPropertyAsBool("NO_SONAME") &&
2207 this->Makefile->GetSONameFlag(this->GetLinkerLanguage(config)));
2210 bool cmGeneratorTarget::NeedRelinkBeforeInstall(
2211 const std::string& config) const
2213 // Only executables and shared libraries can have an rpath and may
2215 if (this->GetType() != cmStateEnums::EXECUTABLE &&
2216 this->GetType() != cmStateEnums::SHARED_LIBRARY &&
2217 this->GetType() != cmStateEnums::MODULE_LIBRARY) {
2221 // If there is no install location this target will not be installed
2222 // and therefore does not need relinking.
2223 if (!this->Target->GetHaveInstallRule()) {
2227 // If skipping all rpaths completely then no relinking is needed.
2228 if (this->Makefile->IsOn("CMAKE_SKIP_RPATH")) {
2232 // If building with the install-tree rpath no relinking is needed.
2233 if (this->GetPropertyAsBool("BUILD_WITH_INSTALL_RPATH")) {
2237 // If chrpath is going to be used no relinking is needed.
2238 if (this->IsChrpathUsed(config)) {
2242 // Check for rpath support on this platform.
2243 std::string ll = this->GetLinkerLanguage(config);
2245 std::string flagVar =
2246 cmStrCat("CMAKE_SHARED_LIBRARY_RUNTIME_", ll, "_FLAG");
2247 if (!this->Makefile->IsSet(flagVar)) {
2248 // There is no rpath support on this platform so nothing needs
2253 // No linker language is known. This error will be reported by
2258 // If either a build or install tree rpath is set then the rpath
2259 // will likely change between the build tree and install tree and
2260 // this target must be relinked.
2262 this->HaveBuildTreeRPATH(config) || this->HaveInstallTreeRPATH(config);
2263 bool is_ninja = this->LocalGenerator->GetGlobalGenerator()->IsNinja();
2265 if (have_rpath && is_ninja) {
2266 std::ostringstream w;
2267 /* clang-format off */
2269 "The install of the " << this->GetName() << " target requires changing "
2270 "an RPATH from the build tree, but this is not supported with the Ninja "
2271 "generator unless on an ELF-based or XCOFF-based platform. "
2272 "The CMAKE_BUILD_WITH_INSTALL_RPATH variable may be set to avoid this "
2275 /* clang-format on */
2277 cmake* cm = this->LocalGenerator->GetCMakeInstance();
2278 cm->IssueMessage(MessageType::FATAL_ERROR, w.str(), this->GetBacktrace());
2284 bool cmGeneratorTarget::IsChrpathUsed(const std::string& config) const
2286 // Only certain target types have an rpath.
2287 if (!(this->GetType() == cmStateEnums::SHARED_LIBRARY ||
2288 this->GetType() == cmStateEnums::MODULE_LIBRARY ||
2289 this->GetType() == cmStateEnums::EXECUTABLE)) {
2293 // If the target will not be installed we do not need to change its
2295 if (!this->Target->GetHaveInstallRule()) {
2299 // Skip chrpath if skipping rpath altogether.
2300 if (this->Makefile->IsOn("CMAKE_SKIP_RPATH")) {
2304 // Skip chrpath if it does not need to be changed at install time.
2305 if (this->GetPropertyAsBool("BUILD_WITH_INSTALL_RPATH")) {
2309 // Allow the user to disable builtin chrpath explicitly.
2310 if (this->Makefile->IsOn("CMAKE_NO_BUILTIN_CHRPATH")) {
2314 if (this->Makefile->IsOn("CMAKE_PLATFORM_HAS_INSTALLNAME")) {
2318 // Enable if the rpath flag uses a separator and the target uses
2319 // binaries we know how to edit.
2320 std::string ll = this->GetLinkerLanguage(config);
2322 std::string sepVar =
2323 cmStrCat("CMAKE_SHARED_LIBRARY_RUNTIME_", ll, "_FLAG_SEP");
2324 cmValue sep = this->Makefile->GetDefinition(sepVar);
2325 if (cmNonempty(sep)) {
2326 // TODO: Add binary format check to ABI detection and get rid of
2327 // CMAKE_EXECUTABLE_FORMAT.
2329 this->Makefile->GetDefinition("CMAKE_EXECUTABLE_FORMAT")) {
2330 if (*fmt == "ELF") {
2333 #if defined(CMake_USE_XCOFF_PARSER)
2334 if (*fmt == "XCOFF") {
2344 bool cmGeneratorTarget::IsImportedSharedLibWithoutSOName(
2345 const std::string& config) const
2347 if (this->IsImported() && this->GetType() == cmStateEnums::SHARED_LIBRARY) {
2348 if (cmGeneratorTarget::ImportInfo const* info =
2349 this->GetImportInfo(config)) {
2350 return info->NoSOName;
2356 bool cmGeneratorTarget::HasMacOSXRpathInstallNameDir(
2357 const std::string& config) const
2359 TargetPtrToBoolMap& cache = this->MacOSXRpathInstallNameDirCache[config];
2360 const auto lookup = cache.find(this->Target);
2362 if (lookup != cache.cend()) {
2363 return lookup->second;
2366 const bool result = this->DetermineHasMacOSXRpathInstallNameDir(config);
2367 cache[this->Target] = result;
2371 bool cmGeneratorTarget::DetermineHasMacOSXRpathInstallNameDir(
2372 const std::string& config) const
2374 bool install_name_is_rpath = false;
2375 bool macosx_rpath = false;
2377 if (!this->IsImported()) {
2378 if (this->GetType() != cmStateEnums::SHARED_LIBRARY) {
2381 cmValue install_name = this->GetProperty("INSTALL_NAME_DIR");
2382 bool use_install_name = this->MacOSXUseInstallNameDir();
2383 if (install_name && use_install_name && *install_name == "@rpath") {
2384 install_name_is_rpath = true;
2385 } else if (install_name && use_install_name) {
2388 if (!install_name_is_rpath) {
2389 macosx_rpath = this->MacOSXRpathInstallNameDirDefault();
2392 // Lookup the imported soname.
2393 if (cmGeneratorTarget::ImportInfo const* info =
2394 this->GetImportInfo(config)) {
2395 if (!info->NoSOName && !info->SOName.empty()) {
2396 if (cmHasLiteralPrefix(info->SOName, "@rpath/")) {
2397 install_name_is_rpath = true;
2400 std::string install_name;
2401 cmSystemTools::GuessLibraryInstallName(info->Location, install_name);
2402 if (install_name.find("@rpath") != std::string::npos) {
2403 install_name_is_rpath = true;
2409 if (!install_name_is_rpath && !macosx_rpath) {
2413 if (!this->Makefile->IsSet("CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG")) {
2414 std::ostringstream w;
2415 w << "Attempting to use ";
2417 w << "MACOSX_RPATH";
2421 w << " without CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG being set.";
2422 w << " This could be because you are using a Mac OS X version";
2423 w << " less than 10.5 or because CMake's platform configuration is";
2425 cmake* cm = this->LocalGenerator->GetCMakeInstance();
2426 cm->IssueMessage(MessageType::FATAL_ERROR, w.str(), this->GetBacktrace());
2432 bool cmGeneratorTarget::MacOSXRpathInstallNameDirDefault() const
2434 // we can't do rpaths when unsupported
2435 if (!this->Makefile->IsSet("CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG")) {
2439 cmValue macosx_rpath_str = this->GetProperty("MACOSX_RPATH");
2440 if (macosx_rpath_str) {
2441 return this->GetPropertyAsBool("MACOSX_RPATH");
2444 cmPolicies::PolicyStatus cmp0042 = this->GetPolicyStatusCMP0042();
2446 if (cmp0042 == cmPolicies::WARN) {
2447 this->LocalGenerator->GetGlobalGenerator()->AddCMP0042WarnTarget(
2451 return cmp0042 == cmPolicies::NEW;
2454 bool cmGeneratorTarget::MacOSXUseInstallNameDir() const
2456 cmValue build_with_install_name =
2457 this->GetProperty("BUILD_WITH_INSTALL_NAME_DIR");
2458 if (build_with_install_name) {
2459 return cmIsOn(*build_with_install_name);
2462 cmPolicies::PolicyStatus cmp0068 = this->GetPolicyStatusCMP0068();
2463 if (cmp0068 == cmPolicies::NEW) {
2467 bool use_install_name = this->GetPropertyAsBool("BUILD_WITH_INSTALL_RPATH");
2469 if (use_install_name && cmp0068 == cmPolicies::WARN) {
2470 this->LocalGenerator->GetGlobalGenerator()->AddCMP0068WarnTarget(
2474 return use_install_name;
2477 bool cmGeneratorTarget::CanGenerateInstallNameDir(
2478 InstallNameType name_type) const
2480 cmPolicies::PolicyStatus cmp0068 = this->GetPolicyStatusCMP0068();
2482 if (cmp0068 == cmPolicies::NEW) {
2486 bool skip = this->Makefile->IsOn("CMAKE_SKIP_RPATH");
2487 if (name_type == INSTALL_NAME_FOR_INSTALL) {
2488 skip |= this->Makefile->IsOn("CMAKE_SKIP_INSTALL_RPATH");
2490 skip |= this->GetPropertyAsBool("SKIP_BUILD_RPATH");
2493 if (skip && cmp0068 == cmPolicies::WARN) {
2494 this->LocalGenerator->GetGlobalGenerator()->AddCMP0068WarnTarget(
2501 std::string cmGeneratorTarget::GetSOName(const std::string& config) const
2503 if (this->IsImported()) {
2504 // Lookup the imported soname.
2505 if (cmGeneratorTarget::ImportInfo const* info =
2506 this->GetImportInfo(config)) {
2507 if (info->NoSOName) {
2508 // The imported library has no builtin soname so the name
2509 // searched at runtime will be just the filename.
2510 return cmSystemTools::GetFilenameName(info->Location);
2512 // Use the soname given if any.
2513 if (this->IsFrameworkOnApple()) {
2514 cmsys::RegularExpressionMatch match;
2515 if (FrameworkRegularExpression.find(info->SOName.c_str(), match)) {
2516 auto frameworkName = match.match(2);
2517 auto fileName = match.match(3);
2518 return cmStrCat(frameworkName, ".framework/", fileName);
2521 if (cmHasLiteralPrefix(info->SOName, "@rpath/")) {
2522 return info->SOName.substr(cmStrLen("@rpath/"));
2524 return info->SOName;
2528 // Compute the soname that will be built.
2529 return this->GetLibraryNames(config).SharedObject;
2533 bool shouldAddFullLevel(cmGeneratorTarget::BundleDirectoryLevel level)
2535 return level == cmGeneratorTarget::FullLevel;
2538 bool shouldAddContentLevel(cmGeneratorTarget::BundleDirectoryLevel level)
2540 return level == cmGeneratorTarget::ContentLevel || shouldAddFullLevel(level);
2544 std::string cmGeneratorTarget::GetAppBundleDirectory(
2545 const std::string& config, BundleDirectoryLevel level) const
2547 std::string fpath = cmStrCat(
2548 this->GetFullName(config, cmStateEnums::RuntimeBinaryArtifact), '.');
2549 cmValue ext = this->GetProperty("BUNDLE_EXTENSION");
2550 fpath += (ext ? *ext : "app");
2551 if (shouldAddContentLevel(level) &&
2552 !this->Makefile->PlatformIsAppleEmbedded()) {
2553 fpath += "/Contents";
2554 if (shouldAddFullLevel(level)) {
2561 bool cmGeneratorTarget::IsBundleOnApple() const
2563 return this->IsFrameworkOnApple() || this->IsAppBundleOnApple() ||
2564 this->IsCFBundleOnApple();
2567 bool cmGeneratorTarget::IsWin32Executable(const std::string& config) const
2569 return cmIsOn(cmGeneratorExpression::Evaluate(
2570 this->GetSafeProperty("WIN32_EXECUTABLE"), this->LocalGenerator, config));
2573 std::string cmGeneratorTarget::GetCFBundleDirectory(
2574 const std::string& config, BundleDirectoryLevel level) const
2576 std::string fpath = cmStrCat(
2577 this->GetOutputName(config, cmStateEnums::RuntimeBinaryArtifact), '.');
2579 if (cmValue p = this->GetProperty("BUNDLE_EXTENSION")) {
2582 if (this->IsXCTestOnApple()) {
2589 if (shouldAddContentLevel(level) &&
2590 !this->Makefile->PlatformIsAppleEmbedded()) {
2591 fpath += "/Contents";
2592 if (shouldAddFullLevel(level)) {
2599 std::string cmGeneratorTarget::GetFrameworkDirectory(
2600 const std::string& config, BundleDirectoryLevel level) const
2602 std::string fpath = cmStrCat(
2603 this->GetOutputName(config, cmStateEnums::RuntimeBinaryArtifact), '.');
2604 cmValue ext = this->GetProperty("BUNDLE_EXTENSION");
2605 fpath += (ext ? *ext : "framework");
2606 if (shouldAddFullLevel(level) &&
2607 !this->Makefile->PlatformIsAppleEmbedded()) {
2608 fpath += "/Versions/";
2609 fpath += this->GetFrameworkVersion();
2614 std::string cmGeneratorTarget::GetFullName(
2615 const std::string& config, cmStateEnums::ArtifactType artifact) const
2617 if (this->IsImported()) {
2618 return this->GetFullNameImported(config, artifact);
2620 return this->GetFullNameInternal(config, artifact);
2623 std::string cmGeneratorTarget::GetInstallNameDirForBuildTree(
2624 const std::string& config) const
2626 if (this->Makefile->IsOn("CMAKE_PLATFORM_HAS_INSTALLNAME")) {
2628 // If building directly for installation then the build tree install_name
2629 // is the same as the install tree.
2630 if (this->MacOSXUseInstallNameDir()) {
2631 std::string installPrefix =
2632 this->Makefile->GetSafeDefinition("CMAKE_INSTALL_PREFIX");
2633 return this->GetInstallNameDirForInstallTree(config, installPrefix);
2636 // Use the build tree directory for the target.
2637 if (this->CanGenerateInstallNameDir(INSTALL_NAME_FOR_BUILD)) {
2639 if (this->MacOSXRpathInstallNameDirDefault()) {
2642 dir = this->GetDirectory(config);
2651 std::string cmGeneratorTarget::GetInstallNameDirForInstallTree(
2652 const std::string& config, const std::string& installPrefix) const
2654 if (this->Makefile->IsOn("CMAKE_PLATFORM_HAS_INSTALLNAME")) {
2656 cmValue install_name_dir = this->GetProperty("INSTALL_NAME_DIR");
2658 if (this->CanGenerateInstallNameDir(INSTALL_NAME_FOR_INSTALL)) {
2659 if (cmNonempty(install_name_dir)) {
2660 dir = *install_name_dir;
2661 cmGeneratorExpression::ReplaceInstallPrefix(dir, installPrefix);
2663 cmGeneratorExpression::Evaluate(dir, this->LocalGenerator, config);
2665 dir = cmStrCat(dir, '/');
2669 if (!install_name_dir) {
2670 if (this->MacOSXRpathInstallNameDirDefault()) {
2679 cmListFileBacktrace cmGeneratorTarget::GetBacktrace() const
2681 return this->Target->GetBacktrace();
2684 const std::set<BT<std::pair<std::string, bool>>>&
2685 cmGeneratorTarget::GetUtilities() const
2687 return this->Target->GetUtilities();
2690 bool cmGeneratorTarget::HaveWellDefinedOutputFiles() const
2692 return this->GetType() == cmStateEnums::STATIC_LIBRARY ||
2693 this->GetType() == cmStateEnums::SHARED_LIBRARY ||
2694 this->GetType() == cmStateEnums::MODULE_LIBRARY ||
2695 this->GetType() == cmStateEnums::OBJECT_LIBRARY ||
2696 this->GetType() == cmStateEnums::EXECUTABLE;
2699 const std::string* cmGeneratorTarget::GetExportMacro() const
2701 // Define the symbol for targets that export symbols.
2702 if (this->GetType() == cmStateEnums::SHARED_LIBRARY ||
2703 this->GetType() == cmStateEnums::MODULE_LIBRARY ||
2704 this->IsExecutableWithExports()) {
2705 if (cmValue custom_export_name = this->GetProperty("DEFINE_SYMBOL")) {
2706 this->ExportMacro = *custom_export_name;
2708 std::string in = cmStrCat(this->GetName(), "_EXPORTS");
2709 this->ExportMacro = cmSystemTools::MakeCidentifier(in);
2711 return &this->ExportMacro;
2716 class cmTargetCollectLinkLanguages
2719 cmTargetCollectLinkLanguages(cmGeneratorTarget const* target,
2721 std::unordered_set<std::string>& languages,
2722 cmGeneratorTarget const* head, bool secondPass)
2723 : Config(std::move(config))
2724 , Languages(languages)
2726 , SecondPass(secondPass)
2728 this->Visited.insert(target);
2731 void Visit(cmLinkItem const& item)
2736 if (!this->Visited.insert(item.Target).second) {
2739 cmLinkInterface const* iface = item.Target->GetLinkInterface(
2740 this->Config, this->HeadTarget, this->SecondPass);
2744 if (iface->HadLinkLanguageSensitiveCondition) {
2745 this->HadLinkLanguageSensitiveCondition = true;
2748 for (std::string const& language : iface->Languages) {
2749 this->Languages.insert(language);
2752 for (cmLinkItem const& lib : iface->Libraries) {
2757 bool GetHadLinkLanguageSensitiveCondition() const
2759 return this->HadLinkLanguageSensitiveCondition;
2764 std::unordered_set<std::string>& Languages;
2765 cmGeneratorTarget const* HeadTarget;
2766 std::set<cmGeneratorTarget const*> Visited;
2768 bool HadLinkLanguageSensitiveCondition = false;
2771 cmGeneratorTarget::LinkClosure const* cmGeneratorTarget::GetLinkClosure(
2772 const std::string& config) const
2774 // There is no link implementation for targets that cannot compile sources.
2775 if (!this->CanCompileSources()) {
2776 static LinkClosure const empty = { {}, {} };
2780 std::string key(cmSystemTools::UpperCase(config));
2781 auto i = this->LinkClosureMap.find(key);
2782 if (i == this->LinkClosureMap.end()) {
2784 this->ComputeLinkClosure(config, lc);
2785 LinkClosureMapType::value_type entry(key, lc);
2786 i = this->LinkClosureMap.insert(entry).first;
2791 class cmTargetSelectLinker
2794 cmGeneratorTarget const* Target;
2795 cmGlobalGenerator* GG;
2796 std::set<std::string> Preferred;
2799 cmTargetSelectLinker(cmGeneratorTarget const* target)
2802 this->GG = this->Target->GetLocalGenerator()->GetGlobalGenerator();
2804 void Consider(const std::string& lang)
2806 int preference = this->GG->GetLinkerPreference(lang);
2807 if (preference > this->Preference) {
2808 this->Preference = preference;
2809 this->Preferred.clear();
2811 if (preference == this->Preference) {
2812 this->Preferred.insert(lang);
2815 std::string Choose()
2817 if (this->Preferred.empty()) {
2820 if (this->Preferred.size() > 1) {
2821 std::ostringstream e;
2822 e << "Target " << this->Target->GetName()
2823 << " contains multiple languages with the highest linker preference"
2824 << " (" << this->Preference << "):\n";
2825 for (std::string const& li : this->Preferred) {
2826 e << " " << li << "\n";
2828 e << "Set the LINKER_LANGUAGE property for this target.";
2829 cmake* cm = this->Target->GetLocalGenerator()->GetCMakeInstance();
2830 cm->IssueMessage(MessageType::FATAL_ERROR, e.str(),
2831 this->Target->GetBacktrace());
2833 return *this->Preferred.begin();
2837 bool cmGeneratorTarget::ComputeLinkClosure(const std::string& config,
2839 bool secondPass) const
2841 // Get languages built in this target.
2842 std::unordered_set<std::string> languages;
2843 cmLinkImplementation const* impl =
2844 this->GetLinkImplementation(config, LinkInterfaceFor::Link, secondPass);
2846 languages.insert(impl->Languages.cbegin(), impl->Languages.cend());
2848 // Add interface languages from linked targets.
2849 // cmTargetCollectLinkLanguages cll(this, config, languages, this,
2851 cmTargetCollectLinkLanguages cll(this, config, languages, this, secondPass);
2852 for (cmLinkImplItem const& lib : impl->Libraries) {
2856 // Store the transitive closure of languages.
2857 cm::append(lc.Languages, languages);
2859 // Choose the language whose linker should be used.
2860 if (secondPass || lc.LinkerLanguage.empty()) {
2861 // Find the language with the highest preference value.
2862 cmTargetSelectLinker tsl(this);
2864 // First select from the languages compiled directly in this target.
2865 for (std::string const& l : impl->Languages) {
2869 // Now consider languages that propagate from linked targets.
2870 for (std::string const& lang : languages) {
2871 std::string propagates =
2872 "CMAKE_" + lang + "_LINKER_PREFERENCE_PROPAGATES";
2873 if (this->Makefile->IsOn(propagates)) {
2878 lc.LinkerLanguage = tsl.Choose();
2881 return impl->HadLinkLanguageSensitiveCondition ||
2882 cll.GetHadLinkLanguageSensitiveCondition();
2885 void cmGeneratorTarget::ComputeLinkClosure(const std::string& config,
2886 LinkClosure& lc) const
2888 bool secondPass = false;
2891 LinkClosure linkClosure;
2892 linkClosure.LinkerLanguage = this->LinkerLanguage;
2894 bool hasHardCodedLinkerLanguage = this->Target->GetProperty("HAS_CXX") ||
2895 !this->Target->GetSafeProperty("LINKER_LANGUAGE").empty();
2897 // Get languages built in this target.
2898 secondPass = this->ComputeLinkClosure(config, linkClosure, false) &&
2899 !hasHardCodedLinkerLanguage;
2900 this->LinkerLanguage = linkClosure.LinkerLanguage;
2902 lc = std::move(linkClosure);
2907 LinkClosure linkClosure;
2909 this->ComputeLinkClosure(config, linkClosure, secondPass);
2910 lc = std::move(linkClosure);
2912 // linker language must not be changed between the two passes
2913 if (this->LinkerLanguage != lc.LinkerLanguage) {
2914 std::ostringstream e;
2915 e << "Evaluation of $<LINK_LANGUAGE:...> or $<LINK_LAND_AND_ID:...> "
2916 "changes\nthe linker language for target \""
2917 << this->GetName() << "\" (from '" << this->LinkerLanguage << "' to '"
2918 << lc.LinkerLanguage << "') which is invalid.";
2919 cmSystemTools::Error(e.str());
2924 void cmGeneratorTarget::GetFullNameComponents(
2925 std::string& prefix, std::string& base, std::string& suffix,
2926 const std::string& config, cmStateEnums::ArtifactType artifact) const
2928 this->GetFullNameInternal(config, artifact, prefix, base, suffix);
2931 std::string cmGeneratorTarget::BuildBundleDirectory(
2932 const std::string& base, const std::string& config,
2933 BundleDirectoryLevel level) const
2935 std::string fpath = base;
2936 if (this->IsAppBundleOnApple()) {
2937 fpath += this->GetAppBundleDirectory(config, level);
2939 if (this->IsFrameworkOnApple()) {
2940 fpath += this->GetFrameworkDirectory(config, level);
2942 if (this->IsCFBundleOnApple()) {
2943 fpath += this->GetCFBundleDirectory(config, level);
2948 std::string cmGeneratorTarget::GetMacContentDirectory(
2949 const std::string& config, cmStateEnums::ArtifactType artifact) const
2951 // Start with the output directory for the target.
2952 std::string fpath = cmStrCat(this->GetDirectory(config, artifact), '/');
2953 BundleDirectoryLevel level = ContentLevel;
2954 if (this->IsFrameworkOnApple()) {
2955 // additional files with a framework go into the version specific
2959 fpath = this->BuildBundleDirectory(fpath, config, level);
2963 std::string cmGeneratorTarget::GetEffectiveFolderName() const
2965 std::string effectiveFolder;
2967 if (!this->GlobalGenerator->UseFolderProperty()) {
2968 return effectiveFolder;
2971 cmValue targetFolder = this->GetProperty("FOLDER");
2973 effectiveFolder += *targetFolder;
2976 return effectiveFolder;
2979 cmGeneratorTarget::CompileInfo const* cmGeneratorTarget::GetCompileInfo(
2980 const std::string& config) const
2982 // There is no compile information for imported targets.
2983 if (this->IsImported()) {
2987 if (this->GetType() > cmStateEnums::OBJECT_LIBRARY) {
2988 std::string msg = cmStrCat("cmTarget::GetCompileInfo called for ",
2989 this->GetName(), " which has type ",
2990 cmState::GetTargetTypeName(this->GetType()));
2991 this->LocalGenerator->IssueMessage(MessageType::INTERNAL_ERROR, msg);
2995 // Lookup/compute/cache the compile information for this configuration.
2996 std::string config_upper;
2997 if (!config.empty()) {
2998 config_upper = cmSystemTools::UpperCase(config);
3000 auto i = this->CompileInfoMap.find(config_upper);
3001 if (i == this->CompileInfoMap.end()) {
3003 this->ComputePDBOutputDir("COMPILE_PDB", config, info.CompilePdbDir);
3004 CompileInfoMapType::value_type entry(config_upper, info);
3005 i = this->CompileInfoMap.insert(entry).first;
3010 cmGeneratorTarget::ModuleDefinitionInfo const*
3011 cmGeneratorTarget::GetModuleDefinitionInfo(std::string const& config) const
3013 // A module definition file only makes sense on certain target types.
3014 if (this->GetType() != cmStateEnums::SHARED_LIBRARY &&
3015 this->GetType() != cmStateEnums::MODULE_LIBRARY &&
3016 !this->IsExecutableWithExports()) {
3020 // Lookup/compute/cache the compile information for this configuration.
3021 std::string config_upper;
3022 if (!config.empty()) {
3023 config_upper = cmSystemTools::UpperCase(config);
3025 auto i = this->ModuleDefinitionInfoMap.find(config_upper);
3026 if (i == this->ModuleDefinitionInfoMap.end()) {
3027 ModuleDefinitionInfo info;
3028 this->ComputeModuleDefinitionInfo(config, info);
3029 ModuleDefinitionInfoMapType::value_type entry(config_upper, info);
3030 i = this->ModuleDefinitionInfoMap.insert(entry).first;
3035 void cmGeneratorTarget::ComputeModuleDefinitionInfo(
3036 std::string const& config, ModuleDefinitionInfo& info) const
3038 this->GetModuleDefinitionSources(info.Sources, config);
3039 info.WindowsExportAllSymbols =
3040 this->Makefile->IsOn("CMAKE_SUPPORT_WINDOWS_EXPORT_ALL_SYMBOLS") &&
3041 this->GetPropertyAsBool("WINDOWS_EXPORT_ALL_SYMBOLS");
3042 #if !defined(CMAKE_BOOTSTRAP)
3043 info.DefFileGenerated =
3044 info.WindowsExportAllSymbols || info.Sources.size() > 1;
3046 // Our __create_def helper is not available during CMake bootstrap.
3047 info.DefFileGenerated = false;
3049 if (info.DefFileGenerated) {
3051 this->GetObjectDirectory(config) /* has slash */ + "exports.def";
3052 } else if (!info.Sources.empty()) {
3053 info.DefFile = info.Sources.front()->GetFullPath();
3057 bool cmGeneratorTarget::IsDLLPlatform() const
3059 return this->Target->IsDLLPlatform();
3062 void cmGeneratorTarget::GetAutoUicOptions(std::vector<std::string>& result,
3063 const std::string& config) const
3066 this->GetLinkInterfaceDependentStringProperty("AUTOUIC_OPTIONS", config);
3071 cmGeneratorExpressionDAGChecker dagChecker(this, "AUTOUIC_OPTIONS", nullptr,
3073 cmExpandList(cmGeneratorExpression::Evaluate(prop, this->LocalGenerator,
3074 config, this, &dagChecker),
3078 static void processILibs(const std::string& config,
3079 cmGeneratorTarget const* headTarget,
3080 cmLinkItem const& item, cmGlobalGenerator* gg,
3081 std::vector<cmGeneratorTarget const*>& tgts,
3082 std::set<cmGeneratorTarget const*>& emitted)
3084 if (item.Target && emitted.insert(item.Target).second) {
3085 tgts.push_back(item.Target);
3086 if (cmLinkInterfaceLibraries const* iface =
3087 item.Target->GetLinkInterfaceLibraries(config, headTarget,
3088 LinkInterfaceFor::Usage)) {
3089 for (cmLinkItem const& lib : iface->Libraries) {
3090 processILibs(config, headTarget, lib, gg, tgts, emitted);
3096 const std::vector<const cmGeneratorTarget*>&
3097 cmGeneratorTarget::GetLinkImplementationClosure(
3098 const std::string& config) const
3100 // There is no link implementation for targets that cannot compile sources.
3101 if (!this->CanCompileSources()) {
3102 static std::vector<const cmGeneratorTarget*> const empty;
3106 LinkImplClosure& tgts = this->LinkImplClosureMap[config];
3109 std::set<cmGeneratorTarget const*> emitted;
3111 cmLinkImplementationLibraries const* impl =
3112 this->GetLinkImplementationLibraries(config, LinkInterfaceFor::Usage);
3115 for (cmLinkImplItem const& lib : impl->Libraries) {
3116 processILibs(config, this, lib,
3117 this->LocalGenerator->GetGlobalGenerator(), tgts, emitted);
3123 class cmTargetTraceDependencies
3126 cmTargetTraceDependencies(cmGeneratorTarget* target);
3130 cmGeneratorTarget* GeneratorTarget;
3131 cmMakefile* Makefile;
3132 cmLocalGenerator* LocalGenerator;
3133 cmGlobalGenerator const* GlobalGenerator;
3134 using SourceEntry = cmGeneratorTarget::SourceEntry;
3135 SourceEntry* CurrentEntry;
3136 std::queue<cmSourceFile*> SourceQueue;
3137 std::set<cmSourceFile*> SourcesQueued;
3138 using NameMapType = std::map<std::string, cmSourcesWithOutput>;
3139 NameMapType NameMap;
3140 std::vector<std::string> NewSources;
3142 void QueueSource(cmSourceFile* sf);
3143 void FollowName(std::string const& name);
3144 void FollowNames(std::vector<std::string> const& names);
3145 bool IsUtility(std::string const& dep);
3146 void CheckCustomCommand(cmCustomCommand const& cc);
3147 void CheckCustomCommands(const std::vector<cmCustomCommand>& commands);
3150 cmTargetTraceDependencies::cmTargetTraceDependencies(cmGeneratorTarget* target)
3151 : GeneratorTarget(target)
3154 this->Makefile = target->Target->GetMakefile();
3155 this->LocalGenerator = target->GetLocalGenerator();
3156 this->GlobalGenerator = this->LocalGenerator->GetGlobalGenerator();
3157 this->CurrentEntry = nullptr;
3159 // Queue all the source files already specified for the target.
3160 std::set<cmSourceFile*> emitted;
3161 std::vector<std::string> const& configs =
3162 this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
3163 for (std::string const& c : configs) {
3164 std::vector<cmSourceFile*> sources;
3165 this->GeneratorTarget->GetSourceFiles(sources, c);
3166 for (cmSourceFile* sf : sources) {
3167 const std::set<cmGeneratorTarget const*> tgts =
3168 this->GlobalGenerator->GetFilenameTargetDepends(sf);
3169 if (cm::contains(tgts, this->GeneratorTarget)) {
3170 std::ostringstream e;
3171 e << "Evaluation output file\n \"" << sf->ResolveFullPath()
3172 << "\"\ndepends on the sources of a target it is used in. This "
3173 "is a dependency loop and is not allowed.";
3174 this->GeneratorTarget->LocalGenerator->IssueMessage(
3175 MessageType::FATAL_ERROR, e.str());
3178 if (emitted.insert(sf).second && this->SourcesQueued.insert(sf).second) {
3179 this->SourceQueue.push(sf);
3184 // Queue pre-build, pre-link, and post-build rule dependencies.
3185 this->CheckCustomCommands(this->GeneratorTarget->GetPreBuildCommands());
3186 this->CheckCustomCommands(this->GeneratorTarget->GetPreLinkCommands());
3187 this->CheckCustomCommands(this->GeneratorTarget->GetPostBuildCommands());
3190 void cmTargetTraceDependencies::Trace()
3192 // Process one dependency at a time until the queue is empty.
3193 while (!this->SourceQueue.empty()) {
3194 // Get the next source from the queue.
3195 cmSourceFile* sf = this->SourceQueue.front();
3196 this->SourceQueue.pop();
3197 this->CurrentEntry = &this->GeneratorTarget->SourceDepends[sf];
3199 // Queue dependencies added explicitly by the user.
3200 if (cmValue additionalDeps = sf->GetProperty("OBJECT_DEPENDS")) {
3201 std::vector<std::string> objDeps = cmExpandedList(*additionalDeps);
3202 for (std::string& objDep : objDeps) {
3203 if (cmSystemTools::FileIsFullPath(objDep)) {
3204 objDep = cmSystemTools::CollapseFullPath(objDep);
3207 this->FollowNames(objDeps);
3210 // Queue the source needed to generate this file, if any.
3211 this->FollowName(sf->ResolveFullPath());
3213 // Queue dependencies added programmatically by commands.
3214 this->FollowNames(sf->GetDepends());
3216 // Queue custom command dependencies.
3217 if (cmCustomCommand const* cc = sf->GetCustomCommand()) {
3218 this->CheckCustomCommand(*cc);
3221 this->CurrentEntry = nullptr;
3223 this->GeneratorTarget->AddTracedSources(this->NewSources);
3226 void cmTargetTraceDependencies::QueueSource(cmSourceFile* sf)
3228 if (this->SourcesQueued.insert(sf).second) {
3229 this->SourceQueue.push(sf);
3231 // Make sure this file is in the target at the end.
3232 this->NewSources.push_back(sf->ResolveFullPath());
3236 void cmTargetTraceDependencies::FollowName(std::string const& name)
3238 // Use lower bound with key comparison to not repeat the search for the
3239 // insert position if the name could not be found (which is the common case).
3240 auto i = this->NameMap.lower_bound(name);
3241 if (i == this->NameMap.end() || i->first != name) {
3242 // Check if we know how to generate this file.
3243 cmSourcesWithOutput sources =
3244 this->LocalGenerator->GetSourcesWithOutput(name);
3245 // If we failed to find a target or source and we have a relative path, it
3246 // might be a valid source if made relative to the current binary
3248 if (!sources.Target && !sources.Source &&
3249 !cmSystemTools::FileIsFullPath(name)) {
3251 cmStrCat(this->Makefile->GetCurrentBinaryDirectory(), '/', name);
3252 fullname = cmSystemTools::CollapseFullPath(
3253 fullname, this->Makefile->GetHomeOutputDirectory());
3254 sources = this->LocalGenerator->GetSourcesWithOutput(fullname);
3256 i = this->NameMap.emplace_hint(i, name, sources);
3258 if (cmTarget* t = i->second.Target) {
3259 // The name is a byproduct of a utility target or a PRE_BUILD, PRE_LINK, or
3260 // POST_BUILD command.
3261 this->GeneratorTarget->Target->AddUtility(t->GetName(), false);
3263 if (cmSourceFile* sf = i->second.Source) {
3264 // For now only follow the dependency if the source file is not a
3265 // byproduct. Semantics of byproducts in a non-Ninja context will have to
3266 // be defined first.
3267 if (!i->second.SourceIsByproduct) {
3268 // Record the dependency we just followed.
3269 if (this->CurrentEntry) {
3270 this->CurrentEntry->Depends.push_back(sf);
3272 this->QueueSource(sf);
3277 void cmTargetTraceDependencies::FollowNames(
3278 std::vector<std::string> const& names)
3280 for (std::string const& name : names) {
3281 this->FollowName(name);
3285 bool cmTargetTraceDependencies::IsUtility(std::string const& dep)
3287 // Dependencies on targets (utilities) are supposed to be named by
3288 // just the target name. However for compatibility we support
3289 // naming the output file generated by the target (assuming there is
3290 // no output-name property which old code would not have set). In
3291 // that case the target name will be the file basename of the
3293 std::string util = cmSystemTools::GetFilenameName(dep);
3294 if (cmSystemTools::GetFilenameLastExtension(util) == ".exe") {
3295 util = cmSystemTools::GetFilenameWithoutLastExtension(util);
3298 // Check for a target with this name.
3299 if (cmGeneratorTarget* t =
3300 this->GeneratorTarget->GetLocalGenerator()->FindGeneratorTargetToUse(
3302 // If we find the target and the dep was given as a full path,
3303 // then make sure it was not a full path to something else, and
3304 // the fact that the name matched a target was just a coincidence.
3305 if (cmSystemTools::FileIsFullPath(dep)) {
3306 if (t->GetType() >= cmStateEnums::EXECUTABLE &&
3307 t->GetType() <= cmStateEnums::MODULE_LIBRARY) {
3308 // This is really only for compatibility so we do not need to
3309 // worry about configuration names and output names.
3310 std::string tLocation = t->GetLocationForBuild();
3311 tLocation = cmSystemTools::GetFilenamePath(tLocation);
3312 std::string depLocation = cmSystemTools::GetFilenamePath(dep);
3313 depLocation = cmSystemTools::CollapseFullPath(depLocation);
3314 tLocation = cmSystemTools::CollapseFullPath(tLocation);
3315 if (depLocation == tLocation) {
3316 this->GeneratorTarget->Target->AddUtility(util, false);
3321 // The original name of the dependency was not a full path. It
3322 // must name a target, so add the target-level dependency.
3323 this->GeneratorTarget->Target->AddUtility(util, true);
3328 // The dependency does not name a target built in this project.
3332 void cmTargetTraceDependencies::CheckCustomCommand(cmCustomCommand const& cc)
3334 // Collect dependencies referenced by all configurations.
3335 std::set<std::string> depends;
3336 for (std::string const& config :
3337 this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig)) {
3338 for (cmCustomCommandGenerator const& ccg :
3339 this->LocalGenerator->MakeCustomCommandGenerators(cc, config)) {
3340 // Collect target-level dependencies referenced in command lines.
3341 for (auto const& util : ccg.GetUtilities()) {
3342 this->GeneratorTarget->Target->AddUtility(util);
3345 // Collect file-level dependencies referenced in DEPENDS.
3346 depends.insert(ccg.GetDepends().begin(), ccg.GetDepends().end());
3350 // Queue file-level dependencies.
3351 for (std::string const& dep : depends) {
3352 if (!this->IsUtility(dep)) {
3353 // The dependency does not name a target and may be a file we
3354 // know how to generate. Queue it.
3355 this->FollowName(dep);
3360 void cmTargetTraceDependencies::CheckCustomCommands(
3361 const std::vector<cmCustomCommand>& commands)
3363 for (cmCustomCommand const& command : commands) {
3364 this->CheckCustomCommand(command);
3368 void cmGeneratorTarget::TraceDependencies()
3370 // CMake-generated targets have no dependencies to trace. Normally tracing
3371 // would find nothing anyway, but when building CMake itself the "install"
3372 // target command ends up referencing the "cmake" target but we do not
3373 // really want the dependency because "install" depend on "all" anyway.
3374 if (this->GetType() == cmStateEnums::GLOBAL_TARGET) {
3378 // Use a helper object to trace the dependencies.
3379 cmTargetTraceDependencies tracer(this);
3383 std::string cmGeneratorTarget::GetCompilePDBDirectory(
3384 const std::string& config) const
3386 if (CompileInfo const* info = this->GetCompileInfo(config)) {
3387 return info->CompilePdbDir;
3392 void cmGeneratorTarget::GetAppleArchs(const std::string& config,
3393 std::vector<std::string>& archVec) const
3395 if (!this->Makefile->IsOn("APPLE")) {
3398 cmValue archs = nullptr;
3399 if (!config.empty()) {
3400 std::string defVarName =
3401 cmStrCat("OSX_ARCHITECTURES_", cmSystemTools::UpperCase(config));
3402 archs = this->GetProperty(defVarName);
3405 archs = this->GetProperty("OSX_ARCHITECTURES");
3408 cmExpandList(*archs, archVec);
3410 if (archVec.empty()) {
3411 this->Makefile->GetDefExpandList("_CMAKE_APPLE_ARCHS_DEFAULT", archVec);
3415 void cmGeneratorTarget::AddExplicitLanguageFlags(std::string& flags,
3416 cmSourceFile const& sf) const
3418 cmValue lang = sf.GetProperty("LANGUAGE");
3423 switch (this->GetPolicyStatusCMP0119()) {
3424 case cmPolicies::WARN:
3426 case cmPolicies::OLD:
3427 // The OLD behavior is to not add explicit language flags.
3429 case cmPolicies::REQUIRED_ALWAYS:
3430 case cmPolicies::REQUIRED_IF_USED:
3431 case cmPolicies::NEW:
3432 // The NEW behavior is to add explicit language flags.
3436 this->LocalGenerator->AppendFeatureOptions(flags, *lang,
3437 "EXPLICIT_LANGUAGE");
3440 void cmGeneratorTarget::AddCUDAArchitectureFlags(cmBuildStep compileOrLink,
3441 const std::string& config,
3442 std::string& flags) const
3444 std::string property = this->GetSafeProperty("CUDA_ARCHITECTURES");
3446 if (property.empty()) {
3447 switch (this->GetPolicyStatusCMP0104()) {
3448 case cmPolicies::WARN:
3449 if (!this->LocalGenerator->GetCMakeInstance()->GetIsInTryCompile()) {
3450 this->Makefile->IssueMessage(
3451 MessageType::AUTHOR_WARNING,
3452 cmPolicies::GetPolicyWarning(cmPolicies::CMP0104) +
3453 "\nCUDA_ARCHITECTURES is empty for target \"" + this->GetName() +
3457 case cmPolicies::OLD:
3460 this->Makefile->IssueMessage(
3461 MessageType::FATAL_ERROR,
3462 "CUDA_ARCHITECTURES is empty for target \"" + this->GetName() +
3467 // If CUDA_ARCHITECTURES is false we don't add any architectures.
3468 if (cmIsOff(property)) {
3472 std::string const& compiler =
3473 this->Makefile->GetSafeDefinition("CMAKE_CUDA_COMPILER_ID");
3474 const bool ipoEnabled = this->IsIPOEnabled("CUDA", config);
3476 // Check for special modes: `all`, `all-major`.
3477 if (property == "all" || property == "all-major") {
3478 if (compiler == "NVIDIA" &&
3479 cmSystemTools::VersionCompare(
3480 cmSystemTools::OP_GREATER_EQUAL,
3481 this->Makefile->GetDefinition("CMAKE_CUDA_COMPILER_VERSION"),
3483 flags = cmStrCat(flags, " -arch=", property);
3486 if (property == "all") {
3488 *this->Makefile->GetDefinition("CMAKE_CUDA_ARCHITECTURES_ALL");
3489 } else if (property == "all-major") {
3491 *this->Makefile->GetDefinition("CMAKE_CUDA_ARCHITECTURES_ALL_MAJOR");
3493 } else if (property == "native") {
3495 this->Makefile->GetDefinition("CMAKE_CUDA_ARCHITECTURES_NATIVE");
3496 if (native.IsEmpty()) {
3497 this->Makefile->IssueMessage(
3498 MessageType::FATAL_ERROR,
3499 "CUDA_ARCHITECTURES is set to \"native\", but no GPU was detected.");
3501 if (compiler == "NVIDIA" &&
3502 cmSystemTools::VersionCompare(
3503 cmSystemTools::OP_GREATER_EQUAL,
3504 this->Makefile->GetDefinition("CMAKE_CUDA_COMPILER_VERSION"),
3506 flags = cmStrCat(flags, " -arch=", property);
3512 struct CudaArchitecture
3516 bool virtual_{ true };
3518 std::vector<CudaArchitecture> architectures;
3521 std::vector<std::string> options;
3522 cmExpandList(property, options);
3524 for (std::string& option : options) {
3525 CudaArchitecture architecture;
3527 // Architecture name is up to the first specifier.
3528 std::size_t pos = option.find_first_of('-');
3529 architecture.name = option.substr(0, pos);
3531 if (pos != std::string::npos) {
3532 cm::string_view specifier{ option.c_str() + pos + 1,
3533 option.length() - pos - 1 };
3535 if (specifier == "real") {
3536 architecture.real = true;
3537 architecture.virtual_ = false;
3538 } else if (specifier == "virtual") {
3539 architecture.real = false;
3540 architecture.virtual_ = true;
3542 this->Makefile->IssueMessage(
3543 MessageType::FATAL_ERROR,
3544 "Unknown CUDA architecture specifier \"" + std::string(specifier) +
3549 architectures.emplace_back(architecture);
3553 if (compiler == "NVIDIA") {
3554 if (ipoEnabled && compileOrLink == cmBuildStep::Link) {
3555 if (cmValue cudaIPOFlags =
3556 this->Makefile->GetDefinition("CMAKE_CUDA_LINK_OPTIONS_IPO")) {
3557 flags += cudaIPOFlags;
3561 for (CudaArchitecture& architecture : architectures) {
3563 " --generate-code=arch=compute_" + architecture.name + ",code=[";
3565 if (architecture.virtual_) {
3566 flags += "compute_" + architecture.name;
3568 if (architecture.real) {
3574 if (compileOrLink == cmBuildStep::Compile) {
3575 flags += "lto_" + architecture.name;
3576 } else if (compileOrLink == cmBuildStep::Link) {
3577 flags += "sm_" + architecture.name;
3579 } else if (architecture.real) {
3580 flags += "sm_" + architecture.name;
3585 } else if (compiler == "Clang") {
3586 for (CudaArchitecture& architecture : architectures) {
3587 flags += " --cuda-gpu-arch=sm_" + architecture.name;
3589 if (!architecture.real) {
3590 this->Makefile->IssueMessage(
3591 MessageType::WARNING,
3592 "Clang doesn't support disabling CUDA real code generation.");
3595 if (!architecture.virtual_) {
3596 flags += " --no-cuda-include-ptx=sm_" + architecture.name;
3602 void cmGeneratorTarget::AddISPCTargetFlags(std::string& flags) const
3604 const std::string& property = this->GetSafeProperty("ISPC_INSTRUCTION_SETS");
3606 // If ISPC_TARGET is false we don't add any architectures.
3607 if (cmIsOff(property)) {
3611 std::string const& compiler =
3612 this->Makefile->GetSafeDefinition("CMAKE_ISPC_COMPILER_ID");
3614 if (compiler == "Intel") {
3615 std::vector<std::string> targets;
3616 cmExpandList(property, targets);
3617 if (!targets.empty()) {
3618 flags += cmStrCat(" --target=", cmWrap("", targets, "", ","));
3623 void cmGeneratorTarget::AddHIPArchitectureFlags(std::string& flags) const
3625 const std::string& property = this->GetSafeProperty("HIP_ARCHITECTURES");
3627 if (property.empty()) {
3628 this->Makefile->IssueMessage(MessageType::FATAL_ERROR,
3629 "HIP_ARCHITECTURES is empty for target \"" +
3630 this->GetName() + "\".");
3633 // If HIP_ARCHITECTURES is false we don't add any architectures.
3634 if (cmIsOff(property)) {
3638 std::vector<std::string> options;
3639 cmExpandList(property, options);
3641 for (std::string& option : options) {
3642 flags += " --offload-arch=" + option;
3646 void cmGeneratorTarget::AddCUDAToolkitFlags(std::string& flags) const
3648 std::string const& compiler =
3649 this->Makefile->GetSafeDefinition("CMAKE_CUDA_COMPILER_ID");
3651 if (compiler == "Clang") {
3652 // Pass CUDA toolkit explicitly to Clang.
3653 // Clang's searching for the system CUDA toolkit isn't very good and it's
3654 // expected the user will explicitly pass the toolkit path.
3655 // This also avoids Clang having to search for the toolkit on every
3657 std::string toolkitRoot =
3658 this->Makefile->GetSafeDefinition("CMAKE_CUDA_COMPILER_LIBRARY_ROOT");
3660 if (!toolkitRoot.empty()) {
3661 flags += " --cuda-path=" +
3662 this->LocalGenerator->ConvertToOutputFormat(toolkitRoot,
3663 cmOutputConverter::SHELL);
3668 //----------------------------------------------------------------------------
3669 std::string cmGeneratorTarget::GetFeatureSpecificLinkRuleVariable(
3670 std::string const& var, std::string const& lang,
3671 std::string const& config) const
3673 if (this->IsIPOEnabled(lang, config)) {
3674 std::string varIPO = var + "_IPO";
3675 if (this->Makefile->IsDefinitionSet(varIPO)) {
3683 //----------------------------------------------------------------------------
3684 std::string cmGeneratorTarget::GetCreateRuleVariable(
3685 std::string const& lang, std::string const& config) const
3687 switch (this->GetType()) {
3688 case cmStateEnums::STATIC_LIBRARY: {
3689 std::string var = "CMAKE_" + lang + "_CREATE_STATIC_LIBRARY";
3690 return this->GetFeatureSpecificLinkRuleVariable(var, lang, config);
3692 case cmStateEnums::SHARED_LIBRARY:
3693 return "CMAKE_" + lang + "_CREATE_SHARED_LIBRARY";
3694 case cmStateEnums::MODULE_LIBRARY:
3695 return "CMAKE_" + lang + "_CREATE_SHARED_MODULE";
3696 case cmStateEnums::EXECUTABLE:
3697 if (this->IsExecutableWithExports()) {
3698 std::string linkExeWithExports =
3699 "CMAKE_" + lang + "_LINK_EXECUTABLE_WITH_EXPORTS";
3700 if (this->Makefile->IsDefinitionSet(linkExeWithExports)) {
3701 return linkExeWithExports;
3704 return "CMAKE_" + lang + "_LINK_EXECUTABLE";
3712 void processIncludeDirectories(cmGeneratorTarget const* tgt,
3713 EvaluatedTargetPropertyEntries& entries,
3714 std::vector<BT<std::string>>& includes,
3715 std::unordered_set<std::string>& uniqueIncludes,
3718 for (EvaluatedTargetPropertyEntry& entry : entries.Entries) {
3719 cmLinkImplItem const& item = entry.LinkImplItem;
3720 std::string const& targetName = item.AsStr();
3721 bool const fromImported = item.Target && item.Target->IsImported();
3722 bool const checkCMP0027 = item.CheckCMP0027;
3724 std::string usedIncludes;
3725 for (std::string& entryInclude : entry.Values) {
3726 if (fromImported && !cmSystemTools::FileExists(entryInclude)) {
3727 std::ostringstream e;
3728 MessageType messageType = MessageType::FATAL_ERROR;
3730 switch (tgt->GetPolicyStatusCMP0027()) {
3731 case cmPolicies::WARN:
3732 e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0027) << "\n";
3734 case cmPolicies::OLD:
3735 messageType = MessageType::AUTHOR_WARNING;
3737 case cmPolicies::REQUIRED_ALWAYS:
3738 case cmPolicies::REQUIRED_IF_USED:
3739 case cmPolicies::NEW:
3743 /* clang-format off */
3744 e << "Imported target \"" << targetName << "\" includes "
3745 "non-existent path\n \"" << entryInclude << "\"\nin its "
3746 "INTERFACE_INCLUDE_DIRECTORIES. Possible reasons include:\n"
3747 "* The path was deleted, renamed, or moved to another "
3749 "* An install or uninstall procedure did not complete "
3751 "* The installation package was faulty and references files it "
3752 "does not provide.\n";
3753 /* clang-format on */
3754 tgt->GetLocalGenerator()->IssueMessage(messageType, e.str());
3758 if (!cmSystemTools::FileIsFullPath(entryInclude)) {
3759 std::ostringstream e;
3760 bool noMessage = false;
3761 MessageType messageType = MessageType::FATAL_ERROR;
3762 if (!targetName.empty()) {
3763 /* clang-format off */
3764 e << "Target \"" << targetName << "\" contains relative "
3765 "path in its INTERFACE_INCLUDE_DIRECTORIES:\n"
3766 " \"" << entryInclude << "\"";
3767 /* clang-format on */
3769 switch (tgt->GetPolicyStatusCMP0021()) {
3770 case cmPolicies::WARN: {
3771 e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0021) << "\n";
3772 messageType = MessageType::AUTHOR_WARNING;
3774 case cmPolicies::OLD:
3777 case cmPolicies::REQUIRED_IF_USED:
3778 case cmPolicies::REQUIRED_ALWAYS:
3779 case cmPolicies::NEW:
3780 // Issue the fatal message.
3783 e << "Found relative path while evaluating include directories of "
3785 << tgt->GetName() << "\":\n \"" << entryInclude << "\"\n";
3788 tgt->GetLocalGenerator()->IssueMessage(messageType, e.str());
3789 if (messageType == MessageType::FATAL_ERROR) {
3795 if (!cmIsOff(entryInclude)) {
3796 cmSystemTools::ConvertToUnixSlashes(entryInclude);
3799 if (uniqueIncludes.insert(entryInclude).second) {
3800 includes.emplace_back(entryInclude, entry.Backtrace);
3801 if (debugIncludes) {
3802 usedIncludes += " * " + entryInclude + "\n";
3806 if (!usedIncludes.empty()) {
3807 tgt->GetLocalGenerator()->GetCMakeInstance()->IssueMessage(
3809 std::string("Used includes for target ") + tgt->GetName() + ":\n" +
3817 std::vector<BT<std::string>> cmGeneratorTarget::GetIncludeDirectories(
3818 const std::string& config, const std::string& lang) const
3820 std::vector<BT<std::string>> includes;
3821 std::unordered_set<std::string> uniqueIncludes;
3823 cmGeneratorExpressionDAGChecker dagChecker(this, "INCLUDE_DIRECTORIES",
3826 std::vector<std::string> debugProperties;
3827 this->Makefile->GetDefExpandList("CMAKE_DEBUG_TARGET_PROPERTIES",
3830 bool debugIncludes = !this->DebugIncludesDone &&
3831 cm::contains(debugProperties, "INCLUDE_DIRECTORIES");
3833 if (this->GlobalGenerator->GetConfigureDoneCMP0026()) {
3834 this->DebugIncludesDone = true;
3837 EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries(
3838 this, config, lang, &dagChecker, this->IncludeDirectoriesEntries);
3840 if (lang == "Swift") {
3841 AddLangSpecificImplicitIncludeDirectories(
3842 this, lang, config, "Swift_MODULE_DIRECTORY",
3843 IncludeDirectoryFallBack::BINARY, entries);
3846 if (this->CanCompileSources() && (lang != "Swift" && lang != "Fortran")) {
3848 const std::string propertyName = "ISPC_HEADER_DIRECTORY";
3850 // If this target has ISPC sources make sure to add the header
3851 // directory to other compilation units
3852 if (cm::contains(this->GetAllConfigCompileLanguages(), "ISPC")) {
3853 if (cmValue val = this->GetProperty(propertyName)) {
3854 includes.emplace_back(*val);
3856 includes.emplace_back(this->GetObjectDirectory(config));
3860 AddLangSpecificImplicitIncludeDirectories(
3861 this, "ISPC", config, propertyName, IncludeDirectoryFallBack::OBJECT,
3865 AddInterfaceEntries(this, config, "INTERFACE_INCLUDE_DIRECTORIES", lang,
3866 &dagChecker, entries, IncludeRuntimeInterface::Yes);
3868 if (this->Makefile->IsOn("APPLE")) {
3869 if (cmLinkImplementationLibraries const* impl =
3870 this->GetLinkImplementationLibraries(config,
3871 LinkInterfaceFor::Usage)) {
3872 for (cmLinkImplItem const& lib : impl->Libraries) {
3873 std::string libDir = cmSystemTools::CollapseFullPath(
3874 lib.AsStr(), this->Makefile->GetHomeOutputDirectory());
3876 static cmsys::RegularExpression frameworkCheck(
3877 "(.*\\.framework)(/Versions/[^/]+)?/[^/]+$");
3878 if (!frameworkCheck.find(libDir)) {
3882 libDir = frameworkCheck.match(1);
3884 EvaluatedTargetPropertyEntry ee(lib, cmListFileBacktrace());
3885 ee.Values.emplace_back(std::move(libDir));
3886 entries.Entries.emplace_back(std::move(ee));
3891 processIncludeDirectories(this, entries, includes, uniqueIncludes,
3897 enum class OptionsParse
3904 const auto DL_BEGIN = "<DEVICE_LINK>"_s;
3905 const auto DL_END = "</DEVICE_LINK>"_s;
3907 void processOptions(cmGeneratorTarget const* tgt,
3908 EvaluatedTargetPropertyEntries const& entries,
3909 std::vector<BT<std::string>>& options,
3910 std::unordered_set<std::string>& uniqueOptions,
3911 bool debugOptions, const char* logName, OptionsParse parse,
3912 bool processDeviceOptions = false)
3914 bool splitOption = !processDeviceOptions;
3915 for (EvaluatedTargetPropertyEntry const& entry : entries.Entries) {
3916 std::string usedOptions;
3917 for (std::string const& opt : entry.Values) {
3918 if (processDeviceOptions && (opt == DL_BEGIN || opt == DL_END)) {
3919 options.emplace_back(opt, entry.Backtrace);
3920 splitOption = opt == DL_BEGIN;
3924 if (uniqueOptions.insert(opt).second) {
3925 if (parse == OptionsParse::Shell &&
3926 cmHasLiteralPrefix(opt, "SHELL:")) {
3928 std::vector<std::string> tmp;
3929 cmSystemTools::ParseUnixCommandLine(opt.c_str() + 6, tmp);
3930 for (std::string& o : tmp) {
3931 options.emplace_back(std::move(o), entry.Backtrace);
3934 options.emplace_back(std::string(opt.c_str() + 6),
3938 options.emplace_back(opt, entry.Backtrace);
3941 usedOptions += " * " + opt + "\n";
3945 if (!usedOptions.empty()) {
3946 tgt->GetLocalGenerator()->GetCMakeInstance()->IssueMessage(
3948 std::string("Used ") + logName + std::string(" for target ") +
3949 tgt->GetName() + ":\n" + usedOptions,
3955 std::vector<BT<std::string>> wrapOptions(
3956 std::vector<std::string>& options, const cmListFileBacktrace& bt,
3957 const std::vector<std::string>& wrapperFlag, const std::string& wrapperSep,
3958 bool concatFlagAndArgs)
3960 std::vector<BT<std::string>> result;
3962 if (options.empty()) {
3966 if (wrapperFlag.empty()) {
3967 // nothing specified, insert elements as is
3968 result.reserve(options.size());
3969 for (std::string& o : options) {
3970 result.emplace_back(std::move(o), bt);
3975 for (std::vector<std::string>::size_type index = 0; index < options.size();
3977 if (cmHasLiteralPrefix(options[index], "LINKER:")) {
3978 // LINKER wrapper specified, insert elements as is
3979 result.emplace_back(std::move(options[index]), bt);
3982 if (cmHasLiteralPrefix(options[index], "-Wl,")) {
3983 // replace option by LINKER wrapper
3984 result.emplace_back(options[index].replace(0, 4, "LINKER:"), bt);
3987 if (cmHasLiteralPrefix(options[index], "-Xlinker=")) {
3988 // replace option by LINKER wrapper
3989 result.emplace_back(options[index].replace(0, 9, "LINKER:"), bt);
3992 if (options[index] == "-Xlinker") {
3993 // replace option by LINKER wrapper
3994 if (index + 1 < options.size()) {
3995 result.emplace_back("LINKER:" + options[++index], bt);
3997 result.emplace_back(std::move(options[index]), bt);
4002 // collect all options which must be transformed
4003 std::vector<std::string> opts;
4004 while (index < options.size()) {
4005 if (!cmHasLiteralPrefix(options[index], "LINKER:") &&
4006 !cmHasLiteralPrefix(options[index], "-Wl,") &&
4007 !cmHasLiteralPrefix(options[index], "-Xlinker")) {
4008 opts.emplace_back(std::move(options[index++]));
4018 if (!wrapperSep.empty()) {
4019 if (concatFlagAndArgs) {
4020 // insert flag elements except last one
4021 for (auto i = wrapperFlag.begin(); i != wrapperFlag.end() - 1; ++i) {
4022 result.emplace_back(*i, bt);
4024 // concatenate last flag element and all list values
4026 result.emplace_back(wrapperFlag.back() + cmJoin(opts, wrapperSep), bt);
4028 for (std::string const& i : wrapperFlag) {
4029 result.emplace_back(i, bt);
4031 // concatenate all list values in one option
4032 result.emplace_back(cmJoin(opts, wrapperSep), bt);
4035 // prefix each element of list with wrapper
4036 if (concatFlagAndArgs) {
4037 std::transform(opts.begin(), opts.end(), opts.begin(),
4038 [&wrapperFlag](std::string const& o) -> std::string {
4039 return wrapperFlag.back() + o;
4042 for (std::string& o : opts) {
4043 for (auto i = wrapperFlag.begin(),
4044 e = concatFlagAndArgs ? wrapperFlag.end() - 1
4045 : wrapperFlag.end();
4047 result.emplace_back(*i, bt);
4049 result.emplace_back(std::move(o), bt);
4057 void cmGeneratorTarget::GetCompileOptions(std::vector<std::string>& result,
4058 const std::string& config,
4059 const std::string& language) const
4061 std::vector<BT<std::string>> tmp = this->GetCompileOptions(config, language);
4062 result.reserve(tmp.size());
4063 for (BT<std::string>& v : tmp) {
4064 result.emplace_back(std::move(v.Value));
4068 std::vector<BT<std::string>> cmGeneratorTarget::GetCompileOptions(
4069 std::string const& config, std::string const& language) const
4071 std::vector<BT<std::string>> result;
4072 std::unordered_set<std::string> uniqueOptions;
4074 cmGeneratorExpressionDAGChecker dagChecker(this, "COMPILE_OPTIONS", nullptr,
4077 std::vector<std::string> debugProperties;
4078 this->Makefile->GetDefExpandList("CMAKE_DEBUG_TARGET_PROPERTIES",
4081 bool debugOptions = !this->DebugCompileOptionsDone &&
4082 cm::contains(debugProperties, "COMPILE_OPTIONS");
4084 if (this->GlobalGenerator->GetConfigureDoneCMP0026()) {
4085 this->DebugCompileOptionsDone = true;
4088 EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries(
4089 this, config, language, &dagChecker, this->CompileOptionsEntries);
4091 AddInterfaceEntries(this, config, "INTERFACE_COMPILE_OPTIONS", language,
4092 &dagChecker, entries, IncludeRuntimeInterface::Yes);
4094 processOptions(this, entries, result, uniqueOptions, debugOptions,
4095 "compile options", OptionsParse::Shell);
4100 void cmGeneratorTarget::GetCompileFeatures(std::vector<std::string>& result,
4101 const std::string& config) const
4103 std::vector<BT<std::string>> tmp = this->GetCompileFeatures(config);
4104 result.reserve(tmp.size());
4105 for (BT<std::string>& v : tmp) {
4106 result.emplace_back(std::move(v.Value));
4110 std::vector<BT<std::string>> cmGeneratorTarget::GetCompileFeatures(
4111 std::string const& config) const
4113 std::vector<BT<std::string>> result;
4114 std::unordered_set<std::string> uniqueFeatures;
4116 cmGeneratorExpressionDAGChecker dagChecker(this, "COMPILE_FEATURES", nullptr,
4119 std::vector<std::string> debugProperties;
4120 this->Makefile->GetDefExpandList("CMAKE_DEBUG_TARGET_PROPERTIES",
4123 bool debugFeatures = !this->DebugCompileFeaturesDone &&
4124 cm::contains(debugProperties, "COMPILE_FEATURES");
4126 if (this->GlobalGenerator->GetConfigureDoneCMP0026()) {
4127 this->DebugCompileFeaturesDone = true;
4130 EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries(
4131 this, config, std::string(), &dagChecker, this->CompileFeaturesEntries);
4133 AddInterfaceEntries(this, config, "INTERFACE_COMPILE_FEATURES",
4134 std::string(), &dagChecker, entries,
4135 IncludeRuntimeInterface::Yes);
4137 processOptions(this, entries, result, uniqueFeatures, debugFeatures,
4138 "compile features", OptionsParse::None);
4143 void cmGeneratorTarget::GetCompileDefinitions(
4144 std::vector<std::string>& result, const std::string& config,
4145 const std::string& language) const
4147 std::vector<BT<std::string>> tmp =
4148 this->GetCompileDefinitions(config, language);
4149 result.reserve(tmp.size());
4150 for (BT<std::string>& v : tmp) {
4151 result.emplace_back(std::move(v.Value));
4155 std::vector<BT<std::string>> cmGeneratorTarget::GetCompileDefinitions(
4156 std::string const& config, std::string const& language) const
4158 std::vector<BT<std::string>> list;
4159 std::unordered_set<std::string> uniqueOptions;
4161 cmGeneratorExpressionDAGChecker dagChecker(this, "COMPILE_DEFINITIONS",
4164 std::vector<std::string> debugProperties;
4165 this->Makefile->GetDefExpandList("CMAKE_DEBUG_TARGET_PROPERTIES",
4168 bool debugDefines = !this->DebugCompileDefinitionsDone &&
4169 cm::contains(debugProperties, "COMPILE_DEFINITIONS");
4171 if (this->GlobalGenerator->GetConfigureDoneCMP0026()) {
4172 this->DebugCompileDefinitionsDone = true;
4175 EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries(
4176 this, config, language, &dagChecker, this->CompileDefinitionsEntries);
4178 AddInterfaceEntries(this, config, "INTERFACE_COMPILE_DEFINITIONS", language,
4179 &dagChecker, entries, IncludeRuntimeInterface::Yes);
4181 if (!config.empty()) {
4182 std::string configPropName =
4183 "COMPILE_DEFINITIONS_" + cmSystemTools::UpperCase(config);
4184 cmValue configProp = this->GetProperty(configPropName);
4186 switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0043)) {
4187 case cmPolicies::WARN: {
4188 this->LocalGenerator->IssueMessage(
4189 MessageType::AUTHOR_WARNING,
4190 cmPolicies::GetPolicyWarning(cmPolicies::CMP0043));
4193 case cmPolicies::OLD: {
4194 std::unique_ptr<TargetPropertyEntry> entry =
4195 CreateTargetPropertyEntry(*configProp);
4196 entries.Entries.emplace_back(EvaluateTargetPropertyEntry(
4197 this, config, language, &dagChecker, *entry));
4199 case cmPolicies::NEW:
4200 case cmPolicies::REQUIRED_ALWAYS:
4201 case cmPolicies::REQUIRED_IF_USED:
4207 processOptions(this, entries, list, uniqueOptions, debugDefines,
4208 "compile definitions", OptionsParse::None);
4213 std::vector<BT<std::string>> cmGeneratorTarget::GetPrecompileHeaders(
4214 const std::string& config, const std::string& language) const
4216 std::unordered_set<std::string> uniqueOptions;
4218 cmGeneratorExpressionDAGChecker dagChecker(this, "PRECOMPILE_HEADERS",
4221 std::vector<std::string> debugProperties;
4222 this->Makefile->GetDefExpandList("CMAKE_DEBUG_TARGET_PROPERTIES",
4225 bool debugDefines = !this->DebugPrecompileHeadersDone &&
4226 std::find(debugProperties.begin(), debugProperties.end(),
4227 "PRECOMPILE_HEADERS") != debugProperties.end();
4229 if (this->GlobalGenerator->GetConfigureDoneCMP0026()) {
4230 this->DebugPrecompileHeadersDone = true;
4233 EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries(
4234 this, config, language, &dagChecker, this->PrecompileHeadersEntries);
4236 AddInterfaceEntries(this, config, "INTERFACE_PRECOMPILE_HEADERS", language,
4237 &dagChecker, entries, IncludeRuntimeInterface::Yes);
4239 std::vector<BT<std::string>> list;
4240 processOptions(this, entries, list, uniqueOptions, debugDefines,
4241 "precompile headers", OptionsParse::None);
4246 std::string cmGeneratorTarget::GetPchHeader(const std::string& config,
4247 const std::string& language,
4248 const std::string& arch) const
4250 if (language != "C" && language != "CXX" && language != "OBJC" &&
4251 language != "OBJCXX") {
4252 return std::string();
4255 if (this->GetPropertyAsBool("DISABLE_PRECOMPILE_HEADERS")) {
4256 return std::string();
4258 const cmGeneratorTarget* generatorTarget = this;
4259 cmValue pchReuseFrom =
4260 generatorTarget->GetProperty("PRECOMPILE_HEADERS_REUSE_FROM");
4262 const auto inserted =
4263 this->PchHeaders.insert(std::make_pair(language + config + arch, ""));
4264 if (inserted.second) {
4265 const std::vector<BT<std::string>> headers =
4266 this->GetPrecompileHeaders(config, language);
4267 if (headers.empty() && !pchReuseFrom) {
4268 return std::string();
4270 std::string& filename = inserted.first->second;
4274 this->GetGlobalGenerator()->FindGeneratorTarget(*pchReuseFrom);
4277 filename = cmStrCat(
4278 generatorTarget->LocalGenerator->GetCurrentBinaryDirectory(), "/");
4280 const std::map<std::string, std::string> languageToExtension = {
4283 { "OBJC", ".objc.h" },
4284 { "OBJCXX", ".objcxx.hxx" }
4288 cmStrCat(filename, "CMakeFiles/", generatorTarget->GetName(), ".dir");
4290 if (this->GetGlobalGenerator()->IsMultiConfig()) {
4291 filename = cmStrCat(filename, "/", config);
4295 cmStrCat(filename, "/cmake_pch", arch.empty() ? "" : cmStrCat("_", arch),
4296 languageToExtension.at(language));
4298 const std::string filename_tmp = cmStrCat(filename, ".tmp");
4299 if (!pchReuseFrom) {
4300 cmValue pchPrologue =
4301 this->Makefile->GetDefinition("CMAKE_PCH_PROLOGUE");
4302 cmValue pchEpilogue =
4303 this->Makefile->GetDefinition("CMAKE_PCH_EPILOGUE");
4305 std::string firstHeaderOnDisk;
4307 cmGeneratedFileStream file(
4308 filename_tmp, false,
4309 this->GetGlobalGenerator()->GetMakefileEncoding());
4310 file << "/* generated by CMake */\n\n";
4312 file << *pchPrologue << "\n";
4314 if (this->GetGlobalGenerator()->IsXcode()) {
4315 file << "#ifndef CMAKE_SKIP_PRECOMPILE_HEADERS\n";
4317 if (language == "CXX" && !this->GetGlobalGenerator()->IsXcode()) {
4318 file << "#ifdef __cplusplus\n";
4320 for (auto const& header_bt : headers) {
4321 if (header_bt.Value.empty()) {
4324 if (header_bt.Value[0] == '<' || header_bt.Value[0] == '\"') {
4325 file << "#include " << header_bt.Value << "\n";
4327 file << "#include \"" << header_bt.Value << "\"\n";
4330 if (cmSystemTools::FileExists(header_bt.Value) &&
4331 firstHeaderOnDisk.empty()) {
4332 firstHeaderOnDisk = header_bt.Value;
4335 if (language == "CXX" && !this->GetGlobalGenerator()->IsXcode()) {
4336 file << "#endif // __cplusplus\n";
4338 if (this->GetGlobalGenerator()->IsXcode()) {
4339 file << "#endif // CMAKE_SKIP_PRECOMPILE_HEADERS\n";
4342 file << *pchEpilogue << "\n";
4346 if (!firstHeaderOnDisk.empty()) {
4347 cmFileTimes::Copy(firstHeaderOnDisk, filename_tmp);
4350 cmSystemTools::MoveFileIfDifferent(filename_tmp, filename);
4353 return inserted.first->second;
4356 std::string cmGeneratorTarget::GetPchSource(const std::string& config,
4357 const std::string& language,
4358 const std::string& arch) const
4360 if (language != "C" && language != "CXX" && language != "OBJC" &&
4361 language != "OBJCXX") {
4362 return std::string();
4364 const auto inserted =
4365 this->PchSources.insert(std::make_pair(language + config + arch, ""));
4366 if (inserted.second) {
4367 const std::string pchHeader = this->GetPchHeader(config, language, arch);
4368 if (pchHeader.empty()) {
4369 return std::string();
4371 std::string& filename = inserted.first->second;
4373 const cmGeneratorTarget* generatorTarget = this;
4374 cmValue pchReuseFrom =
4375 generatorTarget->GetProperty("PRECOMPILE_HEADERS_REUSE_FROM");
4378 this->GetGlobalGenerator()->FindGeneratorTarget(*pchReuseFrom);
4382 cmStrCat(generatorTarget->LocalGenerator->GetCurrentBinaryDirectory(),
4383 "/CMakeFiles/", generatorTarget->GetName(), ".dir/cmake_pch");
4385 // For GCC the source extension will be transformed into .h[xx].gch
4386 if (!this->Makefile->IsOn("CMAKE_LINK_PCH")) {
4387 const std::map<std::string, std::string> languageToExtension = {
4389 { "CXX", ".hxx.cxx" },
4390 { "OBJC", ".objc.h.m" },
4391 { "OBJCXX", ".objcxx.hxx.mm" }
4394 filename = cmStrCat(filename, arch.empty() ? "" : cmStrCat("_", arch),
4395 languageToExtension.at(language));
4397 const std::map<std::string, std::string> languageToExtension = {
4398 { "C", ".c" }, { "CXX", ".cxx" }, { "OBJC", ".m" }, { "OBJCXX", ".mm" }
4401 filename = cmStrCat(filename, arch.empty() ? "" : cmStrCat("_", arch),
4402 languageToExtension.at(language));
4405 const std::string filename_tmp = cmStrCat(filename, ".tmp");
4406 if (!pchReuseFrom) {
4408 cmGeneratedFileStream file(filename_tmp);
4409 file << "/* generated by CMake */\n";
4411 cmFileTimes::Copy(pchHeader, filename_tmp);
4412 cmSystemTools::MoveFileIfDifferent(filename_tmp, filename);
4415 return inserted.first->second;
4418 std::string cmGeneratorTarget::GetPchFileObject(const std::string& config,
4419 const std::string& language,
4420 const std::string& arch)
4422 if (language != "C" && language != "CXX" && language != "OBJC" &&
4423 language != "OBJCXX") {
4424 return std::string();
4426 const auto inserted =
4427 this->PchObjectFiles.insert(std::make_pair(language + config + arch, ""));
4428 if (inserted.second) {
4429 const std::string pchSource = this->GetPchSource(config, language, arch);
4430 if (pchSource.empty()) {
4431 return std::string();
4433 std::string& filename = inserted.first->second;
4435 auto* pchSf = this->Makefile->GetOrCreateSource(
4436 pchSource, false, cmSourceFileLocationKind::Known);
4438 filename = cmStrCat(this->ObjectDirectory, this->GetObjectName(pchSf));
4439 if (this->GetGlobalGenerator()->IsMultiConfig()) {
4440 cmSystemTools::ReplaceString(
4441 filename, this->GetGlobalGenerator()->GetCMakeCFGIntDir(), config);
4444 return inserted.first->second;
4447 std::string cmGeneratorTarget::GetPchFile(const std::string& config,
4448 const std::string& language,
4449 const std::string& arch)
4451 const auto inserted =
4452 this->PchFiles.insert(std::make_pair(language + config + arch, ""));
4453 if (inserted.second) {
4454 std::string& pchFile = inserted.first->second;
4456 const std::string pchExtension =
4457 this->Makefile->GetSafeDefinition("CMAKE_PCH_EXTENSION");
4459 if (this->Makefile->IsOn("CMAKE_LINK_PCH")) {
4460 auto replaceExtension = [](const std::string& str,
4461 const std::string& ext) -> std::string {
4462 auto dot_pos = str.rfind('.');
4464 if (dot_pos != std::string::npos) {
4465 result = str.substr(0, dot_pos);
4471 cmGeneratorTarget* generatorTarget = this;
4472 cmValue pchReuseFrom =
4473 generatorTarget->GetProperty("PRECOMPILE_HEADERS_REUSE_FROM");
4476 this->GetGlobalGenerator()->FindGeneratorTarget(*pchReuseFrom);
4479 const std::string pchFileObject =
4480 generatorTarget->GetPchFileObject(config, language, arch);
4481 if (!pchExtension.empty()) {
4482 pchFile = replaceExtension(pchFileObject, pchExtension);
4485 pchFile = this->GetPchHeader(config, language, arch);
4486 pchFile += pchExtension;
4489 return inserted.first->second;
4492 std::string cmGeneratorTarget::GetPchCreateCompileOptions(
4493 const std::string& config, const std::string& language,
4494 const std::string& arch)
4496 const auto inserted = this->PchCreateCompileOptions.insert(
4497 std::make_pair(language + config + arch, ""));
4498 if (inserted.second) {
4499 std::string& createOptionList = inserted.first->second;
4501 if (this->GetPropertyAsBool("PCH_WARN_INVALID")) {
4502 createOptionList = this->Makefile->GetSafeDefinition(
4503 cmStrCat("CMAKE_", language, "_COMPILE_OPTIONS_INVALID_PCH"));
4506 if (this->GetPropertyAsBool("PCH_INSTANTIATE_TEMPLATES")) {
4507 std::string varName = cmStrCat(
4508 "CMAKE_", language, "_COMPILE_OPTIONS_INSTANTIATE_TEMPLATES_PCH");
4509 std::string instantiateOption =
4510 this->Makefile->GetSafeDefinition(varName);
4511 if (!instantiateOption.empty()) {
4512 createOptionList = cmStrCat(createOptionList, ";", instantiateOption);
4516 const std::string createOptVar =
4517 cmStrCat("CMAKE_", language, "_COMPILE_OPTIONS_CREATE_PCH");
4519 createOptionList = cmStrCat(
4520 createOptionList, ";", this->Makefile->GetSafeDefinition(createOptVar));
4522 const std::string pchHeader = this->GetPchHeader(config, language, arch);
4523 const std::string pchFile = this->GetPchFile(config, language, arch);
4525 cmSystemTools::ReplaceString(createOptionList, "<PCH_HEADER>", pchHeader);
4526 cmSystemTools::ReplaceString(createOptionList, "<PCH_FILE>", pchFile);
4528 return inserted.first->second;
4531 std::string cmGeneratorTarget::GetPchUseCompileOptions(
4532 const std::string& config, const std::string& language,
4533 const std::string& arch)
4535 const auto inserted = this->PchUseCompileOptions.insert(
4536 std::make_pair(language + config + arch, ""));
4537 if (inserted.second) {
4538 std::string& useOptionList = inserted.first->second;
4540 if (this->GetPropertyAsBool("PCH_WARN_INVALID")) {
4541 useOptionList = this->Makefile->GetSafeDefinition(
4542 cmStrCat("CMAKE_", language, "_COMPILE_OPTIONS_INVALID_PCH"));
4545 const std::string useOptVar =
4546 cmStrCat(language, "_COMPILE_OPTIONS_USE_PCH");
4548 std::string const& useOptionListProperty =
4549 this->GetSafeProperty(useOptVar);
4551 useOptionList = cmStrCat(
4553 useOptionListProperty.empty()
4554 ? this->Makefile->GetSafeDefinition(cmStrCat("CMAKE_", useOptVar))
4555 : useOptionListProperty);
4557 const std::string pchHeader = this->GetPchHeader(config, language, arch);
4558 const std::string pchFile = this->GetPchFile(config, language, arch);
4560 cmSystemTools::ReplaceString(useOptionList, "<PCH_HEADER>", pchHeader);
4561 cmSystemTools::ReplaceString(useOptionList, "<PCH_FILE>", pchFile);
4563 return inserted.first->second;
4566 void cmGeneratorTarget::AddSourceFileToUnityBatch(
4567 const std::string& sourceFilename)
4569 this->UnityBatchedSourceFiles.insert(sourceFilename);
4572 bool cmGeneratorTarget::IsSourceFilePartOfUnityBatch(
4573 const std::string& sourceFilename) const
4575 if (!this->GetPropertyAsBool("UNITY_BUILD")) {
4579 return this->UnityBatchedSourceFiles.find(sourceFilename) !=
4580 this->UnityBatchedSourceFiles.end();
4583 void cmGeneratorTarget::GetLinkOptions(std::vector<std::string>& result,
4584 const std::string& config,
4585 const std::string& language) const
4587 if (this->IsDeviceLink() &&
4588 this->GetPolicyStatusCMP0105() != cmPolicies::NEW) {
4589 // link options are not propagated to the device link step
4593 std::vector<BT<std::string>> tmp = this->GetLinkOptions(config, language);
4594 result.reserve(tmp.size());
4595 for (BT<std::string>& v : tmp) {
4596 result.emplace_back(std::move(v.Value));
4600 std::vector<BT<std::string>> cmGeneratorTarget::GetLinkOptions(
4601 std::string const& config, std::string const& language) const
4603 std::vector<BT<std::string>> result;
4604 std::unordered_set<std::string> uniqueOptions;
4606 cmGeneratorExpressionDAGChecker dagChecker(this, "LINK_OPTIONS", nullptr,
4609 std::vector<std::string> debugProperties;
4610 this->Makefile->GetDefExpandList("CMAKE_DEBUG_TARGET_PROPERTIES",
4613 bool debugOptions = !this->DebugLinkOptionsDone &&
4614 cm::contains(debugProperties, "LINK_OPTIONS");
4616 if (this->GlobalGenerator->GetConfigureDoneCMP0026()) {
4617 this->DebugLinkOptionsDone = true;
4620 EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries(
4621 this, config, language, &dagChecker, this->LinkOptionsEntries);
4623 AddInterfaceEntries(this, config, "INTERFACE_LINK_OPTIONS", language,
4624 &dagChecker, entries, IncludeRuntimeInterface::Yes,
4625 this->GetPolicyStatusCMP0099() == cmPolicies::NEW
4626 ? LinkInterfaceFor::Link
4627 : LinkInterfaceFor::Usage);
4629 processOptions(this, entries, result, uniqueOptions, debugOptions,
4630 "link options", OptionsParse::Shell, this->IsDeviceLink());
4632 if (this->IsDeviceLink()) {
4633 // wrap host link options
4634 const std::string wrapper(this->Makefile->GetSafeDefinition(
4635 "CMAKE_" + language + "_DEVICE_COMPILER_WRAPPER_FLAG"));
4636 std::vector<std::string> wrapperFlag = cmExpandedList(wrapper);
4637 const std::string wrapperSep(this->Makefile->GetSafeDefinition(
4638 "CMAKE_" + language + "_DEVICE_COMPILER_WRAPPER_FLAG_SEP"));
4639 bool concatFlagAndArgs = true;
4640 if (!wrapperFlag.empty() && wrapperFlag.back() == " ") {
4641 concatFlagAndArgs = false;
4642 wrapperFlag.pop_back();
4645 auto it = result.begin();
4646 while (it != result.end()) {
4647 if (it->Value == DL_BEGIN) {
4648 // device link options, no treatment
4649 it = result.erase(it);
4650 it = std::find_if(it, result.end(), [](const BT<std::string>& item) {
4651 return item.Value == DL_END;
4653 if (it != result.end()) {
4654 it = result.erase(it);
4657 // host link options must be wrapped
4658 std::vector<std::string> options;
4659 cmSystemTools::ParseUnixCommandLine(it->Value.c_str(), options);
4660 auto hostOptions = wrapOptions(options, it->Backtrace, wrapperFlag,
4661 wrapperSep, concatFlagAndArgs);
4662 it = result.erase(it);
4663 // some compilers (like gcc 4.8 or Intel 19.0 or XLC 16) do not respect
4664 // C++11 standard: 'std::vector::insert()' do not returns an iterator,
4665 // so need to recompute the iterator after insertion.
4666 if (it == result.end()) {
4667 cm::append(result, hostOptions);
4670 auto index = it - result.begin();
4671 result.insert(it, hostOptions.begin(), hostOptions.end());
4672 it = result.begin() + index + hostOptions.size();
4678 // Last step: replace "LINKER:" prefixed elements by
4679 // actual linker wrapper
4680 return this->ResolveLinkerWrapper(result, language);
4683 std::vector<BT<std::string>>& cmGeneratorTarget::ResolveLinkerWrapper(
4684 std::vector<BT<std::string>>& result, const std::string& language,
4685 bool joinItems) const
4687 // replace "LINKER:" prefixed elements by actual linker wrapper
4688 const std::string wrapper(this->Makefile->GetSafeDefinition(
4689 "CMAKE_" + language +
4690 (this->IsDeviceLink() ? "_DEVICE_LINKER_WRAPPER_FLAG"
4691 : "_LINKER_WRAPPER_FLAG")));
4692 std::vector<std::string> wrapperFlag = cmExpandedList(wrapper);
4693 const std::string wrapperSep(this->Makefile->GetSafeDefinition(
4694 "CMAKE_" + language +
4695 (this->IsDeviceLink() ? "_DEVICE_LINKER_WRAPPER_FLAG_SEP"
4696 : "_LINKER_WRAPPER_FLAG_SEP")));
4697 bool concatFlagAndArgs = true;
4698 if (!wrapperFlag.empty() && wrapperFlag.back() == " ") {
4699 concatFlagAndArgs = false;
4700 wrapperFlag.pop_back();
4703 const std::string LINKER{ "LINKER:" };
4704 const std::string SHELL{ "SHELL:" };
4705 const std::string LINKER_SHELL = LINKER + SHELL;
4707 std::vector<BT<std::string>>::iterator entry;
4708 while ((entry = std::find_if(result.begin(), result.end(),
4709 [&LINKER](BT<std::string> const& item) -> bool {
4710 return item.Value.compare(0, LINKER.length(),
4712 })) != result.end()) {
4713 std::string value = std::move(entry->Value);
4714 cmListFileBacktrace bt = std::move(entry->Backtrace);
4715 entry = result.erase(entry);
4717 std::vector<std::string> linkerOptions;
4718 if (value.compare(0, LINKER_SHELL.length(), LINKER_SHELL) == 0) {
4719 cmSystemTools::ParseUnixCommandLine(
4720 value.c_str() + LINKER_SHELL.length(), linkerOptions);
4722 linkerOptions = cmTokenize(value.substr(LINKER.length()), ",");
4725 if (linkerOptions.empty() ||
4726 (linkerOptions.size() == 1 && linkerOptions.front().empty())) {
4730 // for now, raise an error if prefix SHELL: is part of arguments
4731 if (std::find_if(linkerOptions.begin(), linkerOptions.end(),
4732 [&SHELL](const std::string& item) -> bool {
4733 return item.find(SHELL) != std::string::npos;
4734 }) != linkerOptions.end()) {
4735 this->LocalGenerator->GetCMakeInstance()->IssueMessage(
4736 MessageType::FATAL_ERROR,
4737 "'SHELL:' prefix is not supported as part of 'LINKER:' arguments.",
4738 this->GetBacktrace());
4742 std::vector<BT<std::string>> options = wrapOptions(
4743 linkerOptions, bt, wrapperFlag, wrapperSep, concatFlagAndArgs);
4745 result.insert(entry,
4746 cmJoin(cmRange<decltype(options.cbegin())>(
4747 options.cbegin(), options.cend()),
4750 result.insert(entry, options.begin(), options.end());
4756 void cmGeneratorTarget::GetStaticLibraryLinkOptions(
4757 std::vector<std::string>& result, const std::string& config,
4758 const std::string& language) const
4760 std::vector<BT<std::string>> tmp =
4761 this->GetStaticLibraryLinkOptions(config, language);
4762 result.reserve(tmp.size());
4763 for (BT<std::string>& v : tmp) {
4764 result.emplace_back(std::move(v.Value));
4768 std::vector<BT<std::string>> cmGeneratorTarget::GetStaticLibraryLinkOptions(
4769 std::string const& config, std::string const& language) const
4771 std::vector<BT<std::string>> result;
4772 std::unordered_set<std::string> uniqueOptions;
4774 cmGeneratorExpressionDAGChecker dagChecker(this, "STATIC_LIBRARY_OPTIONS",
4777 EvaluatedTargetPropertyEntries entries;
4778 if (cmValue linkOptions = this->GetProperty("STATIC_LIBRARY_OPTIONS")) {
4779 std::vector<std::string> options = cmExpandedList(*linkOptions);
4780 for (const auto& option : options) {
4781 std::unique_ptr<TargetPropertyEntry> entry =
4782 CreateTargetPropertyEntry(option);
4783 entries.Entries.emplace_back(EvaluateTargetPropertyEntry(
4784 this, config, language, &dagChecker, *entry));
4787 processOptions(this, entries, result, uniqueOptions, false,
4788 "static library link options", OptionsParse::Shell);
4794 void processLinkDirectories(cmGeneratorTarget const* tgt,
4795 EvaluatedTargetPropertyEntries& entries,
4796 std::vector<BT<std::string>>& directories,
4797 std::unordered_set<std::string>& uniqueDirectories,
4798 bool debugDirectories)
4800 for (EvaluatedTargetPropertyEntry& entry : entries.Entries) {
4801 cmLinkImplItem const& item = entry.LinkImplItem;
4802 std::string const& targetName = item.AsStr();
4804 std::string usedDirectories;
4805 for (std::string& entryDirectory : entry.Values) {
4806 if (!cmSystemTools::FileIsFullPath(entryDirectory)) {
4807 std::ostringstream e;
4808 bool noMessage = false;
4809 MessageType messageType = MessageType::FATAL_ERROR;
4810 if (!targetName.empty()) {
4811 /* clang-format off */
4812 e << "Target \"" << targetName << "\" contains relative "
4813 "path in its INTERFACE_LINK_DIRECTORIES:\n"
4814 " \"" << entryDirectory << "\"";
4815 /* clang-format on */
4817 switch (tgt->GetPolicyStatusCMP0081()) {
4818 case cmPolicies::WARN: {
4819 e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0081) << "\n";
4820 messageType = MessageType::AUTHOR_WARNING;
4822 case cmPolicies::OLD:
4825 case cmPolicies::REQUIRED_IF_USED:
4826 case cmPolicies::REQUIRED_ALWAYS:
4827 case cmPolicies::NEW:
4828 // Issue the fatal message.
4831 e << "Found relative path while evaluating link directories of "
4833 << tgt->GetName() << "\":\n \"" << entryDirectory << "\"\n";
4836 tgt->GetLocalGenerator()->IssueMessage(messageType, e.str());
4837 if (messageType == MessageType::FATAL_ERROR) {
4843 // Sanitize the path the same way the link_directories command does
4844 // in case projects set the LINK_DIRECTORIES property directly.
4845 cmSystemTools::ConvertToUnixSlashes(entryDirectory);
4846 if (uniqueDirectories.insert(entryDirectory).second) {
4847 directories.emplace_back(entryDirectory, entry.Backtrace);
4848 if (debugDirectories) {
4849 usedDirectories += " * " + entryDirectory + "\n";
4853 if (!usedDirectories.empty()) {
4854 tgt->GetLocalGenerator()->GetCMakeInstance()->IssueMessage(
4856 std::string("Used link directories for target ") + tgt->GetName() +
4857 ":\n" + usedDirectories,
4864 void cmGeneratorTarget::GetLinkDirectories(std::vector<std::string>& result,
4865 const std::string& config,
4866 const std::string& language) const
4868 std::vector<BT<std::string>> tmp =
4869 this->GetLinkDirectories(config, language);
4870 result.reserve(tmp.size());
4871 for (BT<std::string>& v : tmp) {
4872 result.emplace_back(std::move(v.Value));
4876 std::vector<BT<std::string>> cmGeneratorTarget::GetLinkDirectories(
4877 std::string const& config, std::string const& language) const
4879 std::vector<BT<std::string>> result;
4880 std::unordered_set<std::string> uniqueDirectories;
4882 cmGeneratorExpressionDAGChecker dagChecker(this, "LINK_DIRECTORIES", nullptr,
4885 std::vector<std::string> debugProperties;
4886 this->Makefile->GetDefExpandList("CMAKE_DEBUG_TARGET_PROPERTIES",
4889 bool debugDirectories = !this->DebugLinkDirectoriesDone &&
4890 cm::contains(debugProperties, "LINK_DIRECTORIES");
4892 if (this->GlobalGenerator->GetConfigureDoneCMP0026()) {
4893 this->DebugLinkDirectoriesDone = true;
4896 EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries(
4897 this, config, language, &dagChecker, this->LinkDirectoriesEntries);
4899 AddInterfaceEntries(this, config, "INTERFACE_LINK_DIRECTORIES", language,
4900 &dagChecker, entries, IncludeRuntimeInterface::Yes,
4901 this->GetPolicyStatusCMP0099() == cmPolicies::NEW
4902 ? LinkInterfaceFor::Link
4903 : LinkInterfaceFor::Usage);
4905 processLinkDirectories(this, entries, result, uniqueDirectories,
4911 void cmGeneratorTarget::GetLinkDepends(std::vector<std::string>& result,
4912 const std::string& config,
4913 const std::string& language) const
4915 std::vector<BT<std::string>> tmp = this->GetLinkDepends(config, language);
4916 result.reserve(tmp.size());
4917 for (BT<std::string>& v : tmp) {
4918 result.emplace_back(std::move(v.Value));
4922 std::vector<BT<std::string>> cmGeneratorTarget::GetLinkDepends(
4923 std::string const& config, std::string const& language) const
4925 std::vector<BT<std::string>> result;
4926 std::unordered_set<std::string> uniqueOptions;
4927 cmGeneratorExpressionDAGChecker dagChecker(this, "LINK_DEPENDS", nullptr,
4930 EvaluatedTargetPropertyEntries entries;
4931 if (cmValue linkDepends = this->GetProperty("LINK_DEPENDS")) {
4932 std::vector<std::string> depends = cmExpandedList(*linkDepends);
4933 for (const auto& depend : depends) {
4934 std::unique_ptr<TargetPropertyEntry> entry =
4935 CreateTargetPropertyEntry(depend);
4936 entries.Entries.emplace_back(EvaluateTargetPropertyEntry(
4937 this, config, language, &dagChecker, *entry));
4940 AddInterfaceEntries(this, config, "INTERFACE_LINK_DEPENDS", language,
4941 &dagChecker, entries, IncludeRuntimeInterface::Yes,
4942 this->GetPolicyStatusCMP0099() == cmPolicies::NEW
4943 ? LinkInterfaceFor::Link
4944 : LinkInterfaceFor::Usage);
4946 processOptions(this, entries, result, uniqueOptions, false, "link depends",
4947 OptionsParse::None);
4952 void cmGeneratorTarget::ComputeTargetManifest(const std::string& config) const
4954 if (this->IsImported()) {
4957 cmGlobalGenerator* gg = this->LocalGenerator->GetGlobalGenerator();
4960 cmGeneratorTarget::Names targetNames;
4961 if (this->GetType() == cmStateEnums::EXECUTABLE) {
4962 targetNames = this->GetExecutableNames(config);
4963 } else if (this->GetType() == cmStateEnums::STATIC_LIBRARY ||
4964 this->GetType() == cmStateEnums::SHARED_LIBRARY ||
4965 this->GetType() == cmStateEnums::MODULE_LIBRARY) {
4966 targetNames = this->GetLibraryNames(config);
4971 // Get the directory.
4973 this->GetDirectory(config, cmStateEnums::RuntimeBinaryArtifact);
4977 if (!targetNames.Output.empty()) {
4978 f = cmStrCat(dir, '/', targetNames.Output);
4979 gg->AddToManifest(f);
4981 if (!targetNames.SharedObject.empty()) {
4982 f = cmStrCat(dir, '/', targetNames.SharedObject);
4983 gg->AddToManifest(f);
4985 if (!targetNames.Real.empty()) {
4986 f = cmStrCat(dir, '/', targetNames.Real);
4987 gg->AddToManifest(f);
4989 if (!targetNames.PDB.empty()) {
4990 f = cmStrCat(dir, '/', targetNames.PDB);
4991 gg->AddToManifest(f);
4993 if (!targetNames.ImportLibrary.empty()) {
4995 cmStrCat(this->GetDirectory(config, cmStateEnums::ImportLibraryArtifact),
4996 '/', targetNames.ImportLibrary);
4997 gg->AddToManifest(f);
5001 bool cmGeneratorTarget::ComputeCompileFeatures(std::string const& config) const
5003 // Compute the language standard based on the compile features.
5004 cmStandardLevelResolver standardResolver(this->Makefile);
5005 std::vector<BT<std::string>> features = this->GetCompileFeatures(config);
5006 for (BT<std::string> const& f : features) {
5008 if (!standardResolver.CompileFeatureKnown(this->Target->GetName(), f.Value,
5013 std::string key = cmStrCat(cmSystemTools::UpperCase(config), '-', lang);
5014 cmValue currentLanguageStandard = this->GetLanguageStandard(lang, config);
5016 std::string newRequiredStandard;
5017 if (!standardResolver.GetNewRequiredStandard(
5018 this->Target->GetName(), f.Value, currentLanguageStandard,
5019 newRequiredStandard)) {
5023 if (!newRequiredStandard.empty()) {
5024 BTs<std::string>& languageStandardProperty =
5025 this->LanguageStandardMap[key];
5026 if (languageStandardProperty.Value != newRequiredStandard) {
5027 languageStandardProperty.Value = newRequiredStandard;
5028 languageStandardProperty.Backtraces.clear();
5030 languageStandardProperty.Backtraces.emplace_back(f.Backtrace);
5037 bool cmGeneratorTarget::ComputeCompileFeatures(
5038 std::string const& config, std::set<LanguagePair> const& languagePairs) const
5040 for (const auto& language : languagePairs) {
5041 BTs<std::string> const* generatorTargetLanguageStandard =
5042 this->GetLanguageStandardProperty(language.first, config);
5043 if (!generatorTargetLanguageStandard) {
5044 // If the standard isn't explicitly set we copy it over from the
5045 // specified paired language.
5047 cmStrCat(cmSystemTools::UpperCase(config), '-', language.first);
5048 BTs<std::string> const* standardToCopy =
5049 this->GetLanguageStandardProperty(language.second, config);
5050 if (standardToCopy) {
5051 this->LanguageStandardMap[key] = *standardToCopy;
5052 generatorTargetLanguageStandard = &this->LanguageStandardMap[key];
5054 cmValue defaultStandard = this->Makefile->GetDefinition(
5055 cmStrCat("CMAKE_", language.second, "_STANDARD_DEFAULT"));
5056 if (defaultStandard) {
5057 this->LanguageStandardMap[key] = BTs<std::string>(*defaultStandard);
5058 generatorTargetLanguageStandard = &this->LanguageStandardMap[key];
5062 // Custom updates for the CUDA standard.
5063 if (generatorTargetLanguageStandard != nullptr &&
5064 (language.first == "CUDA")) {
5065 if (generatorTargetLanguageStandard->Value == "98") {
5066 this->LanguageStandardMap[key].Value = "03";
5075 std::string cmGeneratorTarget::GetImportedLibName(
5076 std::string const& config) const
5078 if (cmGeneratorTarget::ImportInfo const* info =
5079 this->GetImportInfo(config)) {
5080 return info->LibName;
5082 return std::string();
5085 std::string cmGeneratorTarget::GetFullPath(const std::string& config,
5086 cmStateEnums::ArtifactType artifact,
5087 bool realname) const
5089 if (this->IsImported()) {
5090 return this->Target->ImportedGetFullPath(config, artifact);
5092 return this->NormalGetFullPath(config, artifact, realname);
5095 std::string cmGeneratorTarget::NormalGetFullPath(
5096 const std::string& config, cmStateEnums::ArtifactType artifact,
5097 bool realname) const
5099 std::string fpath = cmStrCat(this->GetDirectory(config, artifact), '/');
5100 if (this->IsAppBundleOnApple()) {
5102 cmStrCat(this->BuildBundleDirectory(fpath, config, FullLevel), '/');
5105 // Add the full name of the target.
5107 case cmStateEnums::RuntimeBinaryArtifact:
5109 fpath += this->NormalGetRealName(config);
5112 this->GetFullName(config, cmStateEnums::RuntimeBinaryArtifact);
5115 case cmStateEnums::ImportLibraryArtifact:
5116 fpath += this->GetFullName(config, cmStateEnums::ImportLibraryArtifact);
5122 std::string cmGeneratorTarget::NormalGetRealName(
5123 const std::string& config) const
5125 // This should not be called for imported targets.
5126 // TODO: Split cmTarget into a class hierarchy to get compile-time
5127 // enforcement of the limited imported target API.
5128 if (this->IsImported()) {
5129 std::string msg = cmStrCat("NormalGetRealName called on imported target: ",
5131 this->LocalGenerator->IssueMessage(MessageType::INTERNAL_ERROR, msg);
5134 if (this->GetType() == cmStateEnums::EXECUTABLE) {
5135 // Compute the real name that will be built.
5136 return this->GetExecutableNames(config).Real;
5138 // Compute the real name that will be built.
5139 return this->GetLibraryNames(config).Real;
5142 cmGeneratorTarget::Names cmGeneratorTarget::GetLibraryNames(
5143 const std::string& config) const
5145 cmGeneratorTarget::Names targetNames;
5147 // This should not be called for imported targets.
5148 // TODO: Split cmTarget into a class hierarchy to get compile-time
5149 // enforcement of the limited imported target API.
5150 if (this->IsImported()) {
5152 cmStrCat("GetLibraryNames called on imported target: ", this->GetName());
5153 this->LocalGenerator->IssueMessage(MessageType::INTERNAL_ERROR, msg);
5156 // Check for library version properties.
5157 cmValue version = this->GetProperty("VERSION");
5158 cmValue soversion = this->GetProperty("SOVERSION");
5159 if (!this->HasSOName(config) ||
5160 this->Makefile->IsOn("CMAKE_PLATFORM_NO_VERSIONED_SONAME") ||
5161 this->IsFrameworkOnApple()) {
5162 // Versioning is supported only for shared libraries and modules,
5163 // and then only when the platform supports an soname flag.
5165 soversion = nullptr;
5167 if (version && !soversion) {
5168 // The soversion must be set if the library version is set. Use
5169 // the library version as the soversion.
5170 soversion = version;
5172 if (!version && soversion) {
5173 // Use the soversion as the library version.
5174 version = soversion;
5177 // Get the components of the library name.
5180 this->GetFullNameInternal(config, cmStateEnums::RuntimeBinaryArtifact,
5181 prefix, targetNames.Base, suffix);
5183 // The library name.
5184 targetNames.Output = prefix + targetNames.Base + suffix;
5186 if (this->IsFrameworkOnApple()) {
5187 targetNames.Real = prefix;
5188 if (!this->Makefile->PlatformIsAppleEmbedded()) {
5189 targetNames.Real += "Versions/";
5190 targetNames.Real += this->GetFrameworkVersion();
5191 targetNames.Real += "/";
5193 targetNames.Real += targetNames.Base + suffix;
5194 targetNames.SharedObject = targetNames.Real + suffix;
5196 // The library's soname.
5197 this->ComputeVersionedName(targetNames.SharedObject, prefix,
5198 targetNames.Base, suffix, targetNames.Output,
5201 // The library's real name on disk.
5202 this->ComputeVersionedName(targetNames.Real, prefix, targetNames.Base,
5203 suffix, targetNames.Output, version);
5206 // The import library name.
5207 if (this->GetType() == cmStateEnums::SHARED_LIBRARY ||
5208 this->GetType() == cmStateEnums::MODULE_LIBRARY) {
5209 targetNames.ImportLibrary =
5210 this->GetFullNameInternal(config, cmStateEnums::ImportLibraryArtifact);
5213 // The program database file name.
5214 targetNames.PDB = this->GetPDBName(config);
5219 cmGeneratorTarget::Names cmGeneratorTarget::GetExecutableNames(
5220 const std::string& config) const
5222 cmGeneratorTarget::Names targetNames;
5224 // This should not be called for imported targets.
5225 // TODO: Split cmTarget into a class hierarchy to get compile-time
5226 // enforcement of the limited imported target API.
5227 if (this->IsImported()) {
5228 std::string msg = cmStrCat(
5229 "GetExecutableNames called on imported target: ", this->GetName());
5230 this->LocalGenerator->IssueMessage(MessageType::INTERNAL_ERROR, msg);
5233 // This versioning is supported only for executables and then only
5234 // when the platform supports symbolic links.
5235 #if defined(_WIN32) && !defined(__CYGWIN__)
5238 // Check for executable version properties.
5239 cmValue version = this->GetProperty("VERSION");
5240 if (this->GetType() != cmStateEnums::EXECUTABLE ||
5241 this->Makefile->IsOn("XCODE")) {
5246 // Get the components of the executable name.
5249 this->GetFullNameInternal(config, cmStateEnums::RuntimeBinaryArtifact,
5250 prefix, targetNames.Base, suffix);
5252 // The executable name.
5253 targetNames.Output = prefix + targetNames.Base + suffix;
5255 // The executable's real name on disk.
5256 #if defined(__CYGWIN__)
5257 targetNames.Real = prefix + targetNames.Base;
5259 targetNames.Real = targetNames.Output;
5262 targetNames.Real += "-";
5263 targetNames.Real += *version;
5265 #if defined(__CYGWIN__)
5266 targetNames.Real += suffix;
5269 // The import library name.
5270 targetNames.ImportLibrary =
5271 this->GetFullNameInternal(config, cmStateEnums::ImportLibraryArtifact);
5273 // The program database file name.
5274 targetNames.PDB = this->GetPDBName(config);
5279 std::string cmGeneratorTarget::GetFullNameInternal(
5280 const std::string& config, cmStateEnums::ArtifactType artifact) const
5285 this->GetFullNameInternal(config, artifact, prefix, base, suffix);
5286 return prefix + base + suffix;
5289 std::string cmGeneratorTarget::ImportedGetLocation(
5290 const std::string& config) const
5292 assert(this->IsImported());
5293 return this->Target->ImportedGetFullPath(
5294 config, cmStateEnums::RuntimeBinaryArtifact);
5297 std::string cmGeneratorTarget::GetFullNameImported(
5298 const std::string& config, cmStateEnums::ArtifactType artifact) const
5300 return cmSystemTools::GetFilenameName(
5301 this->Target->ImportedGetFullPath(config, artifact));
5304 void cmGeneratorTarget::GetFullNameInternal(
5305 const std::string& config, cmStateEnums::ArtifactType artifact,
5306 std::string& outPrefix, std::string& outBase, std::string& outSuffix) const
5308 // Use just the target name for non-main target types.
5309 if (this->GetType() != cmStateEnums::STATIC_LIBRARY &&
5310 this->GetType() != cmStateEnums::SHARED_LIBRARY &&
5311 this->GetType() != cmStateEnums::MODULE_LIBRARY &&
5312 this->GetType() != cmStateEnums::EXECUTABLE) {
5314 outBase = this->GetName();
5319 const bool isImportedLibraryArtifact =
5320 (artifact == cmStateEnums::ImportLibraryArtifact);
5322 // Return an empty name for the import library if this platform
5323 // does not support import libraries.
5324 if (isImportedLibraryArtifact && !this->NeedImportLibraryName(config)) {
5331 // retrieve prefix and suffix
5332 std::string ll = this->GetLinkerLanguage(config);
5333 cmValue targetPrefix = this->GetFilePrefixInternal(config, artifact, ll);
5334 cmValue targetSuffix = this->GetFileSuffixInternal(config, artifact, ll);
5336 // The implib option is only allowed for shared libraries, module
5337 // libraries, and executables.
5338 if (this->GetType() != cmStateEnums::SHARED_LIBRARY &&
5339 this->GetType() != cmStateEnums::MODULE_LIBRARY &&
5340 this->GetType() != cmStateEnums::EXECUTABLE) {
5341 artifact = cmStateEnums::RuntimeBinaryArtifact;
5344 // Compute the full name for main target types.
5345 const std::string configPostfix = this->GetFilePostfix(config);
5347 // frameworks have directory prefix but no suffix
5348 std::string fw_prefix;
5349 if (this->IsFrameworkOnApple()) {
5351 cmStrCat(this->GetFrameworkDirectory(config, ContentLevel), '/');
5352 targetPrefix = cmValue(fw_prefix);
5353 targetSuffix = nullptr;
5356 if (this->IsCFBundleOnApple()) {
5357 fw_prefix = cmStrCat(this->GetCFBundleDirectory(config, FullLevel), '/');
5358 targetPrefix = cmValue(fw_prefix);
5359 targetSuffix = nullptr;
5362 // Begin the final name with the prefix.
5363 outPrefix = targetPrefix ? *targetPrefix : "";
5365 // Append the target name or property-specified name.
5366 outBase += this->GetOutputName(config, artifact);
5368 // Append the per-configuration postfix.
5369 // When using Xcode, the postfix should be part of the suffix rather than
5370 // the base, because the suffix ends up being used in Xcode's
5371 // EXECUTABLE_SUFFIX attribute.
5372 if (this->IsFrameworkOnApple() &&
5373 this->GetGlobalGenerator()->GetName() == "Xcode") {
5374 targetSuffix = cmValue(configPostfix);
5376 outBase += configPostfix;
5379 // Name shared libraries with their version number on some platforms.
5380 if (cmValue soversion = this->GetProperty("SOVERSION")) {
5381 if (this->GetType() == cmStateEnums::SHARED_LIBRARY &&
5382 !isImportedLibraryArtifact &&
5383 this->Makefile->IsOn("CMAKE_SHARED_LIBRARY_NAME_WITH_VERSION")) {
5385 outBase += *soversion;
5389 // Append the suffix.
5390 outSuffix = targetSuffix ? *targetSuffix : "";
5393 std::string cmGeneratorTarget::GetLinkerLanguage(
5394 const std::string& config) const
5396 return this->GetLinkClosure(config)->LinkerLanguage;
5399 std::string cmGeneratorTarget::GetPDBOutputName(
5400 const std::string& config) const
5403 this->GetOutputName(config, cmStateEnums::RuntimeBinaryArtifact);
5405 std::vector<std::string> props;
5406 std::string configUpper = cmSystemTools::UpperCase(config);
5407 if (!configUpper.empty()) {
5408 // PDB_NAME_<CONFIG>
5409 props.push_back("PDB_NAME_" + configUpper);
5413 props.emplace_back("PDB_NAME");
5415 for (std::string const& p : props) {
5416 if (cmValue outName = this->GetProperty(p)) {
5424 std::string cmGeneratorTarget::GetPDBName(const std::string& config) const
5429 this->GetFullNameInternal(config, cmStateEnums::RuntimeBinaryArtifact,
5430 prefix, base, suffix);
5432 std::vector<std::string> props;
5433 std::string configUpper = cmSystemTools::UpperCase(config);
5434 if (!configUpper.empty()) {
5435 // PDB_NAME_<CONFIG>
5436 props.push_back("PDB_NAME_" + configUpper);
5440 props.emplace_back("PDB_NAME");
5442 for (std::string const& p : props) {
5443 if (cmValue outName = this->GetProperty(p)) {
5448 return prefix + base + ".pdb";
5451 std::string cmGeneratorTarget::GetObjectDirectory(
5452 std::string const& config) const
5454 std::string obj_dir =
5455 this->GlobalGenerator->ExpandCFGIntDir(this->ObjectDirectory, config);
5456 #if defined(__APPLE__)
5457 // Replace Xcode's placeholder for the object file directory since
5458 // installation and export scripts need to know the real directory.
5459 // Xcode has build-time settings (e.g. for sanitizers) that affect this,
5460 // but we use the default here. Users that want to enable sanitizers
5461 // will do so at the cost of object library installation and export.
5462 cmSystemTools::ReplaceString(obj_dir, "$(OBJECT_FILE_DIR_normal:base)",
5468 void cmGeneratorTarget::GetTargetObjectNames(
5469 std::string const& config, std::vector<std::string>& objects) const
5471 std::vector<cmSourceFile const*> objectSources;
5472 this->GetObjectSources(objectSources, config);
5473 std::map<cmSourceFile const*, std::string> mapping;
5475 for (cmSourceFile const* sf : objectSources) {
5479 this->LocalGenerator->ComputeObjectFilenames(mapping, this);
5481 for (cmSourceFile const* src : objectSources) {
5482 // Find the object file name corresponding to this source file.
5483 auto map_it = mapping.find(src);
5484 // It must exist because we populated the mapping just above.
5485 assert(!map_it->second.empty());
5486 objects.push_back(map_it->second);
5489 // We need to compute the relative path from the root of
5490 // of the object directory to handle subdirectory paths
5491 std::string rootObjectDir = this->GetObjectDirectory(config);
5492 rootObjectDir = cmSystemTools::CollapseFullPath(rootObjectDir);
5493 auto ispcObjects = this->GetGeneratedISPCObjects(config);
5494 for (std::string const& output : ispcObjects) {
5495 auto relativePathFromObjectDir = output.substr(rootObjectDir.size());
5496 objects.push_back(relativePathFromObjectDir);
5500 bool cmGeneratorTarget::StrictTargetComparison::operator()(
5501 cmGeneratorTarget const* t1, cmGeneratorTarget const* t2) const
5503 int nameResult = strcmp(t1->GetName().c_str(), t2->GetName().c_str());
5504 if (nameResult == 0) {
5506 t1->GetLocalGenerator()->GetCurrentBinaryDirectory().c_str(),
5507 t2->GetLocalGenerator()->GetCurrentBinaryDirectory().c_str()) < 0;
5509 return nameResult < 0;
5512 struct cmGeneratorTarget::SourceFileFlags
5513 cmGeneratorTarget::GetTargetSourceFileFlags(const cmSourceFile* sf) const
5515 struct SourceFileFlags flags;
5516 this->ConstructSourceFileFlags();
5517 auto si = this->SourceFlagsMap.find(sf);
5518 if (si != this->SourceFlagsMap.end()) {
5521 // Handle the MACOSX_PACKAGE_LOCATION property on source files that
5522 // were not listed in one of the other lists.
5523 if (cmValue location = sf->GetProperty("MACOSX_PACKAGE_LOCATION")) {
5524 flags.MacFolder = location->c_str();
5525 const bool stripResources =
5526 this->GlobalGenerator->ShouldStripResourcePath(this->Makefile);
5527 if (*location == "Resources") {
5528 flags.Type = cmGeneratorTarget::SourceFileTypeResource;
5529 if (stripResources) {
5530 flags.MacFolder = "";
5532 } else if (cmHasLiteralPrefix(*location, "Resources/")) {
5533 flags.Type = cmGeneratorTarget::SourceFileTypeDeepResource;
5534 if (stripResources) {
5535 flags.MacFolder += strlen("Resources/");
5538 flags.Type = cmGeneratorTarget::SourceFileTypeMacContent;
5545 void cmGeneratorTarget::ConstructSourceFileFlags() const
5547 if (this->SourceFileFlagsConstructed) {
5550 this->SourceFileFlagsConstructed = true;
5552 // Process public headers to mark the source files.
5553 if (cmValue files = this->GetProperty("PUBLIC_HEADER")) {
5554 std::vector<std::string> relFiles = cmExpandedList(*files);
5555 for (std::string const& relFile : relFiles) {
5556 if (cmSourceFile* sf = this->Makefile->GetSource(relFile)) {
5557 SourceFileFlags& flags = this->SourceFlagsMap[sf];
5558 flags.MacFolder = "Headers";
5559 flags.Type = cmGeneratorTarget::SourceFileTypePublicHeader;
5564 // Process private headers after public headers so that they take
5565 // precedence if a file is listed in both.
5566 if (cmValue files = this->GetProperty("PRIVATE_HEADER")) {
5567 std::vector<std::string> relFiles = cmExpandedList(*files);
5568 for (std::string const& relFile : relFiles) {
5569 if (cmSourceFile* sf = this->Makefile->GetSource(relFile)) {
5570 SourceFileFlags& flags = this->SourceFlagsMap[sf];
5571 flags.MacFolder = "PrivateHeaders";
5572 flags.Type = cmGeneratorTarget::SourceFileTypePrivateHeader;
5577 // Mark sources listed as resources.
5578 if (cmValue files = this->GetProperty("RESOURCE")) {
5579 std::vector<std::string> relFiles = cmExpandedList(*files);
5580 for (std::string const& relFile : relFiles) {
5581 if (cmSourceFile* sf = this->Makefile->GetSource(relFile)) {
5582 SourceFileFlags& flags = this->SourceFlagsMap[sf];
5583 flags.MacFolder = "";
5584 if (!this->GlobalGenerator->ShouldStripResourcePath(this->Makefile)) {
5585 flags.MacFolder = "Resources";
5587 flags.Type = cmGeneratorTarget::SourceFileTypeResource;
5593 const cmGeneratorTarget::CompatibleInterfacesBase&
5594 cmGeneratorTarget::GetCompatibleInterfaces(std::string const& config) const
5596 cmGeneratorTarget::CompatibleInterfaces& compat =
5597 this->CompatibleInterfacesMap[config];
5600 compat.PropsBool.insert("POSITION_INDEPENDENT_CODE");
5601 compat.PropsString.insert("AUTOUIC_OPTIONS");
5602 std::vector<cmGeneratorTarget const*> const& deps =
5603 this->GetLinkImplementationClosure(config);
5604 for (cmGeneratorTarget const* li : deps) {
5605 #define CM_READ_COMPATIBLE_INTERFACE(X, x) \
5606 if (cmValue prop = li->GetProperty("COMPATIBLE_INTERFACE_" #X)) { \
5607 std::vector<std::string> props; \
5608 cmExpandList(*prop, props); \
5609 compat.Props##x.insert(props.begin(), props.end()); \
5611 CM_READ_COMPATIBLE_INTERFACE(BOOL, Bool)
5612 CM_READ_COMPATIBLE_INTERFACE(STRING, String)
5613 CM_READ_COMPATIBLE_INTERFACE(NUMBER_MIN, NumberMin)
5614 CM_READ_COMPATIBLE_INTERFACE(NUMBER_MAX, NumberMax)
5615 #undef CM_READ_COMPATIBLE_INTERFACE
5621 bool cmGeneratorTarget::IsLinkInterfaceDependentBoolProperty(
5622 const std::string& p, const std::string& config) const
5624 if (this->GetType() == cmStateEnums::OBJECT_LIBRARY ||
5625 this->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
5628 return this->GetCompatibleInterfaces(config).PropsBool.count(p) > 0;
5631 bool cmGeneratorTarget::IsLinkInterfaceDependentStringProperty(
5632 const std::string& p, const std::string& config) const
5634 if (this->GetType() == cmStateEnums::OBJECT_LIBRARY ||
5635 this->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
5638 return this->GetCompatibleInterfaces(config).PropsString.count(p) > 0;
5641 bool cmGeneratorTarget::IsLinkInterfaceDependentNumberMinProperty(
5642 const std::string& p, const std::string& config) const
5644 if (this->GetType() == cmStateEnums::OBJECT_LIBRARY ||
5645 this->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
5648 return this->GetCompatibleInterfaces(config).PropsNumberMin.count(p) > 0;
5651 bool cmGeneratorTarget::IsLinkInterfaceDependentNumberMaxProperty(
5652 const std::string& p, const std::string& config) const
5654 if (this->GetType() == cmStateEnums::OBJECT_LIBRARY ||
5655 this->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
5658 return this->GetCompatibleInterfaces(config).PropsNumberMax.count(p) > 0;
5669 template <typename PropertyType>
5670 PropertyType getLinkInterfaceDependentProperty(cmGeneratorTarget const* tgt,
5671 const std::string& prop,
5672 const std::string& config,
5673 CompatibleType, PropertyType*);
5676 bool getLinkInterfaceDependentProperty(cmGeneratorTarget const* tgt,
5677 const std::string& prop,
5678 const std::string& config,
5679 CompatibleType /*unused*/,
5682 return tgt->GetLinkInterfaceDependentBoolProperty(prop, config);
5686 const char* getLinkInterfaceDependentProperty(cmGeneratorTarget const* tgt,
5687 const std::string& prop,
5688 const std::string& config,
5690 const char** /*unused*/)
5695 "String compatibility check function called for boolean");
5698 return tgt->GetLinkInterfaceDependentStringProperty(prop, config);
5700 return tgt->GetLinkInterfaceDependentNumberMinProperty(prop, config);
5702 return tgt->GetLinkInterfaceDependentNumberMaxProperty(prop, config);
5704 assert(false && "Unreachable!");
5708 template <typename PropertyType>
5709 void checkPropertyConsistency(cmGeneratorTarget const* depender,
5710 cmGeneratorTarget const* dependee,
5711 const std::string& propName,
5712 std::set<std::string>& emitted,
5713 const std::string& config, CompatibleType t,
5714 PropertyType* /*unused*/)
5716 cmValue prop = dependee->GetProperty(propName);
5721 std::vector<std::string> props = cmExpandedList(*prop);
5723 cmStrCat(cmSystemTools::GetCMakeRoot(), "/Help/prop_tgt/");
5725 for (std::string const& p : props) {
5726 std::string pname = cmSystemTools::HelpFileName(p);
5727 std::string pfile = pdir + pname + ".rst";
5728 if (cmSystemTools::FileExists(pfile, true)) {
5729 std::ostringstream e;
5730 e << "Target \"" << dependee->GetName() << "\" has property \"" << p
5731 << "\" listed in its " << propName
5733 "This is not allowed. Only user-defined properties may appear "
5735 << propName << " property.";
5736 depender->GetLocalGenerator()->IssueMessage(MessageType::FATAL_ERROR,
5740 if (emitted.insert(p).second) {
5741 getLinkInterfaceDependentProperty<PropertyType>(depender, p, config, t,
5743 if (cmSystemTools::GetErrorOccurredFlag()) {
5751 std::string intersect(const std::set<std::string>& s1,
5752 const std::set<std::string>& s2)
5754 std::set<std::string> intersect;
5755 std::set_intersection(s1.begin(), s1.end(), s2.begin(), s2.end(),
5756 std::inserter(intersect, intersect.begin()));
5757 if (!intersect.empty()) {
5758 return *intersect.begin();
5763 std::string intersect(const std::set<std::string>& s1,
5764 const std::set<std::string>& s2,
5765 const std::set<std::string>& s3)
5768 result = intersect(s1, s2);
5769 if (!result.empty()) {
5772 result = intersect(s1, s3);
5773 if (!result.empty()) {
5776 return intersect(s2, s3);
5779 std::string intersect(const std::set<std::string>& s1,
5780 const std::set<std::string>& s2,
5781 const std::set<std::string>& s3,
5782 const std::set<std::string>& s4)
5785 result = intersect(s1, s2);
5786 if (!result.empty()) {
5789 result = intersect(s1, s3);
5790 if (!result.empty()) {
5793 result = intersect(s1, s4);
5794 if (!result.empty()) {
5797 return intersect(s2, s3, s4);
5801 void cmGeneratorTarget::CheckPropertyCompatibility(
5802 cmComputeLinkInformation& info, const std::string& config) const
5804 const cmComputeLinkInformation::ItemVector& deps = info.GetItems();
5806 std::set<std::string> emittedBools;
5807 static const std::string strBool = "COMPATIBLE_INTERFACE_BOOL";
5808 std::set<std::string> emittedStrings;
5809 static const std::string strString = "COMPATIBLE_INTERFACE_STRING";
5810 std::set<std::string> emittedMinNumbers;
5811 static const std::string strNumMin = "COMPATIBLE_INTERFACE_NUMBER_MIN";
5812 std::set<std::string> emittedMaxNumbers;
5813 static const std::string strNumMax = "COMPATIBLE_INTERFACE_NUMBER_MAX";
5815 for (auto const& dep : deps) {
5820 checkPropertyConsistency<bool>(this, dep.Target, strBool, emittedBools,
5821 config, BoolType, nullptr);
5822 if (cmSystemTools::GetErrorOccurredFlag()) {
5825 checkPropertyConsistency<const char*>(this, dep.Target, strString,
5826 emittedStrings, config, StringType,
5828 if (cmSystemTools::GetErrorOccurredFlag()) {
5831 checkPropertyConsistency<const char*>(this, dep.Target, strNumMin,
5832 emittedMinNumbers, config,
5833 NumberMinType, nullptr);
5834 if (cmSystemTools::GetErrorOccurredFlag()) {
5837 checkPropertyConsistency<const char*>(this, dep.Target, strNumMax,
5838 emittedMaxNumbers, config,
5839 NumberMaxType, nullptr);
5840 if (cmSystemTools::GetErrorOccurredFlag()) {
5845 std::string prop = intersect(emittedBools, emittedStrings, emittedMinNumbers,
5848 if (!prop.empty()) {
5849 // Use a sorted std::vector to keep the error message sorted.
5850 std::vector<std::string> props;
5851 auto i = emittedBools.find(prop);
5852 if (i != emittedBools.end()) {
5853 props.push_back(strBool);
5855 i = emittedStrings.find(prop);
5856 if (i != emittedStrings.end()) {
5857 props.push_back(strString);
5859 i = emittedMinNumbers.find(prop);
5860 if (i != emittedMinNumbers.end()) {
5861 props.push_back(strNumMin);
5863 i = emittedMaxNumbers.find(prop);
5864 if (i != emittedMaxNumbers.end()) {
5865 props.push_back(strNumMax);
5867 std::sort(props.begin(), props.end());
5869 std::string propsString = cmStrCat(
5870 cmJoin(cmMakeRange(props).retreat(1), ", "), " and the ", props.back());
5872 std::ostringstream e;
5873 e << "Property \"" << prop << "\" appears in both the " << propsString
5874 << " property in the dependencies of target \"" << this->GetName()
5875 << "\". This is not allowed. A property may only require "
5877 "in a boolean interpretation, a numeric minimum, a numeric maximum "
5879 "string interpretation, but not a mixture.";
5880 this->LocalGenerator->IssueMessage(MessageType::FATAL_ERROR, e.str());
5884 template <typename PropertyType>
5885 std::string valueAsString(PropertyType);
5887 std::string valueAsString<bool>(bool value)
5889 return value ? "TRUE" : "FALSE";
5892 std::string valueAsString<const char*>(const char* value)
5894 return value ? value : "(unset)";
5897 std::string valueAsString<std::string>(std::string value)
5902 std::string valueAsString<cmValue>(cmValue value)
5904 return value ? value : std::string("(unset)");
5907 std::string valueAsString<std::nullptr_t>(std::nullptr_t /*unused*/)
5912 static std::string compatibilityType(CompatibleType t)
5916 return "Boolean compatibility";
5918 return "String compatibility";
5920 return "Numeric maximum compatibility";
5922 return "Numeric minimum compatibility";
5924 assert(false && "Unreachable!");
5928 static std::string compatibilityAgree(CompatibleType t, bool dominant)
5933 return dominant ? "(Disagree)\n" : "(Agree)\n";
5936 return dominant ? "(Dominant)\n" : "(Ignored)\n";
5938 assert(false && "Unreachable!");
5942 template <typename PropertyType>
5943 PropertyType getTypedProperty(
5944 cmGeneratorTarget const* tgt, const std::string& prop,
5945 cmGeneratorExpressionInterpreter* genexInterpreter = nullptr);
5948 bool getTypedProperty<bool>(cmGeneratorTarget const* tgt,
5949 const std::string& prop,
5950 cmGeneratorExpressionInterpreter* genexInterpreter)
5952 if (genexInterpreter == nullptr) {
5953 return tgt->GetPropertyAsBool(prop);
5956 cmValue value = tgt->GetProperty(prop);
5957 return cmIsOn(genexInterpreter->Evaluate(value ? *value : "", prop));
5961 const char* getTypedProperty<const char*>(
5962 cmGeneratorTarget const* tgt, const std::string& prop,
5963 cmGeneratorExpressionInterpreter* genexInterpreter)
5965 cmValue value = tgt->GetProperty(prop);
5967 if (genexInterpreter == nullptr) {
5968 return value.GetCStr();
5971 return genexInterpreter->Evaluate(value ? *value : "", prop).c_str();
5975 std::string getTypedProperty<std::string>(
5976 cmGeneratorTarget const* tgt, const std::string& prop,
5977 cmGeneratorExpressionInterpreter* genexInterpreter)
5979 cmValue value = tgt->GetProperty(prop);
5981 if (genexInterpreter == nullptr) {
5982 return valueAsString(value);
5985 return genexInterpreter->Evaluate(value ? *value : "", prop);
5988 template <typename PropertyType>
5989 PropertyType impliedValue(PropertyType);
5991 bool impliedValue<bool>(bool /*unused*/)
5996 const char* impliedValue<const char*>(const char* /*unused*/)
6001 std::string impliedValue<std::string>(std::string /*unused*/) // NOLINT(*)
6003 return std::string();
6006 template <typename PropertyType>
6007 std::pair<bool, PropertyType> consistentProperty(PropertyType lhs,
6012 std::pair<bool, bool> consistentProperty(bool lhs, bool rhs,
6013 CompatibleType /*unused*/)
6015 return { lhs == rhs, lhs };
6018 static std::pair<bool, const char*> consistentStringProperty(const char* lhs,
6021 const bool b = strcmp(lhs, rhs) == 0;
6022 return { b, b ? lhs : nullptr };
6025 static std::pair<bool, std::string> consistentStringProperty(
6026 const std::string& lhs, const std::string& rhs)
6028 const bool b = lhs == rhs;
6029 return { b, b ? lhs : valueAsString(nullptr) };
6032 static std::pair<bool, const char*> consistentNumberProperty(const char* lhs,
6038 long lnum = strtol(lhs, &pEnd, 0);
6039 if (pEnd == lhs || *pEnd != '\0' || errno == ERANGE) {
6040 return { false, nullptr };
6043 long rnum = strtol(rhs, &pEnd, 0);
6044 if (pEnd == rhs || *pEnd != '\0' || errno == ERANGE) {
6045 return { false, nullptr };
6048 if (t == NumberMaxType) {
6049 return { true, std::max(lnum, rnum) == lnum ? lhs : rhs };
6052 return { true, std::min(lnum, rnum) == lnum ? lhs : rhs };
6056 std::pair<bool, const char*> consistentProperty(const char* lhs,
6061 return { true, lhs };
6064 return { true, rhs };
6067 return { true, lhs };
6072 bool same = cmIsOn(lhs) == cmIsOn(rhs);
6073 return { same, same ? lhs : nullptr };
6076 return consistentStringProperty(lhs, rhs);
6079 return consistentNumberProperty(lhs, rhs, t);
6081 assert(false && "Unreachable!");
6082 return { false, nullptr };
6085 static std::pair<bool, std::string> consistentProperty(const std::string& lhs,
6086 const std::string& rhs,
6089 const std::string null_ptr = valueAsString(nullptr);
6091 if (lhs == null_ptr && rhs == null_ptr) {
6092 return { true, lhs };
6094 if (lhs == null_ptr) {
6095 return { true, rhs };
6097 if (rhs == null_ptr) {
6098 return { true, lhs };
6103 bool same = cmIsOn(lhs) == cmIsOn(rhs);
6104 return { same, same ? lhs : null_ptr };
6107 return consistentStringProperty(lhs, rhs);
6109 case NumberMaxType: {
6110 auto value = consistentNumberProperty(lhs.c_str(), rhs.c_str(), t);
6111 return { value.first,
6112 value.first ? std::string(value.second) : null_ptr };
6115 assert(false && "Unreachable!");
6116 return { false, null_ptr };
6119 template <typename PropertyType>
6120 PropertyType checkInterfacePropertyCompatibility(cmGeneratorTarget const* tgt,
6121 const std::string& p,
6122 const std::string& config,
6123 const char* defaultValue,
6125 PropertyType* /*unused*/)
6127 PropertyType propContent = getTypedProperty<PropertyType>(tgt, p);
6129 std::vector<std::string> headPropKeys = tgt->GetPropertyKeys();
6130 const bool explicitlySet = cm::contains(headPropKeys, p);
6132 const bool impliedByUse = tgt->IsNullImpliedByLinkLibraries(p);
6133 assert((impliedByUse ^ explicitlySet) || (!impliedByUse && !explicitlySet));
6135 std::vector<cmGeneratorTarget const*> const& deps =
6136 tgt->GetLinkImplementationClosure(config);
6141 bool propInitialized = explicitlySet;
6143 std::string report = cmStrCat(" * Target \"", tgt->GetName());
6144 if (explicitlySet) {
6145 report += "\" has property content \"";
6146 report += valueAsString<PropertyType>(propContent);
6148 } else if (impliedByUse) {
6149 report += "\" property is implied by use.\n";
6151 report += "\" property not set.\n";
6154 std::string interfaceProperty = "INTERFACE_" + p;
6155 std::unique_ptr<cmGeneratorExpressionInterpreter> genexInterpreter;
6156 if (p == "POSITION_INDEPENDENT_CODE") {
6157 genexInterpreter = cm::make_unique<cmGeneratorExpressionInterpreter>(
6158 tgt->GetLocalGenerator(), config, tgt);
6161 for (cmGeneratorTarget const* theTarget : deps) {
6162 // An error should be reported if one dependency
6163 // has INTERFACE_POSITION_INDEPENDENT_CODE ON and the other
6164 // has INTERFACE_POSITION_INDEPENDENT_CODE OFF, or if the
6165 // target itself has a POSITION_INDEPENDENT_CODE which disagrees
6166 // with a dependency.
6168 std::vector<std::string> propKeys = theTarget->GetPropertyKeys();
6170 const bool ifaceIsSet = cm::contains(propKeys, interfaceProperty);
6171 PropertyType ifacePropContent = getTypedProperty<PropertyType>(
6172 theTarget, interfaceProperty, genexInterpreter.get());
6174 std::string reportEntry;
6176 reportEntry += " * Target \"";
6177 reportEntry += theTarget->GetName();
6178 reportEntry += "\" property value \"";
6179 reportEntry += valueAsString<PropertyType>(ifacePropContent);
6180 reportEntry += "\" ";
6183 if (explicitlySet) {
6185 std::pair<bool, PropertyType> consistent =
6186 consistentProperty(propContent, ifacePropContent, t);
6187 report += reportEntry;
6188 report += compatibilityAgree(t, propContent != consistent.second);
6189 if (!consistent.first) {
6190 std::ostringstream e;
6191 e << "Property " << p << " on target \"" << tgt->GetName()
6192 << "\" does\nnot match the "
6195 << " property requirement\nof "
6197 << theTarget->GetName() << "\".\n";
6198 cmSystemTools::Error(e.str());
6201 propContent = consistent.second;
6204 // Explicitly set on target and not set in iface. Can't disagree.
6208 propContent = impliedValue<PropertyType>(propContent);
6211 std::pair<bool, PropertyType> consistent =
6212 consistentProperty(propContent, ifacePropContent, t);
6213 report += reportEntry;
6214 report += compatibilityAgree(t, propContent != consistent.second);
6215 if (!consistent.first) {
6216 std::ostringstream e;
6217 e << "Property " << p << " on target \"" << tgt->GetName()
6218 << "\" is\nimplied to be " << defaultValue
6219 << " because it was used to determine the link libraries\n"
6220 "already. The INTERFACE_"
6221 << p << " property on\ndependency \"" << theTarget->GetName()
6222 << "\" is in conflict.\n";
6223 cmSystemTools::Error(e.str());
6226 propContent = consistent.second;
6229 // Implicitly set on target and not set in iface. Can't disagree.
6233 if (propInitialized) {
6234 std::pair<bool, PropertyType> consistent =
6235 consistentProperty(propContent, ifacePropContent, t);
6236 report += reportEntry;
6237 report += compatibilityAgree(t, propContent != consistent.second);
6238 if (!consistent.first) {
6239 std::ostringstream e;
6240 e << "The INTERFACE_" << p << " property of \""
6241 << theTarget->GetName() << "\" does\nnot agree with the value of "
6242 << p << " already determined\nfor \"" << tgt->GetName() << "\".\n";
6243 cmSystemTools::Error(e.str());
6246 propContent = consistent.second;
6249 report += reportEntry + "(Interface set)\n";
6250 propContent = ifacePropContent;
6251 propInitialized = true;
6253 // Not set. Nothing to agree on.
6258 tgt->ReportPropertyOrigin(p, valueAsString<PropertyType>(propContent),
6259 report, compatibilityType(t));
6263 bool cmGeneratorTarget::SetDeviceLink(bool deviceLink)
6265 bool previous = this->DeviceLink;
6266 this->DeviceLink = deviceLink;
6270 bool cmGeneratorTarget::GetLinkInterfaceDependentBoolProperty(
6271 const std::string& p, const std::string& config) const
6273 return checkInterfacePropertyCompatibility<bool>(this, p, config, "FALSE",
6277 std::string cmGeneratorTarget::GetLinkInterfaceDependentStringAsBoolProperty(
6278 const std::string& p, const std::string& config) const
6280 return checkInterfacePropertyCompatibility<std::string>(
6281 this, p, config, "FALSE", BoolType, nullptr);
6284 const char* cmGeneratorTarget::GetLinkInterfaceDependentStringProperty(
6285 const std::string& p, const std::string& config) const
6287 return checkInterfacePropertyCompatibility<const char*>(
6288 this, p, config, "empty", StringType, nullptr);
6291 const char* cmGeneratorTarget::GetLinkInterfaceDependentNumberMinProperty(
6292 const std::string& p, const std::string& config) const
6294 return checkInterfacePropertyCompatibility<const char*>(
6295 this, p, config, "empty", NumberMinType, nullptr);
6298 const char* cmGeneratorTarget::GetLinkInterfaceDependentNumberMaxProperty(
6299 const std::string& p, const std::string& config) const
6301 return checkInterfacePropertyCompatibility<const char*>(
6302 this, p, config, "empty", NumberMaxType, nullptr);
6305 cmComputeLinkInformation* cmGeneratorTarget::GetLinkInformation(
6306 const std::string& config) const
6308 // Lookup any existing information for this configuration.
6309 std::string key(cmSystemTools::UpperCase(config));
6310 auto i = this->LinkInformation.find(key);
6311 if (i == this->LinkInformation.end()) {
6312 // Compute information for this configuration.
6313 auto info = cm::make_unique<cmComputeLinkInformation>(this, config);
6314 if (info && !info->Compute()) {
6318 // Store the information for this configuration.
6319 i = this->LinkInformation.emplace(key, std::move(info)).first;
6322 this->CheckPropertyCompatibility(*i->second, config);
6325 return i->second.get();
6328 void cmGeneratorTarget::CheckLinkLibraries() const
6330 bool linkLibrariesOnlyTargets =
6331 this->GetPropertyAsBool("LINK_LIBRARIES_ONLY_TARGETS");
6333 // Evaluate the link interface of this target if needed for extra checks.
6334 if (linkLibrariesOnlyTargets) {
6335 std::vector<std::string> const& configs =
6336 this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
6337 for (std::string const& config : configs) {
6338 this->GetLinkInterfaceLibraries(config, this, LinkInterfaceFor::Link);
6342 // Check link the implementation for each generated configuration.
6343 for (auto const& hmp : this->LinkImplMap) {
6344 HeadToLinkImplementationMap const& hm = hmp.second;
6345 // There could be several entries used when computing the pre-CMP0022
6346 // default link interface. Check only the entry for our own link impl.
6347 auto const hmi = hm.find(this);
6348 if (hmi == hm.end() || !hmi->second.LibrariesDone) {
6351 for (cmLinkImplItem const& item : hmi->second.Libraries) {
6352 if (!this->VerifyLinkItemColons(LinkItemRole::Implementation, item)) {
6355 if (linkLibrariesOnlyTargets &&
6356 !this->VerifyLinkItemIsTarget(LinkItemRole::Implementation, item)) {
6362 // Check link the interface for each generated combination of
6363 // configuration and consuming head target. We should not need to
6364 // consider LinkInterfaceUsageRequirementsOnlyMap because its entries
6365 // should be a subset of LinkInterfaceMap (with LINK_ONLY left out).
6366 for (auto const& hmp : this->LinkInterfaceMap) {
6367 for (auto const& hmi : hmp.second) {
6368 if (!hmi.second.LibrariesDone) {
6371 for (cmLinkItem const& item : hmi.second.Libraries) {
6372 if (!this->VerifyLinkItemColons(LinkItemRole::Interface, item)) {
6375 if (linkLibrariesOnlyTargets &&
6376 !this->VerifyLinkItemIsTarget(LinkItemRole::Interface, item)) {
6385 cm::string_view missingTargetPossibleReasons =
6386 "Possible reasons include:\n"
6387 " * There is a typo in the target name.\n"
6388 " * A find_package call is missing for an IMPORTED target.\n"
6389 " * An ALIAS target is missing.\n"_s;
6392 bool cmGeneratorTarget::VerifyLinkItemColons(LinkItemRole role,
6393 cmLinkItem const& item) const
6395 if (item.Target || cmHasPrefix(item.AsStr(), "<LINK_GROUP:"_s) ||
6396 item.AsStr().find("::") == std::string::npos) {
6399 MessageType messageType = MessageType::FATAL_ERROR;
6401 switch (this->GetLocalGenerator()->GetPolicyStatus(cmPolicies::CMP0028)) {
6402 case cmPolicies::WARN: {
6403 e = cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0028), "\n");
6404 messageType = MessageType::AUTHOR_WARNING;
6406 case cmPolicies::OLD:
6408 case cmPolicies::REQUIRED_IF_USED:
6409 case cmPolicies::REQUIRED_ALWAYS:
6410 case cmPolicies::NEW:
6411 // Issue the fatal message.
6415 if (role == LinkItemRole::Implementation) {
6416 e = cmStrCat(e, "Target \"", this->GetName(), "\" links to");
6418 e = cmStrCat(e, "The link interface of target \"", this->GetName(),
6422 cmStrCat(e, ":\n ", item.AsStr(), "\n", "but the target was not found. ",
6423 missingTargetPossibleReasons);
6424 cmListFileBacktrace backtrace = item.Backtrace;
6425 if (backtrace.Empty()) {
6426 backtrace = this->GetBacktrace();
6428 this->GetLocalGenerator()->GetCMakeInstance()->IssueMessage(messageType, e,
6433 bool cmGeneratorTarget::VerifyLinkItemIsTarget(LinkItemRole role,
6434 cmLinkItem const& item) const
6439 std::string const& str = item.AsStr();
6441 (str[0] == '-' || str[0] == '$' || str[0] == '`' ||
6442 str.find_first_of("/\\") != std::string::npos ||
6443 cmHasPrefix(str, "<LINK_LIBRARY:"_s) ||
6444 cmHasPrefix(str, "<LINK_GROUP:"_s))) {
6448 std::string e = cmStrCat("Target \"", this->GetName(),
6449 "\" has LINK_LIBRARIES_ONLY_TARGETS enabled, but ",
6450 role == LinkItemRole::Implementation
6452 : "its link interface contains",
6453 ":\n ", item.AsStr(), "\nwhich is not a target. ",
6454 missingTargetPossibleReasons);
6455 cmListFileBacktrace backtrace = item.Backtrace;
6456 if (backtrace.Empty()) {
6457 backtrace = this->GetBacktrace();
6459 this->LocalGenerator->GetCMakeInstance()->IssueMessage(
6460 MessageType::FATAL_ERROR, e, backtrace);
6464 void cmGeneratorTarget::GetTargetVersion(int& major, int& minor) const
6467 this->GetTargetVersion("VERSION", major, minor, patch);
6470 void cmGeneratorTarget::GetTargetVersionFallback(
6471 const std::string& property, const std::string& fallback_property,
6472 int& major, int& minor, int& patch) const
6474 if (this->GetProperty(property)) {
6475 this->GetTargetVersion(property, major, minor, patch);
6477 this->GetTargetVersion(fallback_property, major, minor, patch);
6481 void cmGeneratorTarget::GetTargetVersion(const std::string& property,
6482 int& major, int& minor,
6485 // Set the default values.
6490 assert(this->GetType() != cmStateEnums::INTERFACE_LIBRARY);
6492 if (cmValue version = this->GetProperty(property)) {
6493 // Try to parse the version number and store the results that were
6494 // successfully parsed.
6498 switch (sscanf(version->c_str(), "%d.%d.%d", &parsed_major, &parsed_minor,
6501 patch = parsed_patch;
6504 minor = parsed_minor;
6507 major = parsed_major;
6515 std::string cmGeneratorTarget::GetRuntimeLinkLibrary(
6516 std::string const& lang, std::string const& config) const
6518 // This is activated by the presence of a default selection whether or
6519 // not it is overridden by a property.
6520 cmValue runtimeLibraryDefault = this->Makefile->GetDefinition(
6521 cmStrCat("CMAKE_", lang, "_RUNTIME_LIBRARY_DEFAULT"));
6522 if (!cmNonempty(runtimeLibraryDefault)) {
6523 return std::string();
6525 cmValue runtimeLibraryValue =
6526 this->Target->GetProperty(cmStrCat(lang, "_RUNTIME_LIBRARY"));
6527 if (!runtimeLibraryValue) {
6528 runtimeLibraryValue = runtimeLibraryDefault;
6530 return cmSystemTools::UpperCase(cmGeneratorExpression::Evaluate(
6531 *runtimeLibraryValue, this->LocalGenerator, config, this));
6534 std::string cmGeneratorTarget::GetFortranModuleDirectory(
6535 std::string const& working_dir) const
6537 if (!this->FortranModuleDirectoryCreated) {
6538 this->FortranModuleDirectory =
6539 this->CreateFortranModuleDirectory(working_dir);
6540 this->FortranModuleDirectoryCreated = true;
6543 return this->FortranModuleDirectory;
6546 bool cmGeneratorTarget::IsFortranBuildingInstrinsicModules() const
6549 this->GetProperty("Fortran_BUILDING_INSTRINSIC_MODULES")) {
6550 return cmIsOn(*prop);
6555 std::string cmGeneratorTarget::CreateFortranModuleDirectory(
6556 std::string const& working_dir) const
6558 std::string mod_dir;
6559 std::string target_mod_dir;
6560 if (cmValue prop = this->GetProperty("Fortran_MODULE_DIRECTORY")) {
6561 target_mod_dir = *prop;
6563 std::string const& default_mod_dir =
6564 this->LocalGenerator->GetCurrentBinaryDirectory();
6565 if (default_mod_dir != working_dir) {
6566 target_mod_dir = default_mod_dir;
6569 cmValue moddir_flag =
6570 this->Makefile->GetDefinition("CMAKE_Fortran_MODDIR_FLAG");
6571 if (!target_mod_dir.empty() && moddir_flag) {
6572 // Compute the full path to the module directory.
6573 if (cmSystemTools::FileIsFullPath(target_mod_dir)) {
6574 // Already a full path.
6575 mod_dir = target_mod_dir;
6577 // Interpret relative to the current output directory.
6578 mod_dir = cmStrCat(this->LocalGenerator->GetCurrentBinaryDirectory(),
6579 '/', target_mod_dir);
6582 // Make sure the module output directory exists.
6583 cmSystemTools::MakeDirectory(mod_dir);
6588 void cmGeneratorTarget::AddISPCGeneratedHeader(std::string const& header,
6589 std::string const& config)
6591 std::string config_upper;
6592 if (!config.empty()) {
6593 config_upper = cmSystemTools::UpperCase(config);
6595 auto iter = this->ISPCGeneratedHeaders.find(config_upper);
6596 if (iter == this->ISPCGeneratedHeaders.end()) {
6597 std::vector<std::string> headers;
6598 headers.emplace_back(header);
6599 this->ISPCGeneratedHeaders.insert({ config_upper, headers });
6601 iter->second.emplace_back(header);
6605 std::vector<std::string> cmGeneratorTarget::GetGeneratedISPCHeaders(
6606 std::string const& config) const
6608 std::string config_upper;
6609 if (!config.empty()) {
6610 config_upper = cmSystemTools::UpperCase(config);
6612 auto iter = this->ISPCGeneratedHeaders.find(config_upper);
6613 if (iter == this->ISPCGeneratedHeaders.end()) {
6614 return std::vector<std::string>{};
6616 return iter->second;
6619 void cmGeneratorTarget::AddISPCGeneratedObject(std::vector<std::string>&& objs,
6620 std::string const& config)
6622 std::string config_upper;
6623 if (!config.empty()) {
6624 config_upper = cmSystemTools::UpperCase(config);
6626 auto iter = this->ISPCGeneratedObjects.find(config_upper);
6627 if (iter == this->ISPCGeneratedObjects.end()) {
6628 this->ISPCGeneratedObjects.insert({ config_upper, objs });
6630 iter->second.insert(iter->second.end(), objs.begin(), objs.end());
6634 std::vector<std::string> cmGeneratorTarget::GetGeneratedISPCObjects(
6635 std::string const& config) const
6637 std::string config_upper;
6638 if (!config.empty()) {
6639 config_upper = cmSystemTools::UpperCase(config);
6641 auto iter = this->ISPCGeneratedObjects.find(config_upper);
6642 if (iter == this->ISPCGeneratedObjects.end()) {
6643 return std::vector<std::string>{};
6645 return iter->second;
6648 std::string cmGeneratorTarget::GetFrameworkVersion() const
6650 assert(this->GetType() != cmStateEnums::INTERFACE_LIBRARY);
6652 if (cmValue fversion = this->GetProperty("FRAMEWORK_VERSION")) {
6655 if (cmValue tversion = this->GetProperty("VERSION")) {
6661 void cmGeneratorTarget::ComputeVersionedName(
6662 std::string& vName, std::string const& prefix, std::string const& base,
6663 std::string const& suffix, std::string const& name, cmValue version) const
6665 vName = this->Makefile->IsOn("APPLE") ? (prefix + base) : name;
6670 vName += this->Makefile->IsOn("APPLE") ? suffix : std::string();
6673 std::vector<std::string> cmGeneratorTarget::GetPropertyKeys() const
6675 return this->Target->GetProperties().GetKeys();
6678 void cmGeneratorTarget::ReportPropertyOrigin(
6679 const std::string& p, const std::string& result, const std::string& report,
6680 const std::string& compatibilityType) const
6682 std::vector<std::string> debugProperties;
6683 this->Target->GetMakefile()->GetDefExpandList(
6684 "CMAKE_DEBUG_TARGET_PROPERTIES", debugProperties);
6686 bool debugOrigin = !this->DebugCompatiblePropertiesDone[p] &&
6687 cm::contains(debugProperties, p);
6689 if (this->GlobalGenerator->GetConfigureDoneCMP0026()) {
6690 this->DebugCompatiblePropertiesDone[p] = true;
6696 std::string areport =
6697 cmStrCat(compatibilityType, " of property \"", p, "\" for target \"",
6698 this->GetName(), "\" (result: \"", result, "\"):\n", report);
6700 this->LocalGenerator->GetCMakeInstance()->IssueMessage(MessageType::LOG,
6704 bool cmGeneratorTarget::IsLinkLookupScope(std::string const& n,
6705 cmLocalGenerator const*& lg) const
6707 if (cmHasLiteralPrefix(n, CMAKE_DIRECTORY_ID_SEP)) {
6708 cmDirectoryId const dirId = n.substr(sizeof(CMAKE_DIRECTORY_ID_SEP) - 1);
6709 if (dirId.String.empty()) {
6710 lg = this->LocalGenerator;
6713 if (cmLocalGenerator const* otherLG =
6714 this->GlobalGenerator->FindLocalGenerator(dirId)) {
6722 cm::optional<cmLinkItem> cmGeneratorTarget::LookupLinkItem(
6723 std::string const& n, cmListFileBacktrace const& bt,
6724 LookupLinkItemScope* scope, LookupSelf lookupSelf) const
6726 cm::optional<cmLinkItem> maybeItem;
6727 if (this->IsLinkLookupScope(n, scope->LG)) {
6731 std::string name = this->CheckCMP0004(n);
6733 (lookupSelf == LookupSelf::No && name == this->GetName())) {
6736 maybeItem = this->ResolveLinkItem(BT<std::string>(name, bt), scope->LG);
6740 void cmGeneratorTarget::ExpandLinkItems(
6741 std::string const& prop, cmBTStringRange entries, std::string const& config,
6742 cmGeneratorTarget const* headTarget, LinkInterfaceFor interfaceFor,
6743 LinkInterfaceField field, cmLinkInterface& iface) const
6745 if (entries.empty()) {
6748 // Keep this logic in sync with ComputeLinkImplementationLibraries.
6749 cmGeneratorExpressionDAGChecker dagChecker(this, prop, nullptr, nullptr);
6750 // The $<LINK_ONLY> expression may be in a link interface to specify
6751 // private link dependencies that are otherwise excluded from usage
6753 if (interfaceFor == LinkInterfaceFor::Usage) {
6754 dagChecker.SetTransitivePropertiesOnly();
6756 cmMakefile const* mf = this->LocalGenerator->GetMakefile();
6757 LookupLinkItemScope scope{ this->LocalGenerator };
6758 for (BT<std::string> const& entry : entries) {
6759 cmGeneratorExpression ge(entry.Backtrace);
6760 std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(entry.Value);
6761 cge->SetEvaluateForBuildsystem(true);
6762 std::vector<std::string> libs = cmExpandedList(
6763 cge->Evaluate(this->LocalGenerator, config, headTarget, &dagChecker,
6764 this, headTarget->LinkerLanguage));
6765 for (std::string const& lib : libs) {
6766 if (cm::optional<cmLinkItem> maybeItem = this->LookupLinkItem(
6767 lib, cge->GetBacktrace(), &scope,
6768 field == LinkInterfaceField::Libraries ? LookupSelf::No
6769 : LookupSelf::Yes)) {
6770 cmLinkItem item = std::move(*maybeItem);
6772 if (field == LinkInterfaceField::HeadInclude) {
6773 iface.HeadInclude.emplace_back(std::move(item));
6776 if (field == LinkInterfaceField::HeadExclude) {
6777 iface.HeadExclude.emplace_back(std::move(item));
6781 // Report explicitly linked object files separately.
6782 std::string const& maybeObj = item.AsStr();
6783 if (cmSystemTools::FileIsFullPath(maybeObj)) {
6784 cmSourceFile const* sf =
6785 mf->GetSource(maybeObj, cmSourceFileLocationKind::Known);
6786 if (sf && sf->GetPropertyAsBool("EXTERNAL_OBJECT")) {
6787 iface.Objects.emplace_back(std::move(item));
6793 iface.Libraries.emplace_back(std::move(item));
6796 if (cge->GetHadHeadSensitiveCondition()) {
6797 iface.HadHeadSensitiveCondition = true;
6799 if (cge->GetHadContextSensitiveCondition()) {
6800 iface.HadContextSensitiveCondition = true;
6802 if (cge->GetHadLinkLanguageSensitiveCondition()) {
6803 iface.HadLinkLanguageSensitiveCondition = true;
6808 cmLinkInterface const* cmGeneratorTarget::GetLinkInterface(
6809 const std::string& config, cmGeneratorTarget const* head) const
6811 return this->GetLinkInterface(config, head, false);
6814 cmLinkInterface const* cmGeneratorTarget::GetLinkInterface(
6815 const std::string& config, cmGeneratorTarget const* head,
6816 bool secondPass) const
6818 // Imported targets have their own link interface.
6819 if (this->IsImported()) {
6820 return this->GetImportLinkInterface(config, head, LinkInterfaceFor::Link,
6824 // Link interfaces are not supported for executables that do not
6826 if (this->GetType() == cmStateEnums::EXECUTABLE &&
6827 !this->IsExecutableWithExports()) {
6831 // Lookup any existing link interface for this configuration.
6832 cmHeadToLinkInterfaceMap& hm = this->GetHeadToLinkInterfaceMap(config);
6834 // If the link interface does not depend on the head target
6835 // then re-use the one from the head we computed first.
6836 if (!hm.empty() && !hm.begin()->second.HadHeadSensitiveCondition) {
6837 head = hm.begin()->first;
6840 cmOptionalLinkInterface& iface = hm[head];
6842 iface = cmOptionalLinkInterface();
6844 if (!iface.LibrariesDone) {
6845 iface.LibrariesDone = true;
6846 this->ComputeLinkInterfaceLibraries(config, iface, head,
6847 LinkInterfaceFor::Link);
6849 if (!iface.AllDone) {
6850 iface.AllDone = true;
6852 this->ComputeLinkInterface(config, iface, head, secondPass);
6853 this->ComputeLinkInterfaceRuntimeLibraries(config, iface);
6857 return iface.Exists ? &iface : nullptr;
6860 void cmGeneratorTarget::ComputeLinkInterface(
6861 const std::string& config, cmOptionalLinkInterface& iface,
6862 cmGeneratorTarget const* headTarget) const
6864 this->ComputeLinkInterface(config, iface, headTarget, false);
6867 void cmGeneratorTarget::ComputeLinkInterface(
6868 const std::string& config, cmOptionalLinkInterface& iface,
6869 cmGeneratorTarget const* headTarget, bool secondPass) const
6871 if (iface.Explicit) {
6872 if (this->GetType() == cmStateEnums::SHARED_LIBRARY ||
6873 this->GetType() == cmStateEnums::STATIC_LIBRARY ||
6874 this->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
6875 // Shared libraries may have runtime implementation dependencies
6876 // on other shared libraries that are not in the interface.
6877 std::set<cmLinkItem> emitted;
6878 for (cmLinkItem const& lib : iface.Libraries) {
6879 emitted.insert(lib);
6881 if (this->GetType() != cmStateEnums::INTERFACE_LIBRARY) {
6882 cmLinkImplementation const* impl = this->GetLinkImplementation(
6883 config, LinkInterfaceFor::Link, secondPass);
6884 for (cmLinkImplItem const& lib : impl->Libraries) {
6885 if (emitted.insert(lib).second) {
6887 // This is a runtime dependency on another shared library.
6888 if (lib.Target->GetType() == cmStateEnums::SHARED_LIBRARY) {
6889 iface.SharedDeps.push_back(lib);
6892 // TODO: Recognize shared library file names. Perhaps this
6893 // should be moved to cmComputeLinkInformation, but that
6894 // creates a chicken-and-egg problem since this list is needed
6895 // for its construction.
6901 } else if (this->GetPolicyStatusCMP0022() == cmPolicies::WARN ||
6902 this->GetPolicyStatusCMP0022() == cmPolicies::OLD) {
6903 // The link implementation is the default link interface.
6904 cmLinkImplementationLibraries const* impl =
6905 this->GetLinkImplementationLibrariesInternal(config, headTarget,
6906 LinkInterfaceFor::Link);
6907 iface.ImplementationIsInterface = true;
6908 iface.WrongConfigLibraries = impl->WrongConfigLibraries;
6911 if (this->LinkLanguagePropagatesToDependents()) {
6912 // Targets using this archive need its language runtime libraries.
6913 if (cmLinkImplementation const* impl = this->GetLinkImplementation(
6914 config, LinkInterfaceFor::Link, secondPass)) {
6915 iface.Languages = impl->Languages;
6919 if (this->GetType() == cmStateEnums::STATIC_LIBRARY) {
6920 // Construct the property name suffix for this configuration.
6921 std::string suffix = "_";
6922 if (!config.empty()) {
6923 suffix += cmSystemTools::UpperCase(config);
6925 suffix += "NOCONFIG";
6928 // How many repetitions are needed if this library has cyclic
6930 std::string propName = cmStrCat("LINK_INTERFACE_MULTIPLICITY", suffix);
6931 if (cmValue config_reps = this->GetProperty(propName)) {
6932 sscanf(config_reps->c_str(), "%u", &iface.Multiplicity);
6933 } else if (cmValue reps =
6934 this->GetProperty("LINK_INTERFACE_MULTIPLICITY")) {
6935 sscanf(reps->c_str(), "%u", &iface.Multiplicity);
6940 const cmLinkInterfaceLibraries* cmGeneratorTarget::GetLinkInterfaceLibraries(
6941 const std::string& config, cmGeneratorTarget const* head,
6942 LinkInterfaceFor interfaceFor) const
6944 // Imported targets have their own link interface.
6945 if (this->IsImported()) {
6946 return this->GetImportLinkInterface(config, head, interfaceFor);
6949 // Link interfaces are not supported for executables that do not
6951 if (this->GetType() == cmStateEnums::EXECUTABLE &&
6952 !this->IsExecutableWithExports()) {
6956 // Lookup any existing link interface for this configuration.
6957 cmHeadToLinkInterfaceMap& hm =
6958 (interfaceFor == LinkInterfaceFor::Usage
6959 ? this->GetHeadToLinkInterfaceUsageRequirementsMap(config)
6960 : this->GetHeadToLinkInterfaceMap(config));
6962 // If the link interface does not depend on the head target
6963 // then re-use the one from the head we computed first.
6964 if (!hm.empty() && !hm.begin()->second.HadHeadSensitiveCondition) {
6965 head = hm.begin()->first;
6968 cmOptionalLinkInterface& iface = hm[head];
6969 if (!iface.LibrariesDone) {
6970 iface.LibrariesDone = true;
6971 this->ComputeLinkInterfaceLibraries(config, iface, head, interfaceFor);
6974 return iface.Exists ? &iface : nullptr;
6977 std::string cmGeneratorTarget::GetDirectory(
6978 const std::string& config, cmStateEnums::ArtifactType artifact) const
6980 if (this->IsImported()) {
6981 auto fullPath = this->Target->ImportedGetFullPath(config, artifact);
6982 if (this->IsFrameworkOnApple()) {
6983 cmsys::RegularExpressionMatch match;
6984 if (FrameworkRegularExpression.find(fullPath.c_str(), match)) {
6985 auto path = match.match(1);
6986 if (!path.empty()) {
6987 path.erase(path.length() - 1);
6992 // Return the directory from which the target is imported.
6993 return cmSystemTools::GetFilenamePath(fullPath);
6995 if (OutputInfo const* info = this->GetOutputInfo(config)) {
6996 // Return the directory in which the target will be built.
6998 case cmStateEnums::RuntimeBinaryArtifact:
6999 return info->OutDir;
7000 case cmStateEnums::ImportLibraryArtifact:
7001 return info->ImpDir;
7007 bool cmGeneratorTarget::UsesDefaultOutputDir(
7008 const std::string& config, cmStateEnums::ArtifactType artifact) const
7011 return this->ComputeOutputDir(config, artifact, dir);
7014 cmGeneratorTarget::OutputInfo const* cmGeneratorTarget::GetOutputInfo(
7015 const std::string& config) const
7017 // There is no output information for imported targets.
7018 if (this->IsImported()) {
7022 // Only libraries and executables have well-defined output files.
7023 if (!this->HaveWellDefinedOutputFiles()) {
7024 std::string msg = cmStrCat("cmGeneratorTarget::GetOutputInfo called for ",
7025 this->GetName(), " which has type ",
7026 cmState::GetTargetTypeName(this->GetType()));
7027 this->LocalGenerator->IssueMessage(MessageType::INTERNAL_ERROR, msg);
7031 // Lookup/compute/cache the output information for this configuration.
7032 std::string config_upper;
7033 if (!config.empty()) {
7034 config_upper = cmSystemTools::UpperCase(config);
7036 auto i = this->OutputInfoMap.find(config_upper);
7037 if (i == this->OutputInfoMap.end()) {
7038 // Add empty info in map to detect potential recursion.
7040 OutputInfoMapType::value_type entry(config_upper, info);
7041 i = this->OutputInfoMap.insert(entry).first;
7043 // Compute output directories.
7044 this->ComputeOutputDir(config, cmStateEnums::RuntimeBinaryArtifact,
7046 this->ComputeOutputDir(config, cmStateEnums::ImportLibraryArtifact,
7048 if (!this->ComputePDBOutputDir("PDB", config, info.PdbDir)) {
7049 info.PdbDir = info.OutDir;
7052 // Now update the previously-prepared map entry.
7054 } else if (i->second.empty()) {
7055 // An empty map entry indicates we have been called recursively
7056 // from the above block.
7057 this->LocalGenerator->GetCMakeInstance()->IssueMessage(
7058 MessageType::FATAL_ERROR,
7059 "Target '" + this->GetName() + "' OUTPUT_DIRECTORY depends on itself.",
7060 this->GetBacktrace());
7066 bool cmGeneratorTarget::ComputeOutputDir(const std::string& config,
7067 cmStateEnums::ArtifactType artifact,
7068 std::string& out) const
7070 bool usesDefaultOutputDir = false;
7071 std::string conf = config;
7073 // Look for a target property defining the target output directory
7074 // based on the target type.
7075 std::string targetTypeName = this->GetOutputTargetType(artifact);
7076 std::string propertyName;
7077 if (!targetTypeName.empty()) {
7078 propertyName = cmStrCat(targetTypeName, "_OUTPUT_DIRECTORY");
7081 // Check for a per-configuration output directory target property.
7082 std::string configUpper = cmSystemTools::UpperCase(conf);
7083 std::string configProp;
7084 if (!targetTypeName.empty()) {
7085 configProp = cmStrCat(targetTypeName, "_OUTPUT_DIRECTORY_", configUpper);
7088 // Select an output directory.
7089 if (cmValue config_outdir = this->GetProperty(configProp)) {
7090 // Use the user-specified per-configuration output directory.
7091 out = cmGeneratorExpression::Evaluate(*config_outdir, this->LocalGenerator,
7094 // Skip per-configuration subdirectory.
7096 } else if (cmValue outdir = this->GetProperty(propertyName)) {
7097 // Use the user-specified output directory.
7098 out = cmGeneratorExpression::Evaluate(*outdir, this->LocalGenerator,
7100 // Skip per-configuration subdirectory if the value contained a
7101 // generator expression.
7102 if (out != *outdir) {
7105 } else if (this->GetType() == cmStateEnums::EXECUTABLE) {
7106 // Lookup the output path for executables.
7107 out = this->Makefile->GetSafeDefinition("EXECUTABLE_OUTPUT_PATH");
7108 } else if (this->GetType() == cmStateEnums::STATIC_LIBRARY ||
7109 this->GetType() == cmStateEnums::SHARED_LIBRARY ||
7110 this->GetType() == cmStateEnums::MODULE_LIBRARY) {
7111 // Lookup the output path for libraries.
7112 out = this->Makefile->GetSafeDefinition("LIBRARY_OUTPUT_PATH");
7115 // Default to the current output directory.
7116 usesDefaultOutputDir = true;
7120 // Convert the output path to a full path in case it is
7121 // specified as a relative path. Treat a relative path as
7122 // relative to the current output directory for this makefile.
7123 out = (cmSystemTools::CollapseFullPath(
7124 out, this->LocalGenerator->GetCurrentBinaryDirectory()));
7126 // The generator may add the configuration's subdirectory.
7127 if (!conf.empty()) {
7129 this->GlobalGenerator->UseEffectivePlatformName(this->Makefile);
7130 std::string suffix =
7131 usesDefaultOutputDir && useEPN ? "${EFFECTIVE_PLATFORM_NAME}" : "";
7132 this->LocalGenerator->GetGlobalGenerator()->AppendDirectoryForConfig(
7133 "/", conf, suffix, out);
7136 return usesDefaultOutputDir;
7139 bool cmGeneratorTarget::ComputePDBOutputDir(const std::string& kind,
7140 const std::string& config,
7141 std::string& out) const
7143 // Look for a target property defining the target output directory
7144 // based on the target type.
7145 std::string propertyName;
7146 if (!kind.empty()) {
7147 propertyName = cmStrCat(kind, "_OUTPUT_DIRECTORY");
7149 std::string conf = config;
7151 // Check for a per-configuration output directory target property.
7152 std::string configUpper = cmSystemTools::UpperCase(conf);
7153 std::string configProp;
7154 if (!kind.empty()) {
7155 configProp = cmStrCat(kind, "_OUTPUT_DIRECTORY_", configUpper);
7158 // Select an output directory.
7159 if (cmValue config_outdir = this->GetProperty(configProp)) {
7160 // Use the user-specified per-configuration output directory.
7161 out = cmGeneratorExpression::Evaluate(*config_outdir, this->LocalGenerator,
7164 // Skip per-configuration subdirectory.
7166 } else if (cmValue outdir = this->GetProperty(propertyName)) {
7167 // Use the user-specified output directory.
7169 cmGeneratorExpression::Evaluate(*outdir, this->LocalGenerator, config);
7171 // Skip per-configuration subdirectory if the value contained a
7172 // generator expression.
7173 if (out != *outdir) {
7181 // Convert the output path to a full path in case it is
7182 // specified as a relative path. Treat a relative path as
7183 // relative to the current output directory for this makefile.
7184 out = (cmSystemTools::CollapseFullPath(
7185 out, this->LocalGenerator->GetCurrentBinaryDirectory()));
7187 // The generator may add the configuration's subdirectory.
7188 if (!conf.empty()) {
7189 this->LocalGenerator->GetGlobalGenerator()->AppendDirectoryForConfig(
7190 "/", conf, "", out);
7195 bool cmGeneratorTarget::HaveInstallTreeRPATH(const std::string& config) const
7197 std::string install_rpath;
7198 this->GetInstallRPATH(config, install_rpath);
7199 return !install_rpath.empty() &&
7200 !this->Makefile->IsOn("CMAKE_SKIP_INSTALL_RPATH");
7203 bool cmGeneratorTarget::GetBuildRPATH(const std::string& config,
7204 std::string& rpath) const
7206 return this->GetRPATH(config, "BUILD_RPATH", rpath);
7209 bool cmGeneratorTarget::GetInstallRPATH(const std::string& config,
7210 std::string& rpath) const
7212 return this->GetRPATH(config, "INSTALL_RPATH", rpath);
7215 bool cmGeneratorTarget::GetRPATH(const std::string& config,
7216 const std::string& prop,
7217 std::string& rpath) const
7219 cmValue value = this->GetProperty(prop);
7225 cmGeneratorExpression::Evaluate(*value, this->LocalGenerator, config);
7230 void cmGeneratorTarget::ComputeLinkInterfaceLibraries(
7231 const std::string& config, cmOptionalLinkInterface& iface,
7232 cmGeneratorTarget const* headTarget, LinkInterfaceFor interfaceFor) const
7234 // Construct the property name suffix for this configuration.
7235 std::string suffix = "_";
7236 if (!config.empty()) {
7237 suffix += cmSystemTools::UpperCase(config);
7239 suffix += "NOCONFIG";
7242 // An explicit list of interface libraries may be set for shared
7243 // libraries and executables that export symbols.
7244 bool haveExplicitLibraries = false;
7245 cmValue explicitLibrariesCMP0022OLD;
7246 std::string linkIfacePropCMP0022OLD;
7247 bool const cmp0022NEW = (this->GetPolicyStatusCMP0022() != cmPolicies::OLD &&
7248 this->GetPolicyStatusCMP0022() != cmPolicies::WARN);
7250 // CMP0022 NEW behavior is to use INTERFACE_LINK_LIBRARIES.
7251 haveExplicitLibraries = !this->Target->GetLinkInterfaceEntries().empty() ||
7252 !this->Target->GetLinkInterfaceDirectEntries().empty() ||
7253 !this->Target->GetLinkInterfaceDirectExcludeEntries().empty();
7255 // CMP0022 OLD behavior is to use LINK_INTERFACE_LIBRARIES if set on a
7256 // shared lib or executable.
7257 if (this->GetType() == cmStateEnums::SHARED_LIBRARY ||
7258 this->IsExecutableWithExports()) {
7259 // Lookup the per-configuration property.
7260 linkIfacePropCMP0022OLD = cmStrCat("LINK_INTERFACE_LIBRARIES", suffix);
7261 explicitLibrariesCMP0022OLD = this->GetProperty(linkIfacePropCMP0022OLD);
7263 // If not set, try the generic property.
7264 if (!explicitLibrariesCMP0022OLD) {
7265 linkIfacePropCMP0022OLD = "LINK_INTERFACE_LIBRARIES";
7266 explicitLibrariesCMP0022OLD =
7267 this->GetProperty(linkIfacePropCMP0022OLD);
7271 if (explicitLibrariesCMP0022OLD &&
7272 this->GetPolicyStatusCMP0022() == cmPolicies::WARN &&
7273 !this->PolicyWarnedCMP0022) {
7274 // Compare the explicitly set old link interface properties to the
7275 // preferred new link interface property one and warn if different.
7276 cmValue newExplicitLibraries =
7277 this->GetProperty("INTERFACE_LINK_LIBRARIES");
7278 if (newExplicitLibraries &&
7279 (*newExplicitLibraries != *explicitLibrariesCMP0022OLD)) {
7280 std::ostringstream w;
7281 /* clang-format off */
7282 w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0022) << "\n"
7283 "Target \"" << this->GetName() << "\" has an "
7284 "INTERFACE_LINK_LIBRARIES property which differs from its " <<
7285 linkIfacePropCMP0022OLD << " properties."
7287 "INTERFACE_LINK_LIBRARIES:\n"
7288 " " << *newExplicitLibraries << "\n" <<
7289 linkIfacePropCMP0022OLD << ":\n"
7290 " " << *explicitLibrariesCMP0022OLD << "\n";
7291 /* clang-format on */
7292 this->LocalGenerator->IssueMessage(MessageType::AUTHOR_WARNING,
7294 this->PolicyWarnedCMP0022 = true;
7298 haveExplicitLibraries = static_cast<bool>(explicitLibrariesCMP0022OLD);
7301 // There is no implicit link interface for executables or modules
7302 // so if none was explicitly set then there is no link interface.
7303 if (!haveExplicitLibraries &&
7304 (this->GetType() == cmStateEnums::EXECUTABLE ||
7305 (this->GetType() == cmStateEnums::MODULE_LIBRARY))) {
7308 iface.Exists = true;
7310 // If CMP0022 is NEW then the plain tll signature sets the
7311 // INTERFACE_LINK_LIBRARIES property. Even if the project
7312 // clears it, the link interface is still explicit.
7313 iface.Explicit = cmp0022NEW || explicitLibrariesCMP0022OLD;
7316 // The interface libraries are specified by INTERFACE_LINK_LIBRARIES.
7317 // Use its special representation directly to get backtraces.
7318 this->ExpandLinkItems(
7319 kINTERFACE_LINK_LIBRARIES, this->Target->GetLinkInterfaceEntries(),
7320 config, headTarget, interfaceFor, LinkInterfaceField::Libraries, iface);
7321 this->ExpandLinkItems(kINTERFACE_LINK_LIBRARIES_DIRECT,
7322 this->Target->GetLinkInterfaceDirectEntries(),
7323 config, headTarget, interfaceFor,
7324 LinkInterfaceField::HeadInclude, iface);
7325 this->ExpandLinkItems(kINTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE,
7326 this->Target->GetLinkInterfaceDirectExcludeEntries(),
7327 config, headTarget, interfaceFor,
7328 LinkInterfaceField::HeadExclude, iface);
7329 } else if (explicitLibrariesCMP0022OLD) {
7330 // The interface libraries have been explicitly set in pre-CMP0022 style.
7331 std::vector<BT<std::string>> entries;
7332 entries.emplace_back(*explicitLibrariesCMP0022OLD);
7333 this->ExpandLinkItems(linkIfacePropCMP0022OLD, cmMakeRange(entries),
7334 config, headTarget, interfaceFor,
7335 LinkInterfaceField::Libraries, iface);
7338 // If the link interface is explicit, do not fall back to the link impl.
7339 if (iface.Explicit) {
7343 // The link implementation is the default link interface.
7344 if (cmLinkImplementationLibraries const* impl =
7345 this->GetLinkImplementationLibrariesInternal(config, headTarget,
7347 iface.Libraries.insert(iface.Libraries.end(), impl->Libraries.begin(),
7348 impl->Libraries.end());
7349 if (this->GetPolicyStatusCMP0022() == cmPolicies::WARN &&
7350 !this->PolicyWarnedCMP0022 && interfaceFor == LinkInterfaceFor::Link) {
7351 // Compare the link implementation fallback link interface to the
7352 // preferred new link interface property and warn if different.
7353 cmLinkInterface ifaceNew;
7354 this->ExpandLinkItems(kINTERFACE_LINK_LIBRARIES,
7355 this->Target->GetLinkInterfaceEntries(), config,
7356 headTarget, interfaceFor,
7357 LinkInterfaceField::Libraries, ifaceNew);
7358 if (ifaceNew.Libraries != iface.Libraries) {
7359 std::string oldLibraries = cmJoin(impl->Libraries, ";");
7360 std::string newLibraries = cmJoin(ifaceNew.Libraries, ";");
7361 if (oldLibraries.empty()) {
7362 oldLibraries = "(empty)";
7364 if (newLibraries.empty()) {
7365 newLibraries = "(empty)";
7368 std::ostringstream w;
7369 /* clang-format off */
7370 w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0022) << "\n"
7371 "Target \"" << this->GetName() << "\" has an "
7372 "INTERFACE_LINK_LIBRARIES property. "
7373 "This should be preferred as the source of the link interface "
7374 "for this library but because CMP0022 is not set CMake is "
7375 "ignoring the property and using the link implementation "
7376 "as the link interface instead."
7378 "INTERFACE_LINK_LIBRARIES:\n"
7379 " " << newLibraries << "\n"
7380 "Link implementation:\n"
7381 " " << oldLibraries << "\n";
7382 /* clang-format on */
7383 this->LocalGenerator->IssueMessage(MessageType::AUTHOR_WARNING,
7385 this->PolicyWarnedCMP0022 = true;
7393 template <typename ReturnType>
7394 ReturnType constructItem(cmGeneratorTarget* target,
7395 cmListFileBacktrace const& bt);
7398 inline cmLinkImplItem constructItem(cmGeneratorTarget* target,
7399 cmListFileBacktrace const& bt)
7401 return cmLinkImplItem(cmLinkItem(target, false, bt), false);
7405 inline cmLinkItem constructItem(cmGeneratorTarget* target,
7406 cmListFileBacktrace const& bt)
7408 return cmLinkItem(target, false, bt);
7411 template <typename ValueType>
7412 std::vector<ValueType> computeImplicitLanguageTargets(
7413 std::string const& lang, std::string const& config,
7414 cmGeneratorTarget const* currentTarget)
7416 cmListFileBacktrace bt;
7417 std::vector<ValueType> result;
7418 cmLocalGenerator* lg = currentTarget->GetLocalGenerator();
7420 std::string const& runtimeLibrary =
7421 currentTarget->GetRuntimeLinkLibrary(lang, config);
7422 if (cmValue runtimeLinkOptions = currentTarget->Makefile->GetDefinition(
7423 "CMAKE_" + lang + "_RUNTIME_LIBRARIES_" + runtimeLibrary)) {
7424 std::vector<std::string> libsVec = cmExpandedList(*runtimeLinkOptions);
7425 result.reserve(libsVec.size());
7427 for (std::string const& i : libsVec) {
7428 cmGeneratorTarget::TargetOrString resolved =
7429 currentTarget->ResolveTargetReference(i, lg);
7430 if (resolved.Target) {
7431 result.emplace_back(constructItem<ValueType>(resolved.Target, bt));
7440 void cmGeneratorTarget::ComputeLinkInterfaceRuntimeLibraries(
7441 const std::string& config, cmOptionalLinkInterface& iface) const
7443 for (std::string const& lang : iface.Languages) {
7444 if ((lang == "CUDA" || lang == "HIP") &&
7445 iface.LanguageRuntimeLibraries.find(lang) ==
7446 iface.LanguageRuntimeLibraries.end()) {
7447 auto implicitTargets =
7448 computeImplicitLanguageTargets<cmLinkItem>(lang, config, this);
7449 iface.LanguageRuntimeLibraries[lang] = std::move(implicitTargets);
7454 void cmGeneratorTarget::ComputeLinkImplementationRuntimeLibraries(
7455 const std::string& config, cmOptionalLinkImplementation& impl) const
7457 for (std::string const& lang : impl.Languages) {
7458 if ((lang == "CUDA" || lang == "HIP") &&
7459 impl.LanguageRuntimeLibraries.find(lang) ==
7460 impl.LanguageRuntimeLibraries.end()) {
7461 auto implicitTargets =
7462 computeImplicitLanguageTargets<cmLinkImplItem>(lang, config, this);
7463 impl.LanguageRuntimeLibraries[lang] = std::move(implicitTargets);
7468 const cmLinkInterface* cmGeneratorTarget::GetImportLinkInterface(
7469 const std::string& config, cmGeneratorTarget const* headTarget,
7470 LinkInterfaceFor interfaceFor, bool secondPass) const
7472 cmGeneratorTarget::ImportInfo const* info = this->GetImportInfo(config);
7477 cmHeadToLinkInterfaceMap& hm =
7478 (interfaceFor == LinkInterfaceFor::Usage
7479 ? this->GetHeadToLinkInterfaceUsageRequirementsMap(config)
7480 : this->GetHeadToLinkInterfaceMap(config));
7482 // If the link interface does not depend on the head target
7483 // then re-use the one from the head we computed first.
7484 if (!hm.empty() && !hm.begin()->second.HadHeadSensitiveCondition) {
7485 headTarget = hm.begin()->first;
7488 cmOptionalLinkInterface& iface = hm[headTarget];
7490 iface = cmOptionalLinkInterface();
7492 if (!iface.AllDone) {
7493 iface.AllDone = true;
7494 iface.LibrariesDone = true;
7495 iface.Multiplicity = info->Multiplicity;
7496 cmExpandList(info->Languages, iface.Languages);
7497 this->ExpandLinkItems(kINTERFACE_LINK_LIBRARIES_DIRECT,
7498 cmMakeRange(info->LibrariesHeadInclude), config,
7499 headTarget, interfaceFor,
7500 LinkInterfaceField::HeadInclude, iface);
7501 this->ExpandLinkItems(kINTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE,
7502 cmMakeRange(info->LibrariesHeadExclude), config,
7503 headTarget, interfaceFor,
7504 LinkInterfaceField::HeadExclude, iface);
7505 this->ExpandLinkItems(info->LibrariesProp, cmMakeRange(info->Libraries),
7506 config, headTarget, interfaceFor,
7507 LinkInterfaceField::Libraries, iface);
7508 std::vector<std::string> deps = cmExpandedList(info->SharedDeps);
7509 LookupLinkItemScope scope{ this->LocalGenerator };
7510 for (std::string const& dep : deps) {
7511 if (cm::optional<cmLinkItem> maybeItem = this->LookupLinkItem(
7512 dep, cmListFileBacktrace(), &scope, LookupSelf::No)) {
7513 iface.SharedDeps.emplace_back(std::move(*maybeItem));
7521 cmGeneratorTarget::ImportInfo const* cmGeneratorTarget::GetImportInfo(
7522 const std::string& config) const
7524 // There is no imported information for non-imported targets.
7525 if (!this->IsImported()) {
7529 // Lookup/compute/cache the import information for this
7531 std::string config_upper;
7532 if (!config.empty()) {
7533 config_upper = cmSystemTools::UpperCase(config);
7535 config_upper = "NOCONFIG";
7538 auto i = this->ImportInfoMap.find(config_upper);
7539 if (i == this->ImportInfoMap.end()) {
7541 this->ComputeImportInfo(config_upper, info);
7542 ImportInfoMapType::value_type entry(config_upper, info);
7543 i = this->ImportInfoMap.insert(entry).first;
7546 if (this->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
7549 // If the location is empty then the target is not available for
7550 // this configuration.
7551 if (i->second.Location.empty() && i->second.ImportLibrary.empty()) {
7555 // Return the import information.
7559 void cmGeneratorTarget::ComputeImportInfo(std::string const& desired_config,
7560 ImportInfo& info) const
7562 // This method finds information about an imported target from its
7563 // properties. The "IMPORTED_" namespace is reserved for properties
7564 // defined by the project exporting the target.
7566 // Initialize members.
7567 info.NoSOName = false;
7569 cmValue loc = nullptr;
7570 cmValue imp = nullptr;
7572 if (!this->Target->GetMappedConfig(desired_config, loc, imp, suffix)) {
7576 // Get the link interface.
7578 // Use the INTERFACE_LINK_LIBRARIES special representation directly
7579 // to get backtraces.
7580 cmBTStringRange entries = this->Target->GetLinkInterfaceEntries();
7581 if (!entries.empty()) {
7582 info.LibrariesProp = "INTERFACE_LINK_LIBRARIES";
7583 for (BT<std::string> const& entry : entries) {
7584 info.Libraries.emplace_back(entry);
7586 } else if (this->GetType() != cmStateEnums::INTERFACE_LIBRARY) {
7587 std::string linkProp =
7588 cmStrCat("IMPORTED_LINK_INTERFACE_LIBRARIES", suffix);
7589 cmValue propertyLibs = this->GetProperty(linkProp);
7590 if (!propertyLibs) {
7591 linkProp = "IMPORTED_LINK_INTERFACE_LIBRARIES";
7592 propertyLibs = this->GetProperty(linkProp);
7595 info.LibrariesProp = linkProp;
7596 info.Libraries.emplace_back(*propertyLibs);
7600 for (BT<std::string> const& entry :
7601 this->Target->GetLinkInterfaceDirectEntries()) {
7602 info.LibrariesHeadInclude.emplace_back(entry);
7604 for (BT<std::string> const& entry :
7605 this->Target->GetLinkInterfaceDirectExcludeEntries()) {
7606 info.LibrariesHeadExclude.emplace_back(entry);
7608 if (this->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
7610 info.LibName = *loc;
7615 // A provided configuration has been chosen. Load the
7616 // configuration's properties.
7618 // Get the location.
7620 info.Location = *loc;
7622 std::string impProp = cmStrCat("IMPORTED_LOCATION", suffix);
7623 if (cmValue config_location = this->GetProperty(impProp)) {
7624 info.Location = *config_location;
7625 } else if (cmValue location = this->GetProperty("IMPORTED_LOCATION")) {
7626 info.Location = *location;
7631 if (this->GetType() == cmStateEnums::SHARED_LIBRARY) {
7632 std::string soProp = cmStrCat("IMPORTED_SONAME", suffix);
7633 if (cmValue config_soname = this->GetProperty(soProp)) {
7634 info.SOName = *config_soname;
7635 } else if (cmValue soname = this->GetProperty("IMPORTED_SONAME")) {
7636 info.SOName = *soname;
7640 // Get the "no-soname" mark.
7641 if (this->GetType() == cmStateEnums::SHARED_LIBRARY) {
7642 std::string soProp = cmStrCat("IMPORTED_NO_SONAME", suffix);
7643 if (cmValue config_no_soname = this->GetProperty(soProp)) {
7644 info.NoSOName = cmIsOn(*config_no_soname);
7645 } else if (cmValue no_soname = this->GetProperty("IMPORTED_NO_SONAME")) {
7646 info.NoSOName = cmIsOn(*no_soname);
7650 // Get the import library.
7652 info.ImportLibrary = *imp;
7653 } else if (this->GetType() == cmStateEnums::SHARED_LIBRARY ||
7654 this->IsExecutableWithExports()) {
7655 std::string impProp = cmStrCat("IMPORTED_IMPLIB", suffix);
7656 if (cmValue config_implib = this->GetProperty(impProp)) {
7657 info.ImportLibrary = *config_implib;
7658 } else if (cmValue implib = this->GetProperty("IMPORTED_IMPLIB")) {
7659 info.ImportLibrary = *implib;
7663 // Get the link dependencies.
7665 std::string linkProp =
7666 cmStrCat("IMPORTED_LINK_DEPENDENT_LIBRARIES", suffix);
7667 if (cmValue config_libs = this->GetProperty(linkProp)) {
7668 info.SharedDeps = *config_libs;
7669 } else if (cmValue libs =
7670 this->GetProperty("IMPORTED_LINK_DEPENDENT_LIBRARIES")) {
7671 info.SharedDeps = *libs;
7675 // Get the link languages.
7676 if (this->LinkLanguagePropagatesToDependents()) {
7677 std::string linkProp =
7678 cmStrCat("IMPORTED_LINK_INTERFACE_LANGUAGES", suffix);
7679 if (cmValue config_libs = this->GetProperty(linkProp)) {
7680 info.Languages = *config_libs;
7681 } else if (cmValue libs =
7682 this->GetProperty("IMPORTED_LINK_INTERFACE_LANGUAGES")) {
7683 info.Languages = *libs;
7687 // Get information if target is managed assembly.
7689 std::string linkProp = "IMPORTED_COMMON_LANGUAGE_RUNTIME";
7690 if (cmValue pc = this->GetProperty(linkProp + suffix)) {
7691 info.Managed = this->CheckManagedType(*pc);
7692 } else if (cmValue p = this->GetProperty(linkProp)) {
7693 info.Managed = this->CheckManagedType(*p);
7697 // Get the cyclic repetition count.
7698 if (this->GetType() == cmStateEnums::STATIC_LIBRARY) {
7699 std::string linkProp =
7700 cmStrCat("IMPORTED_LINK_INTERFACE_MULTIPLICITY", suffix);
7701 if (cmValue config_reps = this->GetProperty(linkProp)) {
7702 sscanf(config_reps->c_str(), "%u", &info.Multiplicity);
7703 } else if (cmValue reps =
7704 this->GetProperty("IMPORTED_LINK_INTERFACE_MULTIPLICITY")) {
7705 sscanf(reps->c_str(), "%u", &info.Multiplicity);
7710 cmHeadToLinkInterfaceMap& cmGeneratorTarget::GetHeadToLinkInterfaceMap(
7711 const std::string& config) const
7713 return this->LinkInterfaceMap[cmSystemTools::UpperCase(config)];
7716 cmHeadToLinkInterfaceMap&
7717 cmGeneratorTarget::GetHeadToLinkInterfaceUsageRequirementsMap(
7718 const std::string& config) const
7721 ->LinkInterfaceUsageRequirementsOnlyMap[cmSystemTools::UpperCase(config)];
7724 const cmLinkImplementation* cmGeneratorTarget::GetLinkImplementation(
7725 const std::string& config, LinkInterfaceFor implFor) const
7727 return this->GetLinkImplementation(config, implFor, false);
7730 const cmLinkImplementation* cmGeneratorTarget::GetLinkImplementation(
7731 const std::string& config, LinkInterfaceFor implFor, bool secondPass) const
7733 // There is no link implementation for targets that cannot compile sources.
7734 if (!this->CanCompileSources()) {
7738 HeadToLinkImplementationMap& hm =
7739 (implFor == LinkInterfaceFor::Usage
7740 ? this->GetHeadToLinkImplementationUsageRequirementsMap(config)
7741 : this->GetHeadToLinkImplementationMap(config));
7742 cmOptionalLinkImplementation& impl = hm[this];
7744 impl = cmOptionalLinkImplementation();
7746 if (!impl.LibrariesDone) {
7747 impl.LibrariesDone = true;
7748 this->ComputeLinkImplementationLibraries(config, impl, this, implFor);
7750 if (!impl.LanguagesDone) {
7751 impl.LanguagesDone = true;
7752 this->ComputeLinkImplementationLanguages(config, impl);
7753 this->ComputeLinkImplementationRuntimeLibraries(config, impl);
7758 cmGeneratorTarget::HeadToLinkImplementationMap&
7759 cmGeneratorTarget::GetHeadToLinkImplementationMap(
7760 std::string const& config) const
7762 return this->LinkImplMap[cmSystemTools::UpperCase(config)];
7765 cmGeneratorTarget::HeadToLinkImplementationMap&
7766 cmGeneratorTarget::GetHeadToLinkImplementationUsageRequirementsMap(
7767 std::string const& config) const
7770 ->LinkImplUsageRequirementsOnlyMap[cmSystemTools::UpperCase(config)];
7773 bool cmGeneratorTarget::GetConfigCommonSourceFilesForXcode(
7774 std::vector<cmSourceFile*>& files) const
7776 std::vector<std::string> const& configs =
7777 this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
7779 auto it = configs.begin();
7780 const std::string& firstConfig = *it;
7781 this->GetSourceFilesWithoutObjectLibraries(files, firstConfig);
7783 for (; it != configs.end(); ++it) {
7784 std::vector<cmSourceFile*> configFiles;
7785 this->GetSourceFilesWithoutObjectLibraries(configFiles, *it);
7786 if (configFiles != files) {
7787 std::string firstConfigFiles;
7788 const char* sep = "";
7789 for (cmSourceFile* f : files) {
7790 firstConfigFiles += sep;
7791 firstConfigFiles += f->ResolveFullPath();
7795 std::string thisConfigFiles;
7797 for (cmSourceFile* f : configFiles) {
7798 thisConfigFiles += sep;
7799 thisConfigFiles += f->ResolveFullPath();
7802 std::ostringstream e;
7803 /* clang-format off */
7804 e << "Target \"" << this->GetName()
7805 << "\" has source files which vary by "
7806 "configuration. This is not supported by the \""
7807 << this->GlobalGenerator->GetName()
7808 << "\" generator.\n"
7809 "Config \"" << firstConfig << "\":\n"
7810 " " << firstConfigFiles << "\n"
7811 "Config \"" << *it << "\":\n"
7812 " " << thisConfigFiles << "\n";
7813 /* clang-format on */
7814 this->LocalGenerator->IssueMessage(MessageType::FATAL_ERROR, e.str());
7821 void cmGeneratorTarget::GetObjectLibrariesCMP0026(
7822 std::vector<cmGeneratorTarget*>& objlibs) const
7824 // At configure-time, this method can be called as part of getting the
7825 // LOCATION property or to export() a file to be include()d. However
7826 // there is no cmGeneratorTarget at configure-time, so search the SOURCES
7827 // for TARGET_OBJECTS instead for backwards compatibility with OLD
7828 // behavior of CMP0024 and CMP0026 only.
7829 cmBTStringRange rng = this->Target->GetSourceEntries();
7830 for (auto const& entry : rng) {
7831 std::vector<std::string> files = cmExpandedList(entry.Value);
7832 for (std::string const& li : files) {
7833 if (cmHasLiteralPrefix(li, "$<TARGET_OBJECTS:") && li.back() == '>') {
7834 std::string objLibName = li.substr(17, li.size() - 18);
7836 if (cmGeneratorExpression::Find(objLibName) != std::string::npos) {
7839 cmGeneratorTarget* objLib =
7840 this->LocalGenerator->FindGeneratorTargetToUse(objLibName);
7842 objlibs.push_back(objLib);
7849 std::string cmGeneratorTarget::CheckCMP0004(std::string const& item) const
7851 // Strip whitespace off the library names because we used to do this
7852 // in case variables were expanded at generate time. We no longer
7853 // do the expansion but users link to libraries like " ${VAR} ".
7854 std::string lib = item;
7855 std::string::size_type pos = lib.find_first_not_of(" \t\r\n");
7856 if (pos != std::string::npos) {
7857 lib = lib.substr(pos);
7859 pos = lib.find_last_not_of(" \t\r\n");
7860 if (pos != std::string::npos) {
7861 lib = lib.substr(0, pos + 1);
7864 cmake* cm = this->LocalGenerator->GetCMakeInstance();
7865 switch (this->GetPolicyStatusCMP0004()) {
7866 case cmPolicies::WARN: {
7867 std::ostringstream w;
7868 w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0004) << "\n"
7869 << "Target \"" << this->GetName() << "\" links to item \"" << item
7870 << "\" which has leading or trailing whitespace.";
7871 cm->IssueMessage(MessageType::AUTHOR_WARNING, w.str(),
7872 this->GetBacktrace());
7875 case cmPolicies::OLD:
7877 case cmPolicies::NEW: {
7878 std::ostringstream e;
7879 e << "Target \"" << this->GetName() << "\" links to item \"" << item
7880 << "\" which has leading or trailing whitespace. "
7881 << "This is now an error according to policy CMP0004.";
7882 cm->IssueMessage(MessageType::FATAL_ERROR, e.str(),
7883 this->GetBacktrace());
7885 case cmPolicies::REQUIRED_IF_USED:
7886 case cmPolicies::REQUIRED_ALWAYS: {
7887 std::ostringstream e;
7888 e << cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0004) << "\n"
7889 << "Target \"" << this->GetName() << "\" links to item \"" << item
7890 << "\" which has leading or trailing whitespace.";
7891 cm->IssueMessage(MessageType::FATAL_ERROR, e.str(),
7892 this->GetBacktrace());
7899 bool cmGeneratorTarget::IsDeprecated() const
7901 cmValue deprecation = this->GetProperty("DEPRECATION");
7902 return cmNonempty(deprecation);
7905 std::string cmGeneratorTarget::GetDeprecation() const
7907 // find DEPRECATION property
7908 if (cmValue deprecation = this->GetProperty("DEPRECATION")) {
7909 return *deprecation;
7911 return std::string();
7914 void cmGeneratorTarget::GetLanguages(std::set<std::string>& languages,
7915 const std::string& config) const
7917 // Targets that do not compile anything have no languages.
7918 if (!this->CanCompileSources()) {
7922 std::vector<cmSourceFile*> sourceFiles;
7923 this->GetSourceFiles(sourceFiles, config);
7924 for (cmSourceFile* src : sourceFiles) {
7925 const std::string& lang = src->GetOrDetermineLanguage();
7926 if (!lang.empty()) {
7927 languages.insert(lang);
7931 std::vector<cmGeneratorTarget*> objectLibraries;
7932 std::vector<cmSourceFile const*> externalObjects;
7933 if (!this->GlobalGenerator->GetConfigureDoneCMP0026()) {
7934 std::vector<cmGeneratorTarget*> objectTargets;
7935 this->GetObjectLibrariesCMP0026(objectTargets);
7936 objectLibraries.reserve(objectTargets.size());
7937 for (cmGeneratorTarget* gt : objectTargets) {
7938 objectLibraries.push_back(gt);
7941 this->GetExternalObjects(externalObjects, config);
7942 for (cmSourceFile const* extObj : externalObjects) {
7943 std::string objLib = extObj->GetObjectLibrary();
7944 if (cmGeneratorTarget* tgt =
7945 this->LocalGenerator->FindGeneratorTargetToUse(objLib)) {
7946 auto const objLibIt =
7947 std::find_if(objectLibraries.cbegin(), objectLibraries.cend(),
7948 [tgt](cmGeneratorTarget* t) { return t == tgt; });
7949 if (objectLibraries.cend() == objLibIt) {
7950 objectLibraries.push_back(tgt);
7955 for (cmGeneratorTarget* objLib : objectLibraries) {
7956 objLib->GetLanguages(languages, config);
7960 bool cmGeneratorTarget::IsLanguageUsed(std::string const& language,
7961 std::string const& config) const
7963 std::set<std::string> languages;
7964 this->GetLanguages(languages, config);
7965 return languages.count(language);
7968 bool cmGeneratorTarget::IsCSharpOnly() const
7970 // Only certain target types may compile CSharp.
7971 if (this->GetType() != cmStateEnums::SHARED_LIBRARY &&
7972 this->GetType() != cmStateEnums::STATIC_LIBRARY &&
7973 this->GetType() != cmStateEnums::EXECUTABLE) {
7976 std::set<std::string> languages = this->GetAllConfigCompileLanguages();
7977 // Consider an explicit linker language property, but *not* the
7978 // computed linker language that may depend on linked targets.
7979 cmValue linkLang = this->GetProperty("LINKER_LANGUAGE");
7980 if (cmNonempty(linkLang)) {
7981 languages.insert(*linkLang);
7983 return languages.size() == 1 && languages.count("CSharp") > 0;
7986 bool cmGeneratorTarget::IsDotNetSdkTarget() const
7988 return !this->GetProperty("DOTNET_SDK").IsEmpty();
7991 void cmGeneratorTarget::ComputeLinkImplementationLanguages(
7992 const std::string& config, cmOptionalLinkImplementation& impl) const
7994 // This target needs runtime libraries for its source languages.
7995 std::set<std::string> languages;
7996 // Get languages used in our source files.
7997 this->GetLanguages(languages, config);
7998 // Copy the set of languages to the link implementation.
7999 impl.Languages.insert(impl.Languages.begin(), languages.begin(),
8003 bool cmGeneratorTarget::HaveBuildTreeRPATH(const std::string& config) const
8005 if (this->GetPropertyAsBool("SKIP_BUILD_RPATH")) {
8008 std::string build_rpath;
8009 if (this->GetBuildRPATH(config, build_rpath)) {
8012 if (cmLinkImplementationLibraries const* impl =
8013 this->GetLinkImplementationLibraries(config, LinkInterfaceFor::Link)) {
8014 return !impl->Libraries.empty();
8019 cmLinkImplementationLibraries const*
8020 cmGeneratorTarget::GetLinkImplementationLibraries(
8021 const std::string& config, LinkInterfaceFor implFor) const
8023 return this->GetLinkImplementationLibrariesInternal(config, this, implFor);
8026 cmLinkImplementationLibraries const*
8027 cmGeneratorTarget::GetLinkImplementationLibrariesInternal(
8028 const std::string& config, cmGeneratorTarget const* head,
8029 LinkInterfaceFor implFor) const
8031 // There is no link implementation for targets that cannot compile sources.
8032 if (!this->CanCompileSources()) {
8036 // Populate the link implementation libraries for this configuration.
8037 HeadToLinkImplementationMap& hm =
8038 (implFor == LinkInterfaceFor::Usage
8039 ? this->GetHeadToLinkImplementationUsageRequirementsMap(config)
8040 : this->GetHeadToLinkImplementationMap(config));
8042 // If the link implementation does not depend on the head target
8043 // then re-use the one from the head we computed first.
8044 if (!hm.empty() && !hm.begin()->second.HadHeadSensitiveCondition) {
8045 head = hm.begin()->first;
8048 cmOptionalLinkImplementation& impl = hm[head];
8049 if (!impl.LibrariesDone) {
8050 impl.LibrariesDone = true;
8051 this->ComputeLinkImplementationLibraries(config, impl, head, implFor);
8056 bool cmGeneratorTarget::IsNullImpliedByLinkLibraries(
8057 const std::string& p) const
8059 return cm::contains(this->LinkImplicitNullProperties, p);
8063 class TransitiveLinkImpl
8065 cmGeneratorTarget const* Self;
8066 std::string const& Config;
8067 LinkInterfaceFor ImplFor;
8068 cmLinkImplementation& Impl;
8070 std::set<cmLinkItem> Emitted;
8071 std::set<cmLinkItem> Excluded;
8072 std::unordered_set<cmGeneratorTarget const*> Followed;
8074 void Follow(cmGeneratorTarget const* target);
8077 TransitiveLinkImpl(cmGeneratorTarget const* self, std::string const& config,
8078 LinkInterfaceFor implFor, cmLinkImplementation& impl)
8089 void TransitiveLinkImpl::Follow(cmGeneratorTarget const* target)
8091 if (!target || !this->Followed.insert(target).second ||
8092 target->GetPolicyStatusCMP0022() == cmPolicies::OLD ||
8093 target->GetPolicyStatusCMP0022() == cmPolicies::WARN) {
8097 // Get this target's usage requirements.
8098 cmLinkInterfaceLibraries const* iface =
8099 target->GetLinkInterfaceLibraries(this->Config, this->Self, this->ImplFor);
8103 if (iface->HadContextSensitiveCondition) {
8104 this->Impl.HadContextSensitiveCondition = true;
8107 // Process 'INTERFACE_LINK_LIBRARIES_DIRECT' usage requirements.
8108 for (cmLinkItem const& item : iface->HeadInclude) {
8109 // Inject direct dependencies from the item's usage requirements
8110 // before the item itself.
8111 this->Follow(item.Target);
8113 // Add the item itself, but at most once.
8114 if (this->Emitted.insert(item).second) {
8115 this->Impl.Libraries.emplace_back(item, /* checkCMP0027= */ false);
8119 // Follow transitive dependencies.
8120 for (cmLinkItem const& item : iface->Libraries) {
8121 this->Follow(item.Target);
8124 // Record exclusions from 'INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE'
8125 // usage requirements.
8126 for (cmLinkItem const& item : iface->HeadExclude) {
8127 this->Excluded.insert(item);
8131 void TransitiveLinkImpl::Compute()
8133 // Save the original items and start with an empty list.
8134 std::vector<cmLinkImplItem> original = std::move(this->Impl.Libraries);
8136 // Avoid injecting any original items as usage requirements.
8137 // This gives LINK_LIBRARIES final control over the order
8138 // if it explicitly lists everything.
8139 this->Emitted.insert(original.cbegin(), original.cend());
8141 // Process each original item.
8142 for (cmLinkImplItem& item : original) {
8143 // Inject direct dependencies listed in 'INTERFACE_LINK_LIBRARIES_DIRECT'
8144 // usage requirements before the item itself.
8145 this->Follow(item.Target);
8147 // Add the item itself.
8148 this->Impl.Libraries.emplace_back(std::move(item));
8151 // Remove items listed in 'INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE'
8152 // usage requirements found through any dependency above.
8153 this->Impl.Libraries.erase(
8154 std::remove_if(this->Impl.Libraries.begin(), this->Impl.Libraries.end(),
8155 [this](cmLinkImplItem const& item) {
8156 return this->Excluded.find(item) != this->Excluded.end();
8158 this->Impl.Libraries.end());
8161 void ComputeLinkImplTransitive(cmGeneratorTarget const* self,
8162 std::string const& config,
8163 LinkInterfaceFor implFor,
8164 cmLinkImplementation& impl)
8166 TransitiveLinkImpl transitiveLinkImpl(self, config, implFor, impl);
8167 transitiveLinkImpl.Compute();
8171 void cmGeneratorTarget::ComputeLinkImplementationLibraries(
8172 const std::string& config, cmOptionalLinkImplementation& impl,
8173 cmGeneratorTarget const* head, LinkInterfaceFor implFor) const
8175 cmLocalGenerator const* lg = this->LocalGenerator;
8176 cmMakefile const* mf = lg->GetMakefile();
8177 cmBTStringRange entryRange = this->Target->GetLinkImplementationEntries();
8178 // Collect libraries directly linked in this configuration.
8179 for (auto const& entry : entryRange) {
8180 std::vector<std::string> llibs;
8181 // Keep this logic in sync with ExpandLinkItems.
8182 cmGeneratorExpressionDAGChecker dagChecker(this, "LINK_LIBRARIES", nullptr,
8184 // The $<LINK_ONLY> expression may be used to specify link dependencies
8185 // that are otherwise excluded from usage requirements.
8186 if (implFor == LinkInterfaceFor::Usage) {
8187 switch (this->GetPolicyStatusCMP0131()) {
8188 case cmPolicies::WARN:
8189 case cmPolicies::OLD:
8191 case cmPolicies::REQUIRED_IF_USED:
8192 case cmPolicies::REQUIRED_ALWAYS:
8193 case cmPolicies::NEW:
8194 dagChecker.SetTransitivePropertiesOnly();
8198 cmGeneratorExpression ge(entry.Backtrace);
8199 std::unique_ptr<cmCompiledGeneratorExpression> const cge =
8200 ge.Parse(entry.Value);
8201 cge->SetEvaluateForBuildsystem(true);
8202 std::string const& evaluated =
8203 cge->Evaluate(this->LocalGenerator, config, head, &dagChecker, nullptr,
8204 this->LinkerLanguage);
8205 bool const checkCMP0027 = evaluated != entry.Value;
8206 cmExpandList(evaluated, llibs);
8207 if (cge->GetHadHeadSensitiveCondition()) {
8208 impl.HadHeadSensitiveCondition = true;
8210 if (cge->GetHadContextSensitiveCondition()) {
8211 impl.HadContextSensitiveCondition = true;
8213 if (cge->GetHadLinkLanguageSensitiveCondition()) {
8214 impl.HadLinkLanguageSensitiveCondition = true;
8217 for (std::string const& lib : llibs) {
8218 if (this->IsLinkLookupScope(lib, lg)) {
8222 // Skip entries that resolve to the target itself or are empty.
8223 std::string name = this->CheckCMP0004(lib);
8224 if (this->GetPolicyStatusCMP0108() == cmPolicies::NEW) {
8225 // resolve alias name
8226 auto* target = this->Makefile->FindTargetToUse(name);
8228 name = target->GetName();
8231 if (name == this->GetName() || name.empty()) {
8232 if (name == this->GetName()) {
8233 bool noMessage = false;
8234 MessageType messageType = MessageType::FATAL_ERROR;
8235 std::ostringstream e;
8236 switch (this->GetPolicyStatusCMP0038()) {
8237 case cmPolicies::WARN: {
8238 e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0038) << "\n";
8239 messageType = MessageType::AUTHOR_WARNING;
8241 case cmPolicies::OLD:
8244 case cmPolicies::REQUIRED_IF_USED:
8245 case cmPolicies::REQUIRED_ALWAYS:
8246 case cmPolicies::NEW:
8247 // Issue the fatal message.
8252 e << "Target \"" << this->GetName() << "\" links to itself.";
8253 this->LocalGenerator->GetCMakeInstance()->IssueMessage(
8254 messageType, e.str(), this->GetBacktrace());
8255 if (messageType == MessageType::FATAL_ERROR) {
8263 // The entry is meant for this configuration.
8265 this->ResolveLinkItem(BT<std::string>(name, entry.Backtrace), lg);
8267 // Report explicitly linked object files separately.
8268 std::string const& maybeObj = item.AsStr();
8269 if (cmSystemTools::FileIsFullPath(maybeObj)) {
8270 cmSourceFile const* sf =
8271 mf->GetSource(maybeObj, cmSourceFileLocationKind::Known);
8272 if (sf && sf->GetPropertyAsBool("EXTERNAL_OBJECT")) {
8273 impl.Objects.emplace_back(std::move(item));
8279 impl.Libraries.emplace_back(std::move(item), checkCMP0027);
8282 std::set<std::string> const& seenProps = cge->GetSeenTargetProperties();
8283 for (std::string const& sp : seenProps) {
8284 if (!this->GetProperty(sp)) {
8285 this->LinkImplicitNullProperties.insert(sp);
8288 cge->GetMaxLanguageStandard(this, this->MaxLanguageStandards);
8291 // Update the list of direct link dependencies from usage requirements.
8293 ComputeLinkImplTransitive(this, config, implFor, impl);
8296 // Get the list of configurations considered to be DEBUG.
8297 std::vector<std::string> debugConfigs =
8298 this->Makefile->GetCMakeInstance()->GetDebugConfigs();
8300 cmTargetLinkLibraryType linkType =
8301 CMP0003_ComputeLinkType(config, debugConfigs);
8302 cmTarget::LinkLibraryVectorType const& oldllibs =
8303 this->Target->GetOriginalLinkLibraries();
8304 for (cmTarget::LibraryID const& oldllib : oldllibs) {
8305 if (oldllib.second != GENERAL_LibraryType && oldllib.second != linkType) {
8306 std::string name = this->CheckCMP0004(oldllib.first);
8307 if (name == this->GetName() || name.empty()) {
8310 // Support OLD behavior for CMP0003.
8311 impl.WrongConfigLibraries.push_back(
8312 this->ResolveLinkItem(BT<std::string>(name)));
8317 cmGeneratorTarget::TargetOrString cmGeneratorTarget::ResolveTargetReference(
8318 std::string const& name) const
8320 return this->ResolveTargetReference(name, this->LocalGenerator);
8323 cmGeneratorTarget::TargetOrString cmGeneratorTarget::ResolveTargetReference(
8324 std::string const& name, cmLocalGenerator const* lg) const
8326 TargetOrString resolved;
8328 if (cmGeneratorTarget* tgt = lg->FindGeneratorTargetToUse(name)) {
8329 resolved.Target = tgt;
8331 resolved.String = name;
8337 cmLinkItem cmGeneratorTarget::ResolveLinkItem(
8338 BT<std::string> const& name) const
8340 return this->ResolveLinkItem(name, this->LocalGenerator);
8343 cmLinkItem cmGeneratorTarget::ResolveLinkItem(BT<std::string> const& name,
8344 cmLocalGenerator const* lg) const
8346 auto bt = name.Backtrace;
8347 TargetOrString resolved = this->ResolveTargetReference(name.Value, lg);
8349 if (!resolved.Target) {
8350 return cmLinkItem(resolved.String, false, bt);
8353 // Check deprecation, issue message with `bt` backtrace.
8354 if (resolved.Target->IsDeprecated()) {
8355 std::ostringstream w;
8356 /* clang-format off */
8358 "The library that is being linked to, " << resolved.Target->GetName() <<
8359 ", is marked as being deprecated by the owner. The message provided by "
8360 "the developer is: \n" << resolved.Target->GetDeprecation() << "\n";
8361 /* clang-format on */
8362 this->LocalGenerator->GetCMakeInstance()->IssueMessage(
8363 MessageType::AUTHOR_WARNING, w.str(), bt);
8366 // Skip targets that will not really be linked. This is probably a
8367 // name conflict between an external library and an executable
8368 // within the project.
8369 if (resolved.Target->GetType() == cmStateEnums::EXECUTABLE &&
8370 !resolved.Target->IsExecutableWithExports()) {
8371 return cmLinkItem(resolved.Target->GetName(), false, bt);
8374 return cmLinkItem(resolved.Target, false, bt);
8377 bool cmGeneratorTarget::HasPackageReferences() const
8379 return this->IsInBuildSystem() &&
8380 !this->GetProperty("VS_PACKAGE_REFERENCES")->empty();
8383 std::vector<std::string> cmGeneratorTarget::GetPackageReferences() const
8385 std::vector<std::string> packageReferences;
8387 if (this->IsInBuildSystem()) {
8388 if (cmValue vsPackageReferences =
8389 this->GetProperty("VS_PACKAGE_REFERENCES")) {
8390 cmExpandList(*vsPackageReferences, packageReferences);
8394 return packageReferences;
8397 std::string cmGeneratorTarget::GetPDBDirectory(const std::string& config) const
8399 if (OutputInfo const* info = this->GetOutputInfo(config)) {
8400 // Return the directory in which the target will be built.
8401 return info->PdbDir;
8406 bool cmGeneratorTarget::HasImplibGNUtoMS(std::string const& config) const
8408 return this->HasImportLibrary(config) && this->GetPropertyAsBool("GNUtoMS");
8411 bool cmGeneratorTarget::GetImplibGNUtoMS(std::string const& config,
8412 std::string const& gnuName,
8414 const char* newExt) const
8416 if (this->HasImplibGNUtoMS(config) && gnuName.size() > 6 &&
8417 gnuName.substr(gnuName.size() - 6) == ".dll.a") {
8418 out = cmStrCat(cm::string_view(gnuName).substr(0, gnuName.size() - 6),
8419 newExt ? newExt : ".lib");
8425 bool cmGeneratorTarget::HasContextDependentSources() const
8427 return this->SourcesAreContextDependent == Tribool::True;
8430 bool cmGeneratorTarget::IsExecutableWithExports() const
8432 return (this->GetType() == cmStateEnums::EXECUTABLE &&
8433 this->GetPropertyAsBool("ENABLE_EXPORTS"));
8436 bool cmGeneratorTarget::HasImportLibrary(std::string const& config) const
8438 return (this->IsDLLPlatform() &&
8439 (this->GetType() == cmStateEnums::SHARED_LIBRARY ||
8440 this->IsExecutableWithExports()) &&
8441 // Assemblies which have only managed code do not have
8442 // import libraries.
8443 this->GetManagedType(config) != ManagedType::Managed) ||
8444 (this->Target->IsAIX() && this->IsExecutableWithExports());
8447 bool cmGeneratorTarget::NeedImportLibraryName(std::string const& config) const
8449 return this->HasImportLibrary(config) ||
8450 // On DLL platforms we always generate the import library name
8451 // just in case the sources have export markup.
8452 (this->IsDLLPlatform() &&
8453 (this->GetType() == cmStateEnums::EXECUTABLE ||
8454 this->GetType() == cmStateEnums::MODULE_LIBRARY));
8457 std::string cmGeneratorTarget::GetSupportDirectory() const
8459 std::string dir = cmStrCat(this->LocalGenerator->GetCurrentBinaryDirectory(),
8460 "/CMakeFiles/", this->GetName());
8469 bool cmGeneratorTarget::IsLinkable() const
8471 return (this->GetType() == cmStateEnums::STATIC_LIBRARY ||
8472 this->GetType() == cmStateEnums::SHARED_LIBRARY ||
8473 this->GetType() == cmStateEnums::MODULE_LIBRARY ||
8474 this->GetType() == cmStateEnums::UNKNOWN_LIBRARY ||
8475 this->GetType() == cmStateEnums::OBJECT_LIBRARY ||
8476 this->GetType() == cmStateEnums::INTERFACE_LIBRARY ||
8477 this->IsExecutableWithExports());
8480 bool cmGeneratorTarget::IsFrameworkOnApple() const
8482 return ((this->GetType() == cmStateEnums::SHARED_LIBRARY ||
8483 this->GetType() == cmStateEnums::STATIC_LIBRARY) &&
8484 this->Makefile->IsOn("APPLE") &&
8485 this->GetPropertyAsBool("FRAMEWORK"));
8488 bool cmGeneratorTarget::IsAppBundleOnApple() const
8490 return (this->GetType() == cmStateEnums::EXECUTABLE &&
8491 this->Makefile->IsOn("APPLE") &&
8492 this->GetPropertyAsBool("MACOSX_BUNDLE"));
8495 bool cmGeneratorTarget::IsXCTestOnApple() const
8497 return (this->IsCFBundleOnApple() && this->GetPropertyAsBool("XCTEST"));
8500 bool cmGeneratorTarget::IsCFBundleOnApple() const
8502 return (this->GetType() == cmStateEnums::MODULE_LIBRARY &&
8503 this->Makefile->IsOn("APPLE") && this->GetPropertyAsBool("BUNDLE"));
8506 cmGeneratorTarget::ManagedType cmGeneratorTarget::CheckManagedType(
8507 std::string const& propval) const
8509 // The type of the managed assembly (mixed unmanaged C++ and C++/CLI,
8510 // or only C++/CLI) does only depend on whether the property is an empty
8511 // string or contains any value at all. In Visual Studio generators
8512 // this propval is prepended with /clr[:] which results in:
8514 // 1. propval does not exist: no /clr flag, unmanaged target, has import
8516 // 2. empty propval: add /clr as flag, mixed unmanaged/managed
8517 // target, has import lib
8518 // 3. any value (safe,pure): add /clr:[propval] as flag, target with
8519 // managed code only, no import lib
8520 return propval.empty() ? ManagedType::Mixed : ManagedType::Managed;
8523 cmGeneratorTarget::ManagedType cmGeneratorTarget::GetManagedType(
8524 const std::string& config) const
8526 // Only libraries and executables can be managed targets.
8527 if (this->GetType() > cmStateEnums::SHARED_LIBRARY) {
8528 return ManagedType::Undefined;
8531 if (this->GetType() == cmStateEnums::STATIC_LIBRARY) {
8532 return ManagedType::Native;
8535 // Check imported target.
8536 if (this->IsImported()) {
8537 if (cmGeneratorTarget::ImportInfo const* info =
8538 this->GetImportInfo(config)) {
8539 return info->Managed;
8541 return ManagedType::Undefined;
8544 // Check for explicitly set clr target property.
8545 if (cmValue clr = this->GetProperty("COMMON_LANGUAGE_RUNTIME")) {
8546 return this->CheckManagedType(*clr);
8549 // C# targets are always managed. This language specific check
8550 // is added to avoid that the COMMON_LANGUAGE_RUNTIME target property
8551 // has to be set manually for C# targets.
8552 return this->IsCSharpOnly() ? ManagedType::Managed : ManagedType::Native;
8555 bool cmGeneratorTarget::AddHeaderSetVerification()
8557 if (!this->GetPropertyAsBool("VERIFY_INTERFACE_HEADER_SETS")) {
8561 if (this->GetType() != cmStateEnums::STATIC_LIBRARY &&
8562 this->GetType() != cmStateEnums::SHARED_LIBRARY &&
8563 this->GetType() != cmStateEnums::UNKNOWN_LIBRARY &&
8564 this->GetType() != cmStateEnums::OBJECT_LIBRARY &&
8565 this->GetType() != cmStateEnums::INTERFACE_LIBRARY &&
8566 !this->IsExecutableWithExports()) {
8570 auto verifyValue = this->GetProperty("INTERFACE_HEADER_SETS_TO_VERIFY");
8571 const bool all = verifyValue.IsEmpty();
8572 std::set<std::string> verifySet;
8574 auto verifyList = cmExpandedList(verifyValue);
8575 verifySet.insert(verifyList.begin(), verifyList.end());
8578 cmTarget* verifyTarget = nullptr;
8579 cmTarget* allVerifyTarget =
8580 this->GlobalGenerator->GetMakefiles().front()->FindTargetToUse(
8581 "all_verify_interface_header_sets", true);
8583 auto interfaceFileSetEntries = this->Target->GetInterfaceHeaderSetsEntries();
8585 std::set<cmFileSet*> fileSets;
8586 for (auto const& entry : interfaceFileSetEntries) {
8587 for (auto const& name : cmExpandedList(entry.Value)) {
8588 if (all || verifySet.count(name)) {
8589 fileSets.insert(this->Target->GetFileSet(name));
8590 verifySet.erase(name);
8594 if (!verifySet.empty()) {
8595 this->Makefile->IssueMessage(
8596 MessageType::FATAL_ERROR,
8597 cmStrCat("Property INTERFACE_HEADER_SETS_TO_VERIFY of target \"",
8599 "\" contained the following header sets that are nonexistent "
8600 "or not INTERFACE:\n ",
8601 cmJoin(verifySet, "\n ")));
8605 cm::optional<std::set<std::string>> languages;
8606 for (auto* fileSet : fileSets) {
8607 auto dirCges = fileSet->CompileDirectoryEntries();
8608 auto fileCges = fileSet->CompileFileEntries();
8610 static auto const contextSensitive =
8611 [](const std::unique_ptr<cmCompiledGeneratorExpression>& cge) {
8612 return cge->GetHadContextSensitiveCondition();
8614 bool dirCgesContextSensitive = false;
8615 bool fileCgesContextSensitive = false;
8617 std::vector<std::string> dirs;
8618 std::map<std::string, std::vector<std::string>> filesPerDir;
8620 for (auto const& config : this->Makefile->GetGeneratorConfigs(
8621 cmMakefile::GeneratorConfigQuery::IncludeEmptyConfig)) {
8622 if (first || dirCgesContextSensitive) {
8623 dirs = fileSet->EvaluateDirectoryEntries(dirCges, this->LocalGenerator,
8625 dirCgesContextSensitive =
8626 std::any_of(dirCges.begin(), dirCges.end(), contextSensitive);
8628 if (first || fileCgesContextSensitive) {
8629 filesPerDir.clear();
8630 for (auto const& fileCge : fileCges) {
8631 fileSet->EvaluateFileEntry(dirs, filesPerDir, fileCge,
8632 this->LocalGenerator, config, this);
8633 if (fileCge->GetHadContextSensitiveCondition()) {
8634 fileCgesContextSensitive = true;
8639 for (auto const& files : filesPerDir) {
8640 for (auto const& file : files.second) {
8641 std::string filename = this->GenerateHeaderSetVerificationFile(
8642 *this->Makefile->GetOrCreateSource(file), files.first, languages);
8643 if (filename.empty()) {
8647 if (!verifyTarget) {
8649 cmMakefile::PolicyPushPop polScope(this->Makefile);
8650 this->Makefile->SetPolicy(cmPolicies::CMP0119, cmPolicies::NEW);
8651 verifyTarget = this->Makefile->AddLibrary(
8652 cmStrCat(this->GetName(), "_verify_interface_header_sets"),
8653 cmStateEnums::OBJECT_LIBRARY, {}, true);
8656 verifyTarget->AddLinkLibrary(
8657 *this->Makefile, this->GetName(),
8658 cmTargetLinkLibraryType::GENERAL_LibraryType);
8659 verifyTarget->SetProperty("AUTOMOC", "OFF");
8660 verifyTarget->SetProperty("AUTORCC", "OFF");
8661 verifyTarget->SetProperty("AUTOUIC", "OFF");
8662 verifyTarget->SetProperty("DISABLE_PRECOMPILE_HEADERS", "ON");
8663 verifyTarget->SetProperty("UNITY_BUILD", "OFF");
8664 cm::optional<std::map<std::string, cmValue>>
8665 perConfigCompileDefinitions;
8666 verifyTarget->FinalizeTargetConfiguration(
8667 this->Makefile->GetCompileDefinitionsEntries(),
8668 perConfigCompileDefinitions);
8670 if (!allVerifyTarget) {
8671 allVerifyTarget = this->GlobalGenerator->GetMakefiles()
8673 ->AddNewUtilityTarget(
8674 "all_verify_interface_header_sets", true);
8677 allVerifyTarget->AddUtility(verifyTarget->GetName(), false);
8680 if (fileCgesContextSensitive) {
8681 filename = cmStrCat("$<$<CONFIG:", config, ">:", filename, ">");
8683 verifyTarget->AddSource(filename);
8687 if (!dirCgesContextSensitive && !fileCgesContextSensitive) {
8695 this->LocalGenerator->AddGeneratorTarget(
8696 cm::make_unique<cmGeneratorTarget>(verifyTarget, this->LocalGenerator));
8702 std::string cmGeneratorTarget::GenerateHeaderSetVerificationFile(
8703 cmSourceFile& source, const std::string& dir,
8704 cm::optional<std::set<std::string>>& languages) const
8706 std::string extension;
8707 std::string language = source.GetOrDetermineLanguage();
8709 if (language.empty()) {
8711 languages.emplace();
8712 for (auto const& tgtSource : this->GetAllConfigSources()) {
8713 auto const& tgtSourceLanguage =
8714 tgtSource.Source->GetOrDetermineLanguage();
8715 if (tgtSourceLanguage == "CXX") {
8716 languages->insert("CXX");
8717 break; // C++ overrides everything else, so we don't need to keep
8720 if (tgtSourceLanguage == "C") {
8721 languages->insert("C");
8725 if (languages->empty()) {
8726 std::vector<std::string> languagesVector;
8727 this->GlobalGenerator->GetEnabledLanguages(languagesVector);
8728 languages->insert(languagesVector.begin(), languagesVector.end());
8732 if (languages->count("CXX")) {
8734 } else if (languages->count("C")) {
8739 if (language == "C") {
8741 } else if (language == "CXX") {
8747 std::string headerFilename = dir;
8748 if (!headerFilename.empty()) {
8749 headerFilename += '/';
8751 headerFilename += source.GetLocation().GetName();
8753 auto filename = cmStrCat(
8754 this->LocalGenerator->GetCurrentBinaryDirectory(), '/', this->GetName(),
8755 "_verify_interface_header_sets/", headerFilename, extension);
8756 auto* verificationSource = this->Makefile->GetOrCreateSource(filename);
8757 verificationSource->SetProperty("LANGUAGE", language);
8759 cmSystemTools::MakeDirectory(cmSystemTools::GetFilenamePath(filename));
8761 cmGeneratedFileStream fout(filename);
8762 fout.SetCopyIfDifferent(true);
8763 // IWYU pragma: associated allows include what you use to
8764 // consider the headerFile as part of the entire language
8765 // unit within include-what-you-use and as a result allows
8766 // one to get IWYU advice for headers :)
8767 fout << "#include <" << headerFilename << "> // IWYU pragma: associated\n";
8773 bool cmGeneratorTarget::HaveCxx20ModuleSources() const
8775 auto const& fs_names = this->Target->GetAllFileSetNames();
8776 return std::any_of(fs_names.begin(), fs_names.end(),
8777 [this](std::string const& name) -> bool {
8778 auto const* file_set = this->Target->GetFileSet(name);
8780 this->Makefile->IssueMessage(
8781 MessageType::INTERNAL_ERROR,
8782 cmStrCat("Target \"", this->Target->GetName(),
8783 "\" is tracked to have file set \"", name,
8784 "\", but it was not found."));
8788 auto const& fs_type = file_set->GetType();
8789 return fs_type == "CXX_MODULES"_s ||
8790 fs_type == "CXX_MODULE_HEADER_UNITS"_s;
8794 cmGeneratorTarget::Cxx20SupportLevel cmGeneratorTarget::HaveCxxModuleSupport(
8795 std::string const& config) const
8797 auto const* state = this->Makefile->GetState();
8798 if (!state->GetLanguageEnabled("CXX")) {
8799 return Cxx20SupportLevel::MissingCxx;
8801 cmValue standardDefault =
8802 this->Target->GetMakefile()->GetDefinition("CMAKE_CXX_STANDARD_DEFAULT");
8803 if (standardDefault && !standardDefault->empty()) {
8804 cmStandardLevelResolver standardResolver(this->Makefile);
8805 if (!standardResolver.HaveStandardAvailable(this, "CXX", config,
8807 return Cxx20SupportLevel::NoCxx20;
8810 // Else, an empty CMAKE_CXX_STANDARD_DEFAULT means CMake does not detect and
8811 // set a default standard level for this compiler, so assume all standards
8813 if (!this->Makefile->IsOn("CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP")) {
8814 return Cxx20SupportLevel::MissingExperimentalFlag;
8816 return Cxx20SupportLevel::Supported;
8819 void cmGeneratorTarget::CheckCxxModuleStatus(std::string const& config) const
8821 // Check for `CXX_MODULE*` file sets and a lack of support.
8822 if (this->HaveCxx20ModuleSources()) {
8823 switch (this->HaveCxxModuleSupport(config)) {
8824 case cmGeneratorTarget::Cxx20SupportLevel::MissingCxx:
8825 this->Makefile->IssueMessage(
8826 MessageType::FATAL_ERROR,
8827 cmStrCat("The \"", this->GetName(),
8828 "\" target has C++ module sources but the \"CXX\" language "
8829 "has not been enabled"));
8831 case cmGeneratorTarget::Cxx20SupportLevel::MissingExperimentalFlag:
8832 this->Makefile->IssueMessage(
8833 MessageType::FATAL_ERROR,
8834 cmStrCat("The \"", this->GetName(),
8835 "\" target has C++ module sources but its experimental "
8836 "support has not been requested"));
8838 case cmGeneratorTarget::Cxx20SupportLevel::NoCxx20:
8839 this->Makefile->IssueMessage(
8840 MessageType::FATAL_ERROR,
8842 "The \"", this->GetName(),
8843 "\" target has C++ module sources but is not using at least "
8846 case cmGeneratorTarget::Cxx20SupportLevel::Supported: