| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying | |
| file LICENSE.rst or https://cmake.org/licensing for details. */ | |
| /* clang-format off */ | |
| #include "cmGeneratorTarget.h" | |
| /* clang-format on */ | |
| #include "cmConfigure.h" | |
| #include <set> | |
| #include <string> | |
| #include <unordered_set> | |
| #include <utility> | |
| #include <vector> | |
| #include <cm/optional> | |
| #include <cmext/algorithm> | |
| #include "cmEvaluatedTargetProperty.h" | |
| #include "cmGenExContext.h" | |
| #include "cmGeneratorExpressionDAGChecker.h" | |
| #include "cmGlobalGenerator.h" | |
| #include "cmLinkItem.h" | |
| #include "cmList.h" | |
| #include "cmListFileCache.h" | |
| #include "cmLocalGenerator.h" | |
| #include "cmMakefile.h" | |
| #include "cmMessageType.h" | |
| #include "cmStringAlgorithms.h" | |
| #include "cmSystemTools.h" | |
| #include "cmTarget.h" | |
| #include "cmValue.h" | |
| #include "cmake.h" | |
| namespace { | |
| using UseTo = cmGeneratorTarget::UseTo; | |
| enum class IncludeDirectoryFallBack | |
| { | |
| BINARY, | |
| OBJECT | |
| }; | |
| std::string AddLangSpecificInterfaceIncludeDirectories( | |
| cmGeneratorTarget const* root, cmGeneratorTarget const* target, | |
| std::string const& lang, std::string const& config, | |
| std::string const& propertyName, IncludeDirectoryFallBack mode, | |
| cmGeneratorExpressionDAGChecker* dagCheckerParent) | |
| { | |
| cm::GenEx::Context context(target->LocalGenerator, config); | |
| cmGeneratorExpressionDAGChecker dagChecker{ | |
| target, propertyName, nullptr, | |
| dagCheckerParent, context, target->GetBacktrace(), | |
| }; | |
| switch (dagChecker.Check()) { | |
| case cmGeneratorExpressionDAGChecker::SELF_REFERENCE: | |
| dagChecker.ReportError( | |
| nullptr, | |
| cmStrCat("$<TARGET_PROPERTY:", target->GetName(), ",propertyName")); | |
| CM_FALLTHROUGH; | |
| case cmGeneratorExpressionDAGChecker::CYCLIC_REFERENCE: | |
| // No error. We just skip cyclic references. | |
| case cmGeneratorExpressionDAGChecker::ALREADY_SEEN: | |
| // No error. We have already seen this transitive property. | |
| return ""; | |
| case cmGeneratorExpressionDAGChecker::DAG: | |
| break; | |
| } | |
| std::string directories; | |
| if (auto const* link_interface = | |
| target->GetLinkInterfaceLibraries(config, root, UseTo::Compile)) { | |
| for (cmLinkItem const& library : link_interface->Libraries) { | |
| if (cmGeneratorTarget const* dependency = library.Target) { | |
| if (cm::contains(dependency->GetAllConfigCompileLanguages(), lang)) { | |
| auto* lg = dependency->GetLocalGenerator(); | |
| std::string value = dependency->GetSafeProperty(propertyName); | |
| if (value.empty()) { | |
| if (mode == IncludeDirectoryFallBack::BINARY) { | |
| value = lg->GetCurrentBinaryDirectory(); | |
| } else if (mode == IncludeDirectoryFallBack::OBJECT) { | |
| value = dependency->GetSupportDirectory(); | |
| } | |
| } | |
| if (!directories.empty()) { | |
| directories += ";"; | |
| } | |
| directories += value; | |
| } | |
| } | |
| } | |
| } | |
| return directories; | |
| } | |
| void AddLangSpecificImplicitIncludeDirectories( | |
| cmGeneratorTarget const* target, std::string const& lang, | |
| std::string const& config, std::string const& propertyName, | |
| IncludeDirectoryFallBack mode, cm::EvaluatedTargetPropertyEntries& entries) | |
| { | |
| if (auto const* libraries = | |
| target->GetLinkImplementationLibraries(config, UseTo::Compile)) { | |
| cm::GenEx::Context context(target->LocalGenerator, config, lang); | |
| cmGeneratorExpressionDAGChecker dagChecker{ | |
| target, propertyName, nullptr, nullptr, context, target->GetBacktrace(), | |
| }; | |
| for (cmLinkItem const& library : libraries->Libraries) { | |
| if (cmGeneratorTarget const* dependency = library.Target) { | |
| if (!dependency->IsInBuildSystem()) { | |
| continue; | |
| } | |
| if (cm::contains(dependency->GetAllConfigCompileLanguages(), lang)) { | |
| auto* lg = dependency->GetLocalGenerator(); | |
| cm::EvaluatedTargetPropertyEntry entry{ library, library.Backtrace }; | |
| if (lang == "Swift") { | |
| entry.Values.emplace_back( | |
| dependency->GetSwiftModuleDirectory(config)); | |
| } else if (cmValue val = dependency->GetProperty(propertyName)) { | |
| entry.Values.emplace_back(*val); | |
| } else { | |
| if (mode == IncludeDirectoryFallBack::BINARY) { | |
| entry.Values.emplace_back(lg->GetCurrentBinaryDirectory()); | |
| } else if (mode == IncludeDirectoryFallBack::OBJECT) { | |
| entry.Values.emplace_back( | |
| dependency->GetObjectDirectory(config)); | |
| } | |
| } | |
| cmExpandList(AddLangSpecificInterfaceIncludeDirectories( | |
| target, dependency, context.Language, context.Config, | |
| propertyName, mode, &dagChecker), | |
| entry.Values); | |
| entries.Entries.emplace_back(std::move(entry)); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| void processIncludeDirectories(cmGeneratorTarget const* tgt, | |
| cm::EvaluatedTargetPropertyEntries& entries, | |
| std::vector<BT<std::string>>& includes, | |
| std::unordered_set<std::string>& uniqueIncludes, | |
| bool debugIncludes) | |
| { | |
| for (cm::EvaluatedTargetPropertyEntry& entry : entries.Entries) { | |
| cmLinkItem const& item = entry.LinkItem; | |
| std::string const& targetName = item.AsStr(); | |
| bool const fromImported = item.Target && item.Target->IsImported(); | |
| std::string usedIncludes; | |
| for (std::string& entryInclude : entry.Values) { | |
| if (fromImported && !cmSystemTools::FileExists(entryInclude)) { | |
| tgt->GetLocalGenerator()->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| cmStrCat( | |
| "Imported target \"", targetName, | |
| "\" includes non-existent path\n \"", entryInclude, | |
| "\"\nin its INTERFACE_INCLUDE_DIRECTORIES. Possible reasons " | |
| "include:\n" | |
| "* The path was deleted, renamed, or moved to another location.\n" | |
| "* An install or uninstall procedure did not complete " | |
| "successfully.\n" | |
| "* The installation package was faulty and references files it " | |
| "does not provide.\n")); | |
| return; | |
| } | |
| if (!cmSystemTools::FileIsFullPath(entryInclude)) { | |
| MessageType messageType = MessageType::FATAL_ERROR; | |
| if (!targetName.empty()) { | |
| tgt->GetLocalGenerator()->IssueMessage( | |
| messageType, | |
| cmStrCat("Target \"", targetName, | |
| "\" contains relative path in its " | |
| "INTERFACE_INCLUDE_DIRECTORIES:\n" | |
| " \"", | |
| entryInclude, '"')); | |
| } else { | |
| tgt->GetLocalGenerator()->IssueMessage( | |
| messageType, | |
| cmStrCat("Found relative path while evaluating include " | |
| "directories of \"", | |
| tgt->GetName(), "\":\n \"", entryInclude, "\"\n")); | |
| } | |
| if (messageType == MessageType::FATAL_ERROR) { | |
| return; | |
| } | |
| } | |
| if (!cmIsOff(entryInclude)) { | |
| cmSystemTools::ConvertToUnixSlashes(entryInclude); | |
| } | |
| if (uniqueIncludes.insert(entryInclude).second) { | |
| includes.emplace_back(entryInclude, entry.Backtrace); | |
| if (debugIncludes) { | |
| usedIncludes = | |
| cmStrCat(std::move(usedIncludes), " * ", entryInclude, '\n'); | |
| } | |
| } | |
| } | |
| if (!usedIncludes.empty()) { | |
| tgt->GetLocalGenerator()->GetCMakeInstance()->IssueMessage( | |
| MessageType::LOG, | |
| cmStrCat("Used includes for target ", tgt->GetName(), ":\n", | |
| usedIncludes), | |
| entry.Backtrace); | |
| } | |
| } | |
| } | |
| } | |
| std::vector<BT<std::string>> cmGeneratorTarget::GetIncludeDirectories( | |
| std::string const& config, std::string const& lang) const | |
| { | |
| ConfigAndLanguage cacheKey(config, lang); | |
| { | |
| auto it = this->IncludeDirectoriesCache.find(cacheKey); | |
| if (it != this->IncludeDirectoriesCache.end()) { | |
| return it->second; | |
| } | |
| } | |
| std::vector<BT<std::string>> includes; | |
| std::unordered_set<std::string> uniqueIncludes; | |
| cm::GenEx::Context context(this->LocalGenerator, config, lang); | |
| cmGeneratorExpressionDAGChecker dagChecker{ | |
| this, "INCLUDE_DIRECTORIES", nullptr, nullptr, context, | |
| }; | |
| cmList debugProperties{ this->Makefile->GetDefinition( | |
| "CMAKE_DEBUG_TARGET_PROPERTIES") }; | |
| bool debugIncludes = !this->DebugIncludesDone && | |
| cm::contains(debugProperties, "INCLUDE_DIRECTORIES"); | |
| this->DebugIncludesDone = true; | |
| cm::EvaluatedTargetPropertyEntries entries = | |
| cm::EvaluateTargetPropertyEntries(this, context, &dagChecker, | |
| this->IncludeDirectoriesEntries); | |
| if (lang == "Swift") { | |
| AddLangSpecificImplicitIncludeDirectories( | |
| this, lang, config, "Swift_MODULE_DIRECTORY", | |
| IncludeDirectoryFallBack::BINARY, entries); | |
| } | |
| if (this->CanCompileSources() && (lang != "Swift" && lang != "Fortran")) { | |
| std::string const propertyName = "ISPC_HEADER_DIRECTORY"; | |
| // If this target has ISPC sources make sure to add the header | |
| // directory to other compilation units | |
| if (cm::contains(this->GetAllConfigCompileLanguages(), "ISPC")) { | |
| if (cmValue val = this->GetProperty(propertyName)) { | |
| includes.emplace_back(*val); | |
| } else { | |
| includes.emplace_back(this->GetObjectDirectory(config)); | |
| } | |
| } | |
| AddLangSpecificImplicitIncludeDirectories( | |
| this, "ISPC", config, propertyName, IncludeDirectoryFallBack::OBJECT, | |
| entries); | |
| } | |
| AddInterfaceEntries(this, "INTERFACE_INCLUDE_DIRECTORIES", context, | |
| &dagChecker, entries, cm::IncludeRuntimeInterface::Yes); | |
| processIncludeDirectories(this, entries, includes, uniqueIncludes, | |
| debugIncludes); | |
| if (this->IsApple()) { | |
| if (cmLinkImplementationLibraries const* impl = | |
| this->GetLinkImplementationLibraries(config, UseTo::Compile)) { | |
| for (cmLinkItem const& lib : impl->Libraries) { | |
| std::string libDir; | |
| if (!lib.Target) { | |
| libDir = cmSystemTools::CollapseFullPath( | |
| lib.AsStr(), this->Makefile->GetHomeOutputDirectory()); | |
| } else if (lib.Target->Target->IsFrameworkOnApple() || | |
| this->IsImportedFrameworkFolderOnApple(config)) { | |
| libDir = lib.Target->GetLocation(config); | |
| } else { | |
| continue; | |
| } | |
| auto fwDescriptor = | |
| this->GetGlobalGenerator()->SplitFrameworkPath(libDir); | |
| if (!fwDescriptor) { | |
| continue; | |
| } | |
| auto fwInclude = fwDescriptor->GetFrameworkPath(); | |
| if (uniqueIncludes.insert(fwInclude).second) { | |
| includes.emplace_back(fwInclude, cmListFileBacktrace()); | |
| } | |
| } | |
| } | |
| } | |
| this->IncludeDirectoriesCache.emplace(cacheKey, includes); | |
| return includes; | |
| } |