Merge topic 'vs-scan-module-deps-settings' into release-3.29
[kiteware-cmake.git] / Source / cmGeneratorTarget.cxx
blob0fe19de314993f0581e82b3327524d7ed9516792
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"
5 #include <algorithm>
6 #include <array>
7 #include <cassert>
8 #include <cctype>
9 #include <cerrno>
10 #include <cstddef>
11 #include <cstdio>
12 #include <cstdlib>
13 #include <cstring>
14 #include <iterator>
15 #include <queue>
16 #include <sstream>
17 #include <type_traits>
18 #include <unordered_set>
19 #include <utility>
21 #include <cm/memory>
22 #include <cm/optional>
23 #include <cm/string_view>
24 #include <cmext/algorithm>
25 #include <cmext/string_view>
27 #include "cmsys/RegularExpression.hxx"
29 #include "cmAlgorithms.h"
30 #include "cmComputeLinkInformation.h"
31 #include "cmCryptoHash.h"
32 #include "cmCustomCommandGenerator.h"
33 #include "cmCxxModuleUsageEffects.h"
34 #include "cmEvaluatedTargetProperty.h"
35 #include "cmFileSet.h"
36 #include "cmFileTimes.h"
37 #include "cmGeneratedFileStream.h"
38 #include "cmGeneratorExpression.h"
39 #include "cmGeneratorExpressionContext.h"
40 #include "cmGeneratorExpressionDAGChecker.h"
41 #include "cmGeneratorExpressionNode.h"
42 #include "cmGlobalGenerator.h"
43 #include "cmList.h"
44 #include "cmLocalGenerator.h"
45 #include "cmMakefile.h"
46 #include "cmMessageType.h"
47 #include "cmOutputConverter.h"
48 #include "cmPropertyMap.h"
49 #include "cmRange.h"
50 #include "cmSourceFile.h"
51 #include "cmSourceFileLocation.h"
52 #include "cmSourceFileLocationKind.h"
53 #include "cmSourceGroup.h"
54 #include "cmStandardLevel.h"
55 #include "cmStandardLevelResolver.h"
56 #include "cmState.h"
57 #include "cmStringAlgorithms.h"
58 #include "cmSyntheticTargetCache.h"
59 #include "cmSystemTools.h"
60 #include "cmTarget.h"
61 #include "cmTargetLinkLibraryType.h"
62 #include "cmTargetPropertyComputer.h"
63 #include "cmake.h"
65 namespace {
66 using LinkInterfaceFor = cmGeneratorTarget::LinkInterfaceFor;
68 const std::string kINTERFACE_LINK_LIBRARIES = "INTERFACE_LINK_LIBRARIES";
69 const std::string kINTERFACE_LINK_LIBRARIES_DIRECT =
70 "INTERFACE_LINK_LIBRARIES_DIRECT";
71 const std::string kINTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE =
72 "INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE";
75 template <>
76 cmValue cmTargetPropertyComputer::GetSources<cmGeneratorTarget>(
77 cmGeneratorTarget const* tgt, cmMakefile const& /* mf */)
79 return tgt->GetSourcesProperty();
82 template <>
83 const std::string&
84 cmTargetPropertyComputer::ComputeLocationForBuild<cmGeneratorTarget>(
85 cmGeneratorTarget const* tgt)
87 return tgt->GetLocation("");
90 template <>
91 const std::string&
92 cmTargetPropertyComputer::ComputeLocation<cmGeneratorTarget>(
93 cmGeneratorTarget const* tgt, const std::string& config)
95 return tgt->GetLocation(config);
98 cmLinkImplItem cmGeneratorTarget::TargetPropertyEntry::NoLinkImplItem;
100 class TargetPropertyEntryString : public cmGeneratorTarget::TargetPropertyEntry
102 public:
103 TargetPropertyEntryString(BT<std::string> propertyValue,
104 cmLinkImplItem const& item = NoLinkImplItem)
105 : cmGeneratorTarget::TargetPropertyEntry(item)
106 , PropertyValue(std::move(propertyValue))
110 const std::string& Evaluate(cmLocalGenerator*, const std::string&,
111 cmGeneratorTarget const*,
112 cmGeneratorExpressionDAGChecker*,
113 std::string const&) const override
115 return this->PropertyValue.Value;
118 cmListFileBacktrace GetBacktrace() const override
120 return this->PropertyValue.Backtrace;
122 std::string const& GetInput() const override
124 return this->PropertyValue.Value;
127 private:
128 BT<std::string> PropertyValue;
131 class TargetPropertyEntryGenex : public cmGeneratorTarget::TargetPropertyEntry
133 public:
134 TargetPropertyEntryGenex(std::unique_ptr<cmCompiledGeneratorExpression> cge,
135 cmLinkImplItem const& item = NoLinkImplItem)
136 : cmGeneratorTarget::TargetPropertyEntry(item)
137 , ge(std::move(cge))
141 const std::string& Evaluate(cmLocalGenerator* lg, const std::string& config,
142 cmGeneratorTarget const* headTarget,
143 cmGeneratorExpressionDAGChecker* dagChecker,
144 std::string const& language) const override
146 return this->ge->Evaluate(lg, config, headTarget, dagChecker, nullptr,
147 language);
150 cmListFileBacktrace GetBacktrace() const override
152 return this->ge->GetBacktrace();
155 std::string const& GetInput() const override { return this->ge->GetInput(); }
157 bool GetHadContextSensitiveCondition() const override
159 return this->ge->GetHadContextSensitiveCondition();
162 private:
163 const std::unique_ptr<cmCompiledGeneratorExpression> ge;
166 class TargetPropertyEntryFileSet
167 : public cmGeneratorTarget::TargetPropertyEntry
169 public:
170 TargetPropertyEntryFileSet(
171 std::vector<std::string> dirs, bool contextSensitiveDirs,
172 std::unique_ptr<cmCompiledGeneratorExpression> entryCge,
173 const cmFileSet* fileSet, cmLinkImplItem const& item = NoLinkImplItem)
174 : cmGeneratorTarget::TargetPropertyEntry(item)
175 , BaseDirs(std::move(dirs))
176 , ContextSensitiveDirs(contextSensitiveDirs)
177 , EntryCge(std::move(entryCge))
178 , FileSet(fileSet)
182 const std::string& Evaluate(cmLocalGenerator* lg, const std::string& config,
183 cmGeneratorTarget const* headTarget,
184 cmGeneratorExpressionDAGChecker* dagChecker,
185 std::string const& /*lang*/) const override
187 std::map<std::string, std::vector<std::string>> filesPerDir;
188 this->FileSet->EvaluateFileEntry(this->BaseDirs, filesPerDir,
189 this->EntryCge, lg, config, headTarget,
190 dagChecker);
192 std::vector<std::string> files;
193 for (auto const& it : filesPerDir) {
194 files.insert(files.end(), it.second.begin(), it.second.end());
197 static std::string filesStr;
198 filesStr = cmList::to_string(files);
199 return filesStr;
202 cmListFileBacktrace GetBacktrace() const override
204 return this->EntryCge->GetBacktrace();
207 std::string const& GetInput() const override
209 return this->EntryCge->GetInput();
212 bool GetHadContextSensitiveCondition() const override
214 return this->ContextSensitiveDirs ||
215 this->EntryCge->GetHadContextSensitiveCondition();
218 private:
219 const std::vector<std::string> BaseDirs;
220 const bool ContextSensitiveDirs;
221 const std::unique_ptr<cmCompiledGeneratorExpression> EntryCge;
222 const cmFileSet* FileSet;
225 std::unique_ptr<
226 cmGeneratorTarget::
227 TargetPropertyEntry> static CreateTargetPropertyEntry(cmake& cmakeInstance,
228 const BT<
229 std::string>&
230 propertyValue,
231 bool
232 evaluateForBuildsystem =
233 false)
235 if (cmGeneratorExpression::Find(propertyValue.Value) != std::string::npos) {
236 cmGeneratorExpression ge(cmakeInstance, propertyValue.Backtrace);
237 std::unique_ptr<cmCompiledGeneratorExpression> cge =
238 ge.Parse(propertyValue.Value);
239 cge->SetEvaluateForBuildsystem(evaluateForBuildsystem);
240 return std::unique_ptr<cmGeneratorTarget::TargetPropertyEntry>(
241 cm::make_unique<TargetPropertyEntryGenex>(std::move(cge)));
244 return std::unique_ptr<cmGeneratorTarget::TargetPropertyEntry>(
245 cm::make_unique<TargetPropertyEntryString>(propertyValue));
248 cmGeneratorTarget::TargetPropertyEntry::TargetPropertyEntry(
249 cmLinkImplItem const& item)
250 : LinkImplItem(item)
254 bool cmGeneratorTarget::TargetPropertyEntry::GetHadContextSensitiveCondition()
255 const
257 return false;
260 static void CreatePropertyGeneratorExpressions(
261 cmake& cmakeInstance, cmBTStringRange entries,
262 std::vector<std::unique_ptr<cmGeneratorTarget::TargetPropertyEntry>>& items,
263 bool evaluateForBuildsystem = false)
265 for (auto const& entry : entries) {
266 items.push_back(
267 CreateTargetPropertyEntry(cmakeInstance, entry, evaluateForBuildsystem));
271 cmGeneratorTarget::cmGeneratorTarget(cmTarget* t, cmLocalGenerator* lg)
272 : Target(t)
274 this->Makefile = this->Target->GetMakefile();
275 this->LocalGenerator = lg;
276 this->GlobalGenerator = this->LocalGenerator->GetGlobalGenerator();
278 this->GlobalGenerator->ComputeTargetObjectDirectory(this);
280 CreatePropertyGeneratorExpressions(*lg->GetCMakeInstance(),
281 t->GetIncludeDirectoriesEntries(),
282 this->IncludeDirectoriesEntries);
284 CreatePropertyGeneratorExpressions(*lg->GetCMakeInstance(),
285 t->GetCompileOptionsEntries(),
286 this->CompileOptionsEntries);
288 CreatePropertyGeneratorExpressions(*lg->GetCMakeInstance(),
289 t->GetCompileFeaturesEntries(),
290 this->CompileFeaturesEntries);
292 CreatePropertyGeneratorExpressions(*lg->GetCMakeInstance(),
293 t->GetCompileDefinitionsEntries(),
294 this->CompileDefinitionsEntries);
296 CreatePropertyGeneratorExpressions(*lg->GetCMakeInstance(),
297 t->GetLinkOptionsEntries(),
298 this->LinkOptionsEntries);
300 CreatePropertyGeneratorExpressions(*lg->GetCMakeInstance(),
301 t->GetLinkDirectoriesEntries(),
302 this->LinkDirectoriesEntries);
304 CreatePropertyGeneratorExpressions(*lg->GetCMakeInstance(),
305 t->GetPrecompileHeadersEntries(),
306 this->PrecompileHeadersEntries);
308 CreatePropertyGeneratorExpressions(
309 *lg->GetCMakeInstance(), t->GetSourceEntries(), this->SourceEntries, true);
311 this->PolicyMap = t->GetPolicyMap();
313 // Get hard-coded linker language
314 if (this->Target->GetProperty("HAS_CXX")) {
315 this->LinkerLanguage = "CXX";
316 } else {
317 this->LinkerLanguage = this->Target->GetSafeProperty("LINKER_LANGUAGE");
321 cmGeneratorTarget::~cmGeneratorTarget() = default;
323 cmValue cmGeneratorTarget::GetSourcesProperty() const
325 std::vector<std::string> values;
326 for (auto const& se : this->SourceEntries) {
327 values.push_back(se->GetInput());
329 static std::string value;
330 value = cmList::to_string(values);
331 return cmValue(value);
334 cmGlobalGenerator* cmGeneratorTarget::GetGlobalGenerator() const
336 return this->GetLocalGenerator()->GetGlobalGenerator();
339 cmLocalGenerator* cmGeneratorTarget::GetLocalGenerator() const
341 return this->LocalGenerator;
344 cmStateEnums::TargetType cmGeneratorTarget::GetType() const
346 return this->Target->GetType();
349 const std::string& cmGeneratorTarget::GetName() const
351 return this->Target->GetName();
354 std::string cmGeneratorTarget::GetExportName() const
356 cmValue exportName = this->GetProperty("EXPORT_NAME");
358 if (cmNonempty(exportName)) {
359 if (!cmGeneratorExpression::IsValidTargetName(*exportName)) {
360 std::ostringstream e;
361 e << "EXPORT_NAME property \"" << *exportName << "\" for \""
362 << this->GetName() << "\": is not valid.";
363 cmSystemTools::Error(e.str());
364 return "";
366 return *exportName;
368 return this->GetName();
371 std::string cmGeneratorTarget::GetFilesystemExportName() const
373 auto fs_safe = this->GetExportName();
374 // First escape any `_` characters to avoid collisions.
375 cmSystemTools::ReplaceString(fs_safe, "_", "__");
376 // Escape other characters that are not generally filesystem-safe.
377 cmSystemTools::ReplaceString(fs_safe, ":", "_c");
378 return fs_safe;
381 cmValue cmGeneratorTarget::GetProperty(const std::string& prop) const
383 if (cmValue result =
384 cmTargetPropertyComputer::GetProperty(this, prop, *this->Makefile)) {
385 return result;
387 if (cmSystemTools::GetFatalErrorOccurred()) {
388 return nullptr;
390 return this->Target->GetProperty(prop);
393 std::string const& cmGeneratorTarget::GetSafeProperty(
394 std::string const& prop) const
396 return this->GetProperty(prop);
399 const char* cmGeneratorTarget::GetOutputTargetType(
400 cmStateEnums::ArtifactType artifact) const
402 if (this->IsFrameworkOnApple() || this->GetGlobalGenerator()->IsXcode()) {
403 // import file (i.e. .tbd file) is always in same location as library
404 artifact = cmStateEnums::RuntimeBinaryArtifact;
407 switch (this->GetType()) {
408 case cmStateEnums::SHARED_LIBRARY:
409 if (this->IsDLLPlatform()) {
410 switch (artifact) {
411 case cmStateEnums::RuntimeBinaryArtifact:
412 // A DLL shared library is treated as a runtime target.
413 return "RUNTIME";
414 case cmStateEnums::ImportLibraryArtifact:
415 // A DLL import library is treated as an archive target.
416 return "ARCHIVE";
418 } else {
419 switch (artifact) {
420 case cmStateEnums::RuntimeBinaryArtifact:
421 // For non-DLL platforms shared libraries are treated as
422 // library targets.
423 return "LIBRARY";
424 case cmStateEnums::ImportLibraryArtifact:
425 // Library import libraries are treated as archive targets.
426 return "ARCHIVE";
429 break;
430 case cmStateEnums::STATIC_LIBRARY:
431 // Static libraries are always treated as archive targets.
432 return "ARCHIVE";
433 case cmStateEnums::MODULE_LIBRARY:
434 switch (artifact) {
435 case cmStateEnums::RuntimeBinaryArtifact:
436 // Module libraries are always treated as library targets.
437 return "LIBRARY";
438 case cmStateEnums::ImportLibraryArtifact:
439 // Module import libraries are treated as archive targets.
440 return "ARCHIVE";
442 break;
443 case cmStateEnums::OBJECT_LIBRARY:
444 // Object libraries are always treated as object targets.
445 return "OBJECT";
446 case cmStateEnums::EXECUTABLE:
447 switch (artifact) {
448 case cmStateEnums::RuntimeBinaryArtifact:
449 // Executables are always treated as runtime targets.
450 return "RUNTIME";
451 case cmStateEnums::ImportLibraryArtifact:
452 // Executable import libraries are treated as archive targets.
453 return "ARCHIVE";
455 break;
456 default:
457 break;
459 return "";
462 std::string cmGeneratorTarget::GetOutputName(
463 const std::string& config, cmStateEnums::ArtifactType artifact) const
465 // Lookup/compute/cache the output name for this configuration.
466 OutputNameKey key(config, artifact);
467 auto i = this->OutputNameMap.find(key);
468 if (i == this->OutputNameMap.end()) {
469 // Add empty name in map to detect potential recursion.
470 OutputNameMapType::value_type entry(key, "");
471 i = this->OutputNameMap.insert(entry).first;
473 // Compute output name.
474 std::vector<std::string> props;
475 std::string type = this->GetOutputTargetType(artifact);
476 std::string configUpper = cmSystemTools::UpperCase(config);
477 if (!type.empty() && !configUpper.empty()) {
478 // <ARCHIVE|LIBRARY|RUNTIME>_OUTPUT_NAME_<CONFIG>
479 props.push_back(type + "_OUTPUT_NAME_" + configUpper);
481 if (!type.empty()) {
482 // <ARCHIVE|LIBRARY|RUNTIME>_OUTPUT_NAME
483 props.push_back(type + "_OUTPUT_NAME");
485 if (!configUpper.empty()) {
486 // OUTPUT_NAME_<CONFIG>
487 props.push_back("OUTPUT_NAME_" + configUpper);
488 // <CONFIG>_OUTPUT_NAME
489 props.push_back(configUpper + "_OUTPUT_NAME");
491 // OUTPUT_NAME
492 props.emplace_back("OUTPUT_NAME");
494 std::string outName;
495 for (std::string const& p : props) {
496 if (cmValue outNameProp = this->GetProperty(p)) {
497 outName = *outNameProp;
498 break;
502 if (outName.empty()) {
503 outName = this->GetName();
506 // Now evaluate genex and update the previously-prepared map entry.
507 i->second =
508 cmGeneratorExpression::Evaluate(outName, this->LocalGenerator, config);
509 } else if (i->second.empty()) {
510 // An empty map entry indicates we have been called recursively
511 // from the above block.
512 this->LocalGenerator->GetCMakeInstance()->IssueMessage(
513 MessageType::FATAL_ERROR,
514 "Target '" + this->GetName() + "' OUTPUT_NAME depends on itself.",
515 this->GetBacktrace());
517 return i->second;
520 std::string cmGeneratorTarget::GetFilePrefix(
521 const std::string& config, cmStateEnums::ArtifactType artifact) const
523 if (this->IsImported()) {
524 cmValue prefix = this->GetFilePrefixInternal(config, artifact);
525 return prefix ? *prefix : std::string();
527 return this->GetFullNameInternalComponents(config, artifact).prefix;
529 std::string cmGeneratorTarget::GetFileSuffix(
530 const std::string& config, cmStateEnums::ArtifactType artifact) const
532 if (this->IsImported()) {
533 cmValue suffix = this->GetFileSuffixInternal(config, artifact);
534 return suffix ? *suffix : std::string();
536 return this->GetFullNameInternalComponents(config, artifact).suffix;
539 std::string cmGeneratorTarget::GetFilePostfix(const std::string& config) const
541 cmValue postfix = nullptr;
542 std::string frameworkPostfix;
543 if (!config.empty()) {
544 std::string configProp =
545 cmStrCat(cmSystemTools::UpperCase(config), "_POSTFIX");
546 postfix = this->GetProperty(configProp);
548 // Mac application bundles and frameworks have no regular postfix like
549 // libraries do.
550 if (!this->IsImported() && postfix &&
551 (this->IsAppBundleOnApple() || this->IsFrameworkOnApple())) {
552 postfix = nullptr;
555 // Frameworks created by multi config generators can have a special
556 // framework postfix.
557 frameworkPostfix = this->GetFrameworkMultiConfigPostfix(config);
558 if (!frameworkPostfix.empty()) {
559 postfix = cmValue(frameworkPostfix);
562 return postfix ? *postfix : std::string();
565 std::string cmGeneratorTarget::GetFrameworkMultiConfigPostfix(
566 const std::string& config) const
568 cmValue postfix = nullptr;
569 if (!config.empty()) {
570 std::string configProp = cmStrCat("FRAMEWORK_MULTI_CONFIG_POSTFIX_",
571 cmSystemTools::UpperCase(config));
572 postfix = this->GetProperty(configProp);
574 if (!this->IsImported() && postfix &&
575 (this->IsFrameworkOnApple() &&
576 !this->GetGlobalGenerator()->IsMultiConfig())) {
577 postfix = nullptr;
580 return postfix ? *postfix : std::string();
583 cmValue cmGeneratorTarget::GetFilePrefixInternal(
584 std::string const& config, cmStateEnums::ArtifactType artifact,
585 const std::string& language) const
587 // no prefix for non-main target types.
588 if (this->GetType() != cmStateEnums::STATIC_LIBRARY &&
589 this->GetType() != cmStateEnums::SHARED_LIBRARY &&
590 this->GetType() != cmStateEnums::MODULE_LIBRARY &&
591 this->GetType() != cmStateEnums::EXECUTABLE) {
592 return nullptr;
595 const bool isImportedLibraryArtifact =
596 (artifact == cmStateEnums::ImportLibraryArtifact);
598 // Return an empty prefix for the import library if this platform
599 // does not support import libraries.
600 if (isImportedLibraryArtifact && !this->NeedImportLibraryName(config)) {
601 return nullptr;
604 // The implib option is only allowed for shared libraries, module
605 // libraries, and executables.
606 if (this->GetType() != cmStateEnums::SHARED_LIBRARY &&
607 this->GetType() != cmStateEnums::MODULE_LIBRARY &&
608 this->GetType() != cmStateEnums::EXECUTABLE) {
609 artifact = cmStateEnums::RuntimeBinaryArtifact;
612 // Compute prefix value.
613 cmValue targetPrefix =
614 (isImportedLibraryArtifact ? this->GetProperty("IMPORT_PREFIX")
615 : this->GetProperty("PREFIX"));
617 if (!targetPrefix) {
618 const char* prefixVar = this->Target->GetPrefixVariableInternal(artifact);
619 if (!language.empty() && cmNonempty(prefixVar)) {
620 std::string langPrefix = cmStrCat(prefixVar, "_", language);
621 targetPrefix = this->Makefile->GetDefinition(langPrefix);
624 // if there is no prefix on the target nor specific language
625 // use the cmake definition.
626 if (!targetPrefix && prefixVar) {
627 targetPrefix = this->Makefile->GetDefinition(prefixVar);
631 return targetPrefix;
634 cmValue cmGeneratorTarget::GetFileSuffixInternal(
635 std::string const& config, cmStateEnums::ArtifactType artifact,
636 const std::string& language) const
638 // no suffix for non-main target types.
639 if (this->GetType() != cmStateEnums::STATIC_LIBRARY &&
640 this->GetType() != cmStateEnums::SHARED_LIBRARY &&
641 this->GetType() != cmStateEnums::MODULE_LIBRARY &&
642 this->GetType() != cmStateEnums::EXECUTABLE) {
643 return nullptr;
646 const bool isImportedLibraryArtifact =
647 (artifact == cmStateEnums::ImportLibraryArtifact);
649 // Return an empty suffix for the import library if this platform
650 // does not support import libraries.
651 if (isImportedLibraryArtifact && !this->NeedImportLibraryName(config)) {
652 return nullptr;
655 // The implib option is only allowed for shared libraries, module
656 // libraries, and executables.
657 if (this->GetType() != cmStateEnums::SHARED_LIBRARY &&
658 this->GetType() != cmStateEnums::MODULE_LIBRARY &&
659 this->GetType() != cmStateEnums::EXECUTABLE) {
660 artifact = cmStateEnums::RuntimeBinaryArtifact;
663 // Compute suffix value.
664 cmValue targetSuffix =
665 (isImportedLibraryArtifact ? this->GetProperty("IMPORT_SUFFIX")
666 : this->GetProperty("SUFFIX"));
668 if (!targetSuffix) {
669 const char* suffixVar = this->Target->GetSuffixVariableInternal(artifact);
670 if (!language.empty() && cmNonempty(suffixVar)) {
671 std::string langSuffix = cmStrCat(suffixVar, "_", language);
672 targetSuffix = this->Makefile->GetDefinition(langSuffix);
675 // if there is no suffix on the target nor specific language
676 // use the cmake definition.
677 if (!targetSuffix && suffixVar) {
678 targetSuffix = this->Makefile->GetDefinition(suffixVar);
682 return targetSuffix;
685 void cmGeneratorTarget::ClearSourcesCache()
687 this->AllConfigSources.clear();
688 this->KindedSourcesMap.clear();
689 this->SourcesAreContextDependent = Tribool::Indeterminate;
690 this->Objects.clear();
691 this->VisitedConfigsForObjects.clear();
692 this->LinkImplMap.clear();
693 this->LinkImplUsageRequirementsOnlyMap.clear();
694 this->IncludeDirectoriesCache.clear();
695 this->CompileOptionsCache.clear();
696 this->CompileDefinitionsCache.clear();
697 this->PrecompileHeadersCache.clear();
698 this->LinkOptionsCache.clear();
699 this->LinkDirectoriesCache.clear();
700 this->RuntimeBinaryFullNameCache.clear();
701 this->ImportLibraryFullNameCache.clear();
704 void cmGeneratorTarget::ClearLinkInterfaceCache()
706 this->LinkInterfaceMap.clear();
707 this->LinkInterfaceUsageRequirementsOnlyMap.clear();
710 void cmGeneratorTarget::AddSourceCommon(const std::string& src, bool before)
712 this->SourceEntries.insert(
713 before ? this->SourceEntries.begin() : this->SourceEntries.end(),
714 CreateTargetPropertyEntry(
715 *this->LocalGenerator->GetCMakeInstance(),
716 BT<std::string>(src, this->Makefile->GetBacktrace()), true));
717 this->ClearSourcesCache();
720 void cmGeneratorTarget::AddSource(const std::string& src, bool before)
722 this->Target->AddSource(src, before);
723 this->AddSourceCommon(src, before);
726 void cmGeneratorTarget::AddTracedSources(std::vector<std::string> const& srcs)
728 this->Target->AddTracedSources(srcs);
729 if (!srcs.empty()) {
730 this->AddSourceCommon(cmJoin(srcs, ";"));
734 void cmGeneratorTarget::AddIncludeDirectory(const std::string& src,
735 bool before)
737 this->Target->InsertInclude(
738 BT<std::string>(src, this->Makefile->GetBacktrace()), before);
739 this->IncludeDirectoriesEntries.insert(
740 before ? this->IncludeDirectoriesEntries.begin()
741 : this->IncludeDirectoriesEntries.end(),
742 CreateTargetPropertyEntry(
743 *this->Makefile->GetCMakeInstance(),
744 BT<std::string>(src, this->Makefile->GetBacktrace()), true));
747 void cmGeneratorTarget::AddSystemIncludeDirectory(std::string const& inc,
748 std::string const& lang)
750 std::string config_upper;
751 auto const& configs =
752 this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
754 for (auto const& config : configs) {
755 std::string inc_with_config = inc;
756 if (!config.empty()) {
757 cmSystemTools::ReplaceString(inc_with_config, "$<CONFIG>", config);
758 config_upper = cmSystemTools::UpperCase(config);
760 auto const& key = cmStrCat(config_upper, "/", lang);
761 this->Target->AddSystemIncludeDirectories({ inc_with_config });
762 this->SystemIncludesCache[key].emplace_back(inc_with_config);
764 // SystemIncludesCache should be sorted so that binary search can be used
765 std::sort(this->SystemIncludesCache[key].begin(),
766 this->SystemIncludesCache[key].end());
770 std::vector<cmSourceFile*> const* cmGeneratorTarget::GetSourceDepends(
771 cmSourceFile const* sf) const
773 auto i = this->SourceDepends.find(sf);
774 if (i != this->SourceDepends.end()) {
775 return &i->second.Depends;
777 return nullptr;
780 namespace {
781 void handleSystemIncludesDep(cmLocalGenerator* lg,
782 cmGeneratorTarget const* depTgt,
783 const std::string& config,
784 cmGeneratorTarget const* headTarget,
785 cmGeneratorExpressionDAGChecker* dagChecker,
786 cmList& result, bool excludeImported,
787 std::string const& language)
789 if (cmValue dirs =
790 depTgt->GetProperty("INTERFACE_SYSTEM_INCLUDE_DIRECTORIES")) {
791 result.append(cmGeneratorExpression::Evaluate(
792 *dirs, lg, config, headTarget, dagChecker, depTgt, language));
794 if (!depTgt->GetPropertyAsBool("SYSTEM")) {
795 return;
797 if (depTgt->IsImported()) {
798 if (excludeImported) {
799 return;
801 if (depTgt->GetPropertyAsBool("IMPORTED_NO_SYSTEM")) {
802 return;
806 if (cmValue dirs = depTgt->GetProperty("INTERFACE_INCLUDE_DIRECTORIES")) {
807 result.append(cmGeneratorExpression::Evaluate(
808 *dirs, lg, config, headTarget, dagChecker, depTgt, language));
811 if (depTgt->Target->IsFrameworkOnApple() ||
812 depTgt->IsImportedFrameworkFolderOnApple(config)) {
813 if (auto fwDescriptor = depTgt->GetGlobalGenerator()->SplitFrameworkPath(
814 depTgt->GetLocation(config))) {
815 result.push_back(fwDescriptor->Directory);
816 result.push_back(fwDescriptor->GetFrameworkPath());
822 /* clang-format off */
823 #define IMPLEMENT_VISIT(KIND) \
824 do { \
825 KindedSources const& kinded = this->GetKindedSources(config); \
826 for (SourceAndKind const& s : kinded.Sources) { \
827 if (s.Kind == KIND) { \
828 data.push_back(s.Source.Value); \
831 } while (false)
832 /* clang-format on */
834 void cmGeneratorTarget::GetObjectSources(
835 std::vector<cmSourceFile const*>& data, const std::string& config) const
837 IMPLEMENT_VISIT(SourceKindObjectSource);
839 if (this->VisitedConfigsForObjects.count(config)) {
840 return;
843 for (cmSourceFile const* it : data) {
844 this->Objects[it];
847 this->LocalGenerator->ComputeObjectFilenames(this->Objects, this);
848 this->VisitedConfigsForObjects.insert(config);
851 void cmGeneratorTarget::ComputeObjectMapping()
853 auto const& configs =
854 this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
855 std::set<std::string> configSet(configs.begin(), configs.end());
856 if (configSet == this->VisitedConfigsForObjects) {
857 return;
860 for (std::string const& c : configs) {
861 std::vector<cmSourceFile const*> sourceFiles;
862 this->GetObjectSources(sourceFiles, c);
866 cmValue cmGeneratorTarget::GetFeature(const std::string& feature,
867 const std::string& config) const
869 if (!config.empty()) {
870 std::string featureConfig =
871 cmStrCat(feature, '_', cmSystemTools::UpperCase(config));
872 if (cmValue value = this->GetProperty(featureConfig)) {
873 return value;
876 if (cmValue value = this->GetProperty(feature)) {
877 return value;
879 return this->LocalGenerator->GetFeature(feature, config);
882 std::string cmGeneratorTarget::GetLinkerTypeProperty(
883 std::string const& lang, std::string const& config) const
885 std::string propName{ "LINKER_TYPE" };
886 auto linkerType = this->GetProperty(propName);
887 if (!linkerType.IsEmpty()) {
888 cmGeneratorExpressionDAGChecker dagChecker(this, propName, nullptr,
889 nullptr);
890 auto ltype =
891 cmGeneratorExpression::Evaluate(*linkerType, this->GetLocalGenerator(),
892 config, this, &dagChecker, this, lang);
893 if (this->IsDeviceLink()) {
894 cmList list{ ltype };
895 const auto DL_BEGIN = "<DEVICE_LINK>"_s;
896 const auto DL_END = "</DEVICE_LINK>"_s;
897 cm::erase_if(list, [&](const std::string& item) {
898 return item == DL_BEGIN || item == DL_END;
900 return list.to_string();
902 return ltype;
904 return std::string{};
907 const char* cmGeneratorTarget::GetLinkPIEProperty(
908 const std::string& config) const
910 static std::string PICValue;
912 PICValue = this->GetLinkInterfaceDependentStringAsBoolProperty(
913 "POSITION_INDEPENDENT_CODE", config);
915 if (PICValue == "(unset)") {
916 // POSITION_INDEPENDENT_CODE is not set
917 return nullptr;
920 auto status = this->GetPolicyStatusCMP0083();
921 return (status != cmPolicies::WARN && status != cmPolicies::OLD)
922 ? PICValue.c_str()
923 : nullptr;
926 bool cmGeneratorTarget::IsIPOEnabled(std::string const& lang,
927 std::string const& config) const
929 cmValue feature = this->GetFeature("INTERPROCEDURAL_OPTIMIZATION", config);
931 if (!cmIsOn(feature)) {
932 // 'INTERPROCEDURAL_OPTIMIZATION' is off, no need to check policies
933 return false;
936 if (lang != "C" && lang != "CXX" && lang != "CUDA" && lang != "Fortran") {
937 // We do not define IPO behavior for other languages.
938 return false;
941 if (lang == "CUDA") {
942 // CUDA IPO requires both CUDA_ARCHITECTURES and CUDA_SEPARABLE_COMPILATION
943 if (cmIsOff(this->GetSafeProperty("CUDA_ARCHITECTURES")) ||
944 cmIsOff(this->GetSafeProperty("CUDA_SEPARABLE_COMPILATION"))) {
945 return false;
949 cmPolicies::PolicyStatus cmp0069 = this->GetPolicyStatusCMP0069();
951 if (cmp0069 == cmPolicies::OLD || cmp0069 == cmPolicies::WARN) {
952 if (this->Makefile->IsOn("_CMAKE_" + lang + "_IPO_LEGACY_BEHAVIOR")) {
953 return true;
955 if (this->PolicyReportedCMP0069) {
956 // problem is already reported, no need to issue a message
957 return false;
959 const bool in_try_compile =
960 this->LocalGenerator->GetCMakeInstance()->GetIsInTryCompile();
961 if (cmp0069 == cmPolicies::WARN && !in_try_compile) {
962 std::ostringstream w;
963 w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0069) << "\n";
964 w << "INTERPROCEDURAL_OPTIMIZATION property will be ignored for target "
965 << "'" << this->GetName() << "'.";
966 this->LocalGenerator->GetCMakeInstance()->IssueMessage(
967 MessageType::AUTHOR_WARNING, w.str(), this->GetBacktrace());
969 this->PolicyReportedCMP0069 = true;
971 return false;
974 // Note: check consistency with messages from CheckIPOSupported
975 const char* message = nullptr;
976 if (!this->Makefile->IsOn("_CMAKE_" + lang + "_IPO_SUPPORTED_BY_CMAKE")) {
977 message = "CMake doesn't support IPO for current compiler";
978 } else if (!this->Makefile->IsOn("_CMAKE_" + lang +
979 "_IPO_MAY_BE_SUPPORTED_BY_COMPILER")) {
980 message = "Compiler doesn't support IPO";
981 } else if (!this->GlobalGenerator->IsIPOSupported()) {
982 message = "CMake doesn't support IPO for current generator";
985 if (!message) {
986 // No error/warning messages
987 return true;
990 if (this->PolicyReportedCMP0069) {
991 // problem is already reported, no need to issue a message
992 return false;
995 this->PolicyReportedCMP0069 = true;
997 this->LocalGenerator->GetCMakeInstance()->IssueMessage(
998 MessageType::FATAL_ERROR, message, this->GetBacktrace());
999 return false;
1002 const std::string& cmGeneratorTarget::GetObjectName(cmSourceFile const* file)
1004 this->ComputeObjectMapping();
1005 return this->Objects[file];
1008 const char* cmGeneratorTarget::GetCustomObjectExtension() const
1010 struct compiler_mode
1012 std::string variable;
1013 std::string extension;
1015 static std::array<compiler_mode, 4> const modes{
1016 { { "CUDA_PTX_COMPILATION", ".ptx" },
1017 { "CUDA_CUBIN_COMPILATION", ".cubin" },
1018 { "CUDA_FATBIN_COMPILATION", ".fatbin" },
1019 { "CUDA_OPTIX_COMPILATION", ".optixir" } }
1022 std::string const& compiler =
1023 this->Makefile->GetSafeDefinition("CMAKE_CUDA_COMPILER_ID");
1024 if (!compiler.empty()) {
1025 for (const auto& m : modes) {
1026 const bool has_extension = this->GetPropertyAsBool(m.variable);
1027 if (has_extension) {
1028 return m.extension.c_str();
1032 return nullptr;
1035 void cmGeneratorTarget::AddExplicitObjectName(cmSourceFile const* sf)
1037 this->ExplicitObjectName.insert(sf);
1040 bool cmGeneratorTarget::HasExplicitObjectName(cmSourceFile const* file) const
1042 const_cast<cmGeneratorTarget*>(this)->ComputeObjectMapping();
1043 auto it = this->ExplicitObjectName.find(file);
1044 return it != this->ExplicitObjectName.end();
1047 BTs<std::string> const* cmGeneratorTarget::GetLanguageStandardProperty(
1048 std::string const& lang, std::string const& config) const
1050 std::string key = cmStrCat(cmSystemTools::UpperCase(config), '-', lang);
1051 auto langStandardIter = this->LanguageStandardMap.find(key);
1052 if (langStandardIter != this->LanguageStandardMap.end()) {
1053 return &langStandardIter->second;
1056 return this->Target->GetLanguageStandardProperty(
1057 cmStrCat(lang, "_STANDARD"));
1060 cmValue cmGeneratorTarget::GetLanguageStandard(std::string const& lang,
1061 std::string const& config) const
1063 BTs<std::string> const* languageStandard =
1064 this->GetLanguageStandardProperty(lang, config);
1066 if (languageStandard) {
1067 return cmValue(languageStandard->Value);
1070 return nullptr;
1073 cmValue cmGeneratorTarget::GetPropertyWithPairedLanguageSupport(
1074 std::string const& lang, const char* suffix) const
1076 cmValue propertyValue = this->Target->GetProperty(cmStrCat(lang, suffix));
1077 if (!propertyValue) {
1078 // Check if we should use the value set by another language.
1079 if (lang == "OBJC") {
1080 propertyValue = this->GetPropertyWithPairedLanguageSupport("C", suffix);
1081 } else if (lang == "OBJCXX" || lang == "CUDA" || lang == "HIP") {
1082 propertyValue =
1083 this->GetPropertyWithPairedLanguageSupport("CXX", suffix);
1086 return propertyValue;
1089 cmValue cmGeneratorTarget::GetLanguageExtensions(std::string const& lang) const
1091 return this->GetPropertyWithPairedLanguageSupport(lang, "_EXTENSIONS");
1094 bool cmGeneratorTarget::GetLanguageStandardRequired(
1095 std::string const& lang) const
1097 return cmIsOn(
1098 this->GetPropertyWithPairedLanguageSupport(lang, "_STANDARD_REQUIRED"));
1101 void cmGeneratorTarget::GetModuleDefinitionSources(
1102 std::vector<cmSourceFile const*>& data, const std::string& config) const
1104 IMPLEMENT_VISIT(SourceKindModuleDefinition);
1107 void cmGeneratorTarget::GetHeaderSources(
1108 std::vector<cmSourceFile const*>& data, const std::string& config) const
1110 IMPLEMENT_VISIT(SourceKindHeader);
1113 void cmGeneratorTarget::GetCxxModuleSources(
1114 std::vector<cmSourceFile const*>& data, const std::string& config) const
1116 IMPLEMENT_VISIT(SourceKindCxxModuleSource);
1119 void cmGeneratorTarget::GetExtraSources(std::vector<cmSourceFile const*>& data,
1120 const std::string& config) const
1122 IMPLEMENT_VISIT(SourceKindExtra);
1125 void cmGeneratorTarget::GetCustomCommands(
1126 std::vector<cmSourceFile const*>& data, const std::string& config) const
1128 IMPLEMENT_VISIT(SourceKindCustomCommand);
1131 void cmGeneratorTarget::GetExternalObjects(
1132 std::vector<cmSourceFile const*>& data, const std::string& config) const
1134 IMPLEMENT_VISIT(SourceKindExternalObject);
1137 void cmGeneratorTarget::GetManifests(std::vector<cmSourceFile const*>& data,
1138 const std::string& config) const
1140 IMPLEMENT_VISIT(SourceKindManifest);
1143 std::set<cmLinkItem> const& cmGeneratorTarget::GetUtilityItems() const
1145 if (!this->UtilityItemsDone) {
1146 this->UtilityItemsDone = true;
1147 std::set<BT<std::pair<std::string, bool>>> const& utilities =
1148 this->GetUtilities();
1149 for (BT<std::pair<std::string, bool>> const& i : utilities) {
1150 if (cmGeneratorTarget* gt =
1151 this->LocalGenerator->FindGeneratorTargetToUse(i.Value.first)) {
1152 this->UtilityItems.insert(cmLinkItem(gt, i.Value.second, i.Backtrace));
1153 } else {
1154 this->UtilityItems.insert(
1155 cmLinkItem(i.Value.first, i.Value.second, i.Backtrace));
1159 return this->UtilityItems;
1162 const std::string& cmGeneratorTarget::GetLocation(
1163 const std::string& config) const
1165 static std::string location;
1166 if (this->IsImported()) {
1167 location = this->Target->ImportedGetFullPath(
1168 config, cmStateEnums::RuntimeBinaryArtifact);
1169 } else {
1170 location = this->GetFullPath(config, cmStateEnums::RuntimeBinaryArtifact);
1172 return location;
1175 cm::optional<std::string> cmGeneratorTarget::MaybeGetLocation(
1176 std::string const& config) const
1178 cm::optional<std::string> location;
1179 if (cmGeneratorTarget::ImportInfo const* imp = this->GetImportInfo(config)) {
1180 if (!imp->Location.empty()) {
1181 location = imp->Location;
1183 } else {
1184 location = this->GetFullPath(config, cmStateEnums::RuntimeBinaryArtifact);
1186 return location;
1189 std::vector<cmCustomCommand> const& cmGeneratorTarget::GetPreBuildCommands()
1190 const
1192 return this->Target->GetPreBuildCommands();
1195 std::vector<cmCustomCommand> const& cmGeneratorTarget::GetPreLinkCommands()
1196 const
1198 return this->Target->GetPreLinkCommands();
1201 std::vector<cmCustomCommand> const& cmGeneratorTarget::GetPostBuildCommands()
1202 const
1204 return this->Target->GetPostBuildCommands();
1207 void cmGeneratorTarget::AppendCustomCommandSideEffects(
1208 std::set<cmGeneratorTarget const*>& sideEffects) const
1210 if (!this->GetPreBuildCommands().empty() ||
1211 !this->GetPreLinkCommands().empty() ||
1212 !this->GetPostBuildCommands().empty()) {
1213 sideEffects.insert(this);
1214 } else {
1215 for (auto const& source : this->GetAllConfigSources()) {
1216 if (source.Source->GetCustomCommand() != nullptr) {
1217 sideEffects.insert(this);
1218 break;
1224 void cmGeneratorTarget::AppendLanguageSideEffects(
1225 std::map<std::string, std::set<cmGeneratorTarget const*>>& sideEffects) const
1227 static const std::set<cm::string_view> LANGS_WITH_NO_SIDE_EFFECTS = {
1228 "C"_s, "CXX"_s, "OBJC"_s, "OBJCXX"_s, "ASM"_s, "CUDA"_s, "HIP"_s
1231 for (auto const& lang : this->GetAllConfigCompileLanguages()) {
1232 if (!LANGS_WITH_NO_SIDE_EFFECTS.count(lang)) {
1233 sideEffects[lang].insert(this);
1238 bool cmGeneratorTarget::IsInBuildSystem() const
1240 if (this->IsImported()) {
1241 return false;
1243 switch (this->Target->GetType()) {
1244 case cmStateEnums::EXECUTABLE:
1245 case cmStateEnums::STATIC_LIBRARY:
1246 case cmStateEnums::SHARED_LIBRARY:
1247 case cmStateEnums::MODULE_LIBRARY:
1248 case cmStateEnums::OBJECT_LIBRARY:
1249 case cmStateEnums::UTILITY:
1250 case cmStateEnums::GLOBAL_TARGET:
1251 return true;
1252 case cmStateEnums::INTERFACE_LIBRARY:
1253 // An INTERFACE library is in the build system if it has SOURCES
1254 // or C++ module filesets.
1255 if (!this->SourceEntries.empty() ||
1256 !this->Target->GetHeaderSetsEntries().empty() ||
1257 !this->Target->GetCxxModuleSetsEntries().empty()) {
1258 return true;
1260 break;
1261 case cmStateEnums::UNKNOWN_LIBRARY:
1262 break;
1264 return false;
1267 bool cmGeneratorTarget::IsNormal() const
1269 return this->Target->IsNormal();
1272 bool cmGeneratorTarget::IsRuntimeBinary() const
1274 return this->Target->IsRuntimeBinary();
1277 bool cmGeneratorTarget::IsSynthetic() const
1279 return this->Target->IsSynthetic();
1282 bool cmGeneratorTarget::IsImported() const
1284 return this->Target->IsImported();
1287 bool cmGeneratorTarget::IsImportedGloballyVisible() const
1289 return this->Target->IsImportedGloballyVisible();
1292 bool cmGeneratorTarget::CanCompileSources() const
1294 return this->Target->CanCompileSources();
1297 bool cmGeneratorTarget::HasKnownRuntimeArtifactLocation(
1298 std::string const& config) const
1300 if (!this->IsRuntimeBinary()) {
1301 return false;
1303 if (!this->IsImported()) {
1304 return true;
1306 ImportInfo const* info = this->GetImportInfo(config);
1307 return info && !info->Location.empty();
1310 const std::string& cmGeneratorTarget::GetLocationForBuild() const
1312 static std::string location;
1313 if (this->IsImported()) {
1314 location = this->Target->ImportedGetFullPath(
1315 "", cmStateEnums::RuntimeBinaryArtifact);
1316 return location;
1319 // Now handle the deprecated build-time configuration location.
1320 std::string const noConfig;
1321 location = this->GetDirectory(noConfig);
1322 cmValue cfgid = this->Makefile->GetDefinition("CMAKE_CFG_INTDIR");
1323 if (cfgid && (*cfgid != ".")) {
1324 location += "/";
1325 location += *cfgid;
1328 if (this->IsAppBundleOnApple()) {
1329 std::string macdir = this->BuildBundleDirectory("", "", FullLevel);
1330 if (!macdir.empty()) {
1331 location += "/";
1332 location += macdir;
1335 location += "/";
1336 location += this->GetFullName("", cmStateEnums::RuntimeBinaryArtifact);
1337 return location;
1340 bool cmGeneratorTarget::IsSystemIncludeDirectory(
1341 const std::string& dir, const std::string& config,
1342 const std::string& language) const
1344 std::string config_upper;
1345 if (!config.empty()) {
1346 config_upper = cmSystemTools::UpperCase(config);
1349 std::string key = cmStrCat(config_upper, '/', language);
1350 auto iter = this->SystemIncludesCache.find(key);
1352 if (iter == this->SystemIncludesCache.end()) {
1353 cmGeneratorExpressionDAGChecker dagChecker(
1354 this, "SYSTEM_INCLUDE_DIRECTORIES", nullptr, nullptr);
1356 bool excludeImported = this->GetPropertyAsBool("NO_SYSTEM_FROM_IMPORTED");
1358 cmList result;
1359 for (std::string const& it : this->Target->GetSystemIncludeDirectories()) {
1360 result.append(cmGeneratorExpression::Evaluate(it, this->LocalGenerator,
1361 config, this, &dagChecker,
1362 nullptr, language));
1365 std::vector<cmGeneratorTarget const*> const& deps =
1366 this->GetLinkImplementationClosure(config);
1367 for (cmGeneratorTarget const* dep : deps) {
1368 handleSystemIncludesDep(this->LocalGenerator, dep, config, this,
1369 &dagChecker, result, excludeImported, language);
1372 cmLinkImplementation const* impl =
1373 this->GetLinkImplementation(config, LinkInterfaceFor::Usage);
1374 if (impl != nullptr) {
1375 auto runtimeEntries = impl->LanguageRuntimeLibraries.find(language);
1376 if (runtimeEntries != impl->LanguageRuntimeLibraries.end()) {
1377 for (auto const& lib : runtimeEntries->second) {
1378 if (lib.Target) {
1379 handleSystemIncludesDep(this->LocalGenerator, lib.Target, config,
1380 this, &dagChecker, result, excludeImported,
1381 language);
1387 std::for_each(result.begin(), result.end(),
1388 cmSystemTools::ConvertToUnixSlashes);
1389 std::sort(result.begin(), result.end());
1390 result.erase(std::unique(result.begin(), result.end()), result.end());
1392 iter = this->SystemIncludesCache.emplace(key, result).first;
1395 return std::binary_search(iter->second.begin(), iter->second.end(), dir);
1398 bool cmGeneratorTarget::GetPropertyAsBool(const std::string& prop) const
1400 return this->Target->GetPropertyAsBool(prop);
1403 bool cmGeneratorTarget::MaybeHaveInterfaceProperty(
1404 std::string const& prop, cmGeneratorExpressionContext* context,
1405 LinkInterfaceFor interfaceFor) const
1407 std::string const key = prop + '@' + context->Config;
1408 auto i = this->MaybeInterfacePropertyExists.find(key);
1409 if (i == this->MaybeInterfacePropertyExists.end()) {
1410 // Insert an entry now in case there is a cycle.
1411 i = this->MaybeInterfacePropertyExists.emplace(key, false).first;
1412 bool& maybeInterfaceProp = i->second;
1414 // If this target itself has a non-empty property value, we are done.
1415 maybeInterfaceProp = cmNonempty(this->GetProperty(prop));
1417 // Otherwise, recurse to interface dependencies.
1418 if (!maybeInterfaceProp) {
1419 cmGeneratorTarget const* headTarget =
1420 context->HeadTarget ? context->HeadTarget : this;
1421 if (cmLinkInterfaceLibraries const* iface =
1422 this->GetLinkInterfaceLibraries(context->Config, headTarget,
1423 interfaceFor)) {
1424 if (iface->HadHeadSensitiveCondition) {
1425 // With a different head target we may get to a library with
1426 // this interface property.
1427 maybeInterfaceProp = true;
1428 } else {
1429 // The transitive interface libraries do not depend on the
1430 // head target, so we can follow them.
1431 for (cmLinkItem const& lib : iface->Libraries) {
1432 if (lib.Target &&
1433 lib.Target->MaybeHaveInterfaceProperty(prop, context,
1434 interfaceFor)) {
1435 maybeInterfaceProp = true;
1436 break;
1443 return i->second;
1446 std::string cmGeneratorTarget::EvaluateInterfaceProperty(
1447 std::string const& prop, cmGeneratorExpressionContext* context,
1448 cmGeneratorExpressionDAGChecker* dagCheckerParent,
1449 LinkInterfaceFor interfaceFor) const
1451 std::string result;
1453 // If the property does not appear transitively at all, we are done.
1454 if (!this->MaybeHaveInterfaceProperty(prop, context, interfaceFor)) {
1455 return result;
1458 // Evaluate $<TARGET_PROPERTY:this,prop> as if it were compiled. This is
1459 // a subset of TargetPropertyNode::Evaluate without stringify/parse steps
1460 // but sufficient for transitive interface properties.
1461 cmGeneratorExpressionDAGChecker dagChecker(context->Backtrace, this, prop,
1462 nullptr, dagCheckerParent);
1463 switch (dagChecker.Check()) {
1464 case cmGeneratorExpressionDAGChecker::SELF_REFERENCE:
1465 dagChecker.ReportError(
1466 context, "$<TARGET_PROPERTY:" + this->GetName() + "," + prop + ">");
1467 return result;
1468 case cmGeneratorExpressionDAGChecker::CYCLIC_REFERENCE:
1469 // No error. We just skip cyclic references.
1470 case cmGeneratorExpressionDAGChecker::ALREADY_SEEN:
1471 // No error. We have already seen this transitive property.
1472 return result;
1473 case cmGeneratorExpressionDAGChecker::DAG:
1474 break;
1477 cmGeneratorTarget const* headTarget =
1478 context->HeadTarget ? context->HeadTarget : this;
1480 if (cmValue p = this->GetProperty(prop)) {
1481 result = cmGeneratorExpressionNode::EvaluateDependentExpression(
1482 *p, context->LG, context, headTarget, &dagChecker, this);
1485 if (cmLinkInterfaceLibraries const* iface = this->GetLinkInterfaceLibraries(
1486 context->Config, headTarget, interfaceFor)) {
1487 context->HadContextSensitiveCondition =
1488 context->HadContextSensitiveCondition ||
1489 iface->HadContextSensitiveCondition;
1490 for (cmLinkItem const& lib : iface->Libraries) {
1491 // Broken code can have a target in its own link interface.
1492 // Don't follow such link interface entries so as not to create a
1493 // self-referencing loop.
1494 if (lib.Target && lib.Target != this) {
1495 // Pretend $<TARGET_PROPERTY:lib.Target,prop> appeared in the
1496 // above property and hand-evaluate it as if it were compiled.
1497 // Create a context as cmCompiledGeneratorExpression::Evaluate does.
1498 cmGeneratorExpressionContext libContext(
1499 context->LG, context->Config, context->Quiet, headTarget, this,
1500 context->EvaluateForBuildsystem, context->Backtrace,
1501 context->Language);
1502 std::string libResult = cmGeneratorExpression::StripEmptyListElements(
1503 lib.Target->EvaluateInterfaceProperty(prop, &libContext, &dagChecker,
1504 interfaceFor));
1505 if (!libResult.empty()) {
1506 if (result.empty()) {
1507 result = std::move(libResult);
1508 } else {
1509 result.reserve(result.size() + 1 + libResult.size());
1510 result += ";";
1511 result += libResult;
1514 context->HadContextSensitiveCondition =
1515 context->HadContextSensitiveCondition ||
1516 libContext.HadContextSensitiveCondition;
1517 context->HadHeadSensitiveCondition =
1518 context->HadHeadSensitiveCondition ||
1519 libContext.HadHeadSensitiveCondition;
1524 return result;
1527 namespace {
1529 enum class IncludeDirectoryFallBack
1531 BINARY,
1532 OBJECT
1535 std::string AddLangSpecificInterfaceIncludeDirectories(
1536 const cmGeneratorTarget* root, const cmGeneratorTarget* target,
1537 const std::string& lang, const std::string& config,
1538 const std::string& propertyName, IncludeDirectoryFallBack mode,
1539 cmGeneratorExpressionDAGChecker* context)
1541 cmGeneratorExpressionDAGChecker dag{ target->GetBacktrace(), target,
1542 propertyName, nullptr, context };
1543 switch (dag.Check()) {
1544 case cmGeneratorExpressionDAGChecker::SELF_REFERENCE:
1545 dag.ReportError(
1546 nullptr, "$<TARGET_PROPERTY:" + target->GetName() + ",propertyName");
1547 CM_FALLTHROUGH;
1548 case cmGeneratorExpressionDAGChecker::CYCLIC_REFERENCE:
1549 // No error. We just skip cyclic references.
1550 case cmGeneratorExpressionDAGChecker::ALREADY_SEEN:
1551 // No error. We have already seen this transitive property.
1552 return "";
1553 case cmGeneratorExpressionDAGChecker::DAG:
1554 break;
1557 std::string directories;
1558 if (const auto* link_interface = target->GetLinkInterfaceLibraries(
1559 config, root, LinkInterfaceFor::Usage)) {
1560 for (const cmLinkItem& library : link_interface->Libraries) {
1561 if (const cmGeneratorTarget* dependency = library.Target) {
1562 if (cm::contains(dependency->GetAllConfigCompileLanguages(), lang)) {
1563 auto* lg = dependency->GetLocalGenerator();
1564 std::string value = dependency->GetSafeProperty(propertyName);
1565 if (value.empty()) {
1566 if (mode == IncludeDirectoryFallBack::BINARY) {
1567 value = lg->GetCurrentBinaryDirectory();
1568 } else if (mode == IncludeDirectoryFallBack::OBJECT) {
1569 value = cmStrCat(lg->GetCurrentBinaryDirectory(), '/',
1570 lg->GetTargetDirectory(dependency));
1574 if (!directories.empty()) {
1575 directories += ";";
1577 directories += value;
1582 return directories;
1585 void AddLangSpecificImplicitIncludeDirectories(
1586 const cmGeneratorTarget* target, const std::string& lang,
1587 const std::string& config, const std::string& propertyName,
1588 IncludeDirectoryFallBack mode, EvaluatedTargetPropertyEntries& entries)
1590 if (const auto* libraries = target->GetLinkImplementationLibraries(
1591 config, LinkInterfaceFor::Usage)) {
1592 cmGeneratorExpressionDAGChecker dag{ target->GetBacktrace(), target,
1593 propertyName, nullptr, nullptr };
1595 for (const cmLinkImplItem& library : libraries->Libraries) {
1596 if (const cmGeneratorTarget* dependency = library.Target) {
1597 if (!dependency->IsInBuildSystem()) {
1598 continue;
1600 if (cm::contains(dependency->GetAllConfigCompileLanguages(), lang)) {
1601 auto* lg = dependency->GetLocalGenerator();
1602 EvaluatedTargetPropertyEntry entry{ library, library.Backtrace };
1604 if (cmValue val = dependency->GetProperty(propertyName)) {
1605 entry.Values.emplace_back(*val);
1606 } else {
1607 if (mode == IncludeDirectoryFallBack::BINARY) {
1608 entry.Values.emplace_back(lg->GetCurrentBinaryDirectory());
1609 } else if (mode == IncludeDirectoryFallBack::OBJECT) {
1610 entry.Values.emplace_back(
1611 dependency->GetObjectDirectory(config));
1615 cmExpandList(
1616 AddLangSpecificInterfaceIncludeDirectories(
1617 target, dependency, lang, config, propertyName, mode, &dag),
1618 entry.Values);
1619 entries.Entries.emplace_back(std::move(entry));
1626 void AddObjectEntries(cmGeneratorTarget const* headTarget,
1627 std::string const& config,
1628 cmGeneratorExpressionDAGChecker* dagChecker,
1629 EvaluatedTargetPropertyEntries& entries)
1631 if (cmLinkImplementationLibraries const* impl =
1632 headTarget->GetLinkImplementationLibraries(config,
1633 LinkInterfaceFor::Usage)) {
1634 entries.HadContextSensitiveCondition = impl->HadContextSensitiveCondition;
1635 for (cmLinkImplItem const& lib : impl->Libraries) {
1636 if (lib.Target &&
1637 lib.Target->GetType() == cmStateEnums::OBJECT_LIBRARY) {
1638 std::string uniqueName =
1639 headTarget->GetGlobalGenerator()->IndexGeneratorTargetUniquely(
1640 lib.Target);
1641 std::string genex = "$<TARGET_OBJECTS:" + std::move(uniqueName) + ">";
1642 cmGeneratorExpression ge(*headTarget->Makefile->GetCMakeInstance(),
1643 lib.Backtrace);
1644 std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(genex);
1645 cge->SetEvaluateForBuildsystem(true);
1647 EvaluatedTargetPropertyEntry ee(lib, lib.Backtrace);
1648 cmExpandList(cge->Evaluate(headTarget->GetLocalGenerator(), config,
1649 headTarget, dagChecker),
1650 ee.Values);
1651 if (cge->GetHadContextSensitiveCondition()) {
1652 ee.ContextDependent = true;
1654 entries.Entries.emplace_back(std::move(ee));
1660 void addFileSetEntry(cmGeneratorTarget const* headTarget,
1661 std::string const& config,
1662 cmGeneratorExpressionDAGChecker* dagChecker,
1663 cmFileSet const* fileSet,
1664 EvaluatedTargetPropertyEntries& entries)
1666 auto dirCges = fileSet->CompileDirectoryEntries();
1667 auto dirs = fileSet->EvaluateDirectoryEntries(
1668 dirCges, headTarget->GetLocalGenerator(), config, headTarget, dagChecker);
1669 bool contextSensitiveDirs = false;
1670 for (auto const& dirCge : dirCges) {
1671 if (dirCge->GetHadContextSensitiveCondition()) {
1672 contextSensitiveDirs = true;
1673 break;
1676 cmake* cm = headTarget->GetLocalGenerator()->GetCMakeInstance();
1677 for (auto& entryCge : fileSet->CompileFileEntries()) {
1678 TargetPropertyEntryFileSet tpe(dirs, contextSensitiveDirs,
1679 std::move(entryCge), fileSet);
1680 entries.Entries.emplace_back(
1681 EvaluateTargetPropertyEntry(headTarget, config, "", dagChecker, tpe));
1682 EvaluatedTargetPropertyEntry const& entry = entries.Entries.back();
1683 for (auto const& file : entry.Values) {
1684 auto* sf = headTarget->Makefile->GetOrCreateSource(file);
1685 if (fileSet->GetType() == "HEADERS"_s) {
1686 sf->SetProperty("HEADER_FILE_ONLY", "TRUE");
1689 #ifndef CMAKE_BOOTSTRAP
1690 std::string e;
1691 std::string w;
1692 auto path = sf->ResolveFullPath(&e, &w);
1693 if (!w.empty()) {
1694 cm->IssueMessage(MessageType::AUTHOR_WARNING, w, entry.Backtrace);
1696 if (path.empty()) {
1697 if (!e.empty()) {
1698 cm->IssueMessage(MessageType::FATAL_ERROR, e, entry.Backtrace);
1700 return;
1702 bool found = false;
1703 for (auto const& sg : headTarget->Makefile->GetSourceGroups()) {
1704 if (sg.MatchChildrenFiles(path)) {
1705 found = true;
1706 break;
1709 if (!found) {
1710 if (fileSet->GetType() == "HEADERS"_s) {
1711 headTarget->Makefile->GetOrCreateSourceGroup("Header Files")
1712 ->AddGroupFile(path);
1715 #endif
1720 void AddFileSetEntries(cmGeneratorTarget const* headTarget,
1721 std::string const& config,
1722 cmGeneratorExpressionDAGChecker* dagChecker,
1723 EvaluatedTargetPropertyEntries& entries)
1725 for (auto const& entry : headTarget->Target->GetHeaderSetsEntries()) {
1726 for (auto const& name : cmList{ entry.Value }) {
1727 auto const* headerSet = headTarget->Target->GetFileSet(name);
1728 addFileSetEntry(headTarget, config, dagChecker, headerSet, entries);
1731 for (auto const& entry : headTarget->Target->GetCxxModuleSetsEntries()) {
1732 for (auto const& name : cmList{ entry.Value }) {
1733 auto const* cxxModuleSet = headTarget->Target->GetFileSet(name);
1734 addFileSetEntry(headTarget, config, dagChecker, cxxModuleSet, entries);
1739 bool processSources(cmGeneratorTarget const* tgt,
1740 EvaluatedTargetPropertyEntries& entries,
1741 std::vector<BT<std::string>>& srcs,
1742 std::unordered_set<std::string>& uniqueSrcs,
1743 bool debugSources)
1745 cmMakefile* mf = tgt->Target->GetMakefile();
1747 bool contextDependent = entries.HadContextSensitiveCondition;
1749 for (EvaluatedTargetPropertyEntry& entry : entries.Entries) {
1750 if (entry.ContextDependent) {
1751 contextDependent = true;
1754 cmLinkImplItem const& item = entry.LinkImplItem;
1755 std::string const& targetName = item.AsStr();
1757 for (std::string& src : entry.Values) {
1758 cmSourceFile* sf = mf->GetOrCreateSource(src);
1759 std::string e;
1760 std::string w;
1761 std::string fullPath = sf->ResolveFullPath(&e, &w);
1762 cmake* cm = tgt->GetLocalGenerator()->GetCMakeInstance();
1763 if (!w.empty()) {
1764 cm->IssueMessage(MessageType::AUTHOR_WARNING, w, entry.Backtrace);
1766 if (fullPath.empty()) {
1767 if (!e.empty()) {
1768 cm->IssueMessage(MessageType::FATAL_ERROR, e, entry.Backtrace);
1770 return contextDependent;
1773 if (!targetName.empty() && !cmSystemTools::FileIsFullPath(src)) {
1774 std::ostringstream err;
1775 if (!targetName.empty()) {
1776 err << "Target \"" << targetName
1777 << "\" contains relative path in its INTERFACE_SOURCES:\n \""
1778 << src << "\"";
1779 } else {
1780 err << "Found relative path while evaluating sources of \""
1781 << tgt->GetName() << "\":\n \"" << src << "\"\n";
1783 tgt->GetLocalGenerator()->IssueMessage(MessageType::FATAL_ERROR,
1784 err.str());
1785 return contextDependent;
1787 src = fullPath;
1789 std::string usedSources;
1790 for (std::string const& src : entry.Values) {
1791 if (uniqueSrcs.insert(src).second) {
1792 srcs.emplace_back(src, entry.Backtrace);
1793 if (debugSources) {
1794 usedSources += " * " + src + "\n";
1798 if (!usedSources.empty()) {
1799 tgt->GetLocalGenerator()->GetCMakeInstance()->IssueMessage(
1800 MessageType::LOG,
1801 std::string("Used sources for target ") + tgt->GetName() + ":\n" +
1802 usedSources,
1803 entry.Backtrace);
1806 return contextDependent;
1810 std::vector<BT<std::string>> cmGeneratorTarget::GetSourceFilePaths(
1811 std::string const& config) const
1813 std::vector<BT<std::string>> files;
1815 if (!this->LocalGenerator->GetGlobalGenerator()->GetConfigureDoneCMP0026()) {
1816 // At configure-time, this method can be called as part of getting the
1817 // LOCATION property or to export() a file to be include()d. However
1818 // there is no cmGeneratorTarget at configure-time, so search the SOURCES
1819 // for TARGET_OBJECTS instead for backwards compatibility with OLD
1820 // behavior of CMP0024 and CMP0026 only.
1822 cmBTStringRange sourceEntries = this->Target->GetSourceEntries();
1823 for (auto const& entry : sourceEntries) {
1824 cmList items{ entry.Value };
1825 for (auto const& item : items) {
1826 if (cmHasLiteralPrefix(item, "$<TARGET_OBJECTS:") &&
1827 item.back() == '>') {
1828 continue;
1830 files.emplace_back(item);
1833 return files;
1836 cmList debugProperties{ this->Makefile->GetDefinition(
1837 "CMAKE_DEBUG_TARGET_PROPERTIES") };
1838 bool debugSources =
1839 !this->DebugSourcesDone && cm::contains(debugProperties, "SOURCES");
1841 if (this->LocalGenerator->GetGlobalGenerator()->GetConfigureDoneCMP0026()) {
1842 this->DebugSourcesDone = true;
1845 cmGeneratorExpressionDAGChecker dagChecker(this, "SOURCES", nullptr,
1846 nullptr);
1848 EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries(
1849 this, config, std::string(), &dagChecker, this->SourceEntries);
1851 std::unordered_set<std::string> uniqueSrcs;
1852 bool contextDependentDirectSources =
1853 processSources(this, entries, files, uniqueSrcs, debugSources);
1855 // Collect INTERFACE_SOURCES of all direct link-dependencies.
1856 EvaluatedTargetPropertyEntries linkInterfaceSourcesEntries;
1857 AddInterfaceEntries(this, config, "INTERFACE_SOURCES", std::string(),
1858 &dagChecker, linkInterfaceSourcesEntries,
1859 IncludeRuntimeInterface::No, LinkInterfaceFor::Usage);
1860 bool contextDependentInterfaceSources = processSources(
1861 this, linkInterfaceSourcesEntries, files, uniqueSrcs, debugSources);
1863 // Collect TARGET_OBJECTS of direct object link-dependencies.
1864 bool contextDependentObjects = false;
1865 if (this->GetType() != cmStateEnums::OBJECT_LIBRARY) {
1866 EvaluatedTargetPropertyEntries linkObjectsEntries;
1867 AddObjectEntries(this, config, &dagChecker, linkObjectsEntries);
1868 contextDependentObjects = processSources(this, linkObjectsEntries, files,
1869 uniqueSrcs, debugSources);
1870 // Note that for imported targets or multi-config generators supporting
1871 // cross-config builds the paths to the object files must be per-config,
1872 // so contextDependentObjects will be true here even if object libraries
1873 // are specified without per-config generator expressions.
1876 // Collect this target's file sets.
1877 EvaluatedTargetPropertyEntries fileSetEntries;
1878 AddFileSetEntries(this, config, &dagChecker, fileSetEntries);
1879 bool contextDependentFileSets =
1880 processSources(this, fileSetEntries, files, uniqueSrcs, debugSources);
1882 // Determine if sources are context-dependent or not.
1883 if (!contextDependentDirectSources && !contextDependentInterfaceSources &&
1884 !contextDependentObjects && !contextDependentFileSets) {
1885 this->SourcesAreContextDependent = Tribool::False;
1886 } else {
1887 this->SourcesAreContextDependent = Tribool::True;
1890 return files;
1893 void cmGeneratorTarget::GetSourceFiles(std::vector<cmSourceFile*>& files,
1894 const std::string& config) const
1896 std::vector<BT<cmSourceFile*>> tmp = this->GetSourceFiles(config);
1897 files.reserve(tmp.size());
1898 for (BT<cmSourceFile*>& v : tmp) {
1899 files.push_back(v.Value);
1903 std::vector<BT<cmSourceFile*>> cmGeneratorTarget::GetSourceFiles(
1904 std::string const& config) const
1906 std::vector<BT<cmSourceFile*>> files;
1907 if (!this->GlobalGenerator->GetConfigureDoneCMP0026()) {
1908 // Since we are still configuring not all sources may exist yet,
1909 // so we need to avoid full source classification because that
1910 // requires the absolute paths to all sources to be determined.
1911 // Since this is only for compatibility with old policies that
1912 // projects should not depend on anymore, just compute the files
1913 // without memoizing them.
1914 std::vector<BT<std::string>> srcs = this->GetSourceFilePaths(config);
1915 std::set<cmSourceFile*> emitted;
1916 for (BT<std::string> const& s : srcs) {
1917 cmSourceFile* sf = this->Makefile->GetOrCreateSource(s.Value);
1918 if (emitted.insert(sf).second) {
1919 files.emplace_back(sf, s.Backtrace);
1922 return files;
1925 KindedSources const& kinded = this->GetKindedSources(config);
1926 files.reserve(kinded.Sources.size());
1927 for (SourceAndKind const& si : kinded.Sources) {
1928 files.push_back(si.Source);
1930 return files;
1933 void cmGeneratorTarget::GetSourceFilesWithoutObjectLibraries(
1934 std::vector<cmSourceFile*>& files, const std::string& config) const
1936 std::vector<BT<cmSourceFile*>> tmp =
1937 this->GetSourceFilesWithoutObjectLibraries(config);
1938 files.reserve(tmp.size());
1939 for (BT<cmSourceFile*>& v : tmp) {
1940 files.push_back(v.Value);
1944 std::vector<BT<cmSourceFile*>>
1945 cmGeneratorTarget::GetSourceFilesWithoutObjectLibraries(
1946 std::string const& config) const
1948 std::vector<BT<cmSourceFile*>> files;
1949 KindedSources const& kinded = this->GetKindedSources(config);
1950 files.reserve(kinded.Sources.size());
1951 for (SourceAndKind const& si : kinded.Sources) {
1952 if (si.Source.Value->GetObjectLibrary().empty()) {
1953 files.push_back(si.Source);
1956 return files;
1959 cmGeneratorTarget::KindedSources const& cmGeneratorTarget::GetKindedSources(
1960 std::string const& config) const
1962 // If we already processed one configuration and found no dependency
1963 // on configuration then always use the one result.
1964 if (this->SourcesAreContextDependent == Tribool::False) {
1965 return this->KindedSourcesMap.begin()->second;
1968 // Lookup any existing link implementation for this configuration.
1969 std::string const key = cmSystemTools::UpperCase(config);
1970 auto it = this->KindedSourcesMap.find(key);
1971 if (it != this->KindedSourcesMap.end()) {
1972 if (!it->second.Initialized) {
1973 std::ostringstream e;
1974 e << "The SOURCES of \"" << this->GetName()
1975 << "\" use a generator expression that depends on the "
1976 "SOURCES themselves.";
1977 this->GlobalGenerator->GetCMakeInstance()->IssueMessage(
1978 MessageType::FATAL_ERROR, e.str(), this->GetBacktrace());
1979 static KindedSources empty;
1980 return empty;
1982 return it->second;
1985 // Add an entry to the map for this configuration.
1986 KindedSources& files = this->KindedSourcesMap[key];
1987 this->ComputeKindedSources(files, config);
1988 files.Initialized = true;
1989 return files;
1992 void cmGeneratorTarget::ComputeKindedSources(KindedSources& files,
1993 std::string const& config) const
1995 // Get the source file paths by string.
1996 std::vector<BT<std::string>> srcs = this->GetSourceFilePaths(config);
1998 cmsys::RegularExpression header_regex(CM_HEADER_REGEX);
1999 std::vector<cmSourceFile*> badObjLib;
2001 std::set<cmSourceFile*> emitted;
2002 for (BT<std::string> const& s : srcs) {
2003 // Create each source at most once.
2004 cmSourceFile* sf = this->Makefile->GetOrCreateSource(s.Value);
2005 if (!emitted.insert(sf).second) {
2006 continue;
2009 // Compute the kind (classification) of this source file.
2010 SourceKind kind;
2011 std::string ext = cmSystemTools::LowerCase(sf->GetExtension());
2012 cmFileSet const* fs = this->GetFileSetForSource(config, sf);
2013 if (sf->GetCustomCommand()) {
2014 kind = SourceKindCustomCommand;
2015 } else if (!this->Target->IsNormal() && !this->Target->IsImported() &&
2016 fs && (fs->GetType() == "CXX_MODULES"_s)) {
2017 kind = SourceKindCxxModuleSource;
2018 } else if (this->Target->GetType() == cmStateEnums::UTILITY ||
2019 this->Target->GetType() == cmStateEnums::INTERFACE_LIBRARY
2020 // XXX(clang-tidy): https://bugs.llvm.org/show_bug.cgi?id=44165
2021 // NOLINTNEXTLINE(bugprone-branch-clone)
2023 kind = SourceKindExtra;
2024 } else if (this->IsSourceFilePartOfUnityBatch(sf->ResolveFullPath())) {
2025 kind = SourceKindUnityBatched;
2026 // XXX(clang-tidy): https://bugs.llvm.org/show_bug.cgi?id=44165
2027 // NOLINTNEXTLINE(bugprone-branch-clone)
2028 } else if (sf->GetPropertyAsBool("HEADER_FILE_ONLY")) {
2029 kind = SourceKindHeader;
2030 } else if (sf->GetPropertyAsBool("EXTERNAL_OBJECT")) {
2031 kind = SourceKindExternalObject;
2032 } else if (!sf->GetOrDetermineLanguage().empty()) {
2033 kind = SourceKindObjectSource;
2034 } else if (ext == "def") {
2035 kind = SourceKindModuleDefinition;
2036 if (this->GetType() == cmStateEnums::OBJECT_LIBRARY) {
2037 badObjLib.push_back(sf);
2039 } else if (ext == "idl") {
2040 kind = SourceKindIDL;
2041 if (this->GetType() == cmStateEnums::OBJECT_LIBRARY) {
2042 badObjLib.push_back(sf);
2044 } else if (ext == "resx") {
2045 kind = SourceKindResx;
2046 } else if (ext == "appxmanifest") {
2047 kind = SourceKindAppManifest;
2048 } else if (ext == "manifest") {
2049 if (sf->GetPropertyAsBool("VS_DEPLOYMENT_CONTENT")) {
2050 kind = SourceKindExtra;
2051 } else {
2052 kind = SourceKindManifest;
2054 } else if (ext == "pfx") {
2055 kind = SourceKindCertificate;
2056 } else if (ext == "xaml") {
2057 kind = SourceKindXaml;
2058 } else if (header_regex.find(sf->ResolveFullPath())) {
2059 kind = SourceKindHeader;
2060 } else {
2061 kind = SourceKindExtra;
2064 // Save this classified source file in the result vector.
2065 files.Sources.push_back({ BT<cmSourceFile*>(sf, s.Backtrace), kind });
2068 if (!badObjLib.empty()) {
2069 std::ostringstream e;
2070 e << "OBJECT library \"" << this->GetName() << "\" contains:\n";
2071 for (cmSourceFile* i : badObjLib) {
2072 e << " " << i->GetLocation().GetName() << "\n";
2074 e << "but may contain only sources that compile, header files, and "
2075 "other files that would not affect linking of a normal library.";
2076 this->GlobalGenerator->GetCMakeInstance()->IssueMessage(
2077 MessageType::FATAL_ERROR, e.str(), this->GetBacktrace());
2081 std::vector<cmGeneratorTarget::AllConfigSource> const&
2082 cmGeneratorTarget::GetAllConfigSources() const
2084 if (this->AllConfigSources.empty()) {
2085 this->ComputeAllConfigSources();
2087 return this->AllConfigSources;
2090 void cmGeneratorTarget::ComputeAllConfigSources() const
2092 std::vector<std::string> configs =
2093 this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
2095 std::map<cmSourceFile const*, size_t> index;
2097 for (size_t ci = 0; ci < configs.size(); ++ci) {
2098 KindedSources const& sources = this->GetKindedSources(configs[ci]);
2099 for (SourceAndKind const& src : sources.Sources) {
2100 auto mi = index.find(src.Source.Value);
2101 if (mi == index.end()) {
2102 AllConfigSource acs;
2103 acs.Source = src.Source.Value;
2104 acs.Kind = src.Kind;
2105 this->AllConfigSources.push_back(std::move(acs));
2106 std::map<cmSourceFile const*, size_t>::value_type entry(
2107 src.Source.Value, this->AllConfigSources.size() - 1);
2108 mi = index.insert(entry).first;
2110 this->AllConfigSources[mi->second].Configs.push_back(ci);
2115 std::vector<cmGeneratorTarget::AllConfigSource>
2116 cmGeneratorTarget::GetAllConfigSources(SourceKind kind) const
2118 std::vector<AllConfigSource> result;
2119 for (AllConfigSource const& source : this->GetAllConfigSources()) {
2120 if (source.Kind == kind) {
2121 result.push_back(source);
2124 return result;
2127 std::set<std::string> cmGeneratorTarget::GetAllConfigCompileLanguages() const
2129 std::set<std::string> languages;
2130 std::vector<AllConfigSource> const& sources = this->GetAllConfigSources();
2131 for (AllConfigSource const& si : sources) {
2132 std::string const& lang = si.Source->GetOrDetermineLanguage();
2133 if (!lang.empty()) {
2134 languages.emplace(lang);
2137 return languages;
2140 std::string cmGeneratorTarget::GetCompilePDBName(
2141 const std::string& config) const
2143 // Check for a per-configuration output directory target property.
2144 std::string configUpper = cmSystemTools::UpperCase(config);
2145 std::string configProp = cmStrCat("COMPILE_PDB_NAME_", configUpper);
2146 cmValue config_name = this->GetProperty(configProp);
2147 if (cmNonempty(config_name)) {
2148 NameComponents const& components = GetFullNameInternalComponents(
2149 config, cmStateEnums::RuntimeBinaryArtifact);
2150 return components.prefix + *config_name + ".pdb";
2153 cmValue name = this->GetProperty("COMPILE_PDB_NAME");
2154 if (cmNonempty(name)) {
2155 NameComponents const& components = GetFullNameInternalComponents(
2156 config, cmStateEnums::RuntimeBinaryArtifact);
2157 return components.prefix + *name + ".pdb";
2160 return "";
2163 std::string cmGeneratorTarget::GetCompilePDBPath(
2164 const std::string& config) const
2166 std::string dir = this->GetCompilePDBDirectory(config);
2167 std::string name = this->GetCompilePDBName(config);
2168 if (dir.empty() && !name.empty() && this->HaveWellDefinedOutputFiles()) {
2169 dir = this->GetPDBDirectory(config);
2171 if (!dir.empty()) {
2172 dir += "/";
2174 return dir + name;
2177 bool cmGeneratorTarget::HasSOName(const std::string& config) const
2179 // soname is supported only for shared libraries and modules,
2180 // and then only when the platform supports an soname flag.
2181 return ((this->GetType() == cmStateEnums::SHARED_LIBRARY) &&
2182 !this->GetPropertyAsBool("NO_SONAME") &&
2183 this->Makefile->GetSONameFlag(this->GetLinkerLanguage(config)));
2186 bool cmGeneratorTarget::NeedRelinkBeforeInstall(
2187 const std::string& config) const
2189 // Only executables and shared libraries can have an rpath and may
2190 // need relinking.
2191 if (this->GetType() != cmStateEnums::EXECUTABLE &&
2192 this->GetType() != cmStateEnums::SHARED_LIBRARY &&
2193 this->GetType() != cmStateEnums::MODULE_LIBRARY) {
2194 return false;
2197 // If there is no install location this target will not be installed
2198 // and therefore does not need relinking.
2199 if (!this->Target->GetHaveInstallRule()) {
2200 return false;
2203 // If skipping all rpaths completely then no relinking is needed.
2204 if (this->Makefile->IsOn("CMAKE_SKIP_RPATH")) {
2205 return false;
2208 // If building with the install-tree rpath no relinking is needed.
2209 if (this->GetPropertyAsBool("BUILD_WITH_INSTALL_RPATH")) {
2210 return false;
2213 // If chrpath is going to be used no relinking is needed.
2214 if (this->IsChrpathUsed(config)) {
2215 return false;
2218 // Check for rpath support on this platform.
2219 std::string ll = this->GetLinkerLanguage(config);
2220 if (!ll.empty()) {
2221 std::string flagVar =
2222 cmStrCat("CMAKE_SHARED_LIBRARY_RUNTIME_", ll, "_FLAG");
2223 if (!this->Makefile->IsSet(flagVar)) {
2224 // There is no rpath support on this platform so nothing needs
2225 // relinking.
2226 return false;
2228 } else {
2229 // No linker language is known. This error will be reported by
2230 // other code.
2231 return false;
2234 // If either a build or install tree rpath is set then the rpath
2235 // will likely change between the build tree and install tree and
2236 // this target must be relinked.
2237 bool have_rpath =
2238 this->HaveBuildTreeRPATH(config) || this->HaveInstallTreeRPATH(config);
2239 bool is_ninja = this->LocalGenerator->GetGlobalGenerator()->IsNinja();
2241 if (have_rpath && is_ninja) {
2242 std::ostringstream w;
2243 /* clang-format off */
2244 w <<
2245 "The install of the " << this->GetName() << " target requires changing "
2246 "an RPATH from the build tree, but this is not supported with the Ninja "
2247 "generator unless on an ELF-based or XCOFF-based platform. "
2248 "The CMAKE_BUILD_WITH_INSTALL_RPATH variable may be set to avoid this "
2249 "relinking step."
2251 /* clang-format on */
2253 cmake* cm = this->LocalGenerator->GetCMakeInstance();
2254 cm->IssueMessage(MessageType::FATAL_ERROR, w.str(), this->GetBacktrace());
2257 return have_rpath;
2260 bool cmGeneratorTarget::IsChrpathUsed(const std::string& config) const
2262 // Only certain target types have an rpath.
2263 if (!(this->GetType() == cmStateEnums::SHARED_LIBRARY ||
2264 this->GetType() == cmStateEnums::MODULE_LIBRARY ||
2265 this->GetType() == cmStateEnums::EXECUTABLE)) {
2266 return false;
2269 // If the target will not be installed we do not need to change its
2270 // rpath.
2271 if (!this->Target->GetHaveInstallRule()) {
2272 return false;
2275 // Skip chrpath if skipping rpath altogether.
2276 if (this->Makefile->IsOn("CMAKE_SKIP_RPATH")) {
2277 return false;
2280 // Skip chrpath if it does not need to be changed at install time.
2281 if (this->GetPropertyAsBool("BUILD_WITH_INSTALL_RPATH")) {
2282 return false;
2285 // Allow the user to disable builtin chrpath explicitly.
2286 if (this->Makefile->IsOn("CMAKE_NO_BUILTIN_CHRPATH")) {
2287 return false;
2290 if (this->Makefile->IsOn("CMAKE_PLATFORM_HAS_INSTALLNAME")) {
2291 return true;
2294 // Enable if the rpath flag uses a separator and the target uses
2295 // binaries we know how to edit.
2296 std::string ll = this->GetLinkerLanguage(config);
2297 if (!ll.empty()) {
2298 std::string sepVar =
2299 cmStrCat("CMAKE_SHARED_LIBRARY_RUNTIME_", ll, "_FLAG_SEP");
2300 cmValue sep = this->Makefile->GetDefinition(sepVar);
2301 if (cmNonempty(sep)) {
2302 // TODO: Add binary format check to ABI detection and get rid of
2303 // CMAKE_EXECUTABLE_FORMAT.
2304 if (cmValue fmt =
2305 this->Makefile->GetDefinition("CMAKE_EXECUTABLE_FORMAT")) {
2306 if (*fmt == "ELF") {
2307 return true;
2309 #if defined(CMake_USE_XCOFF_PARSER)
2310 if (*fmt == "XCOFF") {
2311 return true;
2313 #endif
2317 return false;
2320 bool cmGeneratorTarget::IsImportedSharedLibWithoutSOName(
2321 const std::string& config) const
2323 if (this->IsImported() && this->GetType() == cmStateEnums::SHARED_LIBRARY) {
2324 if (cmGeneratorTarget::ImportInfo const* info =
2325 this->GetImportInfo(config)) {
2326 return info->NoSOName;
2329 return false;
2332 bool cmGeneratorTarget::HasMacOSXRpathInstallNameDir(
2333 const std::string& config) const
2335 TargetPtrToBoolMap& cache = this->MacOSXRpathInstallNameDirCache[config];
2336 const auto lookup = cache.find(this->Target);
2338 if (lookup != cache.cend()) {
2339 return lookup->second;
2342 const bool result = this->DetermineHasMacOSXRpathInstallNameDir(config);
2343 cache[this->Target] = result;
2344 return result;
2347 bool cmGeneratorTarget::DetermineHasMacOSXRpathInstallNameDir(
2348 const std::string& config) const
2350 bool install_name_is_rpath = false;
2351 bool macosx_rpath = false;
2353 if (!this->IsImported()) {
2354 if (this->GetType() != cmStateEnums::SHARED_LIBRARY) {
2355 return false;
2357 cmValue install_name = this->GetProperty("INSTALL_NAME_DIR");
2358 bool use_install_name = this->MacOSXUseInstallNameDir();
2359 if (install_name && use_install_name && *install_name == "@rpath") {
2360 install_name_is_rpath = true;
2361 } else if (install_name && use_install_name) {
2362 return false;
2364 if (!install_name_is_rpath) {
2365 macosx_rpath = this->MacOSXRpathInstallNameDirDefault();
2367 } else {
2368 // Lookup the imported soname.
2369 if (cmGeneratorTarget::ImportInfo const* info =
2370 this->GetImportInfo(config)) {
2371 if (!info->NoSOName && !info->SOName.empty()) {
2372 if (cmHasLiteralPrefix(info->SOName, "@rpath/")) {
2373 install_name_is_rpath = true;
2375 } else {
2376 std::string install_name;
2377 cmSystemTools::GuessLibraryInstallName(info->Location, install_name);
2378 if (install_name.find("@rpath") != std::string::npos) {
2379 install_name_is_rpath = true;
2385 if (!install_name_is_rpath && !macosx_rpath) {
2386 return false;
2389 if (!this->Makefile->IsSet("CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG")) {
2390 std::ostringstream w;
2391 w << "Attempting to use ";
2392 if (macosx_rpath) {
2393 w << "MACOSX_RPATH";
2394 } else {
2395 w << "@rpath";
2397 w << " without CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG being set.";
2398 w << " This could be because you are using a Mac OS X version";
2399 w << " less than 10.5 or because CMake's platform configuration is";
2400 w << " corrupt.";
2401 cmake* cm = this->LocalGenerator->GetCMakeInstance();
2402 cm->IssueMessage(MessageType::FATAL_ERROR, w.str(), this->GetBacktrace());
2405 return true;
2408 bool cmGeneratorTarget::MacOSXRpathInstallNameDirDefault() const
2410 // we can't do rpaths when unsupported
2411 if (!this->Makefile->IsSet("CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG")) {
2412 return false;
2415 cmValue macosx_rpath_str = this->GetProperty("MACOSX_RPATH");
2416 if (macosx_rpath_str) {
2417 return this->GetPropertyAsBool("MACOSX_RPATH");
2420 cmPolicies::PolicyStatus cmp0042 = this->GetPolicyStatusCMP0042();
2422 if (cmp0042 == cmPolicies::WARN) {
2423 this->LocalGenerator->GetGlobalGenerator()->AddCMP0042WarnTarget(
2424 this->GetName());
2427 return cmp0042 == cmPolicies::NEW;
2430 bool cmGeneratorTarget::MacOSXUseInstallNameDir() const
2432 cmValue build_with_install_name =
2433 this->GetProperty("BUILD_WITH_INSTALL_NAME_DIR");
2434 if (build_with_install_name) {
2435 return cmIsOn(*build_with_install_name);
2438 cmPolicies::PolicyStatus cmp0068 = this->GetPolicyStatusCMP0068();
2439 if (cmp0068 == cmPolicies::NEW) {
2440 return false;
2443 bool use_install_name = this->GetPropertyAsBool("BUILD_WITH_INSTALL_RPATH");
2445 if (use_install_name && cmp0068 == cmPolicies::WARN) {
2446 this->LocalGenerator->GetGlobalGenerator()->AddCMP0068WarnTarget(
2447 this->GetName());
2450 return use_install_name;
2453 bool cmGeneratorTarget::CanGenerateInstallNameDir(
2454 InstallNameType name_type) const
2456 cmPolicies::PolicyStatus cmp0068 = this->GetPolicyStatusCMP0068();
2458 if (cmp0068 == cmPolicies::NEW) {
2459 return true;
2462 bool skip = this->Makefile->IsOn("CMAKE_SKIP_RPATH");
2463 if (name_type == INSTALL_NAME_FOR_INSTALL) {
2464 skip |= this->Makefile->IsOn("CMAKE_SKIP_INSTALL_RPATH");
2465 } else {
2466 skip |= this->GetPropertyAsBool("SKIP_BUILD_RPATH");
2469 if (skip && cmp0068 == cmPolicies::WARN) {
2470 this->LocalGenerator->GetGlobalGenerator()->AddCMP0068WarnTarget(
2471 this->GetName());
2474 return !skip;
2477 std::string cmGeneratorTarget::GetSOName(
2478 const std::string& config, cmStateEnums::ArtifactType artifact) const
2480 if (this->IsImported()) {
2481 // Lookup the imported soname.
2482 if (cmGeneratorTarget::ImportInfo const* info =
2483 this->GetImportInfo(config)) {
2484 if (info->NoSOName) {
2485 // The imported library has no builtin soname so the name
2486 // searched at runtime will be just the filename.
2487 return cmSystemTools::GetFilenameName(info->Location);
2489 // Use the soname given if any.
2490 if (this->IsFrameworkOnApple()) {
2491 auto fwDescriptor = this->GetGlobalGenerator()->SplitFrameworkPath(
2492 info->SOName, cmGlobalGenerator::FrameworkFormat::Strict);
2493 if (fwDescriptor) {
2494 return fwDescriptor->GetVersionedName();
2497 if (cmHasLiteralPrefix(info->SOName, "@rpath/")) {
2498 return info->SOName.substr(cmStrLen("@rpath/"));
2500 return info->SOName;
2502 return "";
2504 // Compute the soname that will be built.
2505 return artifact == cmStateEnums::RuntimeBinaryArtifact
2506 ? this->GetLibraryNames(config).SharedObject
2507 : this->GetLibraryNames(config).ImportLibrary;
2510 namespace {
2511 bool shouldAddFullLevel(cmGeneratorTarget::BundleDirectoryLevel level)
2513 return level == cmGeneratorTarget::FullLevel;
2516 bool shouldAddContentLevel(cmGeneratorTarget::BundleDirectoryLevel level)
2518 return level == cmGeneratorTarget::ContentLevel || shouldAddFullLevel(level);
2522 std::string cmGeneratorTarget::GetAppBundleDirectory(
2523 const std::string& config, BundleDirectoryLevel level) const
2525 std::string fpath = cmStrCat(
2526 this->GetFullName(config, cmStateEnums::RuntimeBinaryArtifact), '.');
2527 cmValue ext = this->GetProperty("BUNDLE_EXTENSION");
2528 fpath += (ext ? *ext : "app");
2529 if (shouldAddContentLevel(level) &&
2530 !this->Makefile->PlatformIsAppleEmbedded()) {
2531 fpath += "/Contents";
2532 if (shouldAddFullLevel(level)) {
2533 fpath += "/MacOS";
2536 return fpath;
2539 bool cmGeneratorTarget::IsBundleOnApple() const
2541 return this->IsFrameworkOnApple() || this->IsAppBundleOnApple() ||
2542 this->IsCFBundleOnApple();
2545 bool cmGeneratorTarget::IsWin32Executable(const std::string& config) const
2547 return cmIsOn(cmGeneratorExpression::Evaluate(
2548 this->GetSafeProperty("WIN32_EXECUTABLE"), this->LocalGenerator, config));
2551 std::string cmGeneratorTarget::GetCFBundleDirectory(
2552 const std::string& config, BundleDirectoryLevel level) const
2554 std::string fpath = cmStrCat(
2555 this->GetOutputName(config, cmStateEnums::RuntimeBinaryArtifact), '.');
2556 std::string ext;
2557 if (cmValue p = this->GetProperty("BUNDLE_EXTENSION")) {
2558 ext = *p;
2559 } else {
2560 if (this->IsXCTestOnApple()) {
2561 ext = "xctest";
2562 } else {
2563 ext = "bundle";
2566 fpath += ext;
2567 if (shouldAddContentLevel(level) &&
2568 !this->Makefile->PlatformIsAppleEmbedded()) {
2569 fpath += "/Contents";
2570 if (shouldAddFullLevel(level)) {
2571 fpath += "/MacOS";
2574 return fpath;
2577 std::string cmGeneratorTarget::GetFrameworkDirectory(
2578 const std::string& config, BundleDirectoryLevel level) const
2580 std::string fpath = cmStrCat(
2581 this->GetOutputName(config, cmStateEnums::RuntimeBinaryArtifact), '.');
2582 cmValue ext = this->GetProperty("BUNDLE_EXTENSION");
2583 fpath += (ext ? *ext : "framework");
2584 if (shouldAddFullLevel(level) &&
2585 !this->Makefile->PlatformIsAppleEmbedded()) {
2586 fpath += "/Versions/";
2587 fpath += this->GetFrameworkVersion();
2589 return fpath;
2592 std::string cmGeneratorTarget::GetFullName(
2593 const std::string& config, cmStateEnums::ArtifactType artifact) const
2595 if (this->IsImported()) {
2596 return this->GetFullNameImported(config, artifact);
2598 return this->GetFullNameInternal(config, artifact);
2601 std::string cmGeneratorTarget::GetInstallNameDirForBuildTree(
2602 const std::string& config) const
2604 if (this->Makefile->IsOn("CMAKE_PLATFORM_HAS_INSTALLNAME")) {
2606 // If building directly for installation then the build tree install_name
2607 // is the same as the install tree.
2608 if (this->MacOSXUseInstallNameDir()) {
2609 std::string installPrefix =
2610 this->Makefile->GetSafeDefinition("CMAKE_INSTALL_PREFIX");
2611 return this->GetInstallNameDirForInstallTree(config, installPrefix);
2614 // Use the build tree directory for the target.
2615 if (this->CanGenerateInstallNameDir(INSTALL_NAME_FOR_BUILD)) {
2616 std::string dir;
2617 if (this->MacOSXRpathInstallNameDirDefault()) {
2618 dir = "@rpath";
2619 } else {
2620 dir = this->GetDirectory(config);
2622 dir += "/";
2623 return dir;
2626 return "";
2629 std::string cmGeneratorTarget::GetInstallNameDirForInstallTree(
2630 const std::string& config, const std::string& installPrefix) const
2632 if (this->Makefile->IsOn("CMAKE_PLATFORM_HAS_INSTALLNAME")) {
2633 std::string dir;
2634 cmValue install_name_dir = this->GetProperty("INSTALL_NAME_DIR");
2636 if (this->CanGenerateInstallNameDir(INSTALL_NAME_FOR_INSTALL)) {
2637 if (cmNonempty(install_name_dir)) {
2638 dir = *install_name_dir;
2639 cmGeneratorExpression::ReplaceInstallPrefix(dir, installPrefix);
2640 dir =
2641 cmGeneratorExpression::Evaluate(dir, this->LocalGenerator, config);
2642 if (!dir.empty()) {
2643 dir = cmStrCat(dir, '/');
2647 if (!install_name_dir) {
2648 if (this->MacOSXRpathInstallNameDirDefault()) {
2649 dir = "@rpath/";
2652 return dir;
2654 return "";
2657 cmListFileBacktrace cmGeneratorTarget::GetBacktrace() const
2659 return this->Target->GetBacktrace();
2662 const std::set<BT<std::pair<std::string, bool>>>&
2663 cmGeneratorTarget::GetUtilities() const
2665 return this->Target->GetUtilities();
2668 bool cmGeneratorTarget::HaveWellDefinedOutputFiles() const
2670 return this->GetType() == cmStateEnums::STATIC_LIBRARY ||
2671 this->GetType() == cmStateEnums::SHARED_LIBRARY ||
2672 this->GetType() == cmStateEnums::MODULE_LIBRARY ||
2673 this->GetType() == cmStateEnums::OBJECT_LIBRARY ||
2674 this->GetType() == cmStateEnums::EXECUTABLE;
2677 const std::string* cmGeneratorTarget::GetExportMacro() const
2679 // Define the symbol for targets that export symbols.
2680 if (this->GetType() == cmStateEnums::SHARED_LIBRARY ||
2681 this->GetType() == cmStateEnums::MODULE_LIBRARY ||
2682 this->IsExecutableWithExports()) {
2683 if (cmValue custom_export_name = this->GetProperty("DEFINE_SYMBOL")) {
2684 this->ExportMacro = *custom_export_name;
2685 } else {
2686 std::string in = cmStrCat(this->GetName(), "_EXPORTS");
2687 this->ExportMacro = cmSystemTools::MakeCidentifier(in);
2689 return &this->ExportMacro;
2691 return nullptr;
2694 class cmTargetCollectLinkLanguages
2696 public:
2697 cmTargetCollectLinkLanguages(cmGeneratorTarget const* target,
2698 std::string config,
2699 std::unordered_set<std::string>& languages,
2700 cmGeneratorTarget const* head, bool secondPass)
2701 : Config(std::move(config))
2702 , Languages(languages)
2703 , HeadTarget(head)
2704 , SecondPass(secondPass)
2706 this->Visited.insert(target);
2709 void Visit(cmLinkItem const& item)
2711 if (!item.Target) {
2712 return;
2714 if (!this->Visited.insert(item.Target).second) {
2715 return;
2717 cmLinkInterface const* iface = item.Target->GetLinkInterface(
2718 this->Config, this->HeadTarget, this->SecondPass);
2719 if (!iface) {
2720 return;
2722 if (iface->HadLinkLanguageSensitiveCondition) {
2723 this->HadLinkLanguageSensitiveCondition = true;
2726 for (std::string const& language : iface->Languages) {
2727 this->Languages.insert(language);
2730 for (cmLinkItem const& lib : iface->Libraries) {
2731 this->Visit(lib);
2735 bool GetHadLinkLanguageSensitiveCondition() const
2737 return this->HadLinkLanguageSensitiveCondition;
2740 private:
2741 std::string Config;
2742 std::unordered_set<std::string>& Languages;
2743 cmGeneratorTarget const* HeadTarget;
2744 std::set<cmGeneratorTarget const*> Visited;
2745 bool SecondPass;
2746 bool HadLinkLanguageSensitiveCondition = false;
2749 cmGeneratorTarget::LinkClosure const* cmGeneratorTarget::GetLinkClosure(
2750 const std::string& config) const
2752 // There is no link implementation for targets that cannot compile sources.
2753 if (!this->CanCompileSources()) {
2754 static LinkClosure const empty = { {}, {} };
2755 return &empty;
2758 std::string key(cmSystemTools::UpperCase(config));
2759 auto i = this->LinkClosureMap.find(key);
2760 if (i == this->LinkClosureMap.end()) {
2761 LinkClosure lc;
2762 this->ComputeLinkClosure(config, lc);
2763 LinkClosureMapType::value_type entry(key, lc);
2764 i = this->LinkClosureMap.insert(entry).first;
2766 return &i->second;
2769 class cmTargetSelectLinker
2771 int Preference = 0;
2772 cmGeneratorTarget const* Target;
2773 cmGlobalGenerator* GG;
2774 std::set<std::string> Preferred;
2776 public:
2777 cmTargetSelectLinker(cmGeneratorTarget const* target)
2778 : Target(target)
2780 this->GG = this->Target->GetLocalGenerator()->GetGlobalGenerator();
2782 void Consider(const std::string& lang)
2784 int preference = this->GG->GetLinkerPreference(lang);
2785 if (preference > this->Preference) {
2786 this->Preference = preference;
2787 this->Preferred.clear();
2789 if (preference == this->Preference) {
2790 this->Preferred.insert(lang);
2793 std::string Choose()
2795 if (this->Preferred.empty()) {
2796 return "";
2798 if (this->Preferred.size() > 1) {
2799 std::ostringstream e;
2800 e << "Target " << this->Target->GetName()
2801 << " contains multiple languages with the highest linker preference"
2802 << " (" << this->Preference << "):\n";
2803 for (std::string const& li : this->Preferred) {
2804 e << " " << li << "\n";
2806 e << "Set the LINKER_LANGUAGE property for this target.";
2807 cmake* cm = this->Target->GetLocalGenerator()->GetCMakeInstance();
2808 cm->IssueMessage(MessageType::FATAL_ERROR, e.str(),
2809 this->Target->GetBacktrace());
2811 return *this->Preferred.begin();
2815 bool cmGeneratorTarget::ComputeLinkClosure(const std::string& config,
2816 LinkClosure& lc,
2817 bool secondPass) const
2819 // Get languages built in this target.
2820 std::unordered_set<std::string> languages;
2821 cmLinkImplementation const* impl =
2822 this->GetLinkImplementation(config, LinkInterfaceFor::Link, secondPass);
2823 assert(impl);
2824 languages.insert(impl->Languages.cbegin(), impl->Languages.cend());
2826 // Add interface languages from linked targets.
2827 // cmTargetCollectLinkLanguages cll(this, config, languages, this,
2828 // secondPass);
2829 cmTargetCollectLinkLanguages cll(this, config, languages, this, secondPass);
2830 for (cmLinkImplItem const& lib : impl->Libraries) {
2831 cll.Visit(lib);
2834 // Store the transitive closure of languages.
2835 cm::append(lc.Languages, languages);
2837 // Choose the language whose linker should be used.
2838 if (secondPass || lc.LinkerLanguage.empty()) {
2839 // Find the language with the highest preference value.
2840 cmTargetSelectLinker tsl(this);
2842 // First select from the languages compiled directly in this target.
2843 for (std::string const& l : impl->Languages) {
2844 tsl.Consider(l);
2847 // Now consider languages that propagate from linked targets.
2848 for (std::string const& lang : languages) {
2849 std::string propagates =
2850 "CMAKE_" + lang + "_LINKER_PREFERENCE_PROPAGATES";
2851 if (this->Makefile->IsOn(propagates)) {
2852 tsl.Consider(lang);
2856 lc.LinkerLanguage = tsl.Choose();
2859 return impl->HadLinkLanguageSensitiveCondition ||
2860 cll.GetHadLinkLanguageSensitiveCondition();
2863 void cmGeneratorTarget::ComputeLinkClosure(const std::string& config,
2864 LinkClosure& lc) const
2866 bool secondPass = false;
2869 LinkClosure linkClosure;
2870 linkClosure.LinkerLanguage = this->LinkerLanguage;
2872 bool hasHardCodedLinkerLanguage = this->Target->GetProperty("HAS_CXX") ||
2873 !this->Target->GetSafeProperty("LINKER_LANGUAGE").empty();
2875 // Get languages built in this target.
2876 secondPass = this->ComputeLinkClosure(config, linkClosure, false) &&
2877 !hasHardCodedLinkerLanguage;
2878 this->LinkerLanguage = linkClosure.LinkerLanguage;
2879 if (!secondPass) {
2880 lc = std::move(linkClosure);
2884 if (secondPass) {
2885 LinkClosure linkClosure;
2887 this->ComputeLinkClosure(config, linkClosure, secondPass);
2888 lc = std::move(linkClosure);
2890 // linker language must not be changed between the two passes
2891 if (this->LinkerLanguage != lc.LinkerLanguage) {
2892 std::ostringstream e;
2893 e << "Evaluation of $<LINK_LANGUAGE:...> or $<LINK_LAND_AND_ID:...> "
2894 "changes\nthe linker language for target \""
2895 << this->GetName() << "\" (from '" << this->LinkerLanguage << "' to '"
2896 << lc.LinkerLanguage << "') which is invalid.";
2897 cmSystemTools::Error(e.str());
2902 cmGeneratorTarget::NameComponents const&
2903 cmGeneratorTarget::GetFullNameComponents(
2904 std::string const& config, cmStateEnums::ArtifactType artifact) const
2906 return this->GetFullNameInternalComponents(config, artifact);
2909 std::string cmGeneratorTarget::BuildBundleDirectory(
2910 const std::string& base, const std::string& config,
2911 BundleDirectoryLevel level) const
2913 std::string fpath = base;
2914 if (this->IsAppBundleOnApple()) {
2915 fpath += this->GetAppBundleDirectory(config, level);
2917 if (this->IsFrameworkOnApple()) {
2918 fpath += this->GetFrameworkDirectory(config, level);
2920 if (this->IsCFBundleOnApple()) {
2921 fpath += this->GetCFBundleDirectory(config, level);
2923 return fpath;
2926 std::string cmGeneratorTarget::GetMacContentDirectory(
2927 const std::string& config, cmStateEnums::ArtifactType artifact) const
2929 // Start with the output directory for the target.
2930 std::string fpath = cmStrCat(this->GetDirectory(config, artifact), '/');
2931 BundleDirectoryLevel level = ContentLevel;
2932 if (this->IsFrameworkOnApple()) {
2933 // additional files with a framework go into the version specific
2934 // directory
2935 level = FullLevel;
2937 fpath = this->BuildBundleDirectory(fpath, config, level);
2938 return fpath;
2941 std::string cmGeneratorTarget::GetEffectiveFolderName() const
2943 std::string effectiveFolder;
2945 if (!this->GlobalGenerator->UseFolderProperty()) {
2946 return effectiveFolder;
2949 cmValue targetFolder = this->GetProperty("FOLDER");
2950 if (targetFolder) {
2951 effectiveFolder += *targetFolder;
2954 return effectiveFolder;
2957 cmGeneratorTarget::CompileInfo const* cmGeneratorTarget::GetCompileInfo(
2958 const std::string& config) const
2960 // There is no compile information for imported targets.
2961 if (this->IsImported()) {
2962 return nullptr;
2965 if (this->GetType() > cmStateEnums::OBJECT_LIBRARY) {
2966 std::string msg = cmStrCat("cmTarget::GetCompileInfo called for ",
2967 this->GetName(), " which has type ",
2968 cmState::GetTargetTypeName(this->GetType()));
2969 this->LocalGenerator->IssueMessage(MessageType::INTERNAL_ERROR, msg);
2970 return nullptr;
2973 // Lookup/compute/cache the compile information for this configuration.
2974 std::string config_upper;
2975 if (!config.empty()) {
2976 config_upper = cmSystemTools::UpperCase(config);
2978 auto i = this->CompileInfoMap.find(config_upper);
2979 if (i == this->CompileInfoMap.end()) {
2980 CompileInfo info;
2981 this->ComputePDBOutputDir("COMPILE_PDB", config, info.CompilePdbDir);
2982 CompileInfoMapType::value_type entry(config_upper, info);
2983 i = this->CompileInfoMap.insert(entry).first;
2985 return &i->second;
2988 cmGeneratorTarget::ModuleDefinitionInfo const*
2989 cmGeneratorTarget::GetModuleDefinitionInfo(std::string const& config) const
2991 // A module definition file only makes sense on certain target types.
2992 if (this->GetType() != cmStateEnums::SHARED_LIBRARY &&
2993 this->GetType() != cmStateEnums::MODULE_LIBRARY &&
2994 !this->IsExecutableWithExports()) {
2995 return nullptr;
2998 // Lookup/compute/cache the compile information for this configuration.
2999 std::string config_upper;
3000 if (!config.empty()) {
3001 config_upper = cmSystemTools::UpperCase(config);
3003 auto i = this->ModuleDefinitionInfoMap.find(config_upper);
3004 if (i == this->ModuleDefinitionInfoMap.end()) {
3005 ModuleDefinitionInfo info;
3006 this->ComputeModuleDefinitionInfo(config, info);
3007 ModuleDefinitionInfoMapType::value_type entry(config_upper, info);
3008 i = this->ModuleDefinitionInfoMap.insert(entry).first;
3010 return &i->second;
3013 void cmGeneratorTarget::ComputeModuleDefinitionInfo(
3014 std::string const& config, ModuleDefinitionInfo& info) const
3016 this->GetModuleDefinitionSources(info.Sources, config);
3017 info.WindowsExportAllSymbols =
3018 this->Makefile->IsOn("CMAKE_SUPPORT_WINDOWS_EXPORT_ALL_SYMBOLS") &&
3019 this->GetPropertyAsBool("WINDOWS_EXPORT_ALL_SYMBOLS");
3020 #if !defined(CMAKE_BOOTSTRAP)
3021 info.DefFileGenerated =
3022 info.WindowsExportAllSymbols || info.Sources.size() > 1;
3023 #else
3024 // Our __create_def helper is not available during CMake bootstrap.
3025 info.DefFileGenerated = false;
3026 #endif
3027 if (info.DefFileGenerated) {
3028 info.DefFile =
3029 this->GetObjectDirectory(config) /* has slash */ + "exports.def";
3030 } else if (!info.Sources.empty()) {
3031 info.DefFile = info.Sources.front()->GetFullPath();
3035 bool cmGeneratorTarget::IsAIX() const
3037 return this->Target->IsAIX();
3040 bool cmGeneratorTarget::IsApple() const
3042 return this->Target->IsApple();
3045 bool cmGeneratorTarget::IsDLLPlatform() const
3047 return this->Target->IsDLLPlatform();
3050 void cmGeneratorTarget::GetAutoUicOptions(std::vector<std::string>& result,
3051 const std::string& config) const
3053 const char* prop =
3054 this->GetLinkInterfaceDependentStringProperty("AUTOUIC_OPTIONS", config);
3055 if (!prop) {
3056 return;
3059 cmGeneratorExpressionDAGChecker dagChecker(this, "AUTOUIC_OPTIONS", nullptr,
3060 nullptr);
3061 cmExpandList(cmGeneratorExpression::Evaluate(prop, this->LocalGenerator,
3062 config, this, &dagChecker),
3063 result);
3066 static void processILibs(const std::string& config,
3067 cmGeneratorTarget const* headTarget,
3068 cmLinkItem const& item, cmGlobalGenerator* gg,
3069 std::vector<cmGeneratorTarget const*>& tgts,
3070 std::set<cmGeneratorTarget const*>& emitted)
3072 if (item.Target && emitted.insert(item.Target).second) {
3073 tgts.push_back(item.Target);
3074 if (cmLinkInterfaceLibraries const* iface =
3075 item.Target->GetLinkInterfaceLibraries(config, headTarget,
3076 LinkInterfaceFor::Usage)) {
3077 for (cmLinkItem const& lib : iface->Libraries) {
3078 processILibs(config, headTarget, lib, gg, tgts, emitted);
3084 const std::vector<const cmGeneratorTarget*>&
3085 cmGeneratorTarget::GetLinkImplementationClosure(
3086 const std::string& config) const
3088 // There is no link implementation for targets that cannot compile sources.
3089 if (!this->CanCompileSources()) {
3090 static std::vector<const cmGeneratorTarget*> const empty;
3091 return empty;
3094 LinkImplClosure& tgts = this->LinkImplClosureMap[config];
3095 if (!tgts.Done) {
3096 tgts.Done = true;
3097 std::set<cmGeneratorTarget const*> emitted;
3099 cmLinkImplementationLibraries const* impl =
3100 this->GetLinkImplementationLibraries(config, LinkInterfaceFor::Usage);
3101 assert(impl);
3103 for (cmLinkImplItem const& lib : impl->Libraries) {
3104 processILibs(config, this, lib,
3105 this->LocalGenerator->GetGlobalGenerator(), tgts, emitted);
3108 return tgts;
3111 class cmTargetTraceDependencies
3113 public:
3114 cmTargetTraceDependencies(cmGeneratorTarget* target);
3115 void Trace();
3117 private:
3118 cmGeneratorTarget* GeneratorTarget;
3119 cmMakefile* Makefile;
3120 cmLocalGenerator* LocalGenerator;
3121 cmGlobalGenerator const* GlobalGenerator;
3122 using SourceEntry = cmGeneratorTarget::SourceEntry;
3123 SourceEntry* CurrentEntry;
3124 std::queue<cmSourceFile*> SourceQueue;
3125 std::set<cmSourceFile*> SourcesQueued;
3126 using NameMapType = std::map<std::string, cmSourcesWithOutput>;
3127 NameMapType NameMap;
3128 std::vector<std::string> NewSources;
3130 void QueueSource(cmSourceFile* sf);
3131 void FollowName(std::string const& name);
3132 void FollowNames(std::vector<std::string> const& names);
3133 bool IsUtility(std::string const& dep);
3134 void CheckCustomCommand(cmCustomCommand const& cc);
3135 void CheckCustomCommands(const std::vector<cmCustomCommand>& commands);
3138 cmTargetTraceDependencies::cmTargetTraceDependencies(cmGeneratorTarget* target)
3139 : GeneratorTarget(target)
3141 // Convenience.
3142 this->Makefile = target->Target->GetMakefile();
3143 this->LocalGenerator = target->GetLocalGenerator();
3144 this->GlobalGenerator = this->LocalGenerator->GetGlobalGenerator();
3145 this->CurrentEntry = nullptr;
3147 // Queue all the source files already specified for the target.
3148 std::set<cmSourceFile*> emitted;
3149 std::vector<std::string> const& configs =
3150 this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
3151 for (std::string const& c : configs) {
3152 std::vector<cmSourceFile*> sources;
3153 this->GeneratorTarget->GetSourceFiles(sources, c);
3154 for (cmSourceFile* sf : sources) {
3155 const std::set<cmGeneratorTarget const*> tgts =
3156 this->GlobalGenerator->GetFilenameTargetDepends(sf);
3157 if (cm::contains(tgts, this->GeneratorTarget)) {
3158 std::ostringstream e;
3159 e << "Evaluation output file\n \"" << sf->ResolveFullPath()
3160 << "\"\ndepends on the sources of a target it is used in. This "
3161 "is a dependency loop and is not allowed.";
3162 this->GeneratorTarget->LocalGenerator->IssueMessage(
3163 MessageType::FATAL_ERROR, e.str());
3164 return;
3166 if (emitted.insert(sf).second && this->SourcesQueued.insert(sf).second) {
3167 this->SourceQueue.push(sf);
3172 // Queue pre-build, pre-link, and post-build rule dependencies.
3173 this->CheckCustomCommands(this->GeneratorTarget->GetPreBuildCommands());
3174 this->CheckCustomCommands(this->GeneratorTarget->GetPreLinkCommands());
3175 this->CheckCustomCommands(this->GeneratorTarget->GetPostBuildCommands());
3178 void cmTargetTraceDependencies::Trace()
3180 // Process one dependency at a time until the queue is empty.
3181 while (!this->SourceQueue.empty()) {
3182 // Get the next source from the queue.
3183 cmSourceFile* sf = this->SourceQueue.front();
3184 this->SourceQueue.pop();
3185 this->CurrentEntry = &this->GeneratorTarget->SourceDepends[sf];
3187 // Queue dependencies added explicitly by the user.
3188 if (cmValue additionalDeps = sf->GetProperty("OBJECT_DEPENDS")) {
3189 cmList objDeps{ *additionalDeps };
3190 for (auto& objDep : objDeps) {
3191 if (cmSystemTools::FileIsFullPath(objDep)) {
3192 objDep = cmSystemTools::CollapseFullPath(objDep);
3195 this->FollowNames(objDeps);
3198 // Queue the source needed to generate this file, if any.
3199 this->FollowName(sf->ResolveFullPath());
3201 // Queue dependencies added programmatically by commands.
3202 this->FollowNames(sf->GetDepends());
3204 // Queue custom command dependencies.
3205 if (cmCustomCommand const* cc = sf->GetCustomCommand()) {
3206 this->CheckCustomCommand(*cc);
3209 this->CurrentEntry = nullptr;
3211 this->GeneratorTarget->AddTracedSources(this->NewSources);
3214 void cmTargetTraceDependencies::QueueSource(cmSourceFile* sf)
3216 if (this->SourcesQueued.insert(sf).second) {
3217 this->SourceQueue.push(sf);
3219 // Make sure this file is in the target at the end.
3220 this->NewSources.push_back(sf->ResolveFullPath());
3224 void cmTargetTraceDependencies::FollowName(std::string const& name)
3226 // Use lower bound with key comparison to not repeat the search for the
3227 // insert position if the name could not be found (which is the common case).
3228 auto i = this->NameMap.lower_bound(name);
3229 if (i == this->NameMap.end() || i->first != name) {
3230 // Check if we know how to generate this file.
3231 cmSourcesWithOutput sources =
3232 this->LocalGenerator->GetSourcesWithOutput(name);
3233 // If we failed to find a target or source and we have a relative path, it
3234 // might be a valid source if made relative to the current binary
3235 // directory.
3236 if (!sources.Target && !sources.Source &&
3237 !cmSystemTools::FileIsFullPath(name)) {
3238 auto fullname =
3239 cmStrCat(this->Makefile->GetCurrentBinaryDirectory(), '/', name);
3240 fullname = cmSystemTools::CollapseFullPath(
3241 fullname, this->Makefile->GetHomeOutputDirectory());
3242 sources = this->LocalGenerator->GetSourcesWithOutput(fullname);
3244 i = this->NameMap.emplace_hint(i, name, sources);
3246 if (cmTarget* t = i->second.Target) {
3247 // The name is a byproduct of a utility target or a PRE_BUILD, PRE_LINK, or
3248 // POST_BUILD command.
3249 this->GeneratorTarget->Target->AddUtility(t->GetName(), false);
3251 if (cmSourceFile* sf = i->second.Source) {
3252 // For now only follow the dependency if the source file is not a
3253 // byproduct. Semantics of byproducts in a non-Ninja context will have to
3254 // be defined first.
3255 if (!i->second.SourceIsByproduct) {
3256 // Record the dependency we just followed.
3257 if (this->CurrentEntry) {
3258 this->CurrentEntry->Depends.push_back(sf);
3260 this->QueueSource(sf);
3265 void cmTargetTraceDependencies::FollowNames(
3266 std::vector<std::string> const& names)
3268 for (std::string const& name : names) {
3269 this->FollowName(name);
3273 bool cmTargetTraceDependencies::IsUtility(std::string const& dep)
3275 // Dependencies on targets (utilities) are supposed to be named by
3276 // just the target name. However for compatibility we support
3277 // naming the output file generated by the target (assuming there is
3278 // no output-name property which old code would not have set). In
3279 // that case the target name will be the file basename of the
3280 // dependency.
3281 std::string util = cmSystemTools::GetFilenameName(dep);
3282 if (cmSystemTools::GetFilenameLastExtension(util) == ".exe") {
3283 util = cmSystemTools::GetFilenameWithoutLastExtension(util);
3286 // Check for a target with this name.
3287 if (cmGeneratorTarget* t =
3288 this->GeneratorTarget->GetLocalGenerator()->FindGeneratorTargetToUse(
3289 util)) {
3290 // If we find the target and the dep was given as a full path,
3291 // then make sure it was not a full path to something else, and
3292 // the fact that the name matched a target was just a coincidence.
3293 if (cmSystemTools::FileIsFullPath(dep)) {
3294 if (t->GetType() >= cmStateEnums::EXECUTABLE &&
3295 t->GetType() <= cmStateEnums::MODULE_LIBRARY) {
3296 // This is really only for compatibility so we do not need to
3297 // worry about configuration names and output names.
3298 std::string tLocation = t->GetLocationForBuild();
3299 tLocation = cmSystemTools::GetFilenamePath(tLocation);
3300 std::string depLocation = cmSystemTools::GetFilenamePath(dep);
3301 depLocation = cmSystemTools::CollapseFullPath(depLocation);
3302 tLocation = cmSystemTools::CollapseFullPath(tLocation);
3303 if (depLocation == tLocation) {
3304 this->GeneratorTarget->Target->AddUtility(util, false);
3305 return true;
3308 } else {
3309 // The original name of the dependency was not a full path. It
3310 // must name a target, so add the target-level dependency.
3311 this->GeneratorTarget->Target->AddUtility(util, true);
3312 return true;
3316 // The dependency does not name a target built in this project.
3317 return false;
3320 void cmTargetTraceDependencies::CheckCustomCommand(cmCustomCommand const& cc)
3322 // Collect dependencies referenced by all configurations.
3323 std::set<std::string> depends;
3324 for (std::string const& config :
3325 this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig)) {
3326 for (cmCustomCommandGenerator const& ccg :
3327 this->LocalGenerator->MakeCustomCommandGenerators(cc, config)) {
3328 // Collect target-level dependencies referenced in command lines.
3329 for (auto const& util : ccg.GetUtilities()) {
3330 this->GeneratorTarget->Target->AddUtility(util);
3333 // Collect file-level dependencies referenced in DEPENDS.
3334 depends.insert(ccg.GetDepends().begin(), ccg.GetDepends().end());
3338 // Queue file-level dependencies.
3339 for (std::string const& dep : depends) {
3340 if (!this->IsUtility(dep)) {
3341 // The dependency does not name a target and may be a file we
3342 // know how to generate. Queue it.
3343 this->FollowName(dep);
3348 void cmTargetTraceDependencies::CheckCustomCommands(
3349 const std::vector<cmCustomCommand>& commands)
3351 for (cmCustomCommand const& command : commands) {
3352 this->CheckCustomCommand(command);
3356 void cmGeneratorTarget::TraceDependencies()
3358 // CMake-generated targets have no dependencies to trace. Normally tracing
3359 // would find nothing anyway, but when building CMake itself the "install"
3360 // target command ends up referencing the "cmake" target but we do not
3361 // really want the dependency because "install" depend on "all" anyway.
3362 if (this->GetType() == cmStateEnums::GLOBAL_TARGET) {
3363 return;
3366 // Use a helper object to trace the dependencies.
3367 cmTargetTraceDependencies tracer(this);
3368 tracer.Trace();
3371 std::string cmGeneratorTarget::GetCompilePDBDirectory(
3372 const std::string& config) const
3374 if (CompileInfo const* info = this->GetCompileInfo(config)) {
3375 return info->CompilePdbDir;
3377 return "";
3380 std::vector<std::string> cmGeneratorTarget::GetAppleArchs(
3381 std::string const& config, cm::optional<std::string> lang) const
3383 cmList archList;
3384 if (!this->IsApple()) {
3385 return std::move(archList.data());
3387 cmValue archs = nullptr;
3388 if (!config.empty()) {
3389 std::string defVarName =
3390 cmStrCat("OSX_ARCHITECTURES_", cmSystemTools::UpperCase(config));
3391 archs = this->GetProperty(defVarName);
3393 if (!archs) {
3394 archs = this->GetProperty("OSX_ARCHITECTURES");
3396 if (archs) {
3397 archList.assign(*archs);
3399 if (archList.empty() &&
3400 // Fall back to a default architecture if no compiler target is set.
3401 (!lang ||
3402 this->Makefile
3403 ->GetDefinition(cmStrCat("CMAKE_", *lang, "_COMPILER_TARGET"))
3404 .IsEmpty())) {
3405 archList.assign(
3406 this->Makefile->GetDefinition("_CMAKE_APPLE_ARCHS_DEFAULT"));
3408 return std::move(archList.data());
3411 void cmGeneratorTarget::AddExplicitLanguageFlags(std::string& flags,
3412 cmSourceFile const& sf) const
3414 cmValue lang = sf.GetProperty("LANGUAGE");
3415 if (!lang) {
3416 return;
3419 switch (this->GetPolicyStatusCMP0119()) {
3420 case cmPolicies::WARN:
3421 CM_FALLTHROUGH;
3422 case cmPolicies::OLD:
3423 // The OLD behavior is to not add explicit language flags.
3424 return;
3425 case cmPolicies::REQUIRED_ALWAYS:
3426 case cmPolicies::REQUIRED_IF_USED:
3427 case cmPolicies::NEW:
3428 // The NEW behavior is to add explicit language flags.
3429 break;
3432 this->LocalGenerator->AppendFeatureOptions(flags, *lang,
3433 "EXPLICIT_LANGUAGE");
3436 void cmGeneratorTarget::AddCUDAArchitectureFlags(cmBuildStep compileOrLink,
3437 std::string const& config,
3438 std::string& flags) const
3440 std::string arch = this->GetSafeProperty("CUDA_ARCHITECTURES");
3442 if (arch.empty()) {
3443 switch (this->GetPolicyStatusCMP0104()) {
3444 case cmPolicies::WARN:
3445 if (!this->LocalGenerator->GetCMakeInstance()->GetIsInTryCompile()) {
3446 this->Makefile->IssueMessage(
3447 MessageType::AUTHOR_WARNING,
3448 cmPolicies::GetPolicyWarning(cmPolicies::CMP0104) +
3449 "\nCUDA_ARCHITECTURES is empty for target \"" + this->GetName() +
3450 "\".");
3452 CM_FALLTHROUGH;
3453 case cmPolicies::OLD:
3454 break;
3455 default:
3456 this->Makefile->IssueMessage(
3457 MessageType::FATAL_ERROR,
3458 "CUDA_ARCHITECTURES is empty for target \"" + this->GetName() +
3459 "\".");
3463 // If CUDA_ARCHITECTURES is false we don't add any architectures.
3464 if (cmIsOff(arch)) {
3465 return;
3468 return this->AddCUDAArchitectureFlagsImpl(compileOrLink, config, "CUDA",
3469 std::move(arch), flags);
3472 void cmGeneratorTarget::AddCUDAArchitectureFlagsImpl(cmBuildStep compileOrLink,
3473 std::string const& config,
3474 std::string const& lang,
3475 std::string arch,
3476 std::string& flags) const
3478 std::string const& compiler = this->Makefile->GetSafeDefinition(
3479 cmStrCat("CMAKE_", lang, "_COMPILER_ID"));
3480 const bool ipoEnabled = this->IsIPOEnabled(lang, config);
3482 // Check for special modes: `all`, `all-major`.
3483 if (arch == "all" || arch == "all-major") {
3484 if (compiler == "NVIDIA" &&
3485 cmSystemTools::VersionCompare(cmSystemTools::OP_GREATER_EQUAL,
3486 this->Makefile->GetDefinition(cmStrCat(
3487 "CMAKE_", lang, "_COMPILER_VERSION")),
3488 "11.5")) {
3489 flags = cmStrCat(flags, " -arch=", arch);
3490 return;
3492 if (arch == "all") {
3493 arch = *this->Makefile->GetDefinition(
3494 cmStrCat("CMAKE_", lang, "_ARCHITECTURES_ALL"));
3495 } else if (arch == "all-major") {
3496 arch = *this->Makefile->GetDefinition(
3497 cmStrCat("CMAKE_", lang, "_ARCHITECTURES_ALL_MAJOR"));
3499 } else if (arch == "native") {
3500 cmValue native = this->Makefile->GetDefinition(
3501 cmStrCat("CMAKE_", lang, "_ARCHITECTURES_NATIVE"));
3502 if (native.IsEmpty()) {
3503 this->Makefile->IssueMessage(
3504 MessageType::FATAL_ERROR,
3505 cmStrCat(lang,
3506 "_ARCHITECTURES is set to \"native\", but no NVIDIA GPU was "
3507 "detected."));
3509 if (compiler == "NVIDIA" &&
3510 cmSystemTools::VersionCompare(cmSystemTools::OP_GREATER_EQUAL,
3511 this->Makefile->GetDefinition(cmStrCat(
3512 "CMAKE_", lang, "_COMPILER_VERSION")),
3513 "11.6")) {
3514 flags = cmStrCat(flags, " -arch=", arch);
3515 return;
3517 arch = *native;
3520 struct CudaArchitecture
3522 std::string name;
3523 bool real{ true };
3524 bool virtual_{ true };
3526 std::vector<CudaArchitecture> architectures;
3529 cmList options(arch);
3531 for (auto& option : options) {
3532 CudaArchitecture architecture;
3534 // Architecture name is up to the first specifier.
3535 std::size_t pos = option.find_first_of('-');
3536 architecture.name = option.substr(0, pos);
3538 if (pos != std::string::npos) {
3539 cm::string_view specifier{ option.c_str() + pos + 1,
3540 option.length() - pos - 1 };
3542 if (specifier == "real") {
3543 architecture.real = true;
3544 architecture.virtual_ = false;
3545 } else if (specifier == "virtual") {
3546 architecture.real = false;
3547 architecture.virtual_ = true;
3548 } else {
3549 this->Makefile->IssueMessage(
3550 MessageType::FATAL_ERROR,
3551 "Unknown CUDA architecture specifier \"" + std::string(specifier) +
3552 "\".");
3556 architectures.emplace_back(architecture);
3560 if (compiler == "NVIDIA") {
3561 if (ipoEnabled && compileOrLink == cmBuildStep::Link) {
3562 if (cmValue cudaIPOFlags = this->Makefile->GetDefinition(
3563 cmStrCat("CMAKE_", lang, "_LINK_OPTIONS_IPO"))) {
3564 flags += *cudaIPOFlags;
3568 for (CudaArchitecture& architecture : architectures) {
3569 flags +=
3570 " \"--generate-code=arch=compute_" + architecture.name + ",code=[";
3572 if (architecture.virtual_) {
3573 flags += "compute_" + architecture.name;
3575 if (ipoEnabled || architecture.real) {
3576 flags += ",";
3580 if (ipoEnabled) {
3581 if (compileOrLink == cmBuildStep::Compile) {
3582 flags += "lto_" + architecture.name;
3583 } else if (compileOrLink == cmBuildStep::Link) {
3584 flags += "sm_" + architecture.name;
3586 } else if (architecture.real) {
3587 flags += "sm_" + architecture.name;
3590 flags += "]\"";
3592 } else if (compiler == "Clang" && compileOrLink == cmBuildStep::Compile) {
3593 for (CudaArchitecture& architecture : architectures) {
3594 flags += " --cuda-gpu-arch=sm_" + architecture.name;
3596 if (!architecture.real) {
3597 this->Makefile->IssueMessage(
3598 MessageType::WARNING,
3599 "Clang doesn't support disabling CUDA real code generation.");
3602 if (!architecture.virtual_) {
3603 flags += " --no-cuda-include-ptx=sm_" + architecture.name;
3609 void cmGeneratorTarget::AddISPCTargetFlags(std::string& flags) const
3611 const std::string& arch = this->GetSafeProperty("ISPC_INSTRUCTION_SETS");
3613 // If ISPC_TARGET is false we don't add any architectures.
3614 if (cmIsOff(arch)) {
3615 return;
3618 std::string const& compiler =
3619 this->Makefile->GetSafeDefinition("CMAKE_ISPC_COMPILER_ID");
3621 if (compiler == "Intel") {
3622 cmList targets(arch);
3623 if (!targets.empty()) {
3624 flags += cmStrCat(" --target=", cmWrap("", targets, "", ","));
3629 void cmGeneratorTarget::AddHIPArchitectureFlags(cmBuildStep compileOrLink,
3630 std::string const& config,
3631 std::string& flags) const
3633 std::string arch = this->GetSafeProperty("HIP_ARCHITECTURES");
3635 if (arch.empty()) {
3636 this->Makefile->IssueMessage(MessageType::FATAL_ERROR,
3637 "HIP_ARCHITECTURES is empty for target \"" +
3638 this->GetName() + "\".");
3641 // If HIP_ARCHITECTURES is false we don't add any architectures.
3642 if (cmIsOff(arch)) {
3643 return;
3646 if (this->Makefile->GetSafeDefinition("CMAKE_HIP_PLATFORM") == "nvidia") {
3647 return this->AddCUDAArchitectureFlagsImpl(compileOrLink, config, "HIP",
3648 std::move(arch), flags);
3651 cmList options(arch);
3653 for (std::string& option : options) {
3654 flags += " --offload-arch=" + option;
3658 void cmGeneratorTarget::AddCUDAToolkitFlags(std::string& flags) const
3660 std::string const& compiler =
3661 this->Makefile->GetSafeDefinition("CMAKE_CUDA_COMPILER_ID");
3663 if (compiler == "Clang") {
3664 // Pass CUDA toolkit explicitly to Clang.
3665 // Clang's searching for the system CUDA toolkit isn't very good and it's
3666 // expected the user will explicitly pass the toolkit path.
3667 // This also avoids Clang having to search for the toolkit on every
3668 // invocation.
3669 std::string toolkitRoot =
3670 this->Makefile->GetSafeDefinition("CMAKE_CUDA_COMPILER_LIBRARY_ROOT");
3672 if (!toolkitRoot.empty()) {
3673 flags += " --cuda-path=" +
3674 this->LocalGenerator->ConvertToOutputFormat(toolkitRoot,
3675 cmOutputConverter::SHELL);
3680 //----------------------------------------------------------------------------
3681 std::string cmGeneratorTarget::GetFeatureSpecificLinkRuleVariable(
3682 std::string const& var, std::string const& lang,
3683 std::string const& config) const
3685 if (this->IsIPOEnabled(lang, config)) {
3686 std::string varIPO = var + "_IPO";
3687 if (this->Makefile->IsDefinitionSet(varIPO)) {
3688 return varIPO;
3692 return var;
3695 //----------------------------------------------------------------------------
3696 std::string cmGeneratorTarget::GetCreateRuleVariable(
3697 std::string const& lang, std::string const& config) const
3699 switch (this->GetType()) {
3700 case cmStateEnums::STATIC_LIBRARY: {
3701 std::string var = "CMAKE_" + lang + "_CREATE_STATIC_LIBRARY";
3702 return this->GetFeatureSpecificLinkRuleVariable(var, lang, config);
3704 case cmStateEnums::SHARED_LIBRARY:
3705 return "CMAKE_" + lang + "_CREATE_SHARED_LIBRARY";
3706 case cmStateEnums::MODULE_LIBRARY:
3707 return "CMAKE_" + lang + "_CREATE_SHARED_MODULE";
3708 case cmStateEnums::EXECUTABLE:
3709 if (this->IsExecutableWithExports()) {
3710 std::string linkExeWithExports =
3711 "CMAKE_" + lang + "_LINK_EXECUTABLE_WITH_EXPORTS";
3712 if (this->Makefile->IsDefinitionSet(linkExeWithExports)) {
3713 return linkExeWithExports;
3716 return "CMAKE_" + lang + "_LINK_EXECUTABLE";
3717 default:
3718 break;
3720 return "";
3723 //----------------------------------------------------------------------------
3724 std::string cmGeneratorTarget::GetClangTidyExportFixesDirectory(
3725 const std::string& lang) const
3727 cmValue val =
3728 this->GetProperty(cmStrCat(lang, "_CLANG_TIDY_EXPORT_FIXES_DIR"));
3729 if (!cmNonempty(val)) {
3730 return {};
3733 std::string path = *val;
3734 if (!cmSystemTools::FileIsFullPath(path)) {
3735 path =
3736 cmStrCat(this->LocalGenerator->GetCurrentBinaryDirectory(), '/', path);
3738 return cmSystemTools::CollapseFullPath(path);
3741 namespace {
3742 void processIncludeDirectories(cmGeneratorTarget const* tgt,
3743 EvaluatedTargetPropertyEntries& entries,
3744 std::vector<BT<std::string>>& includes,
3745 std::unordered_set<std::string>& uniqueIncludes,
3746 bool debugIncludes)
3748 for (EvaluatedTargetPropertyEntry& entry : entries.Entries) {
3749 cmLinkImplItem const& item = entry.LinkImplItem;
3750 std::string const& targetName = item.AsStr();
3751 bool const fromImported = item.Target && item.Target->IsImported();
3752 bool const checkCMP0027 = item.CheckCMP0027;
3754 std::string usedIncludes;
3755 for (std::string& entryInclude : entry.Values) {
3756 if (fromImported && !cmSystemTools::FileExists(entryInclude)) {
3757 std::ostringstream e;
3758 MessageType messageType = MessageType::FATAL_ERROR;
3759 if (checkCMP0027) {
3760 switch (tgt->GetPolicyStatusCMP0027()) {
3761 case cmPolicies::WARN:
3762 e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0027) << "\n";
3763 CM_FALLTHROUGH;
3764 case cmPolicies::OLD:
3765 messageType = MessageType::AUTHOR_WARNING;
3766 break;
3767 case cmPolicies::REQUIRED_ALWAYS:
3768 case cmPolicies::REQUIRED_IF_USED:
3769 case cmPolicies::NEW:
3770 break;
3773 /* clang-format off */
3774 e << "Imported target \"" << targetName << "\" includes "
3775 "non-existent path\n \"" << entryInclude << "\"\nin its "
3776 "INTERFACE_INCLUDE_DIRECTORIES. Possible reasons include:\n"
3777 "* The path was deleted, renamed, or moved to another "
3778 "location.\n"
3779 "* An install or uninstall procedure did not complete "
3780 "successfully.\n"
3781 "* The installation package was faulty and references files it "
3782 "does not provide.\n";
3783 /* clang-format on */
3784 tgt->GetLocalGenerator()->IssueMessage(messageType, e.str());
3785 return;
3788 if (!cmSystemTools::FileIsFullPath(entryInclude)) {
3789 std::ostringstream e;
3790 bool noMessage = false;
3791 MessageType messageType = MessageType::FATAL_ERROR;
3792 if (!targetName.empty()) {
3793 /* clang-format off */
3794 e << "Target \"" << targetName << "\" contains relative "
3795 "path in its INTERFACE_INCLUDE_DIRECTORIES:\n"
3796 " \"" << entryInclude << "\"";
3797 /* clang-format on */
3798 } else {
3799 switch (tgt->GetPolicyStatusCMP0021()) {
3800 case cmPolicies::WARN: {
3801 e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0021) << "\n";
3802 messageType = MessageType::AUTHOR_WARNING;
3803 } break;
3804 case cmPolicies::OLD:
3805 noMessage = true;
3806 break;
3807 case cmPolicies::REQUIRED_IF_USED:
3808 case cmPolicies::REQUIRED_ALWAYS:
3809 case cmPolicies::NEW:
3810 // Issue the fatal message.
3811 break;
3813 e << "Found relative path while evaluating include directories of "
3814 "\""
3815 << tgt->GetName() << "\":\n \"" << entryInclude << "\"\n";
3817 if (!noMessage) {
3818 tgt->GetLocalGenerator()->IssueMessage(messageType, e.str());
3819 if (messageType == MessageType::FATAL_ERROR) {
3820 return;
3825 if (!cmIsOff(entryInclude)) {
3826 cmSystemTools::ConvertToUnixSlashes(entryInclude);
3829 if (uniqueIncludes.insert(entryInclude).second) {
3830 includes.emplace_back(entryInclude, entry.Backtrace);
3831 if (debugIncludes) {
3832 usedIncludes += " * " + entryInclude + "\n";
3836 if (!usedIncludes.empty()) {
3837 tgt->GetLocalGenerator()->GetCMakeInstance()->IssueMessage(
3838 MessageType::LOG,
3839 std::string("Used includes for target ") + tgt->GetName() + ":\n" +
3840 usedIncludes,
3841 entry.Backtrace);
3847 std::vector<BT<std::string>> cmGeneratorTarget::GetIncludeDirectories(
3848 const std::string& config, const std::string& lang) const
3850 ConfigAndLanguage cacheKey(config, lang);
3852 auto it = this->IncludeDirectoriesCache.find(cacheKey);
3853 if (it != this->IncludeDirectoriesCache.end()) {
3854 return it->second;
3857 std::vector<BT<std::string>> includes;
3858 std::unordered_set<std::string> uniqueIncludes;
3860 cmGeneratorExpressionDAGChecker dagChecker(this, "INCLUDE_DIRECTORIES",
3861 nullptr, nullptr);
3863 cmList debugProperties{ this->Makefile->GetDefinition(
3864 "CMAKE_DEBUG_TARGET_PROPERTIES") };
3865 bool debugIncludes = !this->DebugIncludesDone &&
3866 cm::contains(debugProperties, "INCLUDE_DIRECTORIES");
3868 if (this->GlobalGenerator->GetConfigureDoneCMP0026()) {
3869 this->DebugIncludesDone = true;
3872 EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries(
3873 this, config, lang, &dagChecker, this->IncludeDirectoriesEntries);
3875 if (lang == "Swift") {
3876 AddLangSpecificImplicitIncludeDirectories(
3877 this, lang, config, "Swift_MODULE_DIRECTORY",
3878 IncludeDirectoryFallBack::BINARY, entries);
3881 if (this->CanCompileSources() && (lang != "Swift" && lang != "Fortran")) {
3883 const std::string propertyName = "ISPC_HEADER_DIRECTORY";
3885 // If this target has ISPC sources make sure to add the header
3886 // directory to other compilation units
3887 if (cm::contains(this->GetAllConfigCompileLanguages(), "ISPC")) {
3888 if (cmValue val = this->GetProperty(propertyName)) {
3889 includes.emplace_back(*val);
3890 } else {
3891 includes.emplace_back(this->GetObjectDirectory(config));
3895 AddLangSpecificImplicitIncludeDirectories(
3896 this, "ISPC", config, propertyName, IncludeDirectoryFallBack::OBJECT,
3897 entries);
3900 AddInterfaceEntries(this, config, "INTERFACE_INCLUDE_DIRECTORIES", lang,
3901 &dagChecker, entries, IncludeRuntimeInterface::Yes);
3903 processIncludeDirectories(this, entries, includes, uniqueIncludes,
3904 debugIncludes);
3906 if (this->IsApple()) {
3907 if (cmLinkImplementationLibraries const* impl =
3908 this->GetLinkImplementationLibraries(config,
3909 LinkInterfaceFor::Usage)) {
3910 for (cmLinkImplItem const& lib : impl->Libraries) {
3911 std::string libDir;
3912 if (lib.Target == nullptr) {
3913 libDir = cmSystemTools::CollapseFullPath(
3914 lib.AsStr(), this->Makefile->GetHomeOutputDirectory());
3915 } else if (lib.Target->Target->IsFrameworkOnApple() ||
3916 this->IsImportedFrameworkFolderOnApple(config)) {
3917 libDir = lib.Target->GetLocation(config);
3918 } else {
3919 continue;
3922 auto fwDescriptor =
3923 this->GetGlobalGenerator()->SplitFrameworkPath(libDir);
3924 if (!fwDescriptor) {
3925 continue;
3928 auto fwInclude = fwDescriptor->GetFrameworkPath();
3929 if (uniqueIncludes.insert(fwInclude).second) {
3930 includes.emplace_back(fwInclude, cmListFileBacktrace());
3936 this->IncludeDirectoriesCache.emplace(cacheKey, includes);
3937 return includes;
3940 enum class OptionsParse
3942 None,
3943 Shell
3946 namespace {
3947 const auto DL_BEGIN = "<DEVICE_LINK>"_s;
3948 const auto DL_END = "</DEVICE_LINK>"_s;
3950 void processOptions(cmGeneratorTarget const* tgt,
3951 EvaluatedTargetPropertyEntries const& entries,
3952 std::vector<BT<std::string>>& options,
3953 std::unordered_set<std::string>& uniqueOptions,
3954 bool debugOptions, const char* logName, OptionsParse parse,
3955 bool processDeviceOptions = false)
3957 bool splitOption = !processDeviceOptions;
3958 for (EvaluatedTargetPropertyEntry const& entry : entries.Entries) {
3959 std::string usedOptions;
3960 for (std::string const& opt : entry.Values) {
3961 if (processDeviceOptions && (opt == DL_BEGIN || opt == DL_END)) {
3962 options.emplace_back(opt, entry.Backtrace);
3963 splitOption = opt == DL_BEGIN;
3964 continue;
3967 if (uniqueOptions.insert(opt).second) {
3968 if (parse == OptionsParse::Shell &&
3969 cmHasLiteralPrefix(opt, "SHELL:")) {
3970 if (splitOption) {
3971 std::vector<std::string> tmp;
3972 cmSystemTools::ParseUnixCommandLine(opt.c_str() + 6, tmp);
3973 for (std::string& o : tmp) {
3974 options.emplace_back(std::move(o), entry.Backtrace);
3976 } else {
3977 options.emplace_back(std::string(opt.c_str() + 6),
3978 entry.Backtrace);
3980 } else {
3981 options.emplace_back(opt, entry.Backtrace);
3983 if (debugOptions) {
3984 usedOptions += " * " + opt + "\n";
3988 if (!usedOptions.empty()) {
3989 tgt->GetLocalGenerator()->GetCMakeInstance()->IssueMessage(
3990 MessageType::LOG,
3991 std::string("Used ") + logName + std::string(" for target ") +
3992 tgt->GetName() + ":\n" + usedOptions,
3993 entry.Backtrace);
3998 std::vector<BT<std::string>> wrapOptions(
3999 std::vector<std::string>& options, const cmListFileBacktrace& bt,
4000 const std::vector<std::string>& wrapperFlag, const std::string& wrapperSep,
4001 bool concatFlagAndArgs)
4003 std::vector<BT<std::string>> result;
4005 if (options.empty()) {
4006 return result;
4009 if (wrapperFlag.empty()) {
4010 // nothing specified, insert elements as is
4011 result.reserve(options.size());
4012 for (std::string& o : options) {
4013 result.emplace_back(std::move(o), bt);
4015 return result;
4018 for (std::vector<std::string>::size_type index = 0; index < options.size();
4019 index++) {
4020 if (cmHasLiteralPrefix(options[index], "LINKER:")) {
4021 // LINKER wrapper specified, insert elements as is
4022 result.emplace_back(std::move(options[index]), bt);
4023 continue;
4025 if (cmHasLiteralPrefix(options[index], "-Wl,")) {
4026 // replace option by LINKER wrapper
4027 result.emplace_back(options[index].replace(0, 4, "LINKER:"), bt);
4028 continue;
4030 if (cmHasLiteralPrefix(options[index], "-Xlinker=")) {
4031 // replace option by LINKER wrapper
4032 result.emplace_back(options[index].replace(0, 9, "LINKER:"), bt);
4033 continue;
4035 if (options[index] == "-Xlinker") {
4036 // replace option by LINKER wrapper
4037 if (index + 1 < options.size()) {
4038 result.emplace_back("LINKER:" + options[++index], bt);
4039 } else {
4040 result.emplace_back(std::move(options[index]), bt);
4042 continue;
4045 // collect all options which must be transformed
4046 std::vector<std::string> opts;
4047 while (index < options.size()) {
4048 if (!cmHasLiteralPrefix(options[index], "LINKER:") &&
4049 !cmHasLiteralPrefix(options[index], "-Wl,") &&
4050 !cmHasLiteralPrefix(options[index], "-Xlinker")) {
4051 opts.emplace_back(std::move(options[index++]));
4052 } else {
4053 --index;
4054 break;
4057 if (opts.empty()) {
4058 continue;
4061 if (!wrapperSep.empty()) {
4062 if (concatFlagAndArgs) {
4063 // insert flag elements except last one
4064 for (auto i = wrapperFlag.begin(); i != wrapperFlag.end() - 1; ++i) {
4065 result.emplace_back(*i, bt);
4067 // concatenate last flag element and all list values
4068 // in one option
4069 result.emplace_back(wrapperFlag.back() + cmJoin(opts, wrapperSep), bt);
4070 } else {
4071 for (std::string const& i : wrapperFlag) {
4072 result.emplace_back(i, bt);
4074 // concatenate all list values in one option
4075 result.emplace_back(cmJoin(opts, wrapperSep), bt);
4077 } else {
4078 // prefix each element of list with wrapper
4079 if (concatFlagAndArgs) {
4080 std::transform(opts.begin(), opts.end(), opts.begin(),
4081 [&wrapperFlag](std::string const& o) -> std::string {
4082 return wrapperFlag.back() + o;
4085 for (std::string& o : opts) {
4086 for (auto i = wrapperFlag.begin(),
4087 e = concatFlagAndArgs ? wrapperFlag.end() - 1
4088 : wrapperFlag.end();
4089 i != e; ++i) {
4090 result.emplace_back(*i, bt);
4092 result.emplace_back(std::move(o), bt);
4096 return result;
4100 void cmGeneratorTarget::GetCompileOptions(std::vector<std::string>& result,
4101 const std::string& config,
4102 const std::string& language) const
4104 std::vector<BT<std::string>> tmp = this->GetCompileOptions(config, language);
4105 result.reserve(tmp.size());
4106 for (BT<std::string>& v : tmp) {
4107 result.emplace_back(std::move(v.Value));
4111 std::vector<BT<std::string>> cmGeneratorTarget::GetCompileOptions(
4112 std::string const& config, std::string const& language) const
4114 ConfigAndLanguage cacheKey(config, language);
4116 auto it = this->CompileOptionsCache.find(cacheKey);
4117 if (it != this->CompileOptionsCache.end()) {
4118 return it->second;
4121 std::vector<BT<std::string>> result;
4122 std::unordered_set<std::string> uniqueOptions;
4124 cmGeneratorExpressionDAGChecker dagChecker(this, "COMPILE_OPTIONS", nullptr,
4125 nullptr);
4127 cmList debugProperties{ this->Makefile->GetDefinition(
4128 "CMAKE_DEBUG_TARGET_PROPERTIES") };
4129 bool debugOptions = !this->DebugCompileOptionsDone &&
4130 cm::contains(debugProperties, "COMPILE_OPTIONS");
4132 if (this->GlobalGenerator->GetConfigureDoneCMP0026()) {
4133 this->DebugCompileOptionsDone = true;
4136 EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries(
4137 this, config, language, &dagChecker, this->CompileOptionsEntries);
4139 AddInterfaceEntries(this, config, "INTERFACE_COMPILE_OPTIONS", language,
4140 &dagChecker, entries, IncludeRuntimeInterface::Yes);
4142 processOptions(this, entries, result, uniqueOptions, debugOptions,
4143 "compile options", OptionsParse::Shell);
4145 CompileOptionsCache.emplace(cacheKey, result);
4146 return result;
4149 void cmGeneratorTarget::GetCompileFeatures(std::vector<std::string>& result,
4150 const std::string& config) const
4152 std::vector<BT<std::string>> tmp = this->GetCompileFeatures(config);
4153 result.reserve(tmp.size());
4154 for (BT<std::string>& v : tmp) {
4155 result.emplace_back(std::move(v.Value));
4159 std::vector<BT<std::string>> cmGeneratorTarget::GetCompileFeatures(
4160 std::string const& config) const
4162 std::vector<BT<std::string>> result;
4163 std::unordered_set<std::string> uniqueFeatures;
4165 cmGeneratorExpressionDAGChecker dagChecker(this, "COMPILE_FEATURES", nullptr,
4166 nullptr);
4168 cmList debugProperties{ this->Makefile->GetDefinition(
4169 "CMAKE_DEBUG_TARGET_PROPERTIES") };
4170 bool debugFeatures = !this->DebugCompileFeaturesDone &&
4171 cm::contains(debugProperties, "COMPILE_FEATURES");
4173 if (this->GlobalGenerator->GetConfigureDoneCMP0026()) {
4174 this->DebugCompileFeaturesDone = true;
4177 EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries(
4178 this, config, std::string(), &dagChecker, this->CompileFeaturesEntries);
4180 AddInterfaceEntries(this, config, "INTERFACE_COMPILE_FEATURES",
4181 std::string(), &dagChecker, entries,
4182 IncludeRuntimeInterface::Yes);
4184 processOptions(this, entries, result, uniqueFeatures, debugFeatures,
4185 "compile features", OptionsParse::None);
4187 return result;
4190 void cmGeneratorTarget::GetCompileDefinitions(
4191 std::vector<std::string>& result, const std::string& config,
4192 const std::string& language) const
4194 std::vector<BT<std::string>> tmp =
4195 this->GetCompileDefinitions(config, language);
4196 result.reserve(tmp.size());
4197 for (BT<std::string>& v : tmp) {
4198 result.emplace_back(std::move(v.Value));
4202 std::vector<BT<std::string>> cmGeneratorTarget::GetCompileDefinitions(
4203 std::string const& config, std::string const& language) const
4205 ConfigAndLanguage cacheKey(config, language);
4207 auto it = this->CompileDefinitionsCache.find(cacheKey);
4208 if (it != this->CompileDefinitionsCache.end()) {
4209 return it->second;
4212 std::vector<BT<std::string>> list;
4213 std::unordered_set<std::string> uniqueOptions;
4215 cmGeneratorExpressionDAGChecker dagChecker(this, "COMPILE_DEFINITIONS",
4216 nullptr, nullptr);
4218 cmList debugProperties{ this->Makefile->GetDefinition(
4219 "CMAKE_DEBUG_TARGET_PROPERTIES") };
4220 bool debugDefines = !this->DebugCompileDefinitionsDone &&
4221 cm::contains(debugProperties, "COMPILE_DEFINITIONS");
4223 if (this->GlobalGenerator->GetConfigureDoneCMP0026()) {
4224 this->DebugCompileDefinitionsDone = true;
4227 EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries(
4228 this, config, language, &dagChecker, this->CompileDefinitionsEntries);
4230 AddInterfaceEntries(this, config, "INTERFACE_COMPILE_DEFINITIONS", language,
4231 &dagChecker, entries, IncludeRuntimeInterface::Yes);
4233 if (!config.empty()) {
4234 std::string configPropName =
4235 "COMPILE_DEFINITIONS_" + cmSystemTools::UpperCase(config);
4236 cmValue configProp = this->GetProperty(configPropName);
4237 if (configProp) {
4238 switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0043)) {
4239 case cmPolicies::WARN: {
4240 this->LocalGenerator->IssueMessage(
4241 MessageType::AUTHOR_WARNING,
4242 cmPolicies::GetPolicyWarning(cmPolicies::CMP0043));
4243 CM_FALLTHROUGH;
4245 case cmPolicies::OLD: {
4246 std::unique_ptr<TargetPropertyEntry> entry =
4247 CreateTargetPropertyEntry(
4248 *this->LocalGenerator->GetCMakeInstance(), *configProp);
4249 entries.Entries.emplace_back(EvaluateTargetPropertyEntry(
4250 this, config, language, &dagChecker, *entry));
4251 } break;
4252 case cmPolicies::NEW:
4253 case cmPolicies::REQUIRED_ALWAYS:
4254 case cmPolicies::REQUIRED_IF_USED:
4255 break;
4260 processOptions(this, entries, list, uniqueOptions, debugDefines,
4261 "compile definitions", OptionsParse::None);
4263 this->CompileDefinitionsCache.emplace(cacheKey, list);
4264 return list;
4267 std::vector<BT<std::string>> cmGeneratorTarget::GetPrecompileHeaders(
4268 const std::string& config, const std::string& language) const
4270 ConfigAndLanguage cacheKey(config, language);
4272 auto it = this->PrecompileHeadersCache.find(cacheKey);
4273 if (it != this->PrecompileHeadersCache.end()) {
4274 return it->second;
4277 std::unordered_set<std::string> uniqueOptions;
4279 cmGeneratorExpressionDAGChecker dagChecker(this, "PRECOMPILE_HEADERS",
4280 nullptr, nullptr);
4282 cmList debugProperties{ this->Makefile->GetDefinition(
4283 "CMAKE_DEBUG_TARGET_PROPERTIES") };
4284 bool debugDefines = !this->DebugPrecompileHeadersDone &&
4285 std::find(debugProperties.begin(), debugProperties.end(),
4286 "PRECOMPILE_HEADERS") != debugProperties.end();
4288 if (this->GlobalGenerator->GetConfigureDoneCMP0026()) {
4289 this->DebugPrecompileHeadersDone = true;
4292 EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries(
4293 this, config, language, &dagChecker, this->PrecompileHeadersEntries);
4295 AddInterfaceEntries(this, config, "INTERFACE_PRECOMPILE_HEADERS", language,
4296 &dagChecker, entries, IncludeRuntimeInterface::Yes);
4298 std::vector<BT<std::string>> list;
4299 processOptions(this, entries, list, uniqueOptions, debugDefines,
4300 "precompile headers", OptionsParse::None);
4302 this->PrecompileHeadersCache.emplace(cacheKey, list);
4303 return list;
4306 std::string cmGeneratorTarget::GetPchHeader(const std::string& config,
4307 const std::string& language,
4308 const std::string& arch) const
4310 if (language != "C" && language != "CXX" && language != "OBJC" &&
4311 language != "OBJCXX") {
4312 return std::string();
4315 if (this->GetPropertyAsBool("DISABLE_PRECOMPILE_HEADERS")) {
4316 return std::string();
4318 const cmGeneratorTarget* generatorTarget = this;
4319 cmValue pchReuseFrom =
4320 generatorTarget->GetProperty("PRECOMPILE_HEADERS_REUSE_FROM");
4322 const auto inserted =
4323 this->PchHeaders.insert(std::make_pair(language + config + arch, ""));
4324 if (inserted.second) {
4325 const std::vector<BT<std::string>> headers =
4326 this->GetPrecompileHeaders(config, language);
4327 if (headers.empty() && !pchReuseFrom) {
4328 return std::string();
4330 std::string& filename = inserted.first->second;
4332 if (pchReuseFrom) {
4333 generatorTarget =
4334 this->GetGlobalGenerator()->FindGeneratorTarget(*pchReuseFrom);
4337 const std::map<std::string, std::string> languageToExtension = {
4338 { "C", ".h" },
4339 { "CXX", ".hxx" },
4340 { "OBJC", ".objc.h" },
4341 { "OBJCXX", ".objcxx.hxx" }
4344 filename = generatorTarget->GetSupportDirectory();
4346 if (this->GetGlobalGenerator()->IsMultiConfig()) {
4347 filename = cmStrCat(filename, "/", config);
4350 filename =
4351 cmStrCat(filename, "/cmake_pch", arch.empty() ? "" : cmStrCat("_", arch),
4352 languageToExtension.at(language));
4354 const std::string filename_tmp = cmStrCat(filename, ".tmp");
4355 if (!pchReuseFrom) {
4356 cmValue pchPrologue =
4357 this->Makefile->GetDefinition("CMAKE_PCH_PROLOGUE");
4358 cmValue pchEpilogue =
4359 this->Makefile->GetDefinition("CMAKE_PCH_EPILOGUE");
4361 std::string firstHeaderOnDisk;
4363 cmGeneratedFileStream file(
4364 filename_tmp, false,
4365 this->GetGlobalGenerator()->GetMakefileEncoding());
4366 file << "/* generated by CMake */\n\n";
4367 if (pchPrologue) {
4368 file << *pchPrologue << "\n";
4370 if (this->GetGlobalGenerator()->IsXcode()) {
4371 file << "#ifndef CMAKE_SKIP_PRECOMPILE_HEADERS\n";
4373 if (language == "CXX" && !this->GetGlobalGenerator()->IsXcode()) {
4374 file << "#ifdef __cplusplus\n";
4376 for (auto const& header_bt : headers) {
4377 if (header_bt.Value.empty()) {
4378 continue;
4380 if (header_bt.Value[0] == '<' || header_bt.Value[0] == '\"') {
4381 file << "#include " << header_bt.Value << "\n";
4382 } else {
4383 file << "#include \"" << header_bt.Value << "\"\n";
4386 if (cmSystemTools::FileExists(header_bt.Value) &&
4387 firstHeaderOnDisk.empty()) {
4388 firstHeaderOnDisk = header_bt.Value;
4391 if (language == "CXX" && !this->GetGlobalGenerator()->IsXcode()) {
4392 file << "#endif // __cplusplus\n";
4394 if (this->GetGlobalGenerator()->IsXcode()) {
4395 file << "#endif // CMAKE_SKIP_PRECOMPILE_HEADERS\n";
4397 if (pchEpilogue) {
4398 file << *pchEpilogue << "\n";
4402 if (!firstHeaderOnDisk.empty()) {
4403 cmFileTimes::Copy(firstHeaderOnDisk, filename_tmp);
4406 cmSystemTools::MoveFileIfDifferent(filename_tmp, filename);
4409 return inserted.first->second;
4412 std::string cmGeneratorTarget::GetPchSource(const std::string& config,
4413 const std::string& language,
4414 const std::string& arch) const
4416 if (language != "C" && language != "CXX" && language != "OBJC" &&
4417 language != "OBJCXX") {
4418 return std::string();
4420 const auto inserted =
4421 this->PchSources.insert(std::make_pair(language + config + arch, ""));
4422 if (inserted.second) {
4423 const std::string pchHeader = this->GetPchHeader(config, language, arch);
4424 if (pchHeader.empty()) {
4425 return std::string();
4427 std::string& filename = inserted.first->second;
4429 const cmGeneratorTarget* generatorTarget = this;
4430 cmValue pchReuseFrom =
4431 generatorTarget->GetProperty("PRECOMPILE_HEADERS_REUSE_FROM");
4432 if (pchReuseFrom) {
4433 generatorTarget =
4434 this->GetGlobalGenerator()->FindGeneratorTarget(*pchReuseFrom);
4437 filename = cmStrCat(generatorTarget->GetSupportDirectory(), "/cmake_pch");
4439 // For GCC the source extension will be transformed into .h[xx].gch
4440 if (!this->Makefile->IsOn("CMAKE_LINK_PCH")) {
4441 const std::map<std::string, std::string> languageToExtension = {
4442 { "C", ".h.c" },
4443 { "CXX", ".hxx.cxx" },
4444 { "OBJC", ".objc.h.m" },
4445 { "OBJCXX", ".objcxx.hxx.mm" }
4448 filename = cmStrCat(filename, arch.empty() ? "" : cmStrCat("_", arch),
4449 languageToExtension.at(language));
4450 } else {
4451 const std::map<std::string, std::string> languageToExtension = {
4452 { "C", ".c" }, { "CXX", ".cxx" }, { "OBJC", ".m" }, { "OBJCXX", ".mm" }
4455 filename = cmStrCat(filename, arch.empty() ? "" : cmStrCat("_", arch),
4456 languageToExtension.at(language));
4459 const std::string filename_tmp = cmStrCat(filename, ".tmp");
4460 if (!pchReuseFrom) {
4462 cmGeneratedFileStream file(filename_tmp);
4463 file << "/* generated by CMake */\n";
4465 cmFileTimes::Copy(pchHeader, filename_tmp);
4466 cmSystemTools::MoveFileIfDifferent(filename_tmp, filename);
4469 return inserted.first->second;
4472 std::string cmGeneratorTarget::GetPchFileObject(const std::string& config,
4473 const std::string& language,
4474 const std::string& arch)
4476 if (language != "C" && language != "CXX" && language != "OBJC" &&
4477 language != "OBJCXX") {
4478 return std::string();
4480 const auto inserted =
4481 this->PchObjectFiles.insert(std::make_pair(language + config + arch, ""));
4482 if (inserted.second) {
4483 const std::string pchSource = this->GetPchSource(config, language, arch);
4484 if (pchSource.empty()) {
4485 return std::string();
4487 std::string& filename = inserted.first->second;
4489 auto* pchSf = this->Makefile->GetOrCreateSource(
4490 pchSource, false, cmSourceFileLocationKind::Known);
4492 filename = cmStrCat(this->ObjectDirectory, this->GetObjectName(pchSf));
4493 if (this->GetGlobalGenerator()->IsMultiConfig()) {
4494 cmSystemTools::ReplaceString(
4495 filename, this->GetGlobalGenerator()->GetCMakeCFGIntDir(), config);
4498 return inserted.first->second;
4501 std::string cmGeneratorTarget::GetPchFile(const std::string& config,
4502 const std::string& language,
4503 const std::string& arch)
4505 const auto inserted =
4506 this->PchFiles.insert(std::make_pair(language + config + arch, ""));
4507 if (inserted.second) {
4508 std::string& pchFile = inserted.first->second;
4510 const std::string pchExtension =
4511 this->Makefile->GetSafeDefinition("CMAKE_PCH_EXTENSION");
4513 if (this->Makefile->IsOn("CMAKE_LINK_PCH")) {
4514 auto replaceExtension = [](const std::string& str,
4515 const std::string& ext) -> std::string {
4516 auto dot_pos = str.rfind('.');
4517 std::string result;
4518 if (dot_pos != std::string::npos) {
4519 result = str.substr(0, dot_pos);
4521 result += ext;
4522 return result;
4525 cmGeneratorTarget* generatorTarget = this;
4526 cmValue pchReuseFrom =
4527 generatorTarget->GetProperty("PRECOMPILE_HEADERS_REUSE_FROM");
4528 if (pchReuseFrom) {
4529 generatorTarget =
4530 this->GetGlobalGenerator()->FindGeneratorTarget(*pchReuseFrom);
4533 const std::string pchFileObject =
4534 generatorTarget->GetPchFileObject(config, language, arch);
4535 if (!pchExtension.empty()) {
4536 pchFile = replaceExtension(pchFileObject, pchExtension);
4538 } else {
4539 pchFile = this->GetPchHeader(config, language, arch);
4540 pchFile += pchExtension;
4543 return inserted.first->second;
4546 std::string cmGeneratorTarget::GetPchCreateCompileOptions(
4547 const std::string& config, const std::string& language,
4548 const std::string& arch)
4550 const auto inserted = this->PchCreateCompileOptions.insert(
4551 std::make_pair(language + config + arch, ""));
4552 if (inserted.second) {
4553 std::string& createOptionList = inserted.first->second;
4555 if (this->GetPropertyAsBool("PCH_WARN_INVALID")) {
4556 createOptionList = this->Makefile->GetSafeDefinition(
4557 cmStrCat("CMAKE_", language, "_COMPILE_OPTIONS_INVALID_PCH"));
4560 if (this->GetPropertyAsBool("PCH_INSTANTIATE_TEMPLATES")) {
4561 std::string varName = cmStrCat(
4562 "CMAKE_", language, "_COMPILE_OPTIONS_INSTANTIATE_TEMPLATES_PCH");
4563 std::string instantiateOption =
4564 this->Makefile->GetSafeDefinition(varName);
4565 if (!instantiateOption.empty()) {
4566 createOptionList = cmStrCat(createOptionList, ";", instantiateOption);
4570 const std::string createOptVar =
4571 cmStrCat("CMAKE_", language, "_COMPILE_OPTIONS_CREATE_PCH");
4573 createOptionList = cmStrCat(
4574 createOptionList, ";", this->Makefile->GetSafeDefinition(createOptVar));
4576 const std::string pchHeader = this->GetPchHeader(config, language, arch);
4577 const std::string pchFile = this->GetPchFile(config, language, arch);
4579 cmSystemTools::ReplaceString(createOptionList, "<PCH_HEADER>", pchHeader);
4580 cmSystemTools::ReplaceString(createOptionList, "<PCH_FILE>", pchFile);
4582 return inserted.first->second;
4585 std::string cmGeneratorTarget::GetPchUseCompileOptions(
4586 const std::string& config, const std::string& language,
4587 const std::string& arch)
4589 const auto inserted = this->PchUseCompileOptions.insert(
4590 std::make_pair(language + config + arch, ""));
4591 if (inserted.second) {
4592 std::string& useOptionList = inserted.first->second;
4594 if (this->GetPropertyAsBool("PCH_WARN_INVALID")) {
4595 useOptionList = this->Makefile->GetSafeDefinition(
4596 cmStrCat("CMAKE_", language, "_COMPILE_OPTIONS_INVALID_PCH"));
4599 const std::string useOptVar =
4600 cmStrCat(language, "_COMPILE_OPTIONS_USE_PCH");
4602 std::string const& useOptionListProperty =
4603 this->GetSafeProperty(useOptVar);
4605 useOptionList = cmStrCat(
4606 useOptionList, ";",
4607 useOptionListProperty.empty()
4608 ? this->Makefile->GetSafeDefinition(cmStrCat("CMAKE_", useOptVar))
4609 : useOptionListProperty);
4611 const std::string pchHeader = this->GetPchHeader(config, language, arch);
4612 const std::string pchFile = this->GetPchFile(config, language, arch);
4614 cmSystemTools::ReplaceString(useOptionList, "<PCH_HEADER>", pchHeader);
4615 cmSystemTools::ReplaceString(useOptionList, "<PCH_FILE>", pchFile);
4617 return inserted.first->second;
4620 void cmGeneratorTarget::AddSourceFileToUnityBatch(
4621 const std::string& sourceFilename)
4623 this->UnityBatchedSourceFiles.insert(sourceFilename);
4626 bool cmGeneratorTarget::IsSourceFilePartOfUnityBatch(
4627 const std::string& sourceFilename) const
4629 if (!this->GetPropertyAsBool("UNITY_BUILD")) {
4630 return false;
4633 return this->UnityBatchedSourceFiles.find(sourceFilename) !=
4634 this->UnityBatchedSourceFiles.end();
4637 void cmGeneratorTarget::GetLinkOptions(std::vector<std::string>& result,
4638 const std::string& config,
4639 const std::string& language) const
4641 if (this->IsDeviceLink() &&
4642 this->GetPolicyStatusCMP0105() != cmPolicies::NEW) {
4643 // link options are not propagated to the device link step
4644 return;
4647 std::vector<BT<std::string>> tmp = this->GetLinkOptions(config, language);
4648 result.reserve(tmp.size());
4649 for (BT<std::string>& v : tmp) {
4650 result.emplace_back(std::move(v.Value));
4654 std::vector<BT<std::string>> cmGeneratorTarget::GetLinkOptions(
4655 std::string const& config, std::string const& language) const
4657 ConfigAndLanguage cacheKey(
4658 config, cmStrCat(language, this->IsDeviceLink() ? "-device" : ""));
4660 auto it = this->LinkOptionsCache.find(cacheKey);
4661 if (it != this->LinkOptionsCache.end()) {
4662 return it->second;
4665 std::vector<BT<std::string>> result;
4666 std::unordered_set<std::string> uniqueOptions;
4668 cmGeneratorExpressionDAGChecker dagChecker(this, "LINK_OPTIONS", nullptr,
4669 nullptr);
4671 cmList debugProperties{ this->Makefile->GetDefinition(
4672 "CMAKE_DEBUG_TARGET_PROPERTIES") };
4673 bool debugOptions = !this->DebugLinkOptionsDone &&
4674 cm::contains(debugProperties, "LINK_OPTIONS");
4676 if (this->GlobalGenerator->GetConfigureDoneCMP0026()) {
4677 this->DebugLinkOptionsDone = true;
4680 EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries(
4681 this, config, language, &dagChecker, this->LinkOptionsEntries);
4683 AddInterfaceEntries(this, config, "INTERFACE_LINK_OPTIONS", language,
4684 &dagChecker, entries, IncludeRuntimeInterface::Yes,
4685 this->GetPolicyStatusCMP0099() == cmPolicies::NEW
4686 ? LinkInterfaceFor::Link
4687 : LinkInterfaceFor::Usage);
4689 processOptions(this, entries, result, uniqueOptions, debugOptions,
4690 "link options", OptionsParse::Shell, this->IsDeviceLink());
4692 if (this->IsDeviceLink()) {
4693 // wrap host link options
4694 const std::string wrapper(this->Makefile->GetSafeDefinition(
4695 "CMAKE_" + language + "_DEVICE_COMPILER_WRAPPER_FLAG"));
4696 cmList wrapperFlag{ wrapper };
4697 const std::string wrapperSep(this->Makefile->GetSafeDefinition(
4698 "CMAKE_" + language + "_DEVICE_COMPILER_WRAPPER_FLAG_SEP"));
4699 bool concatFlagAndArgs = true;
4700 if (!wrapperFlag.empty() && wrapperFlag.back() == " ") {
4701 concatFlagAndArgs = false;
4702 wrapperFlag.pop_back();
4705 auto it = result.begin();
4706 while (it != result.end()) {
4707 if (it->Value == DL_BEGIN) {
4708 // device link options, no treatment
4709 it = result.erase(it);
4710 it = std::find_if(it, result.end(), [](const BT<std::string>& item) {
4711 return item.Value == DL_END;
4713 if (it != result.end()) {
4714 it = result.erase(it);
4716 } else {
4717 // host link options must be wrapped
4718 std::vector<std::string> options;
4719 cmSystemTools::ParseUnixCommandLine(it->Value.c_str(), options);
4720 auto hostOptions = wrapOptions(options, it->Backtrace, wrapperFlag,
4721 wrapperSep, concatFlagAndArgs);
4722 it = result.erase(it);
4723 // some compilers (like gcc 4.8 or Intel 19.0 or XLC 16) do not respect
4724 // C++11 standard: 'std::vector::insert()' do not returns an iterator,
4725 // so need to recompute the iterator after insertion.
4726 if (it == result.end()) {
4727 cm::append(result, hostOptions);
4728 it = result.end();
4729 } else {
4730 auto index = it - result.begin();
4731 result.insert(it, hostOptions.begin(), hostOptions.end());
4732 it = result.begin() + index + hostOptions.size();
4738 // Last step: replace "LINKER:" prefixed elements by
4739 // actual linker wrapper
4740 result = this->ResolveLinkerWrapper(result, language);
4742 this->LinkOptionsCache.emplace(cacheKey, result);
4743 return result;
4746 std::vector<BT<std::string>>& cmGeneratorTarget::ResolveLinkerWrapper(
4747 std::vector<BT<std::string>>& result, const std::string& language,
4748 bool joinItems) const
4750 // replace "LINKER:" prefixed elements by actual linker wrapper
4751 const std::string wrapper(this->Makefile->GetSafeDefinition(
4752 "CMAKE_" + language +
4753 (this->IsDeviceLink() ? "_DEVICE_LINKER_WRAPPER_FLAG"
4754 : "_LINKER_WRAPPER_FLAG")));
4755 cmList wrapperFlag{ wrapper };
4756 const std::string wrapperSep(this->Makefile->GetSafeDefinition(
4757 "CMAKE_" + language +
4758 (this->IsDeviceLink() ? "_DEVICE_LINKER_WRAPPER_FLAG_SEP"
4759 : "_LINKER_WRAPPER_FLAG_SEP")));
4760 bool concatFlagAndArgs = true;
4761 if (!wrapperFlag.empty() && wrapperFlag.back() == " ") {
4762 concatFlagAndArgs = false;
4763 wrapperFlag.pop_back();
4766 const std::string LINKER{ "LINKER:" };
4767 const std::string SHELL{ "SHELL:" };
4768 const std::string LINKER_SHELL = LINKER + SHELL;
4770 std::vector<BT<std::string>>::iterator entry;
4771 while ((entry = std::find_if(result.begin(), result.end(),
4772 [&LINKER](BT<std::string> const& item) -> bool {
4773 return item.Value.compare(0, LINKER.length(),
4774 LINKER) == 0;
4775 })) != result.end()) {
4776 std::string value = std::move(entry->Value);
4777 cmListFileBacktrace bt = std::move(entry->Backtrace);
4778 entry = result.erase(entry);
4780 std::vector<std::string> linkerOptions;
4781 if (value.compare(0, LINKER_SHELL.length(), LINKER_SHELL) == 0) {
4782 cmSystemTools::ParseUnixCommandLine(
4783 value.c_str() + LINKER_SHELL.length(), linkerOptions);
4784 } else {
4785 linkerOptions = cmTokenize(value.substr(LINKER.length()), ",");
4788 if (linkerOptions.empty() ||
4789 (linkerOptions.size() == 1 && linkerOptions.front().empty())) {
4790 continue;
4793 // for now, raise an error if prefix SHELL: is part of arguments
4794 if (std::find_if(linkerOptions.begin(), linkerOptions.end(),
4795 [&SHELL](const std::string& item) -> bool {
4796 return item.find(SHELL) != std::string::npos;
4797 }) != linkerOptions.end()) {
4798 this->LocalGenerator->GetCMakeInstance()->IssueMessage(
4799 MessageType::FATAL_ERROR,
4800 "'SHELL:' prefix is not supported as part of 'LINKER:' arguments.",
4801 this->GetBacktrace());
4802 return result;
4805 std::vector<BT<std::string>> options = wrapOptions(
4806 linkerOptions, bt, wrapperFlag, wrapperSep, concatFlagAndArgs);
4807 if (joinItems) {
4808 result.insert(entry,
4809 cmJoin(cmRange<decltype(options.cbegin())>(
4810 options.cbegin(), options.cend()),
4811 " "_s));
4812 } else {
4813 result.insert(entry, options.begin(), options.end());
4816 return result;
4819 void cmGeneratorTarget::GetStaticLibraryLinkOptions(
4820 std::vector<std::string>& result, const std::string& config,
4821 const std::string& language) const
4823 std::vector<BT<std::string>> tmp =
4824 this->GetStaticLibraryLinkOptions(config, language);
4825 result.reserve(tmp.size());
4826 for (BT<std::string>& v : tmp) {
4827 result.emplace_back(std::move(v.Value));
4831 std::vector<BT<std::string>> cmGeneratorTarget::GetStaticLibraryLinkOptions(
4832 std::string const& config, std::string const& language) const
4834 std::vector<BT<std::string>> result;
4835 std::unordered_set<std::string> uniqueOptions;
4837 cmGeneratorExpressionDAGChecker dagChecker(this, "STATIC_LIBRARY_OPTIONS",
4838 nullptr, nullptr);
4840 EvaluatedTargetPropertyEntries entries;
4841 if (cmValue linkOptions = this->GetProperty("STATIC_LIBRARY_OPTIONS")) {
4842 std::unique_ptr<TargetPropertyEntry> entry = CreateTargetPropertyEntry(
4843 *this->LocalGenerator->GetCMakeInstance(), *linkOptions);
4844 entries.Entries.emplace_back(EvaluateTargetPropertyEntry(
4845 this, config, language, &dagChecker, *entry));
4847 processOptions(this, entries, result, uniqueOptions, false,
4848 "static library link options", OptionsParse::Shell);
4850 return result;
4853 namespace {
4854 void processLinkDirectories(cmGeneratorTarget const* tgt,
4855 EvaluatedTargetPropertyEntries& entries,
4856 std::vector<BT<std::string>>& directories,
4857 std::unordered_set<std::string>& uniqueDirectories,
4858 bool debugDirectories)
4860 for (EvaluatedTargetPropertyEntry& entry : entries.Entries) {
4861 cmLinkImplItem const& item = entry.LinkImplItem;
4862 std::string const& targetName = item.AsStr();
4864 std::string usedDirectories;
4865 for (std::string& entryDirectory : entry.Values) {
4866 if (!cmSystemTools::FileIsFullPath(entryDirectory)) {
4867 std::ostringstream e;
4868 bool noMessage = false;
4869 MessageType messageType = MessageType::FATAL_ERROR;
4870 if (!targetName.empty()) {
4871 /* clang-format off */
4872 e << "Target \"" << targetName << "\" contains relative "
4873 "path in its INTERFACE_LINK_DIRECTORIES:\n"
4874 " \"" << entryDirectory << "\"";
4875 /* clang-format on */
4876 } else {
4877 switch (tgt->GetPolicyStatusCMP0081()) {
4878 case cmPolicies::WARN: {
4879 e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0081) << "\n";
4880 messageType = MessageType::AUTHOR_WARNING;
4881 } break;
4882 case cmPolicies::OLD:
4883 noMessage = true;
4884 break;
4885 case cmPolicies::REQUIRED_IF_USED:
4886 case cmPolicies::REQUIRED_ALWAYS:
4887 case cmPolicies::NEW:
4888 // Issue the fatal message.
4889 break;
4891 e << "Found relative path while evaluating link directories of "
4892 "\""
4893 << tgt->GetName() << "\":\n \"" << entryDirectory << "\"\n";
4895 if (!noMessage) {
4896 tgt->GetLocalGenerator()->IssueMessage(messageType, e.str());
4897 if (messageType == MessageType::FATAL_ERROR) {
4898 return;
4903 // Sanitize the path the same way the link_directories command does
4904 // in case projects set the LINK_DIRECTORIES property directly.
4905 cmSystemTools::ConvertToUnixSlashes(entryDirectory);
4906 if (uniqueDirectories.insert(entryDirectory).second) {
4907 directories.emplace_back(entryDirectory, entry.Backtrace);
4908 if (debugDirectories) {
4909 usedDirectories += " * " + entryDirectory + "\n";
4913 if (!usedDirectories.empty()) {
4914 tgt->GetLocalGenerator()->GetCMakeInstance()->IssueMessage(
4915 MessageType::LOG,
4916 std::string("Used link directories for target ") + tgt->GetName() +
4917 ":\n" + usedDirectories,
4918 entry.Backtrace);
4924 void cmGeneratorTarget::GetLinkDirectories(std::vector<std::string>& result,
4925 const std::string& config,
4926 const std::string& language) const
4928 std::vector<BT<std::string>> tmp =
4929 this->GetLinkDirectories(config, language);
4930 result.reserve(tmp.size());
4931 for (BT<std::string>& v : tmp) {
4932 result.emplace_back(std::move(v.Value));
4936 std::vector<BT<std::string>> cmGeneratorTarget::GetLinkDirectories(
4937 std::string const& config, std::string const& language) const
4939 ConfigAndLanguage cacheKey(
4940 config, cmStrCat(language, this->IsDeviceLink() ? "-device" : ""));
4942 auto it = this->LinkDirectoriesCache.find(cacheKey);
4943 if (it != this->LinkDirectoriesCache.end()) {
4944 return it->second;
4947 std::vector<BT<std::string>> result;
4948 std::unordered_set<std::string> uniqueDirectories;
4950 cmGeneratorExpressionDAGChecker dagChecker(this, "LINK_DIRECTORIES", nullptr,
4951 nullptr);
4953 cmList debugProperties{ this->Makefile->GetDefinition(
4954 "CMAKE_DEBUG_TARGET_PROPERTIES") };
4955 bool debugDirectories = !this->DebugLinkDirectoriesDone &&
4956 cm::contains(debugProperties, "LINK_DIRECTORIES");
4958 if (this->GlobalGenerator->GetConfigureDoneCMP0026()) {
4959 this->DebugLinkDirectoriesDone = true;
4962 EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries(
4963 this, config, language, &dagChecker, this->LinkDirectoriesEntries);
4965 AddInterfaceEntries(this, config, "INTERFACE_LINK_DIRECTORIES", language,
4966 &dagChecker, entries, IncludeRuntimeInterface::Yes,
4967 this->GetPolicyStatusCMP0099() == cmPolicies::NEW
4968 ? LinkInterfaceFor::Link
4969 : LinkInterfaceFor::Usage);
4971 processLinkDirectories(this, entries, result, uniqueDirectories,
4972 debugDirectories);
4974 this->LinkDirectoriesCache.emplace(cacheKey, result);
4975 return result;
4978 void cmGeneratorTarget::GetLinkDepends(std::vector<std::string>& result,
4979 const std::string& config,
4980 const std::string& language) const
4982 std::vector<BT<std::string>> tmp = this->GetLinkDepends(config, language);
4983 result.reserve(tmp.size());
4984 for (BT<std::string>& v : tmp) {
4985 result.emplace_back(std::move(v.Value));
4989 std::vector<BT<std::string>> cmGeneratorTarget::GetLinkDepends(
4990 std::string const& config, std::string const& language) const
4992 std::vector<BT<std::string>> result;
4993 std::unordered_set<std::string> uniqueOptions;
4994 cmGeneratorExpressionDAGChecker dagChecker(this, "LINK_DEPENDS", nullptr,
4995 nullptr);
4997 EvaluatedTargetPropertyEntries entries;
4998 if (cmValue linkDepends = this->GetProperty("LINK_DEPENDS")) {
4999 cmList depends{ *linkDepends };
5000 for (const auto& depend : depends) {
5001 std::unique_ptr<TargetPropertyEntry> entry = CreateTargetPropertyEntry(
5002 *this->LocalGenerator->GetCMakeInstance(), depend);
5003 entries.Entries.emplace_back(EvaluateTargetPropertyEntry(
5004 this, config, language, &dagChecker, *entry));
5007 AddInterfaceEntries(this, config, "INTERFACE_LINK_DEPENDS", language,
5008 &dagChecker, entries, IncludeRuntimeInterface::Yes,
5009 this->GetPolicyStatusCMP0099() == cmPolicies::NEW
5010 ? LinkInterfaceFor::Link
5011 : LinkInterfaceFor::Usage);
5013 processOptions(this, entries, result, uniqueOptions, false, "link depends",
5014 OptionsParse::None);
5016 return result;
5019 void cmGeneratorTarget::ComputeTargetManifest(const std::string& config) const
5021 if (this->IsImported()) {
5022 return;
5024 cmGlobalGenerator* gg = this->LocalGenerator->GetGlobalGenerator();
5026 // Get the names.
5027 cmGeneratorTarget::Names targetNames;
5028 if (this->GetType() == cmStateEnums::EXECUTABLE) {
5029 targetNames = this->GetExecutableNames(config);
5030 } else if (this->GetType() == cmStateEnums::STATIC_LIBRARY ||
5031 this->GetType() == cmStateEnums::SHARED_LIBRARY ||
5032 this->GetType() == cmStateEnums::MODULE_LIBRARY) {
5033 targetNames = this->GetLibraryNames(config);
5034 } else {
5035 return;
5038 // Get the directory.
5039 std::string dir =
5040 this->GetDirectory(config, cmStateEnums::RuntimeBinaryArtifact);
5042 // Add each name.
5043 std::string f;
5044 if (!targetNames.Output.empty()) {
5045 f = cmStrCat(dir, '/', targetNames.Output);
5046 gg->AddToManifest(f);
5048 if (!targetNames.SharedObject.empty()) {
5049 f = cmStrCat(dir, '/', targetNames.SharedObject);
5050 gg->AddToManifest(f);
5052 if (!targetNames.Real.empty()) {
5053 f = cmStrCat(dir, '/', targetNames.Real);
5054 gg->AddToManifest(f);
5056 if (!targetNames.PDB.empty()) {
5057 f = cmStrCat(dir, '/', targetNames.PDB);
5058 gg->AddToManifest(f);
5061 dir = this->GetDirectory(config, cmStateEnums::ImportLibraryArtifact);
5062 if (!targetNames.ImportOutput.empty()) {
5063 f = cmStrCat(dir, '/', targetNames.ImportOutput);
5064 gg->AddToManifest(f);
5066 if (!targetNames.ImportLibrary.empty()) {
5067 f = cmStrCat(dir, '/', targetNames.ImportLibrary);
5068 gg->AddToManifest(f);
5070 if (!targetNames.ImportReal.empty()) {
5071 f = cmStrCat(dir, '/', targetNames.ImportReal);
5072 gg->AddToManifest(f);
5076 cm::optional<cmStandardLevel> cmGeneratorTarget::GetExplicitStandardLevel(
5077 std::string const& lang, std::string const& config) const
5079 cm::optional<cmStandardLevel> level;
5080 std::string key = cmStrCat(cmSystemTools::UpperCase(config), '-', lang);
5081 auto i = this->ExplicitStandardLevel.find(key);
5082 if (i != this->ExplicitStandardLevel.end()) {
5083 level = i->second;
5085 return level;
5088 void cmGeneratorTarget::UpdateExplicitStandardLevel(std::string const& lang,
5089 std::string const& config,
5090 cmStandardLevel level)
5092 auto e = this->ExplicitStandardLevel.emplace(
5093 cmStrCat(cmSystemTools::UpperCase(config), '-', lang), level);
5094 if (!e.second && e.first->second < level) {
5095 e.first->second = level;
5099 bool cmGeneratorTarget::ComputeCompileFeatures(std::string const& config)
5101 cmStandardLevelResolver standardResolver(this->Makefile);
5103 for (std::string const& lang :
5104 this->Makefile->GetState()->GetEnabledLanguages()) {
5105 if (cmValue languageStd = this->GetLanguageStandard(lang, config)) {
5106 if (cm::optional<cmStandardLevel> langLevel =
5107 standardResolver.LanguageStandardLevel(lang, *languageStd)) {
5108 this->UpdateExplicitStandardLevel(lang, config, *langLevel);
5113 // Compute the language standard based on the compile features.
5114 std::vector<BT<std::string>> features = this->GetCompileFeatures(config);
5115 for (BT<std::string> const& f : features) {
5116 std::string lang;
5117 if (!standardResolver.CompileFeatureKnown(this->Target->GetName(), f.Value,
5118 lang, nullptr)) {
5119 return false;
5122 std::string key = cmStrCat(cmSystemTools::UpperCase(config), '-', lang);
5123 cmValue currentLanguageStandard = this->GetLanguageStandard(lang, config);
5125 cm::optional<cmStandardLevel> featureLevel;
5126 std::string newRequiredStandard;
5127 if (!standardResolver.GetNewRequiredStandard(
5128 this->Target->GetName(), f.Value, currentLanguageStandard,
5129 featureLevel, newRequiredStandard)) {
5130 return false;
5133 if (featureLevel) {
5134 this->UpdateExplicitStandardLevel(lang, config, *featureLevel);
5137 if (!newRequiredStandard.empty()) {
5138 BTs<std::string>& languageStandardProperty =
5139 this->LanguageStandardMap[key];
5140 if (languageStandardProperty.Value != newRequiredStandard) {
5141 languageStandardProperty.Value = newRequiredStandard;
5142 languageStandardProperty.Backtraces.clear();
5144 languageStandardProperty.Backtraces.emplace_back(f.Backtrace);
5148 return true;
5151 bool cmGeneratorTarget::ComputeCompileFeatures(
5152 std::string const& config, std::set<LanguagePair> const& languagePairs)
5154 for (const auto& language : languagePairs) {
5155 BTs<std::string> const* generatorTargetLanguageStandard =
5156 this->GetLanguageStandardProperty(language.first, config);
5157 if (!generatorTargetLanguageStandard) {
5158 // If the standard isn't explicitly set we copy it over from the
5159 // specified paired language.
5160 std::string key =
5161 cmStrCat(cmSystemTools::UpperCase(config), '-', language.first);
5162 BTs<std::string> const* standardToCopy =
5163 this->GetLanguageStandardProperty(language.second, config);
5164 if (standardToCopy) {
5165 this->LanguageStandardMap[key] = *standardToCopy;
5166 generatorTargetLanguageStandard = &this->LanguageStandardMap[key];
5167 } else {
5168 cmValue defaultStandard = this->Makefile->GetDefinition(
5169 cmStrCat("CMAKE_", language.second, "_STANDARD_DEFAULT"));
5170 if (defaultStandard) {
5171 this->LanguageStandardMap[key] = BTs<std::string>(*defaultStandard);
5172 generatorTargetLanguageStandard = &this->LanguageStandardMap[key];
5176 // Custom updates for the CUDA standard.
5177 if (generatorTargetLanguageStandard != nullptr &&
5178 (language.first == "CUDA")) {
5179 if (generatorTargetLanguageStandard->Value == "98") {
5180 this->LanguageStandardMap[key].Value = "03";
5186 return true;
5189 std::string cmGeneratorTarget::GetImportedLibName(
5190 std::string const& config) const
5192 if (cmGeneratorTarget::ImportInfo const* info =
5193 this->GetImportInfo(config)) {
5194 return info->LibName;
5196 return std::string();
5199 std::string cmGeneratorTarget::GetFullPath(const std::string& config,
5200 cmStateEnums::ArtifactType artifact,
5201 bool realname) const
5203 if (this->IsImported()) {
5204 return this->Target->ImportedGetFullPath(config, artifact);
5206 return this->NormalGetFullPath(config, artifact, realname);
5209 std::string cmGeneratorTarget::NormalGetFullPath(
5210 const std::string& config, cmStateEnums::ArtifactType artifact,
5211 bool realname) const
5213 std::string fpath = cmStrCat(this->GetDirectory(config, artifact), '/');
5214 if (this->IsAppBundleOnApple()) {
5215 fpath =
5216 cmStrCat(this->BuildBundleDirectory(fpath, config, FullLevel), '/');
5219 // Add the full name of the target.
5220 switch (artifact) {
5221 case cmStateEnums::RuntimeBinaryArtifact:
5222 if (realname) {
5223 fpath += this->NormalGetRealName(config);
5224 } else {
5225 fpath +=
5226 this->GetFullName(config, cmStateEnums::RuntimeBinaryArtifact);
5228 break;
5229 case cmStateEnums::ImportLibraryArtifact:
5230 if (realname) {
5231 fpath +=
5232 this->NormalGetRealName(config, cmStateEnums::ImportLibraryArtifact);
5233 } else {
5234 fpath +=
5235 this->GetFullName(config, cmStateEnums::ImportLibraryArtifact);
5237 break;
5239 return fpath;
5242 std::string cmGeneratorTarget::NormalGetRealName(
5243 const std::string& config, cmStateEnums::ArtifactType artifact) const
5245 // This should not be called for imported targets.
5246 // TODO: Split cmTarget into a class hierarchy to get compile-time
5247 // enforcement of the limited imported target API.
5248 if (this->IsImported()) {
5249 std::string msg = cmStrCat("NormalGetRealName called on imported target: ",
5250 this->GetName());
5251 this->LocalGenerator->IssueMessage(MessageType::INTERNAL_ERROR, msg);
5254 Names names = this->GetType() == cmStateEnums::EXECUTABLE
5255 ? this->GetExecutableNames(config)
5256 : this->GetLibraryNames(config);
5258 // Compute the real name that will be built.
5259 return artifact == cmStateEnums::RuntimeBinaryArtifact ? names.Real
5260 : names.ImportReal;
5263 cmGeneratorTarget::Names cmGeneratorTarget::GetLibraryNames(
5264 const std::string& config) const
5266 cmGeneratorTarget::Names targetNames;
5268 // This should not be called for imported targets.
5269 // TODO: Split cmTarget into a class hierarchy to get compile-time
5270 // enforcement of the limited imported target API.
5271 if (this->IsImported()) {
5272 std::string msg =
5273 cmStrCat("GetLibraryNames called on imported target: ", this->GetName());
5274 this->LocalGenerator->IssueMessage(MessageType::INTERNAL_ERROR, msg);
5277 // Check for library version properties.
5278 cmValue version = this->GetProperty("VERSION");
5279 cmValue soversion = this->GetProperty("SOVERSION");
5280 if (!this->HasSOName(config) ||
5281 this->Makefile->IsOn("CMAKE_PLATFORM_NO_VERSIONED_SONAME") ||
5282 this->IsFrameworkOnApple()) {
5283 // Versioning is supported only for shared libraries and modules,
5284 // and then only when the platform supports an soname flag.
5285 version = nullptr;
5286 soversion = nullptr;
5288 if (version && !soversion) {
5289 // The soversion must be set if the library version is set. Use
5290 // the library version as the soversion.
5291 soversion = version;
5293 if (!version && soversion) {
5294 // Use the soversion as the library version.
5295 version = soversion;
5298 // Get the components of the library name.
5299 NameComponents const& components = this->GetFullNameInternalComponents(
5300 config, cmStateEnums::RuntimeBinaryArtifact);
5302 // The library name.
5303 targetNames.Base = components.base;
5304 targetNames.Output =
5305 cmStrCat(components.prefix, targetNames.Base, components.suffix);
5307 if (this->IsFrameworkOnApple()) {
5308 targetNames.Real = components.prefix;
5309 if (!this->Makefile->PlatformIsAppleEmbedded()) {
5310 targetNames.Real +=
5311 cmStrCat("Versions/", this->GetFrameworkVersion(), '/');
5313 targetNames.Real += cmStrCat(targetNames.Base, components.suffix);
5314 targetNames.SharedObject = targetNames.Real;
5315 } else {
5316 // The library's soname.
5317 this->ComputeVersionedName(targetNames.SharedObject, components.prefix,
5318 targetNames.Base, components.suffix,
5319 targetNames.Output, soversion);
5321 // The library's real name on disk.
5322 this->ComputeVersionedName(targetNames.Real, components.prefix,
5323 targetNames.Base, components.suffix,
5324 targetNames.Output, version);
5327 // The import library names.
5328 if (this->GetType() == cmStateEnums::SHARED_LIBRARY ||
5329 this->GetType() == cmStateEnums::MODULE_LIBRARY) {
5330 NameComponents const& importComponents =
5331 this->GetFullNameInternalComponents(config,
5332 cmStateEnums::ImportLibraryArtifact);
5333 targetNames.ImportOutput = cmStrCat(
5334 importComponents.prefix, importComponents.base, importComponents.suffix);
5336 if (this->IsFrameworkOnApple() && this->IsSharedLibraryWithExports()) {
5337 targetNames.ImportReal = components.prefix;
5338 if (!this->Makefile->PlatformIsAppleEmbedded()) {
5339 targetNames.ImportReal +=
5340 cmStrCat("Versions/", this->GetFrameworkVersion(), '/');
5342 targetNames.ImportReal +=
5343 cmStrCat(importComponents.base, importComponents.suffix);
5344 targetNames.ImportLibrary = targetNames.ImportOutput;
5345 } else {
5346 // The import library's soname.
5347 this->ComputeVersionedName(
5348 targetNames.ImportLibrary, importComponents.prefix,
5349 importComponents.base, importComponents.suffix,
5350 targetNames.ImportOutput, soversion);
5352 // The import library's real name on disk.
5353 this->ComputeVersionedName(
5354 targetNames.ImportReal, importComponents.prefix, importComponents.base,
5355 importComponents.suffix, targetNames.ImportOutput, version);
5359 // The program database file name.
5360 targetNames.PDB = this->GetPDBName(config);
5362 return targetNames;
5365 cmGeneratorTarget::Names cmGeneratorTarget::GetExecutableNames(
5366 const std::string& config) const
5368 cmGeneratorTarget::Names targetNames;
5370 // This should not be called for imported targets.
5371 // TODO: Split cmTarget into a class hierarchy to get compile-time
5372 // enforcement of the limited imported target API.
5373 if (this->IsImported()) {
5374 std::string msg = cmStrCat(
5375 "GetExecutableNames called on imported target: ", this->GetName());
5376 this->LocalGenerator->IssueMessage(MessageType::INTERNAL_ERROR, msg);
5379 // This versioning is supported only for executables and then only
5380 // when the platform supports symbolic links.
5381 #if defined(_WIN32) && !defined(__CYGWIN__)
5382 cmValue version;
5383 #else
5384 // Check for executable version properties.
5385 cmValue version = this->GetProperty("VERSION");
5386 if (this->GetType() != cmStateEnums::EXECUTABLE ||
5387 this->Makefile->IsOn("XCODE")) {
5388 version = nullptr;
5390 #endif
5392 // Get the components of the executable name.
5393 NameComponents const& components = this->GetFullNameInternalComponents(
5394 config, cmStateEnums::RuntimeBinaryArtifact);
5396 // The executable name.
5397 targetNames.Base = components.base;
5398 targetNames.Output =
5399 components.prefix + targetNames.Base + components.suffix;
5401 // The executable's real name on disk.
5402 #if defined(__CYGWIN__)
5403 targetNames.Real = components.prefix + targetNames.Base;
5404 #else
5405 targetNames.Real = targetNames.Output;
5406 #endif
5407 if (version) {
5408 targetNames.Real += "-";
5409 targetNames.Real += *version;
5411 #if defined(__CYGWIN__)
5412 targetNames.Real += components.suffix;
5413 #endif
5415 // The import library name.
5416 targetNames.ImportLibrary =
5417 this->GetFullNameInternal(config, cmStateEnums::ImportLibraryArtifact);
5418 targetNames.ImportReal = targetNames.ImportLibrary;
5419 targetNames.ImportOutput = targetNames.ImportLibrary;
5421 // The program database file name.
5422 targetNames.PDB = this->GetPDBName(config);
5424 return targetNames;
5427 std::string cmGeneratorTarget::GetFullNameInternal(
5428 const std::string& config, cmStateEnums::ArtifactType artifact) const
5430 NameComponents const& components =
5431 this->GetFullNameInternalComponents(config, artifact);
5432 return components.prefix + components.base + components.suffix;
5435 std::string cmGeneratorTarget::ImportedGetLocation(
5436 const std::string& config) const
5438 assert(this->IsImported());
5439 return this->Target->ImportedGetFullPath(
5440 config, cmStateEnums::RuntimeBinaryArtifact);
5443 std::string cmGeneratorTarget::GetFullNameImported(
5444 const std::string& config, cmStateEnums::ArtifactType artifact) const
5446 return cmSystemTools::GetFilenameName(
5447 this->Target->ImportedGetFullPath(config, artifact));
5450 cmGeneratorTarget::NameComponents const&
5451 cmGeneratorTarget::GetFullNameInternalComponents(
5452 std::string const& config, cmStateEnums::ArtifactType artifact) const
5454 assert(artifact == cmStateEnums::RuntimeBinaryArtifact ||
5455 artifact == cmStateEnums::ImportLibraryArtifact);
5456 FullNameCache& cache = artifact == cmStateEnums::RuntimeBinaryArtifact
5457 ? RuntimeBinaryFullNameCache
5458 : ImportLibraryFullNameCache;
5459 auto search = cache.find(config);
5460 if (search != cache.end()) {
5461 return search->second;
5463 // Use just the target name for non-main target types.
5464 if (this->GetType() != cmStateEnums::STATIC_LIBRARY &&
5465 this->GetType() != cmStateEnums::SHARED_LIBRARY &&
5466 this->GetType() != cmStateEnums::MODULE_LIBRARY &&
5467 this->GetType() != cmStateEnums::EXECUTABLE) {
5468 NameComponents components;
5469 components.base = this->GetName();
5470 return cache.emplace(config, std::move(components)).first->second;
5473 const bool isImportedLibraryArtifact =
5474 (artifact == cmStateEnums::ImportLibraryArtifact);
5476 // Return an empty name for the import library if this platform
5477 // does not support import libraries.
5478 if (isImportedLibraryArtifact && !this->NeedImportLibraryName(config)) {
5479 return cache.emplace(config, NameComponents()).first->second;
5482 NameComponents parts;
5483 std::string& outPrefix = parts.prefix;
5484 std::string& outBase = parts.base;
5485 std::string& outSuffix = parts.suffix;
5487 // retrieve prefix and suffix
5488 std::string ll = this->GetLinkerLanguage(config);
5489 cmValue targetPrefix = this->GetFilePrefixInternal(config, artifact, ll);
5490 cmValue targetSuffix = this->GetFileSuffixInternal(config, artifact, ll);
5492 // The implib option is only allowed for shared libraries, module
5493 // libraries, and executables.
5494 if (this->GetType() != cmStateEnums::SHARED_LIBRARY &&
5495 this->GetType() != cmStateEnums::MODULE_LIBRARY &&
5496 this->GetType() != cmStateEnums::EXECUTABLE) {
5497 artifact = cmStateEnums::RuntimeBinaryArtifact;
5500 // Compute the full name for main target types.
5501 std::string configPostfix = this->GetFilePostfix(config);
5503 // frameworks have directory prefix
5504 std::string fw_prefix;
5505 if (this->IsFrameworkOnApple()) {
5506 fw_prefix =
5507 cmStrCat(this->GetFrameworkDirectory(config, ContentLevel), '/');
5508 targetPrefix = cmValue(fw_prefix);
5509 if (!isImportedLibraryArtifact) {
5510 // no suffix
5511 targetSuffix = nullptr;
5515 if (this->IsCFBundleOnApple()) {
5516 fw_prefix = cmStrCat(this->GetCFBundleDirectory(config, FullLevel), '/');
5517 targetPrefix = cmValue(fw_prefix);
5518 targetSuffix = nullptr;
5521 // Begin the final name with the prefix.
5522 outPrefix = targetPrefix ? *targetPrefix : "";
5524 // Append the target name or property-specified name.
5525 outBase += this->GetOutputName(config, artifact);
5527 // Append the per-configuration postfix.
5528 // When using Xcode, the postfix should be part of the suffix rather than
5529 // the base, because the suffix ends up being used in Xcode's
5530 // EXECUTABLE_SUFFIX attribute.
5531 if (this->IsFrameworkOnApple() && this->GetGlobalGenerator()->IsXcode()) {
5532 configPostfix += *targetSuffix;
5533 targetSuffix = cmValue(configPostfix);
5534 } else {
5535 outBase += configPostfix;
5538 // Name shared libraries with their version number on some platforms.
5539 if (cmValue soversion = this->GetProperty("SOVERSION")) {
5540 cmValue dllProp;
5541 if (this->IsDLLPlatform()) {
5542 dllProp = this->GetProperty("DLL_NAME_WITH_SOVERSION");
5544 if (this->GetType() == cmStateEnums::SHARED_LIBRARY &&
5545 !isImportedLibraryArtifact &&
5546 (dllProp.IsOn() ||
5547 (!dllProp.IsSet() &&
5548 this->Makefile->IsOn("CMAKE_SHARED_LIBRARY_NAME_WITH_VERSION")))) {
5549 outBase += "-";
5550 outBase += *soversion;
5554 // Append the suffix.
5555 outSuffix = targetSuffix ? *targetSuffix : "";
5557 return cache.emplace(config, std::move(parts)).first->second;
5560 std::string cmGeneratorTarget::GetLinkerLanguage(
5561 const std::string& config) const
5563 return this->GetLinkClosure(config)->LinkerLanguage;
5566 std::string cmGeneratorTarget::GetLinkerTool(const std::string& config) const
5568 return this->GetLinkerTool(this->GetLinkerLanguage(config), config);
5571 std::string cmGeneratorTarget::GetLinkerTool(const std::string& lang,
5572 const std::string& config) const
5574 auto usingLinker =
5575 cmStrCat("CMAKE_", lang, "_USING_", this->IsDeviceLink() ? "DEVICE_" : "",
5576 "LINKER_");
5577 auto format = this->Makefile->GetDefinition(cmStrCat(usingLinker, "MODE"));
5578 if (!format || format != "TOOL"_s) {
5579 return this->Makefile->GetDefinition("CMAKE_LINKER");
5582 auto linkerType = this->GetLinkerTypeProperty(lang, config);
5583 if (linkerType.empty()) {
5584 linkerType = "DEFAULT";
5586 usingLinker = cmStrCat(usingLinker, linkerType);
5587 auto linkerTool = this->Makefile->GetDefinition(usingLinker);
5589 if (!linkerTool) {
5590 if (this->GetGlobalGenerator()->IsVisualStudio() &&
5591 linkerType == "DEFAULT"_s) {
5592 return std::string{};
5595 // fall-back to generic definition
5596 linkerTool = this->Makefile->GetDefinition("CMAKE_LINKER");
5598 if (linkerType != "DEFAULT"_s) {
5599 auto isCMakeLinkerType = [](const std::string& type) -> bool {
5600 return std::all_of(type.cbegin(), type.cend(),
5601 [](char c) { return std::isupper(c); });
5603 if (isCMakeLinkerType(linkerType)) {
5604 this->LocalGenerator->IssueMessage(
5605 MessageType::FATAL_ERROR,
5606 cmStrCat("LINKER_TYPE '", linkerType,
5607 "' is unknown or not supported by this toolchain."));
5608 } else {
5609 this->LocalGenerator->IssueMessage(
5610 MessageType::FATAL_ERROR,
5611 cmStrCat("LINKER_TYPE '", linkerType,
5612 "' is unknown. Did you forget to define the '", usingLinker,
5613 "' variable?"));
5618 return linkerTool;
5621 bool cmGeneratorTarget::LinkerEnforcesNoAllowShLibUndefined(
5622 std::string const& config) const
5624 // FIXME(#25486): Account for the LINKER_TYPE target property.
5625 // Also factor out the hard-coded list below into a platform
5626 // information table based on the linker id.
5627 std::string ll = this->GetLinkerLanguage(config);
5628 std::string linkerIdVar = cmStrCat("CMAKE_", ll, "_COMPILER_LINKER_ID");
5629 cmValue linkerId = this->Makefile->GetDefinition(linkerIdVar);
5630 // The GNU bfd-based linker may enforce '--no-allow-shlib-undefined'
5631 // recursively by default. The Solaris linker has similar behavior.
5632 return linkerId && (*linkerId == "GNU" || *linkerId == "Solaris");
5635 std::string cmGeneratorTarget::GetPDBOutputName(
5636 const std::string& config) const
5638 std::string base =
5639 this->GetOutputName(config, cmStateEnums::RuntimeBinaryArtifact);
5641 std::vector<std::string> props;
5642 std::string configUpper = cmSystemTools::UpperCase(config);
5643 if (!configUpper.empty()) {
5644 // PDB_NAME_<CONFIG>
5645 props.push_back("PDB_NAME_" + configUpper);
5648 // PDB_NAME
5649 props.emplace_back("PDB_NAME");
5651 for (std::string const& p : props) {
5652 if (cmValue outName = this->GetProperty(p)) {
5653 base = *outName;
5654 break;
5657 return base;
5660 std::string cmGeneratorTarget::GetPDBName(const std::string& config) const
5662 NameComponents const& parts = this->GetFullNameInternalComponents(
5663 config, cmStateEnums::RuntimeBinaryArtifact);
5665 std::vector<std::string> props;
5666 std::string configUpper = cmSystemTools::UpperCase(config);
5667 if (!configUpper.empty()) {
5668 // PDB_NAME_<CONFIG>
5669 props.push_back("PDB_NAME_" + configUpper);
5672 // PDB_NAME
5673 props.emplace_back("PDB_NAME");
5675 for (std::string const& p : props) {
5676 if (cmValue outName = this->GetProperty(p)) {
5677 return parts.prefix + *outName + ".pdb";
5680 return parts.prefix + parts.base + ".pdb";
5683 std::string cmGeneratorTarget::GetObjectDirectory(
5684 std::string const& config) const
5686 std::string obj_dir =
5687 this->GlobalGenerator->ExpandCFGIntDir(this->ObjectDirectory, config);
5688 #if defined(__APPLE__)
5689 // Replace Xcode's placeholder for the object file directory since
5690 // installation and export scripts need to know the real directory.
5691 // Xcode has build-time settings (e.g. for sanitizers) that affect this,
5692 // but we use the default here. Users that want to enable sanitizers
5693 // will do so at the cost of object library installation and export.
5694 cmSystemTools::ReplaceString(obj_dir, "$(OBJECT_FILE_DIR_normal:base)",
5695 "Objects-normal");
5696 #endif
5697 return obj_dir;
5700 void cmGeneratorTarget::GetTargetObjectNames(
5701 std::string const& config, std::vector<std::string>& objects) const
5703 std::vector<cmSourceFile const*> objectSources;
5704 this->GetObjectSources(objectSources, config);
5705 std::map<cmSourceFile const*, std::string> mapping;
5707 for (cmSourceFile const* sf : objectSources) {
5708 mapping[sf];
5711 this->LocalGenerator->ComputeObjectFilenames(mapping, this);
5713 for (cmSourceFile const* src : objectSources) {
5714 // Find the object file name corresponding to this source file.
5715 auto map_it = mapping.find(src);
5716 // It must exist because we populated the mapping just above.
5717 assert(!map_it->second.empty());
5718 objects.push_back(map_it->second);
5721 // We need to compute the relative path from the root of
5722 // of the object directory to handle subdirectory paths
5723 std::string rootObjectDir = this->GetObjectDirectory(config);
5724 rootObjectDir = cmSystemTools::CollapseFullPath(rootObjectDir);
5725 auto ispcObjects = this->GetGeneratedISPCObjects(config);
5726 for (std::string const& output : ispcObjects) {
5727 auto relativePathFromObjectDir = output.substr(rootObjectDir.size());
5728 objects.push_back(relativePathFromObjectDir);
5732 bool cmGeneratorTarget::StrictTargetComparison::operator()(
5733 cmGeneratorTarget const* t1, cmGeneratorTarget const* t2) const
5735 int nameResult = strcmp(t1->GetName().c_str(), t2->GetName().c_str());
5736 if (nameResult == 0) {
5737 return strcmp(
5738 t1->GetLocalGenerator()->GetCurrentBinaryDirectory().c_str(),
5739 t2->GetLocalGenerator()->GetCurrentBinaryDirectory().c_str()) < 0;
5741 return nameResult < 0;
5744 struct cmGeneratorTarget::SourceFileFlags
5745 cmGeneratorTarget::GetTargetSourceFileFlags(const cmSourceFile* sf) const
5747 struct SourceFileFlags flags;
5748 this->ConstructSourceFileFlags();
5749 auto si = this->SourceFlagsMap.find(sf);
5750 if (si != this->SourceFlagsMap.end()) {
5751 flags = si->second;
5752 } else {
5753 // Handle the MACOSX_PACKAGE_LOCATION property on source files that
5754 // were not listed in one of the other lists.
5755 if (cmValue location = sf->GetProperty("MACOSX_PACKAGE_LOCATION")) {
5756 flags.MacFolder = location->c_str();
5757 const bool stripResources =
5758 this->GlobalGenerator->ShouldStripResourcePath(this->Makefile);
5759 if (*location == "Resources") {
5760 flags.Type = cmGeneratorTarget::SourceFileTypeResource;
5761 if (stripResources) {
5762 flags.MacFolder = "";
5764 } else if (cmHasLiteralPrefix(*location, "Resources/")) {
5765 flags.Type = cmGeneratorTarget::SourceFileTypeDeepResource;
5766 if (stripResources) {
5767 flags.MacFolder += cmStrLen("Resources/");
5769 } else {
5770 flags.Type = cmGeneratorTarget::SourceFileTypeMacContent;
5774 return flags;
5777 void cmGeneratorTarget::ConstructSourceFileFlags() const
5779 if (this->SourceFileFlagsConstructed) {
5780 return;
5782 this->SourceFileFlagsConstructed = true;
5784 // Process public headers to mark the source files.
5785 if (cmValue files = this->GetProperty("PUBLIC_HEADER")) {
5786 cmList relFiles{ *files };
5787 for (auto const& relFile : relFiles) {
5788 if (cmSourceFile* sf = this->Makefile->GetSource(relFile)) {
5789 SourceFileFlags& flags = this->SourceFlagsMap[sf];
5790 flags.MacFolder = "Headers";
5791 flags.Type = cmGeneratorTarget::SourceFileTypePublicHeader;
5796 // Process private headers after public headers so that they take
5797 // precedence if a file is listed in both.
5798 if (cmValue files = this->GetProperty("PRIVATE_HEADER")) {
5799 cmList relFiles{ *files };
5800 for (auto const& relFile : relFiles) {
5801 if (cmSourceFile* sf = this->Makefile->GetSource(relFile)) {
5802 SourceFileFlags& flags = this->SourceFlagsMap[sf];
5803 flags.MacFolder = "PrivateHeaders";
5804 flags.Type = cmGeneratorTarget::SourceFileTypePrivateHeader;
5809 // Mark sources listed as resources.
5810 if (cmValue files = this->GetProperty("RESOURCE")) {
5811 cmList relFiles{ *files };
5812 for (auto const& relFile : relFiles) {
5813 if (cmSourceFile* sf = this->Makefile->GetSource(relFile)) {
5814 SourceFileFlags& flags = this->SourceFlagsMap[sf];
5815 flags.MacFolder = "";
5816 if (!this->GlobalGenerator->ShouldStripResourcePath(this->Makefile)) {
5817 flags.MacFolder = "Resources";
5819 flags.Type = cmGeneratorTarget::SourceFileTypeResource;
5825 const cmGeneratorTarget::CompatibleInterfacesBase&
5826 cmGeneratorTarget::GetCompatibleInterfaces(std::string const& config) const
5828 cmGeneratorTarget::CompatibleInterfaces& compat =
5829 this->CompatibleInterfacesMap[config];
5830 if (!compat.Done) {
5831 compat.Done = true;
5832 compat.PropsBool.insert("POSITION_INDEPENDENT_CODE");
5833 compat.PropsString.insert("AUTOUIC_OPTIONS");
5834 std::vector<cmGeneratorTarget const*> const& deps =
5835 this->GetLinkImplementationClosure(config);
5836 for (cmGeneratorTarget const* li : deps) {
5837 #define CM_READ_COMPATIBLE_INTERFACE(X, x) \
5838 if (cmValue prop = li->GetProperty("COMPATIBLE_INTERFACE_" #X)) { \
5839 cmList props(*prop); \
5840 compat.Props##x.insert(props.begin(), props.end()); \
5842 CM_READ_COMPATIBLE_INTERFACE(BOOL, Bool)
5843 CM_READ_COMPATIBLE_INTERFACE(STRING, String)
5844 CM_READ_COMPATIBLE_INTERFACE(NUMBER_MIN, NumberMin)
5845 CM_READ_COMPATIBLE_INTERFACE(NUMBER_MAX, NumberMax)
5846 #undef CM_READ_COMPATIBLE_INTERFACE
5849 return compat;
5852 bool cmGeneratorTarget::IsLinkInterfaceDependentBoolProperty(
5853 const std::string& p, const std::string& config) const
5855 if (this->GetType() == cmStateEnums::OBJECT_LIBRARY ||
5856 this->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
5857 return false;
5859 return this->GetCompatibleInterfaces(config).PropsBool.count(p) > 0;
5862 bool cmGeneratorTarget::IsLinkInterfaceDependentStringProperty(
5863 const std::string& p, const std::string& config) const
5865 if (this->GetType() == cmStateEnums::OBJECT_LIBRARY ||
5866 this->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
5867 return false;
5869 return this->GetCompatibleInterfaces(config).PropsString.count(p) > 0;
5872 bool cmGeneratorTarget::IsLinkInterfaceDependentNumberMinProperty(
5873 const std::string& p, const std::string& config) const
5875 if (this->GetType() == cmStateEnums::OBJECT_LIBRARY ||
5876 this->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
5877 return false;
5879 return this->GetCompatibleInterfaces(config).PropsNumberMin.count(p) > 0;
5882 bool cmGeneratorTarget::IsLinkInterfaceDependentNumberMaxProperty(
5883 const std::string& p, const std::string& config) const
5885 if (this->GetType() == cmStateEnums::OBJECT_LIBRARY ||
5886 this->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
5887 return false;
5889 return this->GetCompatibleInterfaces(config).PropsNumberMax.count(p) > 0;
5892 enum CompatibleType
5894 BoolType,
5895 StringType,
5896 NumberMinType,
5897 NumberMaxType
5900 template <typename PropertyType>
5901 PropertyType getLinkInterfaceDependentProperty(cmGeneratorTarget const* tgt,
5902 const std::string& prop,
5903 const std::string& config,
5904 CompatibleType, PropertyType*);
5906 template <>
5907 bool getLinkInterfaceDependentProperty(cmGeneratorTarget const* tgt,
5908 const std::string& prop,
5909 const std::string& config,
5910 CompatibleType /*unused*/,
5911 bool* /*unused*/)
5913 return tgt->GetLinkInterfaceDependentBoolProperty(prop, config);
5916 template <>
5917 const char* getLinkInterfaceDependentProperty(cmGeneratorTarget const* tgt,
5918 const std::string& prop,
5919 const std::string& config,
5920 CompatibleType t,
5921 const char** /*unused*/)
5923 switch (t) {
5924 case BoolType:
5925 assert(false &&
5926 "String compatibility check function called for boolean");
5927 return nullptr;
5928 case StringType:
5929 return tgt->GetLinkInterfaceDependentStringProperty(prop, config);
5930 case NumberMinType:
5931 return tgt->GetLinkInterfaceDependentNumberMinProperty(prop, config);
5932 case NumberMaxType:
5933 return tgt->GetLinkInterfaceDependentNumberMaxProperty(prop, config);
5935 assert(false && "Unreachable!");
5936 return nullptr;
5939 template <typename PropertyType>
5940 void checkPropertyConsistency(cmGeneratorTarget const* depender,
5941 cmGeneratorTarget const* dependee,
5942 const std::string& propName,
5943 std::set<std::string>& emitted,
5944 const std::string& config, CompatibleType t,
5945 PropertyType* /*unused*/)
5947 cmValue prop = dependee->GetProperty(propName);
5948 if (!prop) {
5949 return;
5952 cmList props{ *prop };
5953 std::string pdir =
5954 cmStrCat(cmSystemTools::GetCMakeRoot(), "/Help/prop_tgt/");
5956 for (std::string const& p : props) {
5957 std::string pname = cmSystemTools::HelpFileName(p);
5958 std::string pfile = pdir + pname + ".rst";
5959 if (cmSystemTools::FileExists(pfile, true)) {
5960 std::ostringstream e;
5961 e << "Target \"" << dependee->GetName() << "\" has property \"" << p
5962 << "\" listed in its " << propName
5963 << " property. "
5964 "This is not allowed. Only user-defined properties may appear "
5965 "listed in the "
5966 << propName << " property.";
5967 depender->GetLocalGenerator()->IssueMessage(MessageType::FATAL_ERROR,
5968 e.str());
5969 return;
5971 if (emitted.insert(p).second) {
5972 getLinkInterfaceDependentProperty<PropertyType>(depender, p, config, t,
5973 nullptr);
5974 if (cmSystemTools::GetErrorOccurredFlag()) {
5975 return;
5981 namespace {
5982 std::string intersect(const std::set<std::string>& s1,
5983 const std::set<std::string>& s2)
5985 std::set<std::string> intersect;
5986 std::set_intersection(s1.begin(), s1.end(), s2.begin(), s2.end(),
5987 std::inserter(intersect, intersect.begin()));
5988 if (!intersect.empty()) {
5989 return *intersect.begin();
5991 return "";
5994 std::string intersect(const std::set<std::string>& s1,
5995 const std::set<std::string>& s2,
5996 const std::set<std::string>& s3)
5998 std::string result;
5999 result = intersect(s1, s2);
6000 if (!result.empty()) {
6001 return result;
6003 result = intersect(s1, s3);
6004 if (!result.empty()) {
6005 return result;
6007 return intersect(s2, s3);
6010 std::string intersect(const std::set<std::string>& s1,
6011 const std::set<std::string>& s2,
6012 const std::set<std::string>& s3,
6013 const std::set<std::string>& s4)
6015 std::string result;
6016 result = intersect(s1, s2);
6017 if (!result.empty()) {
6018 return result;
6020 result = intersect(s1, s3);
6021 if (!result.empty()) {
6022 return result;
6024 result = intersect(s1, s4);
6025 if (!result.empty()) {
6026 return result;
6028 return intersect(s2, s3, s4);
6032 void cmGeneratorTarget::CheckPropertyCompatibility(
6033 cmComputeLinkInformation& info, const std::string& config) const
6035 const cmComputeLinkInformation::ItemVector& deps = info.GetItems();
6037 std::set<std::string> emittedBools;
6038 static const std::string strBool = "COMPATIBLE_INTERFACE_BOOL";
6039 std::set<std::string> emittedStrings;
6040 static const std::string strString = "COMPATIBLE_INTERFACE_STRING";
6041 std::set<std::string> emittedMinNumbers;
6042 static const std::string strNumMin = "COMPATIBLE_INTERFACE_NUMBER_MIN";
6043 std::set<std::string> emittedMaxNumbers;
6044 static const std::string strNumMax = "COMPATIBLE_INTERFACE_NUMBER_MAX";
6046 for (auto const& dep : deps) {
6047 if (!dep.Target || dep.Target->GetType() == cmStateEnums::OBJECT_LIBRARY) {
6048 continue;
6051 checkPropertyConsistency<bool>(this, dep.Target, strBool, emittedBools,
6052 config, BoolType, nullptr);
6053 if (cmSystemTools::GetErrorOccurredFlag()) {
6054 return;
6056 checkPropertyConsistency<const char*>(this, dep.Target, strString,
6057 emittedStrings, config, StringType,
6058 nullptr);
6059 if (cmSystemTools::GetErrorOccurredFlag()) {
6060 return;
6062 checkPropertyConsistency<const char*>(this, dep.Target, strNumMin,
6063 emittedMinNumbers, config,
6064 NumberMinType, nullptr);
6065 if (cmSystemTools::GetErrorOccurredFlag()) {
6066 return;
6068 checkPropertyConsistency<const char*>(this, dep.Target, strNumMax,
6069 emittedMaxNumbers, config,
6070 NumberMaxType, nullptr);
6071 if (cmSystemTools::GetErrorOccurredFlag()) {
6072 return;
6076 std::string prop = intersect(emittedBools, emittedStrings, emittedMinNumbers,
6077 emittedMaxNumbers);
6079 if (!prop.empty()) {
6080 // Use a sorted std::vector to keep the error message sorted.
6081 std::vector<std::string> props;
6082 auto i = emittedBools.find(prop);
6083 if (i != emittedBools.end()) {
6084 props.push_back(strBool);
6086 i = emittedStrings.find(prop);
6087 if (i != emittedStrings.end()) {
6088 props.push_back(strString);
6090 i = emittedMinNumbers.find(prop);
6091 if (i != emittedMinNumbers.end()) {
6092 props.push_back(strNumMin);
6094 i = emittedMaxNumbers.find(prop);
6095 if (i != emittedMaxNumbers.end()) {
6096 props.push_back(strNumMax);
6098 std::sort(props.begin(), props.end());
6100 std::string propsString = cmStrCat(
6101 cmJoin(cmMakeRange(props).retreat(1), ", "), " and the ", props.back());
6103 std::ostringstream e;
6104 e << "Property \"" << prop << "\" appears in both the " << propsString
6105 << " property in the dependencies of target \"" << this->GetName()
6106 << "\". This is not allowed. A property may only require "
6107 "compatibility "
6108 "in a boolean interpretation, a numeric minimum, a numeric maximum "
6109 "or a "
6110 "string interpretation, but not a mixture.";
6111 this->LocalGenerator->IssueMessage(MessageType::FATAL_ERROR, e.str());
6115 template <typename PropertyType>
6116 std::string valueAsString(PropertyType);
6117 template <>
6118 std::string valueAsString<bool>(bool value)
6120 return value ? "TRUE" : "FALSE";
6122 template <>
6123 std::string valueAsString<const char*>(const char* value)
6125 return value ? value : "(unset)";
6127 template <>
6128 std::string valueAsString<std::string>(std::string value)
6130 return value;
6132 template <>
6133 std::string valueAsString<cmValue>(cmValue value)
6135 return value ? *value : std::string("(unset)");
6137 template <>
6138 std::string valueAsString<std::nullptr_t>(std::nullptr_t /*unused*/)
6140 return "(unset)";
6143 static std::string compatibilityType(CompatibleType t)
6145 switch (t) {
6146 case BoolType:
6147 return "Boolean compatibility";
6148 case StringType:
6149 return "String compatibility";
6150 case NumberMaxType:
6151 return "Numeric maximum compatibility";
6152 case NumberMinType:
6153 return "Numeric minimum compatibility";
6155 assert(false && "Unreachable!");
6156 return "";
6159 static std::string compatibilityAgree(CompatibleType t, bool dominant)
6161 switch (t) {
6162 case BoolType:
6163 case StringType:
6164 return dominant ? "(Disagree)\n" : "(Agree)\n";
6165 case NumberMaxType:
6166 case NumberMinType:
6167 return dominant ? "(Dominant)\n" : "(Ignored)\n";
6169 assert(false && "Unreachable!");
6170 return "";
6173 template <typename PropertyType>
6174 PropertyType getTypedProperty(
6175 cmGeneratorTarget const* tgt, const std::string& prop,
6176 cmGeneratorExpressionInterpreter* genexInterpreter = nullptr);
6178 template <>
6179 bool getTypedProperty<bool>(cmGeneratorTarget const* tgt,
6180 const std::string& prop,
6181 cmGeneratorExpressionInterpreter* genexInterpreter)
6183 if (genexInterpreter == nullptr) {
6184 return tgt->GetPropertyAsBool(prop);
6187 cmValue value = tgt->GetProperty(prop);
6188 return cmIsOn(genexInterpreter->Evaluate(value ? *value : "", prop));
6191 template <>
6192 const char* getTypedProperty<const char*>(
6193 cmGeneratorTarget const* tgt, const std::string& prop,
6194 cmGeneratorExpressionInterpreter* genexInterpreter)
6196 cmValue value = tgt->GetProperty(prop);
6198 if (genexInterpreter == nullptr) {
6199 return value.GetCStr();
6202 return genexInterpreter->Evaluate(value ? *value : "", prop).c_str();
6205 template <>
6206 std::string getTypedProperty<std::string>(
6207 cmGeneratorTarget const* tgt, const std::string& prop,
6208 cmGeneratorExpressionInterpreter* genexInterpreter)
6210 cmValue value = tgt->GetProperty(prop);
6212 if (genexInterpreter == nullptr) {
6213 return valueAsString(value);
6216 return genexInterpreter->Evaluate(value ? *value : "", prop);
6219 template <typename PropertyType>
6220 PropertyType impliedValue(PropertyType);
6221 template <>
6222 bool impliedValue<bool>(bool /*unused*/)
6224 return false;
6226 template <>
6227 const char* impliedValue<const char*>(const char* /*unused*/)
6229 return "";
6231 template <>
6232 std::string impliedValue<std::string>(std::string /*unused*/) // NOLINT(*)
6234 return std::string();
6237 template <typename PropertyType>
6238 std::pair<bool, PropertyType> consistentProperty(PropertyType lhs,
6239 PropertyType rhs,
6240 CompatibleType t);
6242 template <>
6243 std::pair<bool, bool> consistentProperty(bool lhs, bool rhs,
6244 CompatibleType /*unused*/)
6246 return { lhs == rhs, lhs };
6249 static std::pair<bool, const char*> consistentStringProperty(const char* lhs,
6250 const char* rhs)
6252 const bool b = strcmp(lhs, rhs) == 0;
6253 return { b, b ? lhs : nullptr };
6256 static std::pair<bool, std::string> consistentStringProperty(
6257 const std::string& lhs, const std::string& rhs)
6259 const bool b = lhs == rhs;
6260 return { b, b ? lhs : valueAsString(nullptr) };
6263 static std::pair<bool, const char*> consistentNumberProperty(const char* lhs,
6264 const char* rhs,
6265 CompatibleType t)
6267 char* pEnd;
6269 long lnum = strtol(lhs, &pEnd, 0);
6270 if (pEnd == lhs || *pEnd != '\0' || errno == ERANGE) {
6271 return { false, nullptr };
6274 long rnum = strtol(rhs, &pEnd, 0);
6275 if (pEnd == rhs || *pEnd != '\0' || errno == ERANGE) {
6276 return { false, nullptr };
6279 if (t == NumberMaxType) {
6280 return { true, std::max(lnum, rnum) == lnum ? lhs : rhs };
6283 return { true, std::min(lnum, rnum) == lnum ? lhs : rhs };
6286 template <>
6287 std::pair<bool, const char*> consistentProperty(const char* lhs,
6288 const char* rhs,
6289 CompatibleType t)
6291 if (!lhs && !rhs) {
6292 return { true, lhs };
6294 if (!lhs) {
6295 return { true, rhs };
6297 if (!rhs) {
6298 return { true, lhs };
6301 switch (t) {
6302 case BoolType: {
6303 bool same = cmIsOn(lhs) == cmIsOn(rhs);
6304 return { same, same ? lhs : nullptr };
6306 case StringType:
6307 return consistentStringProperty(lhs, rhs);
6308 case NumberMinType:
6309 case NumberMaxType:
6310 return consistentNumberProperty(lhs, rhs, t);
6312 assert(false && "Unreachable!");
6313 return { false, nullptr };
6316 static std::pair<bool, std::string> consistentProperty(const std::string& lhs,
6317 const std::string& rhs,
6318 CompatibleType t)
6320 const std::string null_ptr = valueAsString(nullptr);
6322 if (lhs == null_ptr && rhs == null_ptr) {
6323 return { true, lhs };
6325 if (lhs == null_ptr) {
6326 return { true, rhs };
6328 if (rhs == null_ptr) {
6329 return { true, lhs };
6332 switch (t) {
6333 case BoolType: {
6334 bool same = cmIsOn(lhs) == cmIsOn(rhs);
6335 return { same, same ? lhs : null_ptr };
6337 case StringType:
6338 return consistentStringProperty(lhs, rhs);
6339 case NumberMinType:
6340 case NumberMaxType: {
6341 auto value = consistentNumberProperty(lhs.c_str(), rhs.c_str(), t);
6342 return { value.first,
6343 value.first ? std::string(value.second) : null_ptr };
6346 assert(false && "Unreachable!");
6347 return { false, null_ptr };
6350 template <typename PropertyType>
6351 PropertyType checkInterfacePropertyCompatibility(cmGeneratorTarget const* tgt,
6352 const std::string& p,
6353 const std::string& config,
6354 const char* defaultValue,
6355 CompatibleType t,
6356 PropertyType* /*unused*/)
6358 PropertyType propContent = getTypedProperty<PropertyType>(tgt, p);
6360 std::vector<std::string> headPropKeys = tgt->GetPropertyKeys();
6361 const bool explicitlySet = cm::contains(headPropKeys, p);
6363 const bool impliedByUse = tgt->IsNullImpliedByLinkLibraries(p);
6364 assert((impliedByUse ^ explicitlySet) || (!impliedByUse && !explicitlySet));
6366 std::vector<cmGeneratorTarget const*> const& deps =
6367 tgt->GetLinkImplementationClosure(config);
6369 if (deps.empty()) {
6370 return propContent;
6372 bool propInitialized = explicitlySet;
6374 std::string report = cmStrCat(" * Target \"", tgt->GetName());
6375 if (explicitlySet) {
6376 report += "\" has property content \"";
6377 report += valueAsString<PropertyType>(propContent);
6378 report += "\"\n";
6379 } else if (impliedByUse) {
6380 report += "\" property is implied by use.\n";
6381 } else {
6382 report += "\" property not set.\n";
6385 std::string interfaceProperty = "INTERFACE_" + p;
6386 std::unique_ptr<cmGeneratorExpressionInterpreter> genexInterpreter;
6387 if (p == "POSITION_INDEPENDENT_CODE") {
6388 genexInterpreter = cm::make_unique<cmGeneratorExpressionInterpreter>(
6389 tgt->GetLocalGenerator(), config, tgt);
6392 for (cmGeneratorTarget const* theTarget : deps) {
6393 // An error should be reported if one dependency
6394 // has INTERFACE_POSITION_INDEPENDENT_CODE ON and the other
6395 // has INTERFACE_POSITION_INDEPENDENT_CODE OFF, or if the
6396 // target itself has a POSITION_INDEPENDENT_CODE which disagrees
6397 // with a dependency.
6399 std::vector<std::string> propKeys = theTarget->GetPropertyKeys();
6401 const bool ifaceIsSet = cm::contains(propKeys, interfaceProperty);
6402 PropertyType ifacePropContent = getTypedProperty<PropertyType>(
6403 theTarget, interfaceProperty, genexInterpreter.get());
6405 std::string reportEntry;
6406 if (ifaceIsSet) {
6407 reportEntry += " * Target \"";
6408 reportEntry += theTarget->GetName();
6409 reportEntry += "\" property value \"";
6410 reportEntry += valueAsString<PropertyType>(ifacePropContent);
6411 reportEntry += "\" ";
6414 if (explicitlySet) {
6415 if (ifaceIsSet) {
6416 std::pair<bool, PropertyType> consistent =
6417 consistentProperty(propContent, ifacePropContent, t);
6418 report += reportEntry;
6419 report += compatibilityAgree(t, propContent != consistent.second);
6420 if (!consistent.first) {
6421 std::ostringstream e;
6422 e << "Property " << p << " on target \"" << tgt->GetName()
6423 << "\" does\nnot match the "
6424 "INTERFACE_"
6425 << p
6426 << " property requirement\nof "
6427 "dependency \""
6428 << theTarget->GetName() << "\".\n";
6429 cmSystemTools::Error(e.str());
6430 break;
6432 propContent = consistent.second;
6433 continue;
6435 // Explicitly set on target and not set in iface. Can't disagree.
6436 continue;
6438 if (impliedByUse) {
6439 propContent = impliedValue<PropertyType>(propContent);
6441 if (ifaceIsSet) {
6442 std::pair<bool, PropertyType> consistent =
6443 consistentProperty(propContent, ifacePropContent, t);
6444 report += reportEntry;
6445 report += compatibilityAgree(t, propContent != consistent.second);
6446 if (!consistent.first) {
6447 std::ostringstream e;
6448 e << "Property " << p << " on target \"" << tgt->GetName()
6449 << "\" is\nimplied to be " << defaultValue
6450 << " because it was used to determine the link libraries\n"
6451 "already. The INTERFACE_"
6452 << p << " property on\ndependency \"" << theTarget->GetName()
6453 << "\" is in conflict.\n";
6454 cmSystemTools::Error(e.str());
6455 break;
6457 propContent = consistent.second;
6458 continue;
6460 // Implicitly set on target and not set in iface. Can't disagree.
6461 continue;
6463 if (ifaceIsSet) {
6464 if (propInitialized) {
6465 std::pair<bool, PropertyType> consistent =
6466 consistentProperty(propContent, ifacePropContent, t);
6467 report += reportEntry;
6468 report += compatibilityAgree(t, propContent != consistent.second);
6469 if (!consistent.first) {
6470 std::ostringstream e;
6471 e << "The INTERFACE_" << p << " property of \""
6472 << theTarget->GetName() << "\" does\nnot agree with the value of "
6473 << p << " already determined\nfor \"" << tgt->GetName() << "\".\n";
6474 cmSystemTools::Error(e.str());
6475 break;
6477 propContent = consistent.second;
6478 continue;
6480 report += reportEntry + "(Interface set)\n";
6481 propContent = ifacePropContent;
6482 propInitialized = true;
6483 } else {
6484 // Not set. Nothing to agree on.
6485 continue;
6489 tgt->ReportPropertyOrigin(p, valueAsString<PropertyType>(propContent),
6490 report, compatibilityType(t));
6491 return propContent;
6494 bool cmGeneratorTarget::SetDeviceLink(bool deviceLink)
6496 bool previous = this->DeviceLink;
6497 this->DeviceLink = deviceLink;
6498 return previous;
6501 bool cmGeneratorTarget::GetLinkInterfaceDependentBoolProperty(
6502 const std::string& p, const std::string& config) const
6504 return checkInterfacePropertyCompatibility<bool>(this, p, config, "FALSE",
6505 BoolType, nullptr);
6508 std::string cmGeneratorTarget::GetLinkInterfaceDependentStringAsBoolProperty(
6509 const std::string& p, const std::string& config) const
6511 return checkInterfacePropertyCompatibility<std::string>(
6512 this, p, config, "FALSE", BoolType, nullptr);
6515 const char* cmGeneratorTarget::GetLinkInterfaceDependentStringProperty(
6516 const std::string& p, const std::string& config) const
6518 return checkInterfacePropertyCompatibility<const char*>(
6519 this, p, config, "empty", StringType, nullptr);
6522 const char* cmGeneratorTarget::GetLinkInterfaceDependentNumberMinProperty(
6523 const std::string& p, const std::string& config) const
6525 return checkInterfacePropertyCompatibility<const char*>(
6526 this, p, config, "empty", NumberMinType, nullptr);
6529 const char* cmGeneratorTarget::GetLinkInterfaceDependentNumberMaxProperty(
6530 const std::string& p, const std::string& config) const
6532 return checkInterfacePropertyCompatibility<const char*>(
6533 this, p, config, "empty", NumberMaxType, nullptr);
6536 cmComputeLinkInformation* cmGeneratorTarget::GetLinkInformation(
6537 const std::string& config) const
6539 // Lookup any existing information for this configuration.
6540 std::string key(cmSystemTools::UpperCase(config));
6541 auto i = this->LinkInformation.find(key);
6542 if (i == this->LinkInformation.end()) {
6543 // Compute information for this configuration.
6544 auto info = cm::make_unique<cmComputeLinkInformation>(this, config);
6545 if (info && !info->Compute()) {
6546 info.reset();
6549 // Store the information for this configuration.
6550 i = this->LinkInformation.emplace(key, std::move(info)).first;
6552 if (i->second) {
6553 this->CheckPropertyCompatibility(*i->second, config);
6556 return i->second.get();
6559 void cmGeneratorTarget::CheckLinkLibraries() const
6561 bool linkLibrariesOnlyTargets =
6562 this->GetPropertyAsBool("LINK_LIBRARIES_ONLY_TARGETS");
6564 // Evaluate the link interface of this target if needed for extra checks.
6565 if (linkLibrariesOnlyTargets) {
6566 std::vector<std::string> const& configs =
6567 this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
6568 for (std::string const& config : configs) {
6569 this->GetLinkInterfaceLibraries(config, this, LinkInterfaceFor::Link);
6573 // Check link the implementation for each generated configuration.
6574 for (auto const& hmp : this->LinkImplMap) {
6575 HeadToLinkImplementationMap const& hm = hmp.second;
6576 // There could be several entries used when computing the pre-CMP0022
6577 // default link interface. Check only the entry for our own link impl.
6578 auto const hmi = hm.find(this);
6579 if (hmi == hm.end() || !hmi->second.LibrariesDone) {
6580 continue;
6582 for (cmLinkImplItem const& item : hmi->second.Libraries) {
6583 if (!this->VerifyLinkItemColons(LinkItemRole::Implementation, item)) {
6584 return;
6586 if (linkLibrariesOnlyTargets &&
6587 !this->VerifyLinkItemIsTarget(LinkItemRole::Implementation, item)) {
6588 return;
6593 // Check link the interface for each generated combination of
6594 // configuration and consuming head target. We should not need to
6595 // consider LinkInterfaceUsageRequirementsOnlyMap because its entries
6596 // should be a subset of LinkInterfaceMap (with LINK_ONLY left out).
6597 for (auto const& hmp : this->LinkInterfaceMap) {
6598 for (auto const& hmi : hmp.second) {
6599 if (!hmi.second.LibrariesDone) {
6600 continue;
6602 for (cmLinkItem const& item : hmi.second.Libraries) {
6603 if (!this->VerifyLinkItemColons(LinkItemRole::Interface, item)) {
6604 return;
6606 if (linkLibrariesOnlyTargets &&
6607 !this->VerifyLinkItemIsTarget(LinkItemRole::Interface, item)) {
6608 return;
6615 namespace {
6616 cm::string_view missingTargetPossibleReasons =
6617 "Possible reasons include:\n"
6618 " * There is a typo in the target name.\n"
6619 " * A find_package call is missing for an IMPORTED target.\n"
6620 " * An ALIAS target is missing.\n"_s;
6623 bool cmGeneratorTarget::VerifyLinkItemColons(LinkItemRole role,
6624 cmLinkItem const& item) const
6626 if (item.Target || cmHasPrefix(item.AsStr(), "<LINK_GROUP:"_s) ||
6627 item.AsStr().find("::") == std::string::npos) {
6628 return true;
6630 MessageType messageType = MessageType::FATAL_ERROR;
6631 std::string e;
6632 switch (this->GetLocalGenerator()->GetPolicyStatus(cmPolicies::CMP0028)) {
6633 case cmPolicies::WARN: {
6634 e = cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0028), "\n");
6635 messageType = MessageType::AUTHOR_WARNING;
6636 } break;
6637 case cmPolicies::OLD:
6638 return true;
6639 case cmPolicies::REQUIRED_IF_USED:
6640 case cmPolicies::REQUIRED_ALWAYS:
6641 case cmPolicies::NEW:
6642 // Issue the fatal message.
6643 break;
6646 if (role == LinkItemRole::Implementation) {
6647 e = cmStrCat(e, "Target \"", this->GetName(), "\" links to");
6648 } else {
6649 e = cmStrCat(e, "The link interface of target \"", this->GetName(),
6650 "\" contains");
6653 cmStrCat(e, ":\n ", item.AsStr(), "\n", "but the target was not found. ",
6654 missingTargetPossibleReasons);
6655 cmListFileBacktrace backtrace = item.Backtrace;
6656 if (backtrace.Empty()) {
6657 backtrace = this->GetBacktrace();
6659 this->GetLocalGenerator()->GetCMakeInstance()->IssueMessage(messageType, e,
6660 backtrace);
6661 return false;
6664 bool cmGeneratorTarget::VerifyLinkItemIsTarget(LinkItemRole role,
6665 cmLinkItem const& item) const
6667 if (item.Target) {
6668 return true;
6670 std::string const& str = item.AsStr();
6671 if (!str.empty() &&
6672 (str[0] == '-' || str[0] == '$' || str[0] == '`' ||
6673 str.find_first_of("/\\") != std::string::npos ||
6674 cmHasPrefix(str, "<LINK_LIBRARY:"_s) ||
6675 cmHasPrefix(str, "<LINK_GROUP:"_s))) {
6676 return true;
6679 std::string e = cmStrCat("Target \"", this->GetName(),
6680 "\" has LINK_LIBRARIES_ONLY_TARGETS enabled, but ",
6681 role == LinkItemRole::Implementation
6682 ? "it links to"
6683 : "its link interface contains",
6684 ":\n ", item.AsStr(), "\nwhich is not a target. ",
6685 missingTargetPossibleReasons);
6686 cmListFileBacktrace backtrace = item.Backtrace;
6687 if (backtrace.Empty()) {
6688 backtrace = this->GetBacktrace();
6690 this->LocalGenerator->GetCMakeInstance()->IssueMessage(
6691 MessageType::FATAL_ERROR, e, backtrace);
6692 return false;
6695 void cmGeneratorTarget::GetTargetVersion(int& major, int& minor) const
6697 int patch;
6698 this->GetTargetVersion("VERSION", major, minor, patch);
6701 void cmGeneratorTarget::GetTargetVersionFallback(
6702 const std::string& property, const std::string& fallback_property,
6703 int& major, int& minor, int& patch) const
6705 if (this->GetProperty(property)) {
6706 this->GetTargetVersion(property, major, minor, patch);
6707 } else {
6708 this->GetTargetVersion(fallback_property, major, minor, patch);
6712 void cmGeneratorTarget::GetTargetVersion(const std::string& property,
6713 int& major, int& minor,
6714 int& patch) const
6716 // Set the default values.
6717 major = 0;
6718 minor = 0;
6719 patch = 0;
6721 assert(this->GetType() != cmStateEnums::INTERFACE_LIBRARY);
6723 if (cmValue version = this->GetProperty(property)) {
6724 // Try to parse the version number and store the results that were
6725 // successfully parsed.
6726 int parsed_major;
6727 int parsed_minor;
6728 int parsed_patch;
6729 switch (sscanf(version->c_str(), "%d.%d.%d", &parsed_major, &parsed_minor,
6730 &parsed_patch)) {
6731 case 3:
6732 patch = parsed_patch;
6733 CM_FALLTHROUGH;
6734 case 2:
6735 minor = parsed_minor;
6736 CM_FALLTHROUGH;
6737 case 1:
6738 major = parsed_major;
6739 CM_FALLTHROUGH;
6740 default:
6741 break;
6746 std::string cmGeneratorTarget::GetRuntimeLinkLibrary(
6747 std::string const& lang, std::string const& config) const
6749 // This is activated by the presence of a default selection whether or
6750 // not it is overridden by a property.
6751 cmValue runtimeLibraryDefault = this->Makefile->GetDefinition(
6752 cmStrCat("CMAKE_", lang, "_RUNTIME_LIBRARY_DEFAULT"));
6753 if (!cmNonempty(runtimeLibraryDefault)) {
6754 return std::string();
6756 cmValue runtimeLibraryValue =
6757 this->Target->GetProperty(cmStrCat(lang, "_RUNTIME_LIBRARY"));
6758 if (!runtimeLibraryValue) {
6759 runtimeLibraryValue = runtimeLibraryDefault;
6761 return cmSystemTools::UpperCase(cmGeneratorExpression::Evaluate(
6762 *runtimeLibraryValue, this->LocalGenerator, config, this));
6765 std::string cmGeneratorTarget::GetFortranModuleDirectory(
6766 std::string const& working_dir) const
6768 if (!this->FortranModuleDirectoryCreated) {
6769 this->FortranModuleDirectory =
6770 this->CreateFortranModuleDirectory(working_dir);
6771 this->FortranModuleDirectoryCreated = true;
6774 return this->FortranModuleDirectory;
6777 bool cmGeneratorTarget::IsFortranBuildingInstrinsicModules() const
6779 if (cmValue prop =
6780 this->GetProperty("Fortran_BUILDING_INSTRINSIC_MODULES")) {
6781 return cmIsOn(*prop);
6783 return false;
6786 std::string cmGeneratorTarget::CreateFortranModuleDirectory(
6787 std::string const& working_dir) const
6789 std::string mod_dir;
6790 std::string target_mod_dir;
6791 if (cmValue prop = this->GetProperty("Fortran_MODULE_DIRECTORY")) {
6792 target_mod_dir = *prop;
6793 } else {
6794 std::string const& default_mod_dir =
6795 this->LocalGenerator->GetCurrentBinaryDirectory();
6796 if (default_mod_dir != working_dir) {
6797 target_mod_dir = default_mod_dir;
6800 cmValue moddir_flag =
6801 this->Makefile->GetDefinition("CMAKE_Fortran_MODDIR_FLAG");
6802 if (!target_mod_dir.empty() && moddir_flag) {
6803 // Compute the full path to the module directory.
6804 if (cmSystemTools::FileIsFullPath(target_mod_dir)) {
6805 // Already a full path.
6806 mod_dir = target_mod_dir;
6807 } else {
6808 // Interpret relative to the current output directory.
6809 mod_dir = cmStrCat(this->LocalGenerator->GetCurrentBinaryDirectory(),
6810 '/', target_mod_dir);
6813 // Make sure the module output directory exists.
6814 cmSystemTools::MakeDirectory(mod_dir);
6816 return mod_dir;
6819 void cmGeneratorTarget::AddISPCGeneratedHeader(std::string const& header,
6820 std::string const& config)
6822 std::string config_upper;
6823 if (!config.empty()) {
6824 config_upper = cmSystemTools::UpperCase(config);
6826 auto iter = this->ISPCGeneratedHeaders.find(config_upper);
6827 if (iter == this->ISPCGeneratedHeaders.end()) {
6828 std::vector<std::string> headers;
6829 headers.emplace_back(header);
6830 this->ISPCGeneratedHeaders.insert({ config_upper, headers });
6831 } else {
6832 iter->second.emplace_back(header);
6836 std::vector<std::string> cmGeneratorTarget::GetGeneratedISPCHeaders(
6837 std::string const& config) const
6839 std::string config_upper;
6840 if (!config.empty()) {
6841 config_upper = cmSystemTools::UpperCase(config);
6843 auto iter = this->ISPCGeneratedHeaders.find(config_upper);
6844 if (iter == this->ISPCGeneratedHeaders.end()) {
6845 return std::vector<std::string>{};
6847 return iter->second;
6850 void cmGeneratorTarget::AddISPCGeneratedObject(std::vector<std::string>&& objs,
6851 std::string const& config)
6853 std::string config_upper;
6854 if (!config.empty()) {
6855 config_upper = cmSystemTools::UpperCase(config);
6857 auto iter = this->ISPCGeneratedObjects.find(config_upper);
6858 if (iter == this->ISPCGeneratedObjects.end()) {
6859 this->ISPCGeneratedObjects.insert({ config_upper, objs });
6860 } else {
6861 iter->second.insert(iter->second.end(), objs.begin(), objs.end());
6865 std::vector<std::string> cmGeneratorTarget::GetGeneratedISPCObjects(
6866 std::string const& config) const
6868 std::string config_upper;
6869 if (!config.empty()) {
6870 config_upper = cmSystemTools::UpperCase(config);
6872 auto iter = this->ISPCGeneratedObjects.find(config_upper);
6873 if (iter == this->ISPCGeneratedObjects.end()) {
6874 return std::vector<std::string>{};
6876 return iter->second;
6879 std::string cmGeneratorTarget::GetFrameworkVersion() const
6881 assert(this->GetType() != cmStateEnums::INTERFACE_LIBRARY);
6883 if (cmValue fversion = this->GetProperty("FRAMEWORK_VERSION")) {
6884 return *fversion;
6886 if (cmValue tversion = this->GetProperty("VERSION")) {
6887 return *tversion;
6889 return "A";
6892 void cmGeneratorTarget::ComputeVersionedName(
6893 std::string& vName, std::string const& prefix, std::string const& base,
6894 std::string const& suffix, std::string const& name, cmValue version) const
6896 vName = this->IsApple() ? (prefix + base) : name;
6897 if (version) {
6898 vName += ".";
6899 vName += *version;
6901 vName += this->IsApple() ? suffix : std::string();
6904 std::vector<std::string> cmGeneratorTarget::GetPropertyKeys() const
6906 return this->Target->GetProperties().GetKeys();
6909 void cmGeneratorTarget::ReportPropertyOrigin(
6910 const std::string& p, const std::string& result, const std::string& report,
6911 const std::string& compatibilityType) const
6913 cmList debugProperties{ this->Target->GetMakefile()->GetDefinition(
6914 "CMAKE_DEBUG_TARGET_PROPERTIES") };
6915 bool debugOrigin = !this->DebugCompatiblePropertiesDone[p] &&
6916 cm::contains(debugProperties, p);
6918 if (this->GlobalGenerator->GetConfigureDoneCMP0026()) {
6919 this->DebugCompatiblePropertiesDone[p] = true;
6921 if (!debugOrigin) {
6922 return;
6925 std::string areport =
6926 cmStrCat(compatibilityType, " of property \"", p, "\" for target \"",
6927 this->GetName(), "\" (result: \"", result, "\"):\n", report);
6929 this->LocalGenerator->GetCMakeInstance()->IssueMessage(MessageType::LOG,
6930 areport);
6933 bool cmGeneratorTarget::IsLinkLookupScope(std::string const& n,
6934 cmLocalGenerator const*& lg) const
6936 if (cmHasLiteralPrefix(n, CMAKE_DIRECTORY_ID_SEP)) {
6937 cmDirectoryId const dirId = n.substr(cmStrLen(CMAKE_DIRECTORY_ID_SEP));
6938 if (dirId.String.empty()) {
6939 lg = this->LocalGenerator;
6940 return true;
6942 if (cmLocalGenerator const* otherLG =
6943 this->GlobalGenerator->FindLocalGenerator(dirId)) {
6944 lg = otherLG;
6945 return true;
6948 return false;
6951 cm::optional<cmLinkItem> cmGeneratorTarget::LookupLinkItem(
6952 std::string const& n, cmListFileBacktrace const& bt,
6953 std::string const& linkFeature, LookupLinkItemScope* scope,
6954 LookupSelf lookupSelf) const
6956 cm::optional<cmLinkItem> maybeItem;
6957 if (this->IsLinkLookupScope(n, scope->LG)) {
6958 return maybeItem;
6961 std::string name = this->CheckCMP0004(n);
6962 if (name.empty() ||
6963 (lookupSelf == LookupSelf::No && name == this->GetName())) {
6964 return maybeItem;
6966 maybeItem =
6967 this->ResolveLinkItem(BT<std::string>(name, bt), scope->LG, linkFeature);
6968 return maybeItem;
6971 void cmGeneratorTarget::ExpandLinkItems(
6972 std::string const& prop, cmBTStringRange entries, std::string const& config,
6973 cmGeneratorTarget const* headTarget, LinkInterfaceFor interfaceFor,
6974 LinkInterfaceField field, cmLinkInterface& iface) const
6976 if (entries.empty()) {
6977 return;
6979 // Keep this logic in sync with ComputeLinkImplementationLibraries.
6980 cmGeneratorExpressionDAGChecker dagChecker(this, prop, nullptr, nullptr);
6981 // The $<LINK_ONLY> expression may be in a link interface to specify
6982 // private link dependencies that are otherwise excluded from usage
6983 // requirements.
6984 if (interfaceFor == LinkInterfaceFor::Usage) {
6985 dagChecker.SetTransitivePropertiesOnly();
6986 dagChecker.SetTransitivePropertiesOnlyCMP0131();
6988 cmMakefile const* mf = this->LocalGenerator->GetMakefile();
6989 LookupLinkItemScope scope{ this->LocalGenerator };
6990 for (BT<std::string> const& entry : entries) {
6991 cmGeneratorExpression ge(*this->LocalGenerator->GetCMakeInstance(),
6992 entry.Backtrace);
6993 std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(entry.Value);
6994 cge->SetEvaluateForBuildsystem(true);
6995 cmList libs{ cge->Evaluate(this->LocalGenerator, config, headTarget,
6996 &dagChecker, this,
6997 headTarget->LinkerLanguage) };
6999 auto linkFeature = cmLinkItem::DEFAULT;
7000 for (auto const& lib : libs) {
7001 if (auto maybeLinkFeature = ParseLinkFeature(lib)) {
7002 linkFeature = std::move(*maybeLinkFeature);
7003 continue;
7006 if (cm::optional<cmLinkItem> maybeItem = this->LookupLinkItem(
7007 lib, cge->GetBacktrace(), linkFeature, &scope,
7008 field == LinkInterfaceField::Libraries ? LookupSelf::No
7009 : LookupSelf::Yes)) {
7010 cmLinkItem item = std::move(*maybeItem);
7012 if (field == LinkInterfaceField::HeadInclude) {
7013 iface.HeadInclude.emplace_back(std::move(item));
7014 continue;
7016 if (field == LinkInterfaceField::HeadExclude) {
7017 iface.HeadExclude.emplace_back(std::move(item));
7018 continue;
7020 if (!item.Target) {
7021 // Report explicitly linked object files separately.
7022 std::string const& maybeObj = item.AsStr();
7023 if (cmSystemTools::FileIsFullPath(maybeObj)) {
7024 cmSourceFile const* sf =
7025 mf->GetSource(maybeObj, cmSourceFileLocationKind::Known);
7026 if (sf && sf->GetPropertyAsBool("EXTERNAL_OBJECT")) {
7027 item.ObjectSource = sf;
7028 iface.Objects.emplace_back(std::move(item));
7029 continue;
7034 iface.Libraries.emplace_back(std::move(item));
7037 if (cge->GetHadHeadSensitiveCondition()) {
7038 iface.HadHeadSensitiveCondition = true;
7040 if (cge->GetHadContextSensitiveCondition()) {
7041 iface.HadContextSensitiveCondition = true;
7043 if (cge->GetHadLinkLanguageSensitiveCondition()) {
7044 iface.HadLinkLanguageSensitiveCondition = true;
7049 cmLinkInterface const* cmGeneratorTarget::GetLinkInterface(
7050 const std::string& config, cmGeneratorTarget const* head) const
7052 return this->GetLinkInterface(config, head, false);
7055 cmLinkInterface const* cmGeneratorTarget::GetLinkInterface(
7056 const std::string& config, cmGeneratorTarget const* head,
7057 bool secondPass) const
7059 // Imported targets have their own link interface.
7060 if (this->IsImported()) {
7061 return this->GetImportLinkInterface(config, head, LinkInterfaceFor::Link,
7062 secondPass);
7065 // Link interfaces are not supported for executables that do not
7066 // export symbols.
7067 if (this->GetType() == cmStateEnums::EXECUTABLE &&
7068 !this->IsExecutableWithExports()) {
7069 return nullptr;
7072 // Lookup any existing link interface for this configuration.
7073 cmHeadToLinkInterfaceMap& hm = this->GetHeadToLinkInterfaceMap(config);
7075 // If the link interface does not depend on the head target
7076 // then reuse the one from the head we computed first.
7077 if (!hm.empty() && !hm.begin()->second.HadHeadSensitiveCondition) {
7078 head = hm.begin()->first;
7081 cmOptionalLinkInterface& iface = hm[head];
7082 if (secondPass) {
7083 iface = cmOptionalLinkInterface();
7085 if (!iface.LibrariesDone) {
7086 iface.LibrariesDone = true;
7087 this->ComputeLinkInterfaceLibraries(config, iface, head,
7088 LinkInterfaceFor::Link);
7090 if (!iface.AllDone) {
7091 iface.AllDone = true;
7092 if (iface.Exists) {
7093 this->ComputeLinkInterface(config, iface, head, secondPass);
7094 this->ComputeLinkInterfaceRuntimeLibraries(config, iface);
7098 return iface.Exists ? &iface : nullptr;
7101 void cmGeneratorTarget::ComputeLinkInterface(
7102 const std::string& config, cmOptionalLinkInterface& iface,
7103 cmGeneratorTarget const* headTarget) const
7105 this->ComputeLinkInterface(config, iface, headTarget, false);
7108 void cmGeneratorTarget::ComputeLinkInterface(
7109 const std::string& config, cmOptionalLinkInterface& iface,
7110 cmGeneratorTarget const* headTarget, bool secondPass) const
7112 if (iface.Explicit) {
7113 if (this->GetType() == cmStateEnums::SHARED_LIBRARY ||
7114 this->GetType() == cmStateEnums::STATIC_LIBRARY ||
7115 this->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
7116 // Shared libraries may have runtime implementation dependencies
7117 // on other shared libraries that are not in the interface.
7118 std::set<cmLinkItem> emitted;
7119 for (cmLinkItem const& lib : iface.Libraries) {
7120 emitted.insert(lib);
7122 if (this->GetType() != cmStateEnums::INTERFACE_LIBRARY) {
7123 cmLinkImplementation const* impl = this->GetLinkImplementation(
7124 config, LinkInterfaceFor::Link, secondPass);
7125 for (cmLinkImplItem const& lib : impl->Libraries) {
7126 if (emitted.insert(lib).second) {
7127 if (lib.Target) {
7128 // This is a runtime dependency on another shared library.
7129 if (lib.Target->GetType() == cmStateEnums::SHARED_LIBRARY) {
7130 iface.SharedDeps.push_back(lib);
7132 } else {
7133 // TODO: Recognize shared library file names. Perhaps this
7134 // should be moved to cmComputeLinkInformation, but that
7135 // creates a chicken-and-egg problem since this list is needed
7136 // for its construction.
7142 } else if (this->GetPolicyStatusCMP0022() == cmPolicies::WARN ||
7143 this->GetPolicyStatusCMP0022() == cmPolicies::OLD) {
7144 // The link implementation is the default link interface.
7145 cmLinkImplementationLibraries const* impl =
7146 this->GetLinkImplementationLibrariesInternal(config, headTarget,
7147 LinkInterfaceFor::Link);
7148 iface.ImplementationIsInterface = true;
7149 iface.WrongConfigLibraries = impl->WrongConfigLibraries;
7152 if (this->LinkLanguagePropagatesToDependents()) {
7153 // Targets using this archive need its language runtime libraries.
7154 if (cmLinkImplementation const* impl = this->GetLinkImplementation(
7155 config, LinkInterfaceFor::Link, secondPass)) {
7156 iface.Languages = impl->Languages;
7160 if (this->GetType() == cmStateEnums::STATIC_LIBRARY) {
7161 // Construct the property name suffix for this configuration.
7162 std::string suffix = "_";
7163 if (!config.empty()) {
7164 suffix += cmSystemTools::UpperCase(config);
7165 } else {
7166 suffix += "NOCONFIG";
7169 // How many repetitions are needed if this library has cyclic
7170 // dependencies?
7171 std::string propName = cmStrCat("LINK_INTERFACE_MULTIPLICITY", suffix);
7172 if (cmValue config_reps = this->GetProperty(propName)) {
7173 sscanf(config_reps->c_str(), "%u", &iface.Multiplicity);
7174 } else if (cmValue reps =
7175 this->GetProperty("LINK_INTERFACE_MULTIPLICITY")) {
7176 sscanf(reps->c_str(), "%u", &iface.Multiplicity);
7181 const cmLinkInterfaceLibraries* cmGeneratorTarget::GetLinkInterfaceLibraries(
7182 const std::string& config, cmGeneratorTarget const* head,
7183 LinkInterfaceFor interfaceFor) const
7185 // Imported targets have their own link interface.
7186 if (this->IsImported()) {
7187 return this->GetImportLinkInterface(config, head, interfaceFor);
7190 // Link interfaces are not supported for executables that do not
7191 // export symbols.
7192 if (this->GetType() == cmStateEnums::EXECUTABLE &&
7193 !this->IsExecutableWithExports()) {
7194 return nullptr;
7197 // Lookup any existing link interface for this configuration.
7198 cmHeadToLinkInterfaceMap& hm =
7199 (interfaceFor == LinkInterfaceFor::Usage
7200 ? this->GetHeadToLinkInterfaceUsageRequirementsMap(config)
7201 : this->GetHeadToLinkInterfaceMap(config));
7203 // If the link interface does not depend on the head target
7204 // then reuse the one from the head we computed first.
7205 if (!hm.empty() && !hm.begin()->second.HadHeadSensitiveCondition) {
7206 head = hm.begin()->first;
7209 cmOptionalLinkInterface& iface = hm[head];
7210 if (!iface.LibrariesDone) {
7211 iface.LibrariesDone = true;
7212 this->ComputeLinkInterfaceLibraries(config, iface, head, interfaceFor);
7215 return iface.Exists ? &iface : nullptr;
7218 std::string cmGeneratorTarget::GetDirectory(
7219 const std::string& config, cmStateEnums::ArtifactType artifact) const
7221 if (this->IsImported()) {
7222 auto fullPath = this->Target->ImportedGetFullPath(config, artifact);
7223 if (this->IsFrameworkOnApple()) {
7224 auto fwDescriptor = this->GetGlobalGenerator()->SplitFrameworkPath(
7225 fullPath, cmGlobalGenerator::FrameworkFormat::Strict);
7226 if (fwDescriptor) {
7227 return fwDescriptor->Directory;
7230 // Return the directory from which the target is imported.
7231 return cmSystemTools::GetFilenamePath(fullPath);
7233 if (OutputInfo const* info = this->GetOutputInfo(config)) {
7234 // Return the directory in which the target will be built.
7235 switch (artifact) {
7236 case cmStateEnums::RuntimeBinaryArtifact:
7237 return info->OutDir;
7238 case cmStateEnums::ImportLibraryArtifact:
7239 return info->ImpDir;
7242 return "";
7245 bool cmGeneratorTarget::UsesDefaultOutputDir(
7246 const std::string& config, cmStateEnums::ArtifactType artifact) const
7248 std::string dir;
7249 return this->ComputeOutputDir(config, artifact, dir);
7252 cmGeneratorTarget::OutputInfo const* cmGeneratorTarget::GetOutputInfo(
7253 const std::string& config) const
7255 // There is no output information for imported targets.
7256 if (this->IsImported()) {
7257 return nullptr;
7260 // Synthetic targets don't have output.
7261 if (this->IsSynthetic()) {
7262 return nullptr;
7265 // Only libraries and executables have well-defined output files.
7266 if (!this->HaveWellDefinedOutputFiles()) {
7267 std::string msg = cmStrCat("cmGeneratorTarget::GetOutputInfo called for ",
7268 this->GetName(), " which has type ",
7269 cmState::GetTargetTypeName(this->GetType()));
7270 this->LocalGenerator->IssueMessage(MessageType::INTERNAL_ERROR, msg);
7271 return nullptr;
7274 // Lookup/compute/cache the output information for this configuration.
7275 std::string config_upper;
7276 if (!config.empty()) {
7277 config_upper = cmSystemTools::UpperCase(config);
7279 auto i = this->OutputInfoMap.find(config_upper);
7280 if (i == this->OutputInfoMap.end()) {
7281 // Add empty info in map to detect potential recursion.
7282 OutputInfo info;
7283 OutputInfoMapType::value_type entry(config_upper, info);
7284 i = this->OutputInfoMap.insert(entry).first;
7286 // Compute output directories.
7287 this->ComputeOutputDir(config, cmStateEnums::RuntimeBinaryArtifact,
7288 info.OutDir);
7289 this->ComputeOutputDir(config, cmStateEnums::ImportLibraryArtifact,
7290 info.ImpDir);
7291 if (!this->ComputePDBOutputDir("PDB", config, info.PdbDir)) {
7292 info.PdbDir = info.OutDir;
7295 // Now update the previously-prepared map entry.
7296 i->second = info;
7297 } else if (i->second.empty()) {
7298 // An empty map entry indicates we have been called recursively
7299 // from the above block.
7300 this->LocalGenerator->GetCMakeInstance()->IssueMessage(
7301 MessageType::FATAL_ERROR,
7302 "Target '" + this->GetName() + "' OUTPUT_DIRECTORY depends on itself.",
7303 this->GetBacktrace());
7304 return nullptr;
7306 return &i->second;
7309 bool cmGeneratorTarget::ComputeOutputDir(const std::string& config,
7310 cmStateEnums::ArtifactType artifact,
7311 std::string& out) const
7313 bool usesDefaultOutputDir = false;
7314 std::string conf = config;
7316 // Look for a target property defining the target output directory
7317 // based on the target type.
7318 std::string targetTypeName = this->GetOutputTargetType(artifact);
7319 std::string propertyName;
7320 if (!targetTypeName.empty()) {
7321 propertyName = cmStrCat(targetTypeName, "_OUTPUT_DIRECTORY");
7324 // Check for a per-configuration output directory target property.
7325 std::string configUpper = cmSystemTools::UpperCase(conf);
7326 std::string configProp;
7327 if (!targetTypeName.empty()) {
7328 configProp = cmStrCat(targetTypeName, "_OUTPUT_DIRECTORY_", configUpper);
7331 // Select an output directory.
7332 if (cmValue config_outdir = this->GetProperty(configProp)) {
7333 // Use the user-specified per-configuration output directory.
7334 out = cmGeneratorExpression::Evaluate(*config_outdir, this->LocalGenerator,
7335 config, this);
7337 // Skip per-configuration subdirectory.
7338 conf.clear();
7339 } else if (cmValue outdir = this->GetProperty(propertyName)) {
7340 // Use the user-specified output directory.
7341 out = cmGeneratorExpression::Evaluate(*outdir, this->LocalGenerator,
7342 config, this);
7343 // Skip per-configuration subdirectory if the value contained a
7344 // generator expression.
7345 if (out != *outdir) {
7346 conf.clear();
7348 } else if (this->GetType() == cmStateEnums::EXECUTABLE) {
7349 // Lookup the output path for executables.
7350 out = this->Makefile->GetSafeDefinition("EXECUTABLE_OUTPUT_PATH");
7351 } else if (this->GetType() == cmStateEnums::STATIC_LIBRARY ||
7352 this->GetType() == cmStateEnums::SHARED_LIBRARY ||
7353 this->GetType() == cmStateEnums::MODULE_LIBRARY) {
7354 // Lookup the output path for libraries.
7355 out = this->Makefile->GetSafeDefinition("LIBRARY_OUTPUT_PATH");
7357 if (out.empty()) {
7358 // Default to the current output directory.
7359 usesDefaultOutputDir = true;
7360 out = ".";
7363 // Convert the output path to a full path in case it is
7364 // specified as a relative path. Treat a relative path as
7365 // relative to the current output directory for this makefile.
7366 out = (cmSystemTools::CollapseFullPath(
7367 out, this->LocalGenerator->GetCurrentBinaryDirectory()));
7369 // The generator may add the configuration's subdirectory.
7370 if (!conf.empty()) {
7371 bool useEPN =
7372 this->GlobalGenerator->UseEffectivePlatformName(this->Makefile);
7373 std::string suffix =
7374 usesDefaultOutputDir && useEPN ? "${EFFECTIVE_PLATFORM_NAME}" : "";
7375 this->LocalGenerator->GetGlobalGenerator()->AppendDirectoryForConfig(
7376 "/", conf, suffix, out);
7379 return usesDefaultOutputDir;
7382 bool cmGeneratorTarget::ComputePDBOutputDir(const std::string& kind,
7383 const std::string& config,
7384 std::string& out) const
7386 // Look for a target property defining the target output directory
7387 // based on the target type.
7388 std::string propertyName;
7389 if (!kind.empty()) {
7390 propertyName = cmStrCat(kind, "_OUTPUT_DIRECTORY");
7392 std::string conf = config;
7394 // Check for a per-configuration output directory target property.
7395 std::string configUpper = cmSystemTools::UpperCase(conf);
7396 std::string configProp;
7397 if (!kind.empty()) {
7398 configProp = cmStrCat(kind, "_OUTPUT_DIRECTORY_", configUpper);
7401 // Select an output directory.
7402 if (cmValue config_outdir = this->GetProperty(configProp)) {
7403 // Use the user-specified per-configuration output directory.
7404 out = cmGeneratorExpression::Evaluate(*config_outdir, this->LocalGenerator,
7405 config);
7407 // Skip per-configuration subdirectory.
7408 conf.clear();
7409 } else if (cmValue outdir = this->GetProperty(propertyName)) {
7410 // Use the user-specified output directory.
7411 out =
7412 cmGeneratorExpression::Evaluate(*outdir, this->LocalGenerator, config);
7414 // Skip per-configuration subdirectory if the value contained a
7415 // generator expression.
7416 if (out != *outdir) {
7417 conf.clear();
7420 if (out.empty()) {
7421 return false;
7424 // Convert the output path to a full path in case it is
7425 // specified as a relative path. Treat a relative path as
7426 // relative to the current output directory for this makefile.
7427 out = (cmSystemTools::CollapseFullPath(
7428 out, this->LocalGenerator->GetCurrentBinaryDirectory()));
7430 // The generator may add the configuration's subdirectory.
7431 if (!conf.empty()) {
7432 this->LocalGenerator->GetGlobalGenerator()->AppendDirectoryForConfig(
7433 "/", conf, "", out);
7435 return true;
7438 bool cmGeneratorTarget::HaveInstallTreeRPATH(const std::string& config) const
7440 std::string install_rpath;
7441 this->GetInstallRPATH(config, install_rpath);
7442 return !install_rpath.empty() &&
7443 !this->Makefile->IsOn("CMAKE_SKIP_INSTALL_RPATH");
7446 bool cmGeneratorTarget::GetBuildRPATH(const std::string& config,
7447 std::string& rpath) const
7449 return this->GetRPATH(config, "BUILD_RPATH", rpath);
7452 bool cmGeneratorTarget::GetInstallRPATH(const std::string& config,
7453 std::string& rpath) const
7455 return this->GetRPATH(config, "INSTALL_RPATH", rpath);
7458 bool cmGeneratorTarget::GetRPATH(const std::string& config,
7459 const std::string& prop,
7460 std::string& rpath) const
7462 cmValue value = this->GetProperty(prop);
7463 if (!value) {
7464 return false;
7467 rpath =
7468 cmGeneratorExpression::Evaluate(*value, this->LocalGenerator, config);
7470 return true;
7473 void cmGeneratorTarget::ComputeLinkInterfaceLibraries(
7474 const std::string& config, cmOptionalLinkInterface& iface,
7475 cmGeneratorTarget const* headTarget, LinkInterfaceFor interfaceFor) const
7477 // Construct the property name suffix for this configuration.
7478 std::string suffix = "_";
7479 if (!config.empty()) {
7480 suffix += cmSystemTools::UpperCase(config);
7481 } else {
7482 suffix += "NOCONFIG";
7485 // An explicit list of interface libraries may be set for shared
7486 // libraries and executables that export symbols.
7487 bool haveExplicitLibraries = false;
7488 cmValue explicitLibrariesCMP0022OLD;
7489 std::string linkIfacePropCMP0022OLD;
7490 bool const cmp0022NEW = (this->GetPolicyStatusCMP0022() != cmPolicies::OLD &&
7491 this->GetPolicyStatusCMP0022() != cmPolicies::WARN);
7492 if (cmp0022NEW) {
7493 // CMP0022 NEW behavior is to use INTERFACE_LINK_LIBRARIES.
7494 haveExplicitLibraries = !this->Target->GetLinkInterfaceEntries().empty() ||
7495 !this->Target->GetLinkInterfaceDirectEntries().empty() ||
7496 !this->Target->GetLinkInterfaceDirectExcludeEntries().empty();
7497 } else {
7498 // CMP0022 OLD behavior is to use LINK_INTERFACE_LIBRARIES if set on a
7499 // shared lib or executable.
7500 if (this->GetType() == cmStateEnums::SHARED_LIBRARY ||
7501 this->IsExecutableWithExports()) {
7502 // Lookup the per-configuration property.
7503 linkIfacePropCMP0022OLD = cmStrCat("LINK_INTERFACE_LIBRARIES", suffix);
7504 explicitLibrariesCMP0022OLD = this->GetProperty(linkIfacePropCMP0022OLD);
7506 // If not set, try the generic property.
7507 if (!explicitLibrariesCMP0022OLD) {
7508 linkIfacePropCMP0022OLD = "LINK_INTERFACE_LIBRARIES";
7509 explicitLibrariesCMP0022OLD =
7510 this->GetProperty(linkIfacePropCMP0022OLD);
7514 if (explicitLibrariesCMP0022OLD &&
7515 this->GetPolicyStatusCMP0022() == cmPolicies::WARN &&
7516 !this->PolicyWarnedCMP0022) {
7517 // Compare the explicitly set old link interface properties to the
7518 // preferred new link interface property one and warn if different.
7519 cmValue newExplicitLibraries =
7520 this->GetProperty("INTERFACE_LINK_LIBRARIES");
7521 if (newExplicitLibraries &&
7522 (*newExplicitLibraries != *explicitLibrariesCMP0022OLD)) {
7523 std::ostringstream w;
7524 /* clang-format off */
7525 w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0022) << "\n"
7526 "Target \"" << this->GetName() << "\" has an "
7527 "INTERFACE_LINK_LIBRARIES property which differs from its " <<
7528 linkIfacePropCMP0022OLD << " properties."
7529 "\n"
7530 "INTERFACE_LINK_LIBRARIES:\n"
7531 " " << *newExplicitLibraries << "\n" <<
7532 linkIfacePropCMP0022OLD << ":\n"
7533 " " << *explicitLibrariesCMP0022OLD << "\n";
7534 /* clang-format on */
7535 this->LocalGenerator->IssueMessage(MessageType::AUTHOR_WARNING,
7536 w.str());
7537 this->PolicyWarnedCMP0022 = true;
7541 haveExplicitLibraries = static_cast<bool>(explicitLibrariesCMP0022OLD);
7544 // There is no implicit link interface for executables or modules
7545 // so if none was explicitly set then there is no link interface.
7546 if (!haveExplicitLibraries &&
7547 (this->GetType() == cmStateEnums::EXECUTABLE ||
7548 (this->GetType() == cmStateEnums::MODULE_LIBRARY))) {
7549 return;
7551 iface.Exists = true;
7553 // If CMP0022 is NEW then the plain tll signature sets the
7554 // INTERFACE_LINK_LIBRARIES property. Even if the project
7555 // clears it, the link interface is still explicit.
7556 iface.Explicit = cmp0022NEW || explicitLibrariesCMP0022OLD;
7558 if (cmp0022NEW) {
7559 // The interface libraries are specified by INTERFACE_LINK_LIBRARIES.
7560 // Use its special representation directly to get backtraces.
7561 this->ExpandLinkItems(
7562 kINTERFACE_LINK_LIBRARIES, this->Target->GetLinkInterfaceEntries(),
7563 config, headTarget, interfaceFor, LinkInterfaceField::Libraries, iface);
7564 this->ExpandLinkItems(kINTERFACE_LINK_LIBRARIES_DIRECT,
7565 this->Target->GetLinkInterfaceDirectEntries(),
7566 config, headTarget, interfaceFor,
7567 LinkInterfaceField::HeadInclude, iface);
7568 this->ExpandLinkItems(kINTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE,
7569 this->Target->GetLinkInterfaceDirectExcludeEntries(),
7570 config, headTarget, interfaceFor,
7571 LinkInterfaceField::HeadExclude, iface);
7572 } else if (explicitLibrariesCMP0022OLD) {
7573 // The interface libraries have been explicitly set in pre-CMP0022 style.
7574 std::vector<BT<std::string>> entries;
7575 entries.emplace_back(*explicitLibrariesCMP0022OLD);
7576 this->ExpandLinkItems(linkIfacePropCMP0022OLD, cmMakeRange(entries),
7577 config, headTarget, interfaceFor,
7578 LinkInterfaceField::Libraries, iface);
7581 // If the link interface is explicit, do not fall back to the link impl.
7582 if (iface.Explicit) {
7583 return;
7586 // The link implementation is the default link interface.
7587 if (cmLinkImplementationLibraries const* impl =
7588 this->GetLinkImplementationLibrariesInternal(config, headTarget,
7589 interfaceFor)) {
7590 iface.Libraries.insert(iface.Libraries.end(), impl->Libraries.begin(),
7591 impl->Libraries.end());
7592 if (this->GetPolicyStatusCMP0022() == cmPolicies::WARN &&
7593 !this->PolicyWarnedCMP0022 && interfaceFor == LinkInterfaceFor::Link) {
7594 // Compare the link implementation fallback link interface to the
7595 // preferred new link interface property and warn if different.
7596 cmLinkInterface ifaceNew;
7597 this->ExpandLinkItems(kINTERFACE_LINK_LIBRARIES,
7598 this->Target->GetLinkInterfaceEntries(), config,
7599 headTarget, interfaceFor,
7600 LinkInterfaceField::Libraries, ifaceNew);
7601 if (ifaceNew.Libraries != iface.Libraries) {
7602 std::string oldLibraries = cmJoin(impl->Libraries, ";");
7603 std::string newLibraries = cmJoin(ifaceNew.Libraries, ";");
7604 if (oldLibraries.empty()) {
7605 oldLibraries = "(empty)";
7607 if (newLibraries.empty()) {
7608 newLibraries = "(empty)";
7611 std::ostringstream w;
7612 /* clang-format off */
7613 w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0022) << "\n"
7614 "Target \"" << this->GetName() << "\" has an "
7615 "INTERFACE_LINK_LIBRARIES property. "
7616 "This should be preferred as the source of the link interface "
7617 "for this library but because CMP0022 is not set CMake is "
7618 "ignoring the property and using the link implementation "
7619 "as the link interface instead."
7620 "\n"
7621 "INTERFACE_LINK_LIBRARIES:\n"
7622 " " << newLibraries << "\n"
7623 "Link implementation:\n"
7624 " " << oldLibraries << "\n";
7625 /* clang-format on */
7626 this->LocalGenerator->IssueMessage(MessageType::AUTHOR_WARNING,
7627 w.str());
7628 this->PolicyWarnedCMP0022 = true;
7634 namespace {
7636 template <typename ReturnType>
7637 ReturnType constructItem(cmGeneratorTarget* target,
7638 cmListFileBacktrace const& bt);
7640 template <>
7641 inline cmLinkImplItem constructItem(cmGeneratorTarget* target,
7642 cmListFileBacktrace const& bt)
7644 return cmLinkImplItem(cmLinkItem(target, false, bt), false);
7647 template <>
7648 inline cmLinkItem constructItem(cmGeneratorTarget* target,
7649 cmListFileBacktrace const& bt)
7651 return cmLinkItem(target, false, bt);
7654 template <typename ValueType>
7655 std::vector<ValueType> computeImplicitLanguageTargets(
7656 std::string const& lang, std::string const& config,
7657 cmGeneratorTarget const* currentTarget)
7659 cmListFileBacktrace bt;
7660 std::vector<ValueType> result;
7661 cmLocalGenerator* lg = currentTarget->GetLocalGenerator();
7663 std::string const& runtimeLibrary =
7664 currentTarget->GetRuntimeLinkLibrary(lang, config);
7665 if (cmValue runtimeLinkOptions = currentTarget->Makefile->GetDefinition(
7666 "CMAKE_" + lang + "_RUNTIME_LIBRARIES_" + runtimeLibrary)) {
7667 cmList libsList{ *runtimeLinkOptions };
7668 result.reserve(libsList.size());
7670 for (auto const& i : libsList) {
7671 cmGeneratorTarget::TargetOrString resolved =
7672 currentTarget->ResolveTargetReference(i, lg);
7673 if (resolved.Target) {
7674 result.emplace_back(constructItem<ValueType>(resolved.Target, bt));
7679 return result;
7683 void cmGeneratorTarget::ComputeLinkInterfaceRuntimeLibraries(
7684 const std::string& config, cmOptionalLinkInterface& iface) const
7686 for (std::string const& lang : iface.Languages) {
7687 if ((lang == "CUDA" || lang == "HIP") &&
7688 iface.LanguageRuntimeLibraries.find(lang) ==
7689 iface.LanguageRuntimeLibraries.end()) {
7690 auto implicitTargets =
7691 computeImplicitLanguageTargets<cmLinkItem>(lang, config, this);
7692 iface.LanguageRuntimeLibraries[lang] = std::move(implicitTargets);
7697 void cmGeneratorTarget::ComputeLinkImplementationRuntimeLibraries(
7698 const std::string& config, cmOptionalLinkImplementation& impl) const
7700 for (std::string const& lang : impl.Languages) {
7701 if ((lang == "CUDA" || lang == "HIP") &&
7702 impl.LanguageRuntimeLibraries.find(lang) ==
7703 impl.LanguageRuntimeLibraries.end()) {
7704 auto implicitTargets =
7705 computeImplicitLanguageTargets<cmLinkImplItem>(lang, config, this);
7706 impl.LanguageRuntimeLibraries[lang] = std::move(implicitTargets);
7711 const cmLinkInterface* cmGeneratorTarget::GetImportLinkInterface(
7712 const std::string& config, cmGeneratorTarget const* headTarget,
7713 LinkInterfaceFor interfaceFor, bool secondPass) const
7715 cmGeneratorTarget::ImportInfo const* info = this->GetImportInfo(config);
7716 if (!info) {
7717 return nullptr;
7720 cmHeadToLinkInterfaceMap& hm =
7721 (interfaceFor == LinkInterfaceFor::Usage
7722 ? this->GetHeadToLinkInterfaceUsageRequirementsMap(config)
7723 : this->GetHeadToLinkInterfaceMap(config));
7725 // If the link interface does not depend on the head target
7726 // then reuse the one from the head we computed first.
7727 if (!hm.empty() && !hm.begin()->second.HadHeadSensitiveCondition) {
7728 headTarget = hm.begin()->first;
7731 cmOptionalLinkInterface& iface = hm[headTarget];
7732 if (secondPass) {
7733 iface = cmOptionalLinkInterface();
7735 if (!iface.AllDone) {
7736 iface.AllDone = true;
7737 iface.LibrariesDone = true;
7738 iface.Multiplicity = info->Multiplicity;
7739 cmExpandList(info->Languages, iface.Languages);
7740 this->ExpandLinkItems(kINTERFACE_LINK_LIBRARIES_DIRECT,
7741 cmMakeRange(info->LibrariesHeadInclude), config,
7742 headTarget, interfaceFor,
7743 LinkInterfaceField::HeadInclude, iface);
7744 this->ExpandLinkItems(kINTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE,
7745 cmMakeRange(info->LibrariesHeadExclude), config,
7746 headTarget, interfaceFor,
7747 LinkInterfaceField::HeadExclude, iface);
7748 this->ExpandLinkItems(info->LibrariesProp, cmMakeRange(info->Libraries),
7749 config, headTarget, interfaceFor,
7750 LinkInterfaceField::Libraries, iface);
7751 cmList deps{ info->SharedDeps };
7752 LookupLinkItemScope scope{ this->LocalGenerator };
7754 auto linkFeature = cmLinkItem::DEFAULT;
7755 for (auto const& dep : deps) {
7756 if (auto maybeLinkFeature = ParseLinkFeature(dep)) {
7757 linkFeature = std::move(*maybeLinkFeature);
7758 continue;
7761 if (cm::optional<cmLinkItem> maybeItem = this->LookupLinkItem(
7762 dep, cmListFileBacktrace(), linkFeature, &scope, LookupSelf::No)) {
7763 iface.SharedDeps.emplace_back(std::move(*maybeItem));
7768 return &iface;
7771 cmGeneratorTarget::ImportInfo const* cmGeneratorTarget::GetImportInfo(
7772 const std::string& config) const
7774 // There is no imported information for non-imported targets.
7775 if (!this->IsImported()) {
7776 return nullptr;
7779 // Lookup/compute/cache the import information for this
7780 // configuration.
7781 std::string config_upper;
7782 if (!config.empty()) {
7783 config_upper = cmSystemTools::UpperCase(config);
7784 } else {
7785 config_upper = "NOCONFIG";
7788 auto i = this->ImportInfoMap.find(config_upper);
7789 if (i == this->ImportInfoMap.end()) {
7790 ImportInfo info;
7791 this->ComputeImportInfo(config_upper, info);
7792 ImportInfoMapType::value_type entry(config_upper, info);
7793 i = this->ImportInfoMap.insert(entry).first;
7796 if (this->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
7797 return &i->second;
7799 // If the location is empty then the target is not available for
7800 // this configuration.
7801 if (i->second.Location.empty() && i->second.ImportLibrary.empty()) {
7802 return nullptr;
7805 // Return the import information.
7806 return &i->second;
7809 void cmGeneratorTarget::ComputeImportInfo(std::string const& desired_config,
7810 ImportInfo& info) const
7812 // This method finds information about an imported target from its
7813 // properties. The "IMPORTED_" namespace is reserved for properties
7814 // defined by the project exporting the target.
7816 // Initialize members.
7817 info.NoSOName = false;
7819 cmValue loc = nullptr;
7820 cmValue imp = nullptr;
7821 std::string suffix;
7822 if (!this->Target->GetMappedConfig(desired_config, loc, imp, suffix)) {
7823 return;
7826 // Get the link interface.
7828 // Use the INTERFACE_LINK_LIBRARIES special representation directly
7829 // to get backtraces.
7830 cmBTStringRange entries = this->Target->GetLinkInterfaceEntries();
7831 if (!entries.empty()) {
7832 info.LibrariesProp = "INTERFACE_LINK_LIBRARIES";
7833 for (BT<std::string> const& entry : entries) {
7834 info.Libraries.emplace_back(entry);
7836 } else if (this->GetType() != cmStateEnums::INTERFACE_LIBRARY) {
7837 std::string linkProp =
7838 cmStrCat("IMPORTED_LINK_INTERFACE_LIBRARIES", suffix);
7839 cmValue propertyLibs = this->GetProperty(linkProp);
7840 if (!propertyLibs) {
7841 linkProp = "IMPORTED_LINK_INTERFACE_LIBRARIES";
7842 propertyLibs = this->GetProperty(linkProp);
7844 if (propertyLibs) {
7845 info.LibrariesProp = linkProp;
7846 info.Libraries.emplace_back(*propertyLibs);
7850 for (BT<std::string> const& entry :
7851 this->Target->GetLinkInterfaceDirectEntries()) {
7852 info.LibrariesHeadInclude.emplace_back(entry);
7854 for (BT<std::string> const& entry :
7855 this->Target->GetLinkInterfaceDirectExcludeEntries()) {
7856 info.LibrariesHeadExclude.emplace_back(entry);
7858 if (this->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
7859 if (loc) {
7860 info.LibName = *loc;
7862 return;
7865 // A provided configuration has been chosen. Load the
7866 // configuration's properties.
7868 // Get the location.
7869 if (loc) {
7870 info.Location = *loc;
7871 } else {
7872 std::string impProp = cmStrCat("IMPORTED_LOCATION", suffix);
7873 if (cmValue config_location = this->GetProperty(impProp)) {
7874 info.Location = *config_location;
7875 } else if (cmValue location = this->GetProperty("IMPORTED_LOCATION")) {
7876 info.Location = *location;
7880 // Get the soname.
7881 if (this->GetType() == cmStateEnums::SHARED_LIBRARY) {
7882 std::string soProp = cmStrCat("IMPORTED_SONAME", suffix);
7883 if (cmValue config_soname = this->GetProperty(soProp)) {
7884 info.SOName = *config_soname;
7885 } else if (cmValue soname = this->GetProperty("IMPORTED_SONAME")) {
7886 info.SOName = *soname;
7890 // Get the "no-soname" mark.
7891 if (this->GetType() == cmStateEnums::SHARED_LIBRARY) {
7892 std::string soProp = cmStrCat("IMPORTED_NO_SONAME", suffix);
7893 if (cmValue config_no_soname = this->GetProperty(soProp)) {
7894 info.NoSOName = cmIsOn(*config_no_soname);
7895 } else if (cmValue no_soname = this->GetProperty("IMPORTED_NO_SONAME")) {
7896 info.NoSOName = cmIsOn(*no_soname);
7900 // Get the import library.
7901 if (imp) {
7902 info.ImportLibrary = *imp;
7903 } else if (this->GetType() == cmStateEnums::SHARED_LIBRARY ||
7904 this->IsExecutableWithExports()) {
7905 std::string impProp = cmStrCat("IMPORTED_IMPLIB", suffix);
7906 if (cmValue config_implib = this->GetProperty(impProp)) {
7907 info.ImportLibrary = *config_implib;
7908 } else if (cmValue implib = this->GetProperty("IMPORTED_IMPLIB")) {
7909 info.ImportLibrary = *implib;
7913 // Get the link dependencies.
7915 std::string linkProp =
7916 cmStrCat("IMPORTED_LINK_DEPENDENT_LIBRARIES", suffix);
7917 if (cmValue config_libs = this->GetProperty(linkProp)) {
7918 info.SharedDeps = *config_libs;
7919 } else if (cmValue libs =
7920 this->GetProperty("IMPORTED_LINK_DEPENDENT_LIBRARIES")) {
7921 info.SharedDeps = *libs;
7925 // Get the link languages.
7926 if (this->LinkLanguagePropagatesToDependents()) {
7927 std::string linkProp =
7928 cmStrCat("IMPORTED_LINK_INTERFACE_LANGUAGES", suffix);
7929 if (cmValue config_libs = this->GetProperty(linkProp)) {
7930 info.Languages = *config_libs;
7931 } else if (cmValue libs =
7932 this->GetProperty("IMPORTED_LINK_INTERFACE_LANGUAGES")) {
7933 info.Languages = *libs;
7937 // Get information if target is managed assembly.
7939 std::string linkProp = "IMPORTED_COMMON_LANGUAGE_RUNTIME";
7940 if (cmValue pc = this->GetProperty(linkProp + suffix)) {
7941 info.Managed = this->CheckManagedType(*pc);
7942 } else if (cmValue p = this->GetProperty(linkProp)) {
7943 info.Managed = this->CheckManagedType(*p);
7947 // Get the cyclic repetition count.
7948 if (this->GetType() == cmStateEnums::STATIC_LIBRARY) {
7949 std::string linkProp =
7950 cmStrCat("IMPORTED_LINK_INTERFACE_MULTIPLICITY", suffix);
7951 if (cmValue config_reps = this->GetProperty(linkProp)) {
7952 sscanf(config_reps->c_str(), "%u", &info.Multiplicity);
7953 } else if (cmValue reps =
7954 this->GetProperty("IMPORTED_LINK_INTERFACE_MULTIPLICITY")) {
7955 sscanf(reps->c_str(), "%u", &info.Multiplicity);
7960 cmHeadToLinkInterfaceMap& cmGeneratorTarget::GetHeadToLinkInterfaceMap(
7961 const std::string& config) const
7963 return this->LinkInterfaceMap[cmSystemTools::UpperCase(config)];
7966 cmHeadToLinkInterfaceMap&
7967 cmGeneratorTarget::GetHeadToLinkInterfaceUsageRequirementsMap(
7968 const std::string& config) const
7970 return this
7971 ->LinkInterfaceUsageRequirementsOnlyMap[cmSystemTools::UpperCase(config)];
7974 const cmLinkImplementation* cmGeneratorTarget::GetLinkImplementation(
7975 const std::string& config, LinkInterfaceFor implFor) const
7977 return this->GetLinkImplementation(config, implFor, false);
7980 const cmLinkImplementation* cmGeneratorTarget::GetLinkImplementation(
7981 const std::string& config, LinkInterfaceFor implFor, bool secondPass) const
7983 // There is no link implementation for targets that cannot compile sources.
7984 if (!this->CanCompileSources()) {
7985 return nullptr;
7988 HeadToLinkImplementationMap& hm =
7989 (implFor == LinkInterfaceFor::Usage
7990 ? this->GetHeadToLinkImplementationUsageRequirementsMap(config)
7991 : this->GetHeadToLinkImplementationMap(config));
7992 cmOptionalLinkImplementation& impl = hm[this];
7993 if (secondPass) {
7994 impl = cmOptionalLinkImplementation();
7996 if (!impl.LibrariesDone) {
7997 impl.LibrariesDone = true;
7998 this->ComputeLinkImplementationLibraries(config, impl, this, implFor);
8000 if (!impl.LanguagesDone) {
8001 impl.LanguagesDone = true;
8002 this->ComputeLinkImplementationLanguages(config, impl);
8003 this->ComputeLinkImplementationRuntimeLibraries(config, impl);
8005 return &impl;
8008 cmGeneratorTarget::HeadToLinkImplementationMap&
8009 cmGeneratorTarget::GetHeadToLinkImplementationMap(
8010 std::string const& config) const
8012 return this->LinkImplMap[cmSystemTools::UpperCase(config)];
8015 cmGeneratorTarget::HeadToLinkImplementationMap&
8016 cmGeneratorTarget::GetHeadToLinkImplementationUsageRequirementsMap(
8017 std::string const& config) const
8019 return this
8020 ->LinkImplUsageRequirementsOnlyMap[cmSystemTools::UpperCase(config)];
8023 bool cmGeneratorTarget::GetConfigCommonSourceFilesForXcode(
8024 std::vector<cmSourceFile*>& files) const
8026 std::vector<std::string> const& configs =
8027 this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
8029 auto it = configs.begin();
8030 const std::string& firstConfig = *it;
8031 this->GetSourceFilesWithoutObjectLibraries(files, firstConfig);
8033 for (; it != configs.end(); ++it) {
8034 std::vector<cmSourceFile*> configFiles;
8035 this->GetSourceFilesWithoutObjectLibraries(configFiles, *it);
8036 if (configFiles != files) {
8037 std::string firstConfigFiles;
8038 const char* sep = "";
8039 for (cmSourceFile* f : files) {
8040 firstConfigFiles += sep;
8041 firstConfigFiles += f->ResolveFullPath();
8042 sep = "\n ";
8045 std::string thisConfigFiles;
8046 sep = "";
8047 for (cmSourceFile* f : configFiles) {
8048 thisConfigFiles += sep;
8049 thisConfigFiles += f->ResolveFullPath();
8050 sep = "\n ";
8052 std::ostringstream e;
8053 /* clang-format off */
8054 e << "Target \"" << this->GetName()
8055 << "\" has source files which vary by "
8056 "configuration. This is not supported by the \""
8057 << this->GlobalGenerator->GetName()
8058 << "\" generator.\n"
8059 "Config \"" << firstConfig << "\":\n"
8060 " " << firstConfigFiles << "\n"
8061 "Config \"" << *it << "\":\n"
8062 " " << thisConfigFiles << "\n";
8063 /* clang-format on */
8064 this->LocalGenerator->IssueMessage(MessageType::FATAL_ERROR, e.str());
8065 return false;
8068 return true;
8071 void cmGeneratorTarget::GetObjectLibrariesCMP0026(
8072 std::vector<cmGeneratorTarget*>& objlibs) const
8074 // At configure-time, this method can be called as part of getting the
8075 // LOCATION property or to export() a file to be include()d. However
8076 // there is no cmGeneratorTarget at configure-time, so search the SOURCES
8077 // for TARGET_OBJECTS instead for backwards compatibility with OLD
8078 // behavior of CMP0024 and CMP0026 only.
8079 cmBTStringRange rng = this->Target->GetSourceEntries();
8080 for (auto const& entry : rng) {
8081 cmList files{ entry.Value };
8082 for (auto const& li : files) {
8083 if (cmHasLiteralPrefix(li, "$<TARGET_OBJECTS:") && li.back() == '>') {
8084 std::string objLibName = li.substr(17, li.size() - 18);
8086 if (cmGeneratorExpression::Find(objLibName) != std::string::npos) {
8087 continue;
8089 cmGeneratorTarget* objLib =
8090 this->LocalGenerator->FindGeneratorTargetToUse(objLibName);
8091 if (objLib) {
8092 objlibs.push_back(objLib);
8099 std::string cmGeneratorTarget::CheckCMP0004(std::string const& item) const
8101 // Strip whitespace off the library names because we used to do this
8102 // in case variables were expanded at generate time. We no longer
8103 // do the expansion but users link to libraries like " ${VAR} ".
8104 std::string lib = item;
8105 std::string::size_type pos = lib.find_first_not_of(" \t\r\n");
8106 if (pos != std::string::npos) {
8107 lib = lib.substr(pos);
8109 pos = lib.find_last_not_of(" \t\r\n");
8110 if (pos != std::string::npos) {
8111 lib = lib.substr(0, pos + 1);
8113 if (lib != item) {
8114 cmake* cm = this->LocalGenerator->GetCMakeInstance();
8115 switch (this->GetPolicyStatusCMP0004()) {
8116 case cmPolicies::WARN: {
8117 std::ostringstream w;
8118 w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0004) << "\n"
8119 << "Target \"" << this->GetName() << "\" links to item \"" << item
8120 << "\" which has leading or trailing whitespace.";
8121 cm->IssueMessage(MessageType::AUTHOR_WARNING, w.str(),
8122 this->GetBacktrace());
8124 CM_FALLTHROUGH;
8125 case cmPolicies::OLD:
8126 break;
8127 case cmPolicies::NEW: {
8128 std::ostringstream e;
8129 e << "Target \"" << this->GetName() << "\" links to item \"" << item
8130 << "\" which has leading or trailing whitespace. "
8131 << "This is now an error according to policy CMP0004.";
8132 cm->IssueMessage(MessageType::FATAL_ERROR, e.str(),
8133 this->GetBacktrace());
8134 } break;
8135 case cmPolicies::REQUIRED_IF_USED:
8136 case cmPolicies::REQUIRED_ALWAYS: {
8137 std::ostringstream e;
8138 e << cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0004) << "\n"
8139 << "Target \"" << this->GetName() << "\" links to item \"" << item
8140 << "\" which has leading or trailing whitespace.";
8141 cm->IssueMessage(MessageType::FATAL_ERROR, e.str(),
8142 this->GetBacktrace());
8143 } break;
8146 return lib;
8149 bool cmGeneratorTarget::IsDeprecated() const
8151 cmValue deprecation = this->GetProperty("DEPRECATION");
8152 return cmNonempty(deprecation);
8155 std::string cmGeneratorTarget::GetDeprecation() const
8157 // find DEPRECATION property
8158 if (cmValue deprecation = this->GetProperty("DEPRECATION")) {
8159 return *deprecation;
8161 return std::string();
8164 void cmGeneratorTarget::GetLanguages(std::set<std::string>& languages,
8165 const std::string& config) const
8167 // Targets that do not compile anything have no languages.
8168 if (!this->CanCompileSources()) {
8169 return;
8172 std::vector<cmSourceFile*> sourceFiles;
8173 this->GetSourceFiles(sourceFiles, config);
8174 for (cmSourceFile* src : sourceFiles) {
8175 const std::string& lang = src->GetOrDetermineLanguage();
8176 if (!lang.empty()) {
8177 languages.insert(lang);
8181 std::set<cmGeneratorTarget const*> objectLibraries;
8182 if (!this->GlobalGenerator->GetConfigureDoneCMP0026()) {
8183 std::vector<cmGeneratorTarget*> objectTargets;
8184 this->GetObjectLibrariesCMP0026(objectTargets);
8185 for (cmGeneratorTarget* gt : objectTargets) {
8186 objectLibraries.insert(gt);
8188 } else {
8189 objectLibraries = this->GetSourceObjectLibraries(config);
8191 for (cmGeneratorTarget const* objLib : objectLibraries) {
8192 objLib->GetLanguages(languages, config);
8196 std::set<cmGeneratorTarget const*> cmGeneratorTarget::GetSourceObjectLibraries(
8197 std::string const& config) const
8199 std::set<cmGeneratorTarget const*> objectLibraries;
8200 std::vector<cmSourceFile const*> externalObjects;
8201 this->GetExternalObjects(externalObjects, config);
8202 for (cmSourceFile const* extObj : externalObjects) {
8203 std::string objLib = extObj->GetObjectLibrary();
8204 if (cmGeneratorTarget* tgt =
8205 this->LocalGenerator->FindGeneratorTargetToUse(objLib)) {
8206 objectLibraries.insert(tgt);
8210 return objectLibraries;
8213 bool cmGeneratorTarget::IsLanguageUsed(std::string const& language,
8214 std::string const& config) const
8216 std::set<std::string> languages;
8217 this->GetLanguages(languages, config);
8218 return languages.count(language);
8221 bool cmGeneratorTarget::IsCSharpOnly() const
8223 // Only certain target types may compile CSharp.
8224 if (this->GetType() != cmStateEnums::SHARED_LIBRARY &&
8225 this->GetType() != cmStateEnums::STATIC_LIBRARY &&
8226 this->GetType() != cmStateEnums::EXECUTABLE) {
8227 return false;
8229 std::set<std::string> languages = this->GetAllConfigCompileLanguages();
8230 // Consider an explicit linker language property, but *not* the
8231 // computed linker language that may depend on linked targets.
8232 cmValue linkLang = this->GetProperty("LINKER_LANGUAGE");
8233 if (cmNonempty(linkLang)) {
8234 languages.insert(*linkLang);
8236 return languages.size() == 1 && languages.count("CSharp") > 0;
8239 bool cmGeneratorTarget::IsDotNetSdkTarget() const
8241 return !this->GetProperty("DOTNET_SDK").IsEmpty();
8244 void cmGeneratorTarget::ComputeLinkImplementationLanguages(
8245 const std::string& config, cmOptionalLinkImplementation& impl) const
8247 // This target needs runtime libraries for its source languages.
8248 std::set<std::string> languages;
8249 // Get languages used in our source files.
8250 this->GetLanguages(languages, config);
8251 // Copy the set of languages to the link implementation.
8252 impl.Languages.insert(impl.Languages.begin(), languages.begin(),
8253 languages.end());
8256 bool cmGeneratorTarget::HaveBuildTreeRPATH(const std::string& config) const
8258 if (this->GetPropertyAsBool("SKIP_BUILD_RPATH")) {
8259 return false;
8261 std::string build_rpath;
8262 if (this->GetBuildRPATH(config, build_rpath)) {
8263 return true;
8265 if (cmLinkImplementationLibraries const* impl =
8266 this->GetLinkImplementationLibraries(config, LinkInterfaceFor::Link)) {
8267 return !impl->Libraries.empty();
8269 return false;
8272 cmLinkImplementationLibraries const*
8273 cmGeneratorTarget::GetLinkImplementationLibraries(
8274 const std::string& config, LinkInterfaceFor implFor) const
8276 return this->GetLinkImplementationLibrariesInternal(config, this, implFor);
8279 cmLinkImplementationLibraries const*
8280 cmGeneratorTarget::GetLinkImplementationLibrariesInternal(
8281 const std::string& config, cmGeneratorTarget const* head,
8282 LinkInterfaceFor implFor) const
8284 // There is no link implementation for targets that cannot compile sources.
8285 if (!this->CanCompileSources()) {
8286 return nullptr;
8289 // Populate the link implementation libraries for this configuration.
8290 HeadToLinkImplementationMap& hm =
8291 (implFor == LinkInterfaceFor::Usage
8292 ? this->GetHeadToLinkImplementationUsageRequirementsMap(config)
8293 : this->GetHeadToLinkImplementationMap(config));
8295 // If the link implementation does not depend on the head target
8296 // then reuse the one from the head we computed first.
8297 if (!hm.empty() && !hm.begin()->second.HadHeadSensitiveCondition) {
8298 head = hm.begin()->first;
8301 cmOptionalLinkImplementation& impl = hm[head];
8302 if (!impl.LibrariesDone) {
8303 impl.LibrariesDone = true;
8304 this->ComputeLinkImplementationLibraries(config, impl, head, implFor);
8306 return &impl;
8309 bool cmGeneratorTarget::IsNullImpliedByLinkLibraries(
8310 const std::string& p) const
8312 return cm::contains(this->LinkImplicitNullProperties, p);
8315 namespace {
8316 class TransitiveLinkImpl
8318 cmGeneratorTarget const* Self;
8319 std::string const& Config;
8320 LinkInterfaceFor ImplFor;
8321 cmLinkImplementation& Impl;
8323 std::set<cmLinkItem> Emitted;
8324 std::set<cmLinkItem> Excluded;
8325 std::unordered_set<cmGeneratorTarget const*> Followed;
8327 void Follow(cmGeneratorTarget const* target);
8329 public:
8330 TransitiveLinkImpl(cmGeneratorTarget const* self, std::string const& config,
8331 LinkInterfaceFor implFor, cmLinkImplementation& impl)
8332 : Self(self)
8333 , Config(config)
8334 , ImplFor(implFor)
8335 , Impl(impl)
8339 void Compute();
8342 void TransitiveLinkImpl::Follow(cmGeneratorTarget const* target)
8344 if (!target || !this->Followed.insert(target).second ||
8345 target->GetPolicyStatusCMP0022() == cmPolicies::OLD ||
8346 target->GetPolicyStatusCMP0022() == cmPolicies::WARN) {
8347 return;
8350 // Get this target's usage requirements.
8351 cmLinkInterfaceLibraries const* iface =
8352 target->GetLinkInterfaceLibraries(this->Config, this->Self, this->ImplFor);
8353 if (!iface) {
8354 return;
8356 if (iface->HadContextSensitiveCondition) {
8357 this->Impl.HadContextSensitiveCondition = true;
8360 // Process 'INTERFACE_LINK_LIBRARIES_DIRECT' usage requirements.
8361 for (cmLinkItem const& item : iface->HeadInclude) {
8362 // Inject direct dependencies from the item's usage requirements
8363 // before the item itself.
8364 this->Follow(item.Target);
8366 // Add the item itself, but at most once.
8367 if (this->Emitted.insert(item).second) {
8368 this->Impl.Libraries.emplace_back(item, /* checkCMP0027= */ false);
8372 // Follow transitive dependencies.
8373 for (cmLinkItem const& item : iface->Libraries) {
8374 this->Follow(item.Target);
8377 // Record exclusions from 'INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE'
8378 // usage requirements.
8379 for (cmLinkItem const& item : iface->HeadExclude) {
8380 this->Excluded.insert(item);
8384 void TransitiveLinkImpl::Compute()
8386 // Save the original items and start with an empty list.
8387 std::vector<cmLinkImplItem> original = std::move(this->Impl.Libraries);
8389 // Avoid injecting any original items as usage requirements.
8390 // This gives LINK_LIBRARIES final control over the order
8391 // if it explicitly lists everything.
8392 this->Emitted.insert(original.cbegin(), original.cend());
8394 // Process each original item.
8395 for (cmLinkImplItem& item : original) {
8396 // Inject direct dependencies listed in 'INTERFACE_LINK_LIBRARIES_DIRECT'
8397 // usage requirements before the item itself.
8398 this->Follow(item.Target);
8400 // Add the item itself.
8401 this->Impl.Libraries.emplace_back(std::move(item));
8404 // Remove items listed in 'INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE'
8405 // usage requirements found through any dependency above.
8406 this->Impl.Libraries.erase(
8407 std::remove_if(this->Impl.Libraries.begin(), this->Impl.Libraries.end(),
8408 [this](cmLinkImplItem const& item) {
8409 return this->Excluded.find(item) != this->Excluded.end();
8411 this->Impl.Libraries.end());
8414 void ComputeLinkImplTransitive(cmGeneratorTarget const* self,
8415 std::string const& config,
8416 LinkInterfaceFor implFor,
8417 cmLinkImplementation& impl)
8419 TransitiveLinkImpl transitiveLinkImpl(self, config, implFor, impl);
8420 transitiveLinkImpl.Compute();
8424 bool cmGeneratorTarget::DiscoverSyntheticTargets(cmSyntheticTargetCache& cache,
8425 std::string const& config)
8427 cmOptionalLinkImplementation impl;
8428 this->ComputeLinkImplementationLibraries(config, impl, this,
8429 LinkInterfaceFor::Link);
8431 cmCxxModuleUsageEffects usage(this);
8433 auto& SyntheticDeps = this->Configs[config].SyntheticDeps;
8435 for (auto const& entry : impl.Libraries) {
8436 auto const* gt = entry.Target;
8437 if (!gt || !gt->IsImported()) {
8438 continue;
8441 if (gt->HaveCxx20ModuleSources()) {
8442 cmCryptoHash hasher(cmCryptoHash::AlgoSHA3_512);
8443 constexpr size_t HASH_TRUNCATION = 12;
8444 auto dirhash = hasher.HashString(
8445 gt->GetLocalGenerator()->GetCurrentBinaryDirectory());
8446 std::string safeName = gt->GetName();
8447 cmSystemTools::ReplaceString(safeName, ":", "_");
8448 auto targetIdent =
8449 hasher.HashString(cmStrCat("@d_", dirhash, "@u_", usage.GetHash()));
8450 std::string targetName =
8451 cmStrCat(safeName, "@synth_", targetIdent.substr(0, HASH_TRUNCATION));
8453 // Check the cache to see if this instance of the imported target has
8454 // already been created.
8455 auto cached = cache.CxxModuleTargets.find(targetName);
8456 cmGeneratorTarget const* synthDep = nullptr;
8457 if (cached == cache.CxxModuleTargets.end()) {
8458 auto const* model = gt->Target;
8459 auto* mf = gt->Makefile;
8460 auto* lg = gt->GetLocalGenerator();
8461 auto* tgt = mf->AddSynthesizedTarget(cmStateEnums::INTERFACE_LIBRARY,
8462 targetName);
8464 // Copy relevant information from the existing IMPORTED target.
8466 // Copy policies to the target.
8467 tgt->CopyPolicyStatuses(model);
8469 // Copy file sets.
8471 auto fsNames = model->GetAllFileSetNames();
8472 for (auto const& fsName : fsNames) {
8473 auto const* fs = model->GetFileSet(fsName);
8474 if (!fs) {
8475 mf->IssueMessage(MessageType::INTERNAL_ERROR,
8476 cmStrCat("Failed to find file set named '",
8477 fsName, "' on target '",
8478 tgt->GetName(), '\''));
8479 continue;
8481 auto* newFs = tgt
8482 ->GetOrCreateFileSet(fs->GetName(), fs->GetType(),
8483 fs->GetVisibility())
8484 .first;
8485 newFs->CopyEntries(fs);
8489 // Copy imported C++ module properties.
8490 tgt->CopyImportedCxxModulesEntries(model);
8492 // Copy other properties which may affect the C++ module BMI
8493 // generation.
8494 tgt->CopyImportedCxxModulesProperties(model);
8496 tgt->AddLinkLibrary(*mf,
8497 cmStrCat("$<COMPILE_ONLY:", model->GetName(), '>'),
8498 GENERAL_LibraryType);
8500 // Apply usage requirements to the target.
8501 usage.ApplyToTarget(tgt);
8503 // Create the generator target and attach it to the local generator.
8504 auto gtp = cm::make_unique<cmGeneratorTarget>(tgt, lg);
8505 synthDep = gtp.get();
8506 cache.CxxModuleTargets[targetName] = synthDep;
8507 gtp->DiscoverSyntheticTargets(cache, config);
8508 lg->AddGeneratorTarget(std::move(gtp));
8509 } else {
8510 synthDep = cached->second;
8513 SyntheticDeps[gt].push_back(synthDep);
8517 return true;
8520 void cmGeneratorTarget::ComputeLinkImplementationLibraries(
8521 const std::string& config, cmOptionalLinkImplementation& impl,
8522 cmGeneratorTarget const* head, LinkInterfaceFor implFor) const
8524 cmLocalGenerator const* lg = this->LocalGenerator;
8525 cmMakefile const* mf = lg->GetMakefile();
8526 cmBTStringRange entryRange = this->Target->GetLinkImplementationEntries();
8527 auto const& synthTargetsForConfig = this->Configs[config].SyntheticDeps;
8528 // Collect libraries directly linked in this configuration.
8529 for (auto const& entry : entryRange) {
8530 // Keep this logic in sync with ExpandLinkItems.
8531 cmGeneratorExpressionDAGChecker dagChecker(this, "LINK_LIBRARIES", nullptr,
8532 nullptr);
8533 // The $<LINK_ONLY> expression may be used to specify link dependencies
8534 // that are otherwise excluded from usage requirements.
8535 if (implFor == LinkInterfaceFor::Usage) {
8536 dagChecker.SetTransitivePropertiesOnly();
8537 switch (this->GetPolicyStatusCMP0131()) {
8538 case cmPolicies::WARN:
8539 case cmPolicies::OLD:
8540 break;
8541 case cmPolicies::REQUIRED_IF_USED:
8542 case cmPolicies::REQUIRED_ALWAYS:
8543 case cmPolicies::NEW:
8544 dagChecker.SetTransitivePropertiesOnlyCMP0131();
8545 break;
8548 cmGeneratorExpression ge(*this->LocalGenerator->GetCMakeInstance(),
8549 entry.Backtrace);
8550 std::unique_ptr<cmCompiledGeneratorExpression> const cge =
8551 ge.Parse(entry.Value);
8552 cge->SetEvaluateForBuildsystem(true);
8553 std::string const& evaluated =
8554 cge->Evaluate(this->LocalGenerator, config, head, &dagChecker, nullptr,
8555 this->LinkerLanguage);
8556 bool const checkCMP0027 = evaluated != entry.Value;
8557 cmList llibs(evaluated);
8558 if (cge->GetHadHeadSensitiveCondition()) {
8559 impl.HadHeadSensitiveCondition = true;
8561 if (cge->GetHadContextSensitiveCondition()) {
8562 impl.HadContextSensitiveCondition = true;
8564 if (cge->GetHadLinkLanguageSensitiveCondition()) {
8565 impl.HadLinkLanguageSensitiveCondition = true;
8568 auto linkFeature = cmLinkItem::DEFAULT;
8569 for (auto const& lib : llibs) {
8570 if (auto maybeLinkFeature = ParseLinkFeature(lib)) {
8571 linkFeature = std::move(*maybeLinkFeature);
8572 continue;
8575 if (this->IsLinkLookupScope(lib, lg)) {
8576 continue;
8579 // Skip entries that resolve to the target itself or are empty.
8580 std::string name = this->CheckCMP0004(lib);
8581 if (this->GetPolicyStatusCMP0108() == cmPolicies::NEW) {
8582 // resolve alias name
8583 auto* target = this->Makefile->FindTargetToUse(name);
8584 if (target) {
8585 name = target->GetName();
8588 if (name == this->GetName() || name.empty()) {
8589 if (name == this->GetName()) {
8590 bool noMessage = false;
8591 MessageType messageType = MessageType::FATAL_ERROR;
8592 std::ostringstream e;
8593 switch (this->GetPolicyStatusCMP0038()) {
8594 case cmPolicies::WARN: {
8595 e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0038) << "\n";
8596 messageType = MessageType::AUTHOR_WARNING;
8597 } break;
8598 case cmPolicies::OLD:
8599 noMessage = true;
8600 break;
8601 case cmPolicies::REQUIRED_IF_USED:
8602 case cmPolicies::REQUIRED_ALWAYS:
8603 case cmPolicies::NEW:
8604 // Issue the fatal message.
8605 break;
8608 if (!noMessage) {
8609 e << "Target \"" << this->GetName() << "\" links to itself.";
8610 this->LocalGenerator->GetCMakeInstance()->IssueMessage(
8611 messageType, e.str(), this->GetBacktrace());
8612 if (messageType == MessageType::FATAL_ERROR) {
8613 return;
8617 continue;
8620 // The entry is meant for this configuration.
8621 cmLinkItem item = this->ResolveLinkItem(
8622 BT<std::string>(name, entry.Backtrace), lg, linkFeature);
8623 if (item.Target) {
8624 auto depsForTarget = synthTargetsForConfig.find(item.Target);
8625 if (depsForTarget != synthTargetsForConfig.end()) {
8626 for (auto const* depForTarget : depsForTarget->second) {
8627 cmLinkItem synthItem(depForTarget, item.Cross, item.Backtrace);
8628 impl.Libraries.emplace_back(std::move(synthItem), false);
8631 } else {
8632 // Report explicitly linked object files separately.
8633 std::string const& maybeObj = item.AsStr();
8634 if (cmSystemTools::FileIsFullPath(maybeObj)) {
8635 cmSourceFile const* sf =
8636 mf->GetSource(maybeObj, cmSourceFileLocationKind::Known);
8637 if (sf && sf->GetPropertyAsBool("EXTERNAL_OBJECT")) {
8638 item.ObjectSource = sf;
8639 impl.Objects.emplace_back(std::move(item));
8640 continue;
8645 impl.Libraries.emplace_back(std::move(item), checkCMP0027);
8648 std::set<std::string> const& seenProps = cge->GetSeenTargetProperties();
8649 for (std::string const& sp : seenProps) {
8650 if (!this->GetProperty(sp)) {
8651 this->LinkImplicitNullProperties.insert(sp);
8654 cge->GetMaxLanguageStandard(this, this->MaxLanguageStandards);
8657 // Update the list of direct link dependencies from usage requirements.
8658 if (head == this) {
8659 ComputeLinkImplTransitive(this, config, implFor, impl);
8662 // Get the list of configurations considered to be DEBUG.
8663 std::vector<std::string> debugConfigs =
8664 this->Makefile->GetCMakeInstance()->GetDebugConfigs();
8666 cmTargetLinkLibraryType linkType =
8667 CMP0003_ComputeLinkType(config, debugConfigs);
8668 cmTarget::LinkLibraryVectorType const& oldllibs =
8669 this->Target->GetOriginalLinkLibraries();
8671 auto linkFeature = cmLinkItem::DEFAULT;
8672 for (cmTarget::LibraryID const& oldllib : oldllibs) {
8673 if (auto maybeLinkFeature = ParseLinkFeature(oldllib.first)) {
8674 linkFeature = std::move(*maybeLinkFeature);
8675 continue;
8678 if (oldllib.second != GENERAL_LibraryType && oldllib.second != linkType) {
8679 std::string name = this->CheckCMP0004(oldllib.first);
8680 if (name == this->GetName() || name.empty()) {
8681 continue;
8683 // Support OLD behavior for CMP0003.
8684 impl.WrongConfigLibraries.push_back(
8685 this->ResolveLinkItem(BT<std::string>(name), linkFeature));
8690 cmGeneratorTarget::TargetOrString cmGeneratorTarget::ResolveTargetReference(
8691 std::string const& name) const
8693 return this->ResolveTargetReference(name, this->LocalGenerator);
8696 cmGeneratorTarget::TargetOrString cmGeneratorTarget::ResolveTargetReference(
8697 std::string const& name, cmLocalGenerator const* lg) const
8699 TargetOrString resolved;
8701 if (cmGeneratorTarget* tgt = lg->FindGeneratorTargetToUse(name)) {
8702 resolved.Target = tgt;
8703 } else {
8704 resolved.String = name;
8707 return resolved;
8710 cmLinkItem cmGeneratorTarget::ResolveLinkItem(
8711 BT<std::string> const& name, std::string const& linkFeature) const
8713 return this->ResolveLinkItem(name, this->LocalGenerator, linkFeature);
8716 cmLinkItem cmGeneratorTarget::ResolveLinkItem(
8717 BT<std::string> const& name, cmLocalGenerator const* lg,
8718 std::string const& linkFeature) const
8720 auto bt = name.Backtrace;
8721 TargetOrString resolved = this->ResolveTargetReference(name.Value, lg);
8723 if (!resolved.Target) {
8724 return cmLinkItem(resolved.String, false, bt, linkFeature);
8727 // Check deprecation, issue message with `bt` backtrace.
8728 if (resolved.Target->IsDeprecated()) {
8729 std::ostringstream w;
8730 /* clang-format off */
8731 w <<
8732 "The library that is being linked to, " << resolved.Target->GetName() <<
8733 ", is marked as being deprecated by the owner. The message provided by "
8734 "the developer is: \n" << resolved.Target->GetDeprecation() << "\n";
8735 /* clang-format on */
8736 this->LocalGenerator->GetCMakeInstance()->IssueMessage(
8737 MessageType::AUTHOR_WARNING, w.str(), bt);
8740 // Skip targets that will not really be linked. This is probably a
8741 // name conflict between an external library and an executable
8742 // within the project.
8743 if (resolved.Target->GetType() == cmStateEnums::EXECUTABLE &&
8744 !resolved.Target->IsExecutableWithExports()) {
8745 return cmLinkItem(resolved.Target->GetName(), false, bt, linkFeature);
8748 return cmLinkItem(resolved.Target, false, bt, linkFeature);
8751 bool cmGeneratorTarget::HasPackageReferences() const
8753 return this->IsInBuildSystem() &&
8754 !this->GetProperty("VS_PACKAGE_REFERENCES")->empty();
8757 std::vector<std::string> cmGeneratorTarget::GetPackageReferences() const
8759 cmList packageReferences;
8761 if (this->IsInBuildSystem()) {
8762 if (cmValue vsPackageReferences =
8763 this->GetProperty("VS_PACKAGE_REFERENCES")) {
8764 packageReferences.assign(*vsPackageReferences);
8768 return std::move(packageReferences.data());
8771 std::string cmGeneratorTarget::GetPDBDirectory(const std::string& config) const
8773 if (OutputInfo const* info = this->GetOutputInfo(config)) {
8774 // Return the directory in which the target will be built.
8775 return info->PdbDir;
8777 return "";
8780 bool cmGeneratorTarget::HasImplibGNUtoMS(std::string const& config) const
8782 return this->HasImportLibrary(config) && this->GetPropertyAsBool("GNUtoMS");
8785 bool cmGeneratorTarget::GetImplibGNUtoMS(std::string const& config,
8786 std::string const& gnuName,
8787 std::string& out,
8788 const char* newExt) const
8790 if (this->HasImplibGNUtoMS(config) && gnuName.size() > 6 &&
8791 gnuName.substr(gnuName.size() - 6) == ".dll.a") {
8792 out = cmStrCat(cm::string_view(gnuName).substr(0, gnuName.size() - 6),
8793 newExt ? newExt : ".lib");
8794 return true;
8796 return false;
8799 bool cmGeneratorTarget::HasContextDependentSources() const
8801 return this->SourcesAreContextDependent == Tribool::True;
8804 bool cmGeneratorTarget::IsExecutableWithExports() const
8806 return this->Target->IsExecutableWithExports();
8809 bool cmGeneratorTarget::IsSharedLibraryWithExports() const
8811 return this->Target->IsSharedLibraryWithExports();
8814 bool cmGeneratorTarget::HasImportLibrary(std::string const& config) const
8816 bool generate_Stubs = true;
8817 if (this->GetGlobalGenerator()->IsXcode()) {
8818 // take care of CMAKE_XCODE_ATTRIBUTE_GENERATE_TEXT_BASED_STUBS variable
8819 // as well as XCODE_ATTRIBUTE_GENERATE_TEXT_BASED_STUBS property
8820 if (cmValue propGenStubs =
8821 this->GetProperty("XCODE_ATTRIBUTE_GENERATE_TEXT_BASED_STUBS")) {
8822 generate_Stubs = propGenStubs == "YES";
8823 } else if (cmValue varGenStubs = this->Makefile->GetDefinition(
8824 "CMAKE_XCODE_ATTRIBUTE_GENERATE_TEXT_BASED_STUBS")) {
8825 generate_Stubs = varGenStubs == "YES";
8829 return (this->IsDLLPlatform() &&
8830 (this->GetType() == cmStateEnums::SHARED_LIBRARY ||
8831 this->IsExecutableWithExports()) &&
8832 // Assemblies which have only managed code do not have
8833 // import libraries.
8834 this->GetManagedType(config) != ManagedType::Managed) ||
8835 (this->IsAIX() && this->IsExecutableWithExports()) ||
8836 (this->Makefile->PlatformSupportsAppleTextStubs() &&
8837 this->IsSharedLibraryWithExports() && generate_Stubs);
8840 bool cmGeneratorTarget::NeedImportLibraryName(std::string const& config) const
8842 return this->HasImportLibrary(config) ||
8843 // On DLL platforms we always generate the import library name
8844 // just in case the sources have export markup.
8845 (this->IsDLLPlatform() &&
8846 (this->GetType() == cmStateEnums::EXECUTABLE ||
8847 this->GetType() == cmStateEnums::MODULE_LIBRARY));
8850 std::string cmGeneratorTarget::GetSupportDirectory() const
8852 std::string dir = cmStrCat(this->LocalGenerator->GetCurrentBinaryDirectory(),
8853 "/CMakeFiles/", this->GetName());
8854 #if defined(__VMS)
8855 dir += "_dir";
8856 #else
8857 dir += ".dir";
8858 #endif
8859 return dir;
8862 bool cmGeneratorTarget::IsLinkable() const
8864 return (this->GetType() == cmStateEnums::STATIC_LIBRARY ||
8865 this->GetType() == cmStateEnums::SHARED_LIBRARY ||
8866 this->GetType() == cmStateEnums::MODULE_LIBRARY ||
8867 this->GetType() == cmStateEnums::UNKNOWN_LIBRARY ||
8868 this->GetType() == cmStateEnums::OBJECT_LIBRARY ||
8869 this->GetType() == cmStateEnums::INTERFACE_LIBRARY ||
8870 this->IsExecutableWithExports());
8873 bool cmGeneratorTarget::HasLinkDependencyFile(std::string const& config) const
8875 if (this->GetType() != cmStateEnums::EXECUTABLE &&
8876 this->GetType() != cmStateEnums::SHARED_LIBRARY &&
8877 this->GetType() != cmStateEnums::MODULE_LIBRARY) {
8878 return false;
8881 if (this->Target->GetProperty("LINK_DEPENDS_NO_SHARED").IsOn()) {
8882 // Do not use the linker dependency file because it includes shared
8883 // libraries as well
8884 return false;
8887 const std::string depsUseLinker{ "CMAKE_LINK_DEPENDS_USE_LINKER" };
8888 auto linkLanguage = this->GetLinkerLanguage(config);
8889 const std::string langDepsUseLinker{ cmStrCat("CMAKE_", linkLanguage,
8890 "_LINK_DEPENDS_USE_LINKER") };
8892 return (!this->Makefile->IsDefinitionSet(depsUseLinker) ||
8893 this->Makefile->IsOn(depsUseLinker)) &&
8894 this->Makefile->IsOn(langDepsUseLinker);
8897 bool cmGeneratorTarget::IsFrameworkOnApple() const
8899 return this->Target->IsFrameworkOnApple();
8902 bool cmGeneratorTarget::IsImportedFrameworkFolderOnApple(
8903 const std::string& config) const
8905 if (this->IsApple() && this->IsImported() &&
8906 (this->GetType() == cmStateEnums::STATIC_LIBRARY ||
8907 this->GetType() == cmStateEnums::SHARED_LIBRARY ||
8908 this->GetType() == cmStateEnums::UNKNOWN_LIBRARY)) {
8909 std::string cfg = config;
8910 if (cfg.empty() && this->GetGlobalGenerator()->IsXcode()) {
8911 // FIXME(#25515): Remove the need for this workaround.
8912 // The Xcode generator queries include directories without any
8913 // specific configuration. Pick one in case this target does
8914 // not set either IMPORTED_LOCATION or IMPORTED_CONFIGURATIONS.
8915 cfg =
8916 this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig)[0];
8918 return cmSystemTools::IsPathToFramework(this->GetLocation(cfg));
8921 return false;
8924 bool cmGeneratorTarget::IsAppBundleOnApple() const
8926 return this->Target->IsAppBundleOnApple();
8929 bool cmGeneratorTarget::IsXCTestOnApple() const
8931 return (this->IsCFBundleOnApple() && this->GetPropertyAsBool("XCTEST"));
8934 bool cmGeneratorTarget::IsCFBundleOnApple() const
8936 return (this->GetType() == cmStateEnums::MODULE_LIBRARY && this->IsApple() &&
8937 this->GetPropertyAsBool("BUNDLE"));
8940 cmGeneratorTarget::ManagedType cmGeneratorTarget::CheckManagedType(
8941 std::string const& propval) const
8943 // The type of the managed assembly (mixed unmanaged C++ and C++/CLI,
8944 // or only C++/CLI) does only depend on whether the property is an empty
8945 // string or contains any value at all. In Visual Studio generators
8946 // this propval is prepended with /clr[:] which results in:
8948 // 1. propval does not exist: no /clr flag, unmanaged target, has import
8949 // lib
8950 // 2. empty propval: add /clr as flag, mixed unmanaged/managed
8951 // target, has import lib
8952 // 3. netcore propval: add /clr:netcore as flag, mixed
8953 // unmanaged/managed target, has import lib.
8954 // 4. any value (safe,pure): add /clr:[propval] as flag, target with
8955 // managed code only, no import lib
8956 if (propval.empty() || propval == "netcore") {
8957 return ManagedType::Mixed;
8959 return ManagedType::Managed;
8962 cmGeneratorTarget::ManagedType cmGeneratorTarget::GetManagedType(
8963 const std::string& config) const
8965 // Only libraries and executables can be managed targets.
8966 if (this->GetType() > cmStateEnums::SHARED_LIBRARY) {
8967 return ManagedType::Undefined;
8970 if (this->GetType() == cmStateEnums::STATIC_LIBRARY) {
8971 return ManagedType::Native;
8974 // Check imported target.
8975 if (this->IsImported()) {
8976 if (cmGeneratorTarget::ImportInfo const* info =
8977 this->GetImportInfo(config)) {
8978 return info->Managed;
8980 return ManagedType::Undefined;
8983 // Check for explicitly set clr target property.
8984 if (cmValue clr = this->GetProperty("COMMON_LANGUAGE_RUNTIME")) {
8985 return this->CheckManagedType(*clr);
8988 // C# targets are always managed. This language specific check
8989 // is added to avoid that the COMMON_LANGUAGE_RUNTIME target property
8990 // has to be set manually for C# targets.
8991 return this->IsCSharpOnly() ? ManagedType::Managed : ManagedType::Native;
8994 bool cmGeneratorTarget::AddHeaderSetVerification()
8996 if (!this->GetPropertyAsBool("VERIFY_INTERFACE_HEADER_SETS")) {
8997 return true;
9000 if (this->GetType() != cmStateEnums::STATIC_LIBRARY &&
9001 this->GetType() != cmStateEnums::SHARED_LIBRARY &&
9002 this->GetType() != cmStateEnums::UNKNOWN_LIBRARY &&
9003 this->GetType() != cmStateEnums::OBJECT_LIBRARY &&
9004 this->GetType() != cmStateEnums::INTERFACE_LIBRARY &&
9005 !this->IsExecutableWithExports()) {
9006 return true;
9009 auto verifyValue = this->GetProperty("INTERFACE_HEADER_SETS_TO_VERIFY");
9010 const bool all = verifyValue.IsEmpty();
9011 std::set<std::string> verifySet;
9012 if (!all) {
9013 cmList verifyList{ verifyValue };
9014 verifySet.insert(verifyList.begin(), verifyList.end());
9017 cmTarget* verifyTarget = nullptr;
9018 cmTarget* allVerifyTarget =
9019 this->GlobalGenerator->GetMakefiles().front()->FindTargetToUse(
9020 "all_verify_interface_header_sets", true);
9022 auto interfaceFileSetEntries = this->Target->GetInterfaceHeaderSetsEntries();
9024 std::set<cmFileSet*> fileSets;
9025 for (auto const& entry : interfaceFileSetEntries) {
9026 for (auto const& name : cmList{ entry.Value }) {
9027 if (all || verifySet.count(name)) {
9028 fileSets.insert(this->Target->GetFileSet(name));
9029 verifySet.erase(name);
9033 if (!verifySet.empty()) {
9034 this->Makefile->IssueMessage(
9035 MessageType::FATAL_ERROR,
9036 cmStrCat("Property INTERFACE_HEADER_SETS_TO_VERIFY of target \"",
9037 this->GetName(),
9038 "\" contained the following header sets that are nonexistent "
9039 "or not INTERFACE:\n ",
9040 cmJoin(verifySet, "\n ")));
9041 return false;
9044 cm::optional<std::set<std::string>> languages;
9045 for (auto* fileSet : fileSets) {
9046 auto dirCges = fileSet->CompileDirectoryEntries();
9047 auto fileCges = fileSet->CompileFileEntries();
9049 static auto const contextSensitive =
9050 [](const std::unique_ptr<cmCompiledGeneratorExpression>& cge) {
9051 return cge->GetHadContextSensitiveCondition();
9053 bool dirCgesContextSensitive = false;
9054 bool fileCgesContextSensitive = false;
9056 std::vector<std::string> dirs;
9057 std::map<std::string, std::vector<std::string>> filesPerDir;
9058 bool first = true;
9059 for (auto const& config : this->Makefile->GetGeneratorConfigs(
9060 cmMakefile::GeneratorConfigQuery::IncludeEmptyConfig)) {
9061 if (first || dirCgesContextSensitive) {
9062 dirs = fileSet->EvaluateDirectoryEntries(dirCges, this->LocalGenerator,
9063 config, this);
9064 dirCgesContextSensitive =
9065 std::any_of(dirCges.begin(), dirCges.end(), contextSensitive);
9067 if (first || fileCgesContextSensitive) {
9068 filesPerDir.clear();
9069 for (auto const& fileCge : fileCges) {
9070 fileSet->EvaluateFileEntry(dirs, filesPerDir, fileCge,
9071 this->LocalGenerator, config, this);
9072 if (fileCge->GetHadContextSensitiveCondition()) {
9073 fileCgesContextSensitive = true;
9078 for (auto const& files : filesPerDir) {
9079 for (auto const& file : files.second) {
9080 std::string filename = this->GenerateHeaderSetVerificationFile(
9081 *this->Makefile->GetOrCreateSource(file), files.first, languages);
9082 if (filename.empty()) {
9083 continue;
9086 if (!verifyTarget) {
9088 cmMakefile::PolicyPushPop polScope(this->Makefile);
9089 this->Makefile->SetPolicy(cmPolicies::CMP0119, cmPolicies::NEW);
9090 verifyTarget = this->Makefile->AddLibrary(
9091 cmStrCat(this->GetName(), "_verify_interface_header_sets"),
9092 cmStateEnums::OBJECT_LIBRARY, {}, true);
9095 verifyTarget->AddLinkLibrary(
9096 *this->Makefile, this->GetName(),
9097 cmTargetLinkLibraryType::GENERAL_LibraryType);
9098 verifyTarget->SetProperty("AUTOMOC", "OFF");
9099 verifyTarget->SetProperty("AUTORCC", "OFF");
9100 verifyTarget->SetProperty("AUTOUIC", "OFF");
9101 verifyTarget->SetProperty("DISABLE_PRECOMPILE_HEADERS", "ON");
9102 verifyTarget->SetProperty("UNITY_BUILD", "OFF");
9103 verifyTarget->SetProperty("CXX_SCAN_FOR_MODULES", "OFF");
9104 cm::optional<std::map<std::string, cmValue>>
9105 perConfigCompileDefinitions;
9106 verifyTarget->FinalizeTargetConfiguration(
9107 this->Makefile->GetCompileDefinitionsEntries(),
9108 perConfigCompileDefinitions);
9110 if (!allVerifyTarget) {
9111 allVerifyTarget = this->GlobalGenerator->GetMakefiles()
9112 .front()
9113 ->AddNewUtilityTarget(
9114 "all_verify_interface_header_sets", true);
9117 allVerifyTarget->AddUtility(verifyTarget->GetName(), false);
9120 if (fileCgesContextSensitive) {
9121 filename = cmStrCat("$<$<CONFIG:", config, ">:", filename, ">");
9123 verifyTarget->AddSource(filename);
9127 if (!dirCgesContextSensitive && !fileCgesContextSensitive) {
9128 break;
9130 first = false;
9134 if (verifyTarget) {
9135 this->LocalGenerator->AddGeneratorTarget(
9136 cm::make_unique<cmGeneratorTarget>(verifyTarget, this->LocalGenerator));
9139 return true;
9142 std::string cmGeneratorTarget::GenerateHeaderSetVerificationFile(
9143 cmSourceFile& source, const std::string& dir,
9144 cm::optional<std::set<std::string>>& languages) const
9146 std::string extension;
9147 std::string language = source.GetOrDetermineLanguage();
9149 if (source.GetPropertyAsBool("SKIP_LINTING")) {
9150 return std::string{};
9153 if (language.empty()) {
9154 if (!languages) {
9155 languages.emplace();
9156 for (auto const& tgtSource : this->GetAllConfigSources()) {
9157 auto const& tgtSourceLanguage =
9158 tgtSource.Source->GetOrDetermineLanguage();
9159 if (tgtSourceLanguage == "CXX") {
9160 languages->insert("CXX");
9161 break; // C++ overrides everything else, so we don't need to keep
9162 // checking.
9164 if (tgtSourceLanguage == "C") {
9165 languages->insert("C");
9169 if (languages->empty()) {
9170 std::vector<std::string> languagesVector;
9171 this->GlobalGenerator->GetEnabledLanguages(languagesVector);
9172 languages->insert(languagesVector.begin(), languagesVector.end());
9176 if (languages->count("CXX")) {
9177 language = "CXX";
9178 } else if (languages->count("C")) {
9179 language = "C";
9183 if (language == "C") {
9184 extension = ".c";
9185 } else if (language == "CXX") {
9186 extension = ".cxx";
9187 } else {
9188 return "";
9191 std::string headerFilename = dir;
9192 if (!headerFilename.empty()) {
9193 headerFilename += '/';
9195 headerFilename += source.GetLocation().GetName();
9197 auto filename = cmStrCat(
9198 this->LocalGenerator->GetCurrentBinaryDirectory(), '/', this->GetName(),
9199 "_verify_interface_header_sets/", headerFilename, extension);
9200 auto* verificationSource = this->Makefile->GetOrCreateSource(filename);
9201 verificationSource->SetProperty("LANGUAGE", language);
9203 cmSystemTools::MakeDirectory(cmSystemTools::GetFilenamePath(filename));
9205 cmGeneratedFileStream fout(filename);
9206 fout.SetCopyIfDifferent(true);
9207 // The IWYU "associated" pragma tells include-what-you-use to
9208 // consider the headerFile as part of the entire language
9209 // unit within include-what-you-use and as a result allows
9210 // one to get IWYU advice for headers.
9211 fout << "#include <" << headerFilename << "> // IWYU pragma: associated\n";
9212 fout.close();
9214 return filename;
9217 std::string cmGeneratorTarget::GetImportedXcFrameworkPath(
9218 const std::string& config) const
9220 if (!(this->IsApple() && this->IsImported() &&
9221 (this->GetType() == cmStateEnums::SHARED_LIBRARY ||
9222 this->GetType() == cmStateEnums::STATIC_LIBRARY ||
9223 this->GetType() == cmStateEnums::UNKNOWN_LIBRARY))) {
9224 return {};
9227 std::string desiredConfig = config;
9228 if (config.empty()) {
9229 desiredConfig = "NOCONFIG";
9232 std::string result;
9234 cmValue loc = nullptr;
9235 cmValue imp = nullptr;
9236 std::string suffix;
9238 if (this->Target->GetMappedConfig(desiredConfig, loc, imp, suffix)) {
9239 if (loc) {
9240 result = *loc;
9241 } else {
9242 std::string impProp = cmStrCat("IMPORTED_LOCATION", suffix);
9243 if (cmValue configLocation = this->GetProperty(impProp)) {
9244 result = *configLocation;
9245 } else if (cmValue location = this->GetProperty("IMPORTED_LOCATION")) {
9246 result = *location;
9250 if (cmSystemTools::IsPathToXcFramework(result)) {
9251 return result;
9255 return {};
9258 bool cmGeneratorTarget::HaveFortranSources(std::string const& config) const
9260 auto sources = this->GetSourceFiles(config);
9261 bool const have_direct = std::any_of(
9262 sources.begin(), sources.end(), [](BT<cmSourceFile*> const& sf) -> bool {
9263 return sf.Value->GetLanguage() == "Fortran"_s;
9265 bool have_via_target_objects = false;
9266 if (!have_direct) {
9267 auto const sourceObjectLibraries = this->GetSourceObjectLibraries(config);
9268 have_via_target_objects =
9269 std::any_of(sourceObjectLibraries.begin(), sourceObjectLibraries.end(),
9270 [&config](cmGeneratorTarget const* tgt) -> bool {
9271 return tgt->HaveFortranSources(config);
9274 return have_direct || have_via_target_objects;
9277 bool cmGeneratorTarget::HaveFortranSources() const
9279 auto sources = this->GetAllConfigSources();
9280 bool const have_direct = std::any_of(
9281 sources.begin(), sources.end(), [](AllConfigSource const& sf) -> bool {
9282 return sf.Source->GetLanguage() == "Fortran"_s;
9284 bool have_via_target_objects = false;
9285 if (!have_direct) {
9286 std::vector<std::string> configs =
9287 this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
9288 for (auto const& config : configs) {
9289 auto const sourceObjectLibraries =
9290 this->GetSourceObjectLibraries(config);
9291 have_via_target_objects =
9292 std::any_of(sourceObjectLibraries.begin(), sourceObjectLibraries.end(),
9293 [&config](cmGeneratorTarget const* tgt) -> bool {
9294 return tgt->HaveFortranSources(config);
9296 if (have_via_target_objects) {
9297 break;
9301 return have_direct || have_via_target_objects;
9304 bool cmGeneratorTarget::HaveCxx20ModuleSources(std::string* errorMessage) const
9306 auto const& fs_names = this->Target->GetAllFileSetNames();
9307 return std::any_of(
9308 fs_names.begin(), fs_names.end(),
9309 [this, errorMessage](std::string const& name) -> bool {
9310 auto const* file_set = this->Target->GetFileSet(name);
9311 if (!file_set) {
9312 auto message = cmStrCat("Target \"", this->Target->GetName(),
9313 "\" is tracked to have file set \"", name,
9314 "\", but it was not found.");
9315 if (errorMessage) {
9316 *errorMessage = std::move(message);
9317 } else {
9318 this->Makefile->IssueMessage(MessageType::INTERNAL_ERROR, message);
9320 return false;
9323 auto const& fs_type = file_set->GetType();
9324 return fs_type == "CXX_MODULES"_s;
9328 cmGeneratorTarget::Cxx20SupportLevel cmGeneratorTarget::HaveCxxModuleSupport(
9329 std::string const& config) const
9331 auto const* state = this->Makefile->GetState();
9332 if (!state->GetLanguageEnabled("CXX")) {
9333 return Cxx20SupportLevel::MissingCxx;
9336 cmValue standardDefault =
9337 this->Makefile->GetDefinition("CMAKE_CXX_STANDARD_DEFAULT");
9338 if (!standardDefault || standardDefault->empty()) {
9339 // We do not know any meaningful C++ standard levels for this compiler.
9340 return Cxx20SupportLevel::NoCxx20;
9343 cmStandardLevelResolver standardResolver(this->Makefile);
9344 cmStandardLevel const cxxStd20 =
9345 *standardResolver.LanguageStandardLevel("CXX", "20");
9346 cm::optional<cmStandardLevel> explicitLevel =
9347 this->GetExplicitStandardLevel("CXX", config);
9348 if (!explicitLevel || *explicitLevel < cxxStd20) {
9349 return Cxx20SupportLevel::NoCxx20;
9352 cmValue scandepRule =
9353 this->Makefile->GetDefinition("CMAKE_CXX_SCANDEP_SOURCE");
9354 if (!scandepRule) {
9355 return Cxx20SupportLevel::MissingRule;
9357 return Cxx20SupportLevel::Supported;
9360 void cmGeneratorTarget::CheckCxxModuleStatus(std::string const& config) const
9362 bool haveScannableSources = false;
9364 // Check for `CXX_MODULE*` file sets and a lack of support.
9365 if (this->HaveCxx20ModuleSources()) {
9366 haveScannableSources = true;
9369 if (!haveScannableSources) {
9370 // Check to see if there are regular sources that have requested scanning.
9371 auto sources = this->GetSourceFiles(config);
9372 for (auto const& source : sources) {
9373 auto const* sf = source.Value;
9374 auto const& lang = sf->GetLanguage();
9375 if (lang != "CXX"_s) {
9376 continue;
9378 // Ignore sources which do not need dyndep.
9379 if (this->NeedDyndepForSource(lang, config, sf)) {
9380 haveScannableSources = true;
9385 // If there isn't anything scannable, ignore it.
9386 if (!haveScannableSources) {
9387 return;
9390 // If the generator doesn't support modules at all, error that we have
9391 // sources that require the support.
9392 if (!this->GetGlobalGenerator()->CheckCxxModuleSupport(
9393 cmGlobalGenerator::CxxModuleSupportQuery::Expected)) {
9394 this->Makefile->IssueMessage(
9395 MessageType::FATAL_ERROR,
9396 cmStrCat("The target named \"", this->GetName(),
9397 "\" has C++ sources that may use modules, but modules are not "
9398 "supported by this generator:\n ",
9399 this->GetGlobalGenerator()->GetName(), '\n',
9400 "Modules are supported only by Ninja, Ninja Multi-Config, "
9401 "and Visual Studio generators for VS 17.4 and newer. "
9402 "See the cmake-cxxmodules(7) manual for details. "
9403 "Use the CMAKE_CXX_SCAN_FOR_MODULES variable to enable or "
9404 "disable scanning."));
9405 return;
9408 switch (this->HaveCxxModuleSupport(config)) {
9409 case cmGeneratorTarget::Cxx20SupportLevel::MissingCxx:
9410 this->Makefile->IssueMessage(
9411 MessageType::FATAL_ERROR,
9412 cmStrCat("The target named \"", this->GetName(),
9413 "\" has C++ sources that use modules, but the \"CXX\" "
9414 "language has not been enabled."));
9415 break;
9416 case cmGeneratorTarget::Cxx20SupportLevel::NoCxx20: {
9417 cmStandardLevelResolver standardResolver(this->Makefile);
9418 auto effStandard =
9419 standardResolver.GetEffectiveStandard(this, "CXX", config);
9420 if (effStandard.empty()) {
9421 effStandard = "; no C++ standard found";
9422 } else {
9423 effStandard = cmStrCat("; found \"cxx_std_", effStandard, '"');
9425 this->Makefile->IssueMessage(
9426 MessageType::FATAL_ERROR,
9427 cmStrCat(
9428 "The target named \"", this->GetName(),
9429 "\" has C++ sources that use modules, but does not include "
9430 "\"cxx_std_20\" (or newer) among its `target_compile_features`",
9431 effStandard, '.'));
9432 } break;
9433 case cmGeneratorTarget::Cxx20SupportLevel::MissingRule: {
9434 this->Makefile->IssueMessage(
9435 MessageType::FATAL_ERROR,
9436 cmStrCat("The target named \"", this->GetName(),
9437 "\" has C++ sources that may use modules, but the compiler "
9438 "does not provide a way to discover the import graph "
9439 "dependencies. See the cmake-cxxmodules(7) manual for "
9440 "details. Use the CMAKE_CXX_SCAN_FOR_MODULES variable to "
9441 "enable or disable scanning."));
9442 } break;
9443 case cmGeneratorTarget::Cxx20SupportLevel::Supported:
9444 // All is well.
9445 break;
9449 bool cmGeneratorTarget::NeedCxxModuleSupport(std::string const& lang,
9450 std::string const& config) const
9452 if (lang != "CXX"_s) {
9453 return false;
9455 return this->HaveCxxModuleSupport(config) == Cxx20SupportLevel::Supported &&
9456 this->GetGlobalGenerator()->CheckCxxModuleSupport(
9457 cmGlobalGenerator::CxxModuleSupportQuery::Inspect);
9460 bool cmGeneratorTarget::NeedDyndep(std::string const& lang,
9461 std::string const& config) const
9463 return lang == "Fortran"_s || this->NeedCxxModuleSupport(lang, config);
9466 cmFileSet const* cmGeneratorTarget::GetFileSetForSource(
9467 std::string const& config, cmSourceFile const* sf) const
9469 this->BuildFileSetInfoCache(config);
9471 auto const& path = sf->GetFullPath();
9472 auto const& per_config = this->Configs[config];
9474 auto const fsit = per_config.FileSetCache.find(path);
9475 if (fsit == per_config.FileSetCache.end()) {
9476 return nullptr;
9478 return fsit->second;
9481 bool cmGeneratorTarget::NeedDyndepForSource(std::string const& lang,
9482 std::string const& config,
9483 cmSourceFile const* sf) const
9485 // Fortran always needs to be scanned.
9486 if (lang == "Fortran"_s) {
9487 return true;
9489 // Only C++ code needs scanned otherwise.
9490 if (lang != "CXX"_s) {
9491 return false;
9494 // Any file in `CXX_MODULES` file sets need scanned (it being `CXX` is
9495 // enforced elsewhere).
9496 auto const* fs = this->GetFileSetForSource(config, sf);
9497 if (fs && fs->GetType() == "CXX_MODULES"_s) {
9498 return true;
9501 auto targetDyndep = this->NeedCxxDyndep(config);
9502 if (targetDyndep == CxxModuleSupport::Unavailable) {
9503 return false;
9505 auto const sfProp = sf->GetProperty("CXX_SCAN_FOR_MODULES");
9506 if (sfProp.IsSet()) {
9507 return sfProp.IsOn();
9509 return targetDyndep == CxxModuleSupport::Enabled;
9512 cmGeneratorTarget::CxxModuleSupport cmGeneratorTarget::NeedCxxDyndep(
9513 std::string const& config) const
9515 bool haveRule = false;
9516 switch (this->HaveCxxModuleSupport(config)) {
9517 case Cxx20SupportLevel::MissingCxx:
9518 case Cxx20SupportLevel::NoCxx20:
9519 return CxxModuleSupport::Unavailable;
9520 case Cxx20SupportLevel::MissingRule:
9521 break;
9522 case Cxx20SupportLevel::Supported:
9523 haveRule = true;
9524 break;
9526 bool haveGeneratorSupport =
9527 this->GetGlobalGenerator()->CheckCxxModuleSupport(
9528 cmGlobalGenerator::CxxModuleSupportQuery::Inspect);
9529 auto const tgtProp = this->GetProperty("CXX_SCAN_FOR_MODULES");
9530 if (tgtProp.IsSet()) {
9531 return tgtProp.IsOn() ? CxxModuleSupport::Enabled
9532 : CxxModuleSupport::Disabled;
9535 CxxModuleSupport policyAnswer = CxxModuleSupport::Unavailable;
9536 switch (this->GetPolicyStatusCMP0155()) {
9537 case cmPolicies::WARN:
9538 case cmPolicies::OLD:
9539 // The OLD behavior is to not scan the source.
9540 policyAnswer = CxxModuleSupport::Disabled;
9541 break;
9542 case cmPolicies::REQUIRED_ALWAYS:
9543 case cmPolicies::REQUIRED_IF_USED:
9544 case cmPolicies::NEW:
9545 // The NEW behavior is to scan the source if the compiler supports
9546 // scanning and the generator supports it.
9547 if (haveRule && haveGeneratorSupport) {
9548 policyAnswer = CxxModuleSupport::Enabled;
9549 } else {
9550 policyAnswer = CxxModuleSupport::Disabled;
9552 break;
9554 return policyAnswer;
9557 void cmGeneratorTarget::BuildFileSetInfoCache(std::string const& config) const
9559 auto& per_config = this->Configs[config];
9561 if (per_config.BuiltFileSetCache) {
9562 return;
9565 auto const* tgt = this->Target;
9567 for (auto const& name : tgt->GetAllFileSetNames()) {
9568 auto const* file_set = tgt->GetFileSet(name);
9569 if (!file_set) {
9570 tgt->GetMakefile()->IssueMessage(
9571 MessageType::INTERNAL_ERROR,
9572 cmStrCat("Target \"", tgt->GetName(),
9573 "\" is tracked to have file set \"", name,
9574 "\", but it was not found."));
9575 continue;
9578 auto fileEntries = file_set->CompileFileEntries();
9579 auto directoryEntries = file_set->CompileDirectoryEntries();
9580 auto directories = file_set->EvaluateDirectoryEntries(
9581 directoryEntries, this->LocalGenerator, config, this);
9583 std::map<std::string, std::vector<std::string>> files;
9584 for (auto const& entry : fileEntries) {
9585 file_set->EvaluateFileEntry(directories, files, entry,
9586 this->LocalGenerator, config, this);
9589 for (auto const& it : files) {
9590 for (auto const& filename : it.second) {
9591 auto collapsedFile = cmSystemTools::CollapseFullPath(filename);
9592 per_config.FileSetCache[collapsedFile] = file_set;
9597 per_config.BuiltFileSetCache = true;