/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ /* clang-format off */ #include "cmGeneratorTarget.h" /* clang-format on */ #include "cmConfigure.h" #include #include #include #include #include #include #include #include #include "cmEvaluatedTargetProperty.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* context) { cmGeneratorExpressionDAGChecker dag{ target->GetBacktrace(), target, propertyName, nullptr, context, target->GetLocalGenerator(), config }; switch (dag.Check()) { case cmGeneratorExpressionDAGChecker::SELF_REFERENCE: dag.ReportError( nullptr, "$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 = cmStrCat(lg->GetCurrentBinaryDirectory(), '/', lg->GetTargetDirectory(dependency)); } } 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, EvaluatedTargetPropertyEntries& entries) { if (auto const* libraries = target->GetLinkImplementationLibraries(config, UseTo::Compile)) { cmGeneratorExpressionDAGChecker dag{ target->GetBacktrace(), target, propertyName, nullptr, nullptr, target->GetLocalGenerator(), config }; for (cmLinkImplItem const& library : libraries->Libraries) { if (cmGeneratorTarget const* dependency = library.Target) { if (!dependency->IsInBuildSystem()) { continue; } if (cm::contains(dependency->GetAllConfigCompileLanguages(), lang)) { auto* lg = dependency->GetLocalGenerator(); 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, lang, config, propertyName, mode, &dag), entry.Values); entries.Entries.emplace_back(std::move(entry)); } } } } } void processIncludeDirectories(cmGeneratorTarget const* tgt, EvaluatedTargetPropertyEntries& entries, std::vector>& includes, std::unordered_set& uniqueIncludes, bool debugIncludes) { for (EvaluatedTargetPropertyEntry& entry : entries.Entries) { cmLinkImplItem const& item = entry.LinkImplItem; 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)) { std::ostringstream e; MessageType messageType = MessageType::FATAL_ERROR; if (!targetName.empty()) { /* clang-format off */ e << "Target \"" << targetName << "\" contains relative " "path in its INTERFACE_INCLUDE_DIRECTORIES:\n" " \"" << entryInclude << "\""; /* clang-format on */ } else { e << "Found relative path while evaluating include directories of " "\"" << tgt->GetName() << "\":\n \"" << entryInclude << "\"\n"; } tgt->GetLocalGenerator()->IssueMessage(messageType, e.str()); 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 += " * " + entryInclude + "\n"; } } } if (!usedIncludes.empty()) { tgt->GetLocalGenerator()->GetCMakeInstance()->IssueMessage( MessageType::LOG, std::string("Used includes for target ") + tgt->GetName() + ":\n" + usedIncludes, entry.Backtrace); } } } } std::vector> 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> includes; std::unordered_set uniqueIncludes; cmGeneratorExpressionDAGChecker dagChecker(this, "INCLUDE_DIRECTORIES", nullptr, nullptr, this->LocalGenerator, config); cmList debugProperties{ this->Makefile->GetDefinition( "CMAKE_DEBUG_TARGET_PROPERTIES") }; bool debugIncludes = !this->DebugIncludesDone && cm::contains(debugProperties, "INCLUDE_DIRECTORIES"); this->DebugIncludesDone = true; EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries( this, config, lang, &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, config, "INTERFACE_INCLUDE_DIRECTORIES", lang, &dagChecker, entries, IncludeRuntimeInterface::Yes); processIncludeDirectories(this, entries, includes, uniqueIncludes, debugIncludes); if (this->IsApple()) { if (cmLinkImplementationLibraries const* impl = this->GetLinkImplementationLibraries(config, UseTo::Compile)) { for (cmLinkImplItem 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; }