Files
CMake/Source/cmGeneratorTarget_IncludeDirectories.cxx
Craig Scott 1b764b6816 cmLinkItem: Remove redundant cmLinkImplItem class
The cmLinkImplItem class used to contain some additional members
to support the CMP0027 policy. Since the CMP0027 policy and those
associated members were removed, the cmLinkImplItem class has been
a plain wrapper around cmLinkItem with no additional members.
It is no longer needed and cmLinkItem can be used directly wherever
cmLinkImplItem was used previously.
2025-10-07 10:44:28 -04:00

305 lines
10 KiB
C++

/* 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 <sstream>
#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, "$<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, 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();
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,
EvaluatedTargetPropertyEntries& entries,
std::vector<BT<std::string>>& includes,
std::unordered_set<std::string>& uniqueIncludes,
bool debugIncludes)
{
for (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)) {
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<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;
EvaluatedTargetPropertyEntries entries = 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, 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;
}