Coverage Report

Created: 2026-02-09 06:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmGeneratorTarget_IncludeDirectories.cxx
Line
Count
Source
1
/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2
   file LICENSE.rst or https://cmake.org/licensing for details.  */
3
/* clang-format off */
4
#include "cmGeneratorTarget.h"
5
/* clang-format on */
6
7
#include "cmConfigure.h"
8
9
#include <set>
10
#include <sstream>
11
#include <string>
12
#include <unordered_set>
13
#include <utility>
14
#include <vector>
15
16
#include <cm/optional>
17
#include <cmext/algorithm>
18
19
#include "cmEvaluatedTargetProperty.h"
20
#include "cmGenExContext.h"
21
#include "cmGeneratorExpressionDAGChecker.h"
22
#include "cmGlobalGenerator.h"
23
#include "cmLinkItem.h"
24
#include "cmList.h"
25
#include "cmListFileCache.h"
26
#include "cmLocalGenerator.h"
27
#include "cmMakefile.h"
28
#include "cmMessageType.h"
29
#include "cmStringAlgorithms.h"
30
#include "cmSystemTools.h"
31
#include "cmTarget.h"
32
#include "cmValue.h"
33
#include "cmake.h"
34
35
namespace {
36
using UseTo = cmGeneratorTarget::UseTo;
37
38
enum class IncludeDirectoryFallBack
39
{
40
  BINARY,
41
  OBJECT
42
};
43
44
std::string AddLangSpecificInterfaceIncludeDirectories(
45
  cmGeneratorTarget const* root, cmGeneratorTarget const* target,
46
  std::string const& lang, std::string const& config,
47
  std::string const& propertyName, IncludeDirectoryFallBack mode,
48
  cmGeneratorExpressionDAGChecker* dagCheckerParent)
49
0
{
50
0
  cm::GenEx::Context context(target->LocalGenerator, config);
51
0
  cmGeneratorExpressionDAGChecker dagChecker{
52
0
    target,           propertyName, nullptr,
53
0
    dagCheckerParent, context,      target->GetBacktrace(),
54
0
  };
55
0
  switch (dagChecker.Check()) {
56
0
    case cmGeneratorExpressionDAGChecker::SELF_REFERENCE:
57
0
      dagChecker.ReportError(
58
0
        nullptr,
59
0
        cmStrCat("$<TARGET_PROPERTY:", target->GetName(), ",propertyName"));
60
0
      CM_FALLTHROUGH;
61
0
    case cmGeneratorExpressionDAGChecker::CYCLIC_REFERENCE:
62
      // No error. We just skip cyclic references.
63
0
    case cmGeneratorExpressionDAGChecker::ALREADY_SEEN:
64
      // No error. We have already seen this transitive property.
65
0
      return "";
66
0
    case cmGeneratorExpressionDAGChecker::DAG:
67
0
      break;
68
0
  }
69
70
0
  std::string directories;
71
0
  if (auto const* link_interface =
72
0
        target->GetLinkInterfaceLibraries(config, root, UseTo::Compile)) {
73
0
    for (cmLinkItem const& library : link_interface->Libraries) {
74
0
      if (cmGeneratorTarget const* dependency = library.Target) {
75
0
        if (cm::contains(dependency->GetAllConfigCompileLanguages(), lang)) {
76
0
          auto* lg = dependency->GetLocalGenerator();
77
0
          std::string value = dependency->GetSafeProperty(propertyName);
78
0
          if (value.empty()) {
79
0
            if (mode == IncludeDirectoryFallBack::BINARY) {
80
0
              value = lg->GetCurrentBinaryDirectory();
81
0
            } else if (mode == IncludeDirectoryFallBack::OBJECT) {
82
0
              value = dependency->GetSupportDirectory();
83
0
            }
84
0
          }
85
86
0
          if (!directories.empty()) {
87
0
            directories += ";";
88
0
          }
89
0
          directories += value;
90
0
        }
91
0
      }
92
0
    }
93
0
  }
94
0
  return directories;
95
0
}
96
97
void AddLangSpecificImplicitIncludeDirectories(
98
  cmGeneratorTarget const* target, std::string const& lang,
99
  std::string const& config, std::string const& propertyName,
100
  IncludeDirectoryFallBack mode, EvaluatedTargetPropertyEntries& entries)
101
0
{
102
0
  if (auto const* libraries =
103
0
        target->GetLinkImplementationLibraries(config, UseTo::Compile)) {
104
0
    cm::GenEx::Context context(target->LocalGenerator, config, lang);
105
0
    cmGeneratorExpressionDAGChecker dagChecker{
106
0
      target, propertyName, nullptr, nullptr, context, target->GetBacktrace(),
107
0
    };
108
109
0
    for (cmLinkItem const& library : libraries->Libraries) {
110
0
      if (cmGeneratorTarget const* dependency = library.Target) {
111
0
        if (!dependency->IsInBuildSystem()) {
112
0
          continue;
113
0
        }
114
0
        if (cm::contains(dependency->GetAllConfigCompileLanguages(), lang)) {
115
0
          auto* lg = dependency->GetLocalGenerator();
116
0
          EvaluatedTargetPropertyEntry entry{ library, library.Backtrace };
117
118
0
          if (lang == "Swift") {
119
0
            entry.Values.emplace_back(
120
0
              dependency->GetSwiftModuleDirectory(config));
121
0
          } else if (cmValue val = dependency->GetProperty(propertyName)) {
122
0
            entry.Values.emplace_back(*val);
123
0
          } else {
124
0
            if (mode == IncludeDirectoryFallBack::BINARY) {
125
0
              entry.Values.emplace_back(lg->GetCurrentBinaryDirectory());
126
0
            } else if (mode == IncludeDirectoryFallBack::OBJECT) {
127
0
              entry.Values.emplace_back(
128
0
                dependency->GetObjectDirectory(config));
129
0
            }
130
0
          }
131
132
0
          cmExpandList(AddLangSpecificInterfaceIncludeDirectories(
133
0
                         target, dependency, context.Language, context.Config,
134
0
                         propertyName, mode, &dagChecker),
135
0
                       entry.Values);
136
0
          entries.Entries.emplace_back(std::move(entry));
137
0
        }
138
0
      }
139
0
    }
140
0
  }
141
0
}
142
143
void processIncludeDirectories(cmGeneratorTarget const* tgt,
144
                               EvaluatedTargetPropertyEntries& entries,
145
                               std::vector<BT<std::string>>& includes,
146
                               std::unordered_set<std::string>& uniqueIncludes,
147
                               bool debugIncludes)
148
0
{
149
0
  for (EvaluatedTargetPropertyEntry& entry : entries.Entries) {
150
0
    cmLinkItem const& item = entry.LinkItem;
151
0
    std::string const& targetName = item.AsStr();
152
0
    bool const fromImported = item.Target && item.Target->IsImported();
153
154
0
    std::string usedIncludes;
155
0
    for (std::string& entryInclude : entry.Values) {
156
0
      if (fromImported && !cmSystemTools::FileExists(entryInclude)) {
157
0
        tgt->GetLocalGenerator()->IssueMessage(
158
0
          MessageType::FATAL_ERROR,
159
0
          cmStrCat(
160
0
            "Imported target \"", targetName,
161
0
            "\" includes non-existent path\n  \"", entryInclude,
162
0
            "\"\nin its INTERFACE_INCLUDE_DIRECTORIES. Possible reasons "
163
0
            "include:\n"
164
0
            "* The path was deleted, renamed, or moved to another location.\n"
165
0
            "* An install or uninstall procedure did not complete "
166
0
            "successfully.\n"
167
0
            "* The installation package was faulty and references files it "
168
0
            "does not provide.\n"));
169
0
        return;
170
0
      }
171
172
0
      if (!cmSystemTools::FileIsFullPath(entryInclude)) {
173
0
        std::ostringstream e;
174
0
        MessageType messageType = MessageType::FATAL_ERROR;
175
0
        if (!targetName.empty()) {
176
          /* clang-format off */
177
0
          e << "Target \"" << targetName << "\" contains relative "
178
0
            "path in its INTERFACE_INCLUDE_DIRECTORIES:\n"
179
0
            "  \"" << entryInclude << "\"";
180
          /* clang-format on */
181
0
        } else {
182
0
          e << "Found relative path while evaluating include directories of "
183
0
               "\""
184
0
            << tgt->GetName() << "\":\n  \"" << entryInclude << "\"\n";
185
0
        }
186
0
        tgt->GetLocalGenerator()->IssueMessage(messageType, e.str());
187
0
        if (messageType == MessageType::FATAL_ERROR) {
188
0
          return;
189
0
        }
190
0
      }
191
192
0
      if (!cmIsOff(entryInclude)) {
193
0
        cmSystemTools::ConvertToUnixSlashes(entryInclude);
194
0
      }
195
196
0
      if (uniqueIncludes.insert(entryInclude).second) {
197
0
        includes.emplace_back(entryInclude, entry.Backtrace);
198
0
        if (debugIncludes) {
199
0
          usedIncludes += cmStrCat(" * ", entryInclude, "\n");
200
0
        }
201
0
      }
202
0
    }
203
0
    if (!usedIncludes.empty()) {
204
0
      tgt->GetLocalGenerator()->GetCMakeInstance()->IssueMessage(
205
0
        MessageType::LOG,
206
0
        cmStrCat("Used includes for target ", tgt->GetName(), ":\n",
207
0
                 usedIncludes),
208
0
        entry.Backtrace);
209
0
    }
210
0
  }
211
0
}
212
}
213
214
std::vector<BT<std::string>> cmGeneratorTarget::GetIncludeDirectories(
215
  std::string const& config, std::string const& lang) const
216
0
{
217
0
  ConfigAndLanguage cacheKey(config, lang);
218
0
  {
219
0
    auto it = this->IncludeDirectoriesCache.find(cacheKey);
220
0
    if (it != this->IncludeDirectoriesCache.end()) {
221
0
      return it->second;
222
0
    }
223
0
  }
224
0
  std::vector<BT<std::string>> includes;
225
0
  std::unordered_set<std::string> uniqueIncludes;
226
227
0
  cm::GenEx::Context context(this->LocalGenerator, config, lang);
228
229
0
  cmGeneratorExpressionDAGChecker dagChecker{
230
0
    this, "INCLUDE_DIRECTORIES", nullptr, nullptr, context,
231
0
  };
232
233
0
  cmList debugProperties{ this->Makefile->GetDefinition(
234
0
    "CMAKE_DEBUG_TARGET_PROPERTIES") };
235
0
  bool debugIncludes = !this->DebugIncludesDone &&
236
0
    cm::contains(debugProperties, "INCLUDE_DIRECTORIES");
237
238
0
  this->DebugIncludesDone = true;
239
240
0
  EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries(
241
0
    this, context, &dagChecker, this->IncludeDirectoriesEntries);
242
243
0
  if (lang == "Swift") {
244
0
    AddLangSpecificImplicitIncludeDirectories(
245
0
      this, lang, config, "Swift_MODULE_DIRECTORY",
246
0
      IncludeDirectoryFallBack::BINARY, entries);
247
0
  }
248
249
0
  if (this->CanCompileSources() && (lang != "Swift" && lang != "Fortran")) {
250
251
0
    std::string const propertyName = "ISPC_HEADER_DIRECTORY";
252
253
    // If this target has ISPC sources make sure to add the header
254
    // directory to other compilation units
255
0
    if (cm::contains(this->GetAllConfigCompileLanguages(), "ISPC")) {
256
0
      if (cmValue val = this->GetProperty(propertyName)) {
257
0
        includes.emplace_back(*val);
258
0
      } else {
259
0
        includes.emplace_back(this->GetObjectDirectory(config));
260
0
      }
261
0
    }
262
263
0
    AddLangSpecificImplicitIncludeDirectories(
264
0
      this, "ISPC", config, propertyName, IncludeDirectoryFallBack::OBJECT,
265
0
      entries);
266
0
  }
267
268
0
  AddInterfaceEntries(this, "INTERFACE_INCLUDE_DIRECTORIES", context,
269
0
                      &dagChecker, entries, IncludeRuntimeInterface::Yes);
270
271
0
  processIncludeDirectories(this, entries, includes, uniqueIncludes,
272
0
                            debugIncludes);
273
274
0
  if (this->IsApple()) {
275
0
    if (cmLinkImplementationLibraries const* impl =
276
0
          this->GetLinkImplementationLibraries(config, UseTo::Compile)) {
277
0
      for (cmLinkItem const& lib : impl->Libraries) {
278
0
        std::string libDir;
279
0
        if (!lib.Target) {
280
0
          libDir = cmSystemTools::CollapseFullPath(
281
0
            lib.AsStr(), this->Makefile->GetHomeOutputDirectory());
282
0
        } else if (lib.Target->Target->IsFrameworkOnApple() ||
283
0
                   this->IsImportedFrameworkFolderOnApple(config)) {
284
0
          libDir = lib.Target->GetLocation(config);
285
0
        } else {
286
0
          continue;
287
0
        }
288
289
0
        auto fwDescriptor =
290
0
          this->GetGlobalGenerator()->SplitFrameworkPath(libDir);
291
0
        if (!fwDescriptor) {
292
0
          continue;
293
0
        }
294
295
0
        auto fwInclude = fwDescriptor->GetFrameworkPath();
296
0
        if (uniqueIncludes.insert(fwInclude).second) {
297
0
          includes.emplace_back(fwInclude, cmListFileBacktrace());
298
0
        }
299
0
      }
300
0
    }
301
0
  }
302
303
0
  this->IncludeDirectoriesCache.emplace(cacheKey, includes);
304
0
  return includes;
305
0
}