Coverage Report

Created: 2026-10-28 06:38

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