Coverage Report

Created: 2026-10-28 06:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmRulePlaceholderExpander.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
#include "cmRulePlaceholderExpander.h"
4
5
#include <utility>
6
7
#include "cmOutputConverter.h"
8
#include "cmStringAlgorithms.h"
9
#include "cmSystemTools.h"
10
11
cmRulePlaceholderExpander::cmRulePlaceholderExpander(
12
  cmBuildStep buildStep, std::map<std::string, std::string> compilers,
13
  std::map<std::string, std::string> variableMappings,
14
  std::string compilerSysroot, std::string linkerSysroot,
15
  UseShortPaths useShortPaths)
16
0
  : BuildStep(buildStep)
17
0
  , Compilers(std::move(compilers))
18
0
  , VariableMappings(std::move(variableMappings))
19
0
  , CompilerSysroot(std::move(compilerSysroot))
20
0
  , LinkerSysroot(std::move(linkerSysroot))
21
0
{
22
0
  if (useShortPaths == UseShortPaths::Yes) {
23
0
    this->ConvertToOutputForExisting =
24
0
      [this](cm::string_view path) -> std::string {
25
0
      return this->OutputConverter->ConvertToOutputForExisting(path);
26
0
    };
27
0
  } else {
28
0
    this->ConvertToOutputForExisting =
29
0
      [this](cm::string_view path) -> std::string {
30
0
      return this->OutputConverter->ConvertToOutputFormat(
31
0
        path, cmOutputConverter::SHELL);
32
0
    };
33
0
  }
34
0
}
35
36
std::string cmRulePlaceholderExpander::ExpandVariable(
37
  std::string const& variable)
38
0
{
39
0
  if (this->ReplaceValues->LinkFlags) {
40
0
    if (variable == "LINK_FLAGS") {
41
0
      return this->ReplaceValues->LinkFlags;
42
0
    }
43
0
  }
44
0
  if (this->ReplaceValues->Linker) {
45
0
    if (variable == "CMAKE_LINKER") {
46
0
      auto result =
47
0
        this->ConvertToOutputForExisting(this->ReplaceValues->Linker);
48
0
      if (this->ReplaceValues->Launcher) {
49
        // Add launcher as part of expansion so that it always appears
50
        // immediately before the command itself, regardless of whether the
51
        // overall rule template contains other content at the front.
52
0
        result =
53
0
          cmStrCat(this->ReplaceValues->Launcher, ' ', std::move(result));
54
0
      }
55
0
      return result;
56
0
    }
57
0
  }
58
0
  if (this->ReplaceValues->Language && this->ReplaceValues->Launcher &&
59
0
      variable ==
60
0
        cmStrCat("CMAKE_", this->ReplaceValues->Language,
61
0
                 "_HOST_LINK_LAUNCHER")) {
62
0
    auto mapIt = this->VariableMappings.find(variable);
63
0
    std::string result = mapIt != this->VariableMappings.end()
64
0
      ? this->ConvertToOutputForExisting(mapIt->second)
65
0
      : variable;
66
    // Add launcher as part of expansion so that it always appears
67
    // immediately before the command itself, regardless of whether the
68
    // overall rule template contains other content at the front.
69
0
    return cmStrCat(this->ReplaceValues->Launcher, ' ', result);
70
0
  }
71
0
  if (this->ReplaceValues->Manifests) {
72
0
    if (variable == "MANIFESTS") {
73
0
      return this->ReplaceValues->Manifests;
74
0
    }
75
0
  }
76
0
  if (this->ReplaceValues->Flags) {
77
0
    if (variable == "FLAGS") {
78
0
      return this->ReplaceValues->Flags;
79
0
    }
80
0
  }
81
82
0
  if (this->ReplaceValues->Source) {
83
0
    if (variable == "SOURCE") {
84
0
      return this->ReplaceValues->Source;
85
0
    }
86
0
  }
87
0
  if (this->ReplaceValues->DynDepFile) {
88
0
    if (variable == "DYNDEP_FILE") {
89
0
      return this->ReplaceValues->DynDepFile;
90
0
    }
91
0
  }
92
0
  if (this->ReplaceValues->PreprocessedSource) {
93
0
    if (variable == "PREPROCESSED_SOURCE") {
94
0
      return this->ReplaceValues->PreprocessedSource;
95
0
    }
96
0
  }
97
0
  if (this->ReplaceValues->AssemblySource) {
98
0
    if (variable == "ASSEMBLY_SOURCE") {
99
0
      return this->ReplaceValues->AssemblySource;
100
0
    }
101
0
  }
102
0
  if (this->ReplaceValues->Object) {
103
0
    if (variable == "OBJECT") {
104
0
      return this->ReplaceValues->Object;
105
0
    }
106
0
  }
107
0
  if (this->ReplaceValues->TargetSupportDir) {
108
0
    if (variable == "TARGET_SUPPORT_DIR") {
109
0
      return this->ReplaceValues->TargetSupportDir;
110
0
    }
111
0
  }
112
0
  if (this->ReplaceValues->ObjectDir) {
113
0
    if (variable == "OBJECT_DIR") {
114
0
      return this->ReplaceValues->ObjectDir;
115
0
    }
116
0
  }
117
0
  if (this->ReplaceValues->ObjectFileDir) {
118
0
    if (variable == "OBJECT_FILE_DIR") {
119
0
      return this->ReplaceValues->ObjectFileDir;
120
0
    }
121
0
  }
122
0
  if (this->ReplaceValues->Objects) {
123
0
    if (variable == "OBJECTS") {
124
0
      return this->ReplaceValues->Objects;
125
0
    }
126
0
  }
127
0
  if (this->ReplaceValues->ObjectsQuoted) {
128
0
    if (variable == "OBJECTS_QUOTED") {
129
0
      return this->ReplaceValues->ObjectsQuoted;
130
0
    }
131
0
  }
132
0
  if (this->ReplaceValues->CudaCompileMode) {
133
0
    if (variable == "CUDA_COMPILE_MODE") {
134
0
      return this->ReplaceValues->CudaCompileMode;
135
0
    }
136
0
  }
137
0
  if (this->ReplaceValues->AIXExports) {
138
0
    if (variable == "AIX_EXPORTS") {
139
0
      return this->ReplaceValues->AIXExports;
140
0
    }
141
0
  }
142
0
  if (this->ReplaceValues->ISPCHeader) {
143
0
    if (variable == "ISPC_HEADER") {
144
0
      return this->ReplaceValues->ISPCHeader;
145
0
    }
146
0
  }
147
0
  if (this->ReplaceValues->Defines && variable == "DEFINES") {
148
0
    return this->ReplaceValues->Defines;
149
0
  }
150
0
  if (this->ReplaceValues->Includes && variable == "INCLUDES") {
151
0
    return this->ReplaceValues->Includes;
152
0
  }
153
0
  if (this->ReplaceValues->SwiftLibraryName) {
154
0
    if (variable == "SWIFT_LIBRARY_NAME") {
155
0
      return this->ReplaceValues->SwiftLibraryName;
156
0
    }
157
0
  }
158
0
  if (this->ReplaceValues->SwiftModule) {
159
0
    if (variable == "SWIFT_MODULE") {
160
0
      return this->ReplaceValues->SwiftModule;
161
0
    }
162
0
  }
163
0
  if (this->ReplaceValues->SwiftModuleName) {
164
0
    if (variable == "SWIFT_MODULE_NAME") {
165
0
      return this->ReplaceValues->SwiftModuleName;
166
0
    }
167
0
  }
168
0
  if (this->ReplaceValues->SwiftSources) {
169
0
    if (variable == "SWIFT_SOURCES") {
170
0
      return this->ReplaceValues->SwiftSources;
171
0
    }
172
0
  }
173
0
  if (this->ReplaceValues->RustEmit) {
174
0
    if (variable == "RUST_EMIT") {
175
0
      return this->ReplaceValues->RustEmit;
176
0
    }
177
0
  }
178
0
  if (this->ReplaceValues->RustMainCrateRoot) {
179
0
    if (variable == "RUST_MAIN_CRATE_ROOT") {
180
0
      return this->ReplaceValues->RustMainCrateRoot;
181
0
    }
182
0
  }
183
0
  if (this->ReplaceValues->RustLinkCrates) {
184
0
    if (variable == "RUST_LINK_CRATES") {
185
0
      return this->ReplaceValues->RustLinkCrates;
186
0
    }
187
0
  }
188
0
  if (this->ReplaceValues->RustNativeObjects) {
189
0
    if (variable == "RUST_NATIVE_OBJECTS") {
190
0
      return this->ReplaceValues->RustNativeObjects;
191
0
    }
192
0
  }
193
0
  if (this->ReplaceValues->TargetPDB) {
194
0
    if (variable == "TARGET_PDB") {
195
0
      return this->ReplaceValues->TargetPDB;
196
0
    }
197
0
  }
198
0
  if (this->ReplaceValues->TargetCompilePDB) {
199
0
    if (variable == "TARGET_COMPILE_PDB") {
200
0
      return this->ReplaceValues->TargetCompilePDB;
201
0
    }
202
0
  }
203
0
  if (this->ReplaceValues->DependencyFile) {
204
0
    if (variable == "DEP_FILE") {
205
0
      return this->ReplaceValues->DependencyFile;
206
0
    }
207
0
  }
208
0
  if (this->ReplaceValues->DependencyTarget) {
209
0
    if (variable == "DEP_TARGET") {
210
0
      return this->ReplaceValues->DependencyTarget;
211
0
    }
212
0
  }
213
0
  if (this->ReplaceValues->Fatbinary) {
214
0
    if (variable == "FATBINARY") {
215
0
      return this->ReplaceValues->Fatbinary;
216
0
    }
217
0
  }
218
0
  if (this->ReplaceValues->RegisterFile) {
219
0
    if (variable == "REGISTER_FILE") {
220
0
      return this->ReplaceValues->RegisterFile;
221
0
    }
222
0
  }
223
224
0
  if (this->ReplaceValues->Target) {
225
0
    if (variable == "TARGET_QUOTED") {
226
0
      std::string targetQuoted = this->ReplaceValues->Target;
227
0
      if (!targetQuoted.empty() && targetQuoted.front() != '\"') {
228
0
        targetQuoted = cmStrCat('"', this->ReplaceValues->Target, '"');
229
0
      }
230
0
      return targetQuoted;
231
0
    }
232
0
    if (variable == "TARGET_UNQUOTED") {
233
0
      std::string unquoted = this->ReplaceValues->Target;
234
0
      std::string::size_type sz = unquoted.size();
235
0
      if (sz > 2 && unquoted.front() == '\"' && unquoted.back() == '\"') {
236
0
        unquoted = unquoted.substr(1, sz - 2);
237
0
      }
238
0
      return unquoted;
239
0
    }
240
0
    if (this->ReplaceValues->LanguageCompileFlags) {
241
0
      if (variable == "LANGUAGE_COMPILE_FLAGS") {
242
0
        return this->ReplaceValues->LanguageCompileFlags;
243
0
      }
244
0
    }
245
0
    if (this->ReplaceValues->Target) {
246
0
      if (variable == "TARGET") {
247
0
        return this->ReplaceValues->Target;
248
0
      }
249
0
    }
250
0
    if (variable == "TARGET_IMPLIB") {
251
0
      return this->TargetImpLib;
252
0
    }
253
0
    if (variable == "TARGET_VERSION_MAJOR") {
254
0
      if (this->ReplaceValues->TargetVersionMajor) {
255
0
        return this->ReplaceValues->TargetVersionMajor;
256
0
      }
257
0
      return "0";
258
0
    }
259
0
    if (variable == "TARGET_VERSION_MINOR") {
260
0
      if (this->ReplaceValues->TargetVersionMinor) {
261
0
        return this->ReplaceValues->TargetVersionMinor;
262
0
      }
263
0
      return "0";
264
0
    }
265
0
    if (this->ReplaceValues->Target) {
266
0
      if (variable == "TARGET_BASE") {
267
        // Strip the last extension off the target name.
268
0
        std::string targetBase = this->ReplaceValues->Target;
269
0
        std::string::size_type pos = targetBase.rfind('.');
270
0
        if (pos != std::string::npos) {
271
0
          return targetBase.substr(0, pos);
272
0
        }
273
0
        return targetBase;
274
0
      }
275
0
    }
276
0
  }
277
0
  if (variable == "TARGET_SONAME" || variable == "SONAME_FLAG" ||
278
0
      variable == "TARGET_INSTALLNAME_DIR") {
279
    // All these variables depend on TargetSOName
280
0
    if (this->ReplaceValues->TargetSOName) {
281
0
      if (variable == "TARGET_SONAME") {
282
0
        return this->ReplaceValues->TargetSOName;
283
0
      }
284
0
      if (variable == "SONAME_FLAG" && this->ReplaceValues->SONameFlag) {
285
0
        return this->ReplaceValues->SONameFlag;
286
0
      }
287
0
      if (this->ReplaceValues->TargetInstallNameDir &&
288
0
          variable == "TARGET_INSTALLNAME_DIR") {
289
0
        return this->ReplaceValues->TargetInstallNameDir;
290
0
      }
291
0
    }
292
0
    return "";
293
0
  }
294
0
  if (this->ReplaceValues->LinkLibraries) {
295
0
    if (variable == "LINK_LIBRARIES") {
296
0
      return this->ReplaceValues->LinkLibraries;
297
0
    }
298
0
  }
299
0
  if (this->ReplaceValues->Language) {
300
0
    if (variable == "LANGUAGE") {
301
0
      return this->ReplaceValues->Language;
302
0
    }
303
0
  }
304
0
  if (this->ReplaceValues->CMTargetName) {
305
0
    if (variable == "TARGET_NAME") {
306
0
      return this->ReplaceValues->CMTargetName;
307
0
    }
308
0
  }
309
0
  if (this->ReplaceValues->CMTargetType) {
310
0
    if (variable == "TARGET_TYPE") {
311
0
      return this->ReplaceValues->CMTargetType;
312
0
    }
313
0
  }
314
0
  if (this->ReplaceValues->Output) {
315
0
    if (variable == "OUTPUT") {
316
0
      return this->ReplaceValues->Output;
317
0
    }
318
0
  }
319
0
  if (this->ReplaceValues->FilePathWithOutput) {
320
0
    if (variable == "OUTPUT_STORE_TO_FILE") {
321
0
      return this->ReplaceValues->FilePathWithOutput;
322
0
    }
323
0
  }
324
0
  if (variable == "CMAKE_COMMAND") {
325
0
    return this->OutputConverter->ConvertToOutputFormat(
326
0
      cmSystemTools::GetCMakeCommand(), cmOutputConverter::SHELL);
327
0
  }
328
0
  if (variable == "ROLE") {
329
0
    if (this->ReplaceValues->Role) {
330
0
      return this->ReplaceValues->Role;
331
0
    }
332
0
    return "";
333
0
  }
334
0
  if (variable == "CONFIG") {
335
0
    if (this->ReplaceValues->Config) {
336
0
      return this->ReplaceValues->Config;
337
0
    }
338
0
    return "";
339
0
  }
340
341
0
  auto compIt = this->Compilers.find(variable);
342
343
0
  if (compIt != this->Compilers.end()) {
344
0
    std::string const& compilerPath =
345
0
      this->VariableMappings[cmStrCat("CMAKE_", compIt->second, "_COMPILER")];
346
0
    std::string ret = this->ConvertToOutputForExisting(compilerPath);
347
0
    std::string const& compilerArg1 = this->VariableMappings[cmStrCat(
348
0
      "CMAKE_", compIt->second, "_COMPILER_ARG1")];
349
0
    std::string const& compilerTarget = this->VariableMappings[cmStrCat(
350
0
      "CMAKE_", compIt->second, "_COMPILER_TARGET")];
351
0
    std::string const& compilerOptionTarget = this->VariableMappings[cmStrCat(
352
0
      "CMAKE_", compIt->second, "_COMPILE_OPTIONS_TARGET")];
353
0
    std::string const& compilerExternalToolchain =
354
0
      this->VariableMappings[cmStrCat("CMAKE_", compIt->second,
355
0
                                      "_COMPILER_EXTERNAL_TOOLCHAIN")];
356
0
    std::string const& compilerOptionExternalToolchain =
357
0
      this->VariableMappings[cmStrCat("CMAKE_", compIt->second,
358
0
                                      "_COMPILE_OPTIONS_EXTERNAL_TOOLCHAIN")];
359
0
    std::string const& compilerOptionSysroot = this->VariableMappings[cmStrCat(
360
0
      "CMAKE_", compIt->second, "_COMPILE_OPTIONS_SYSROOT")];
361
362
0
    if (compIt->second == this->ReplaceValues->Language &&
363
0
        this->ReplaceValues->Launcher) {
364
      // Add launcher as part of expansion so that it always appears
365
      // immediately before the command itself, regardless of whether the
366
      // overall rule template contains other content at the front.
367
0
      ret = cmStrCat(this->ReplaceValues->Launcher, ' ', std::move(ret));
368
0
    }
369
370
    // if there are required arguments to the compiler add it
371
    // to the compiler string
372
0
    if (!compilerArg1.empty()) {
373
0
      ret = cmStrCat(std::move(ret), ' ', compilerArg1);
374
0
    }
375
0
    if (!compilerTarget.empty() && !compilerOptionTarget.empty()) {
376
0
      ret =
377
0
        cmStrCat(std::move(ret), ' ', compilerOptionTarget, compilerTarget);
378
0
    }
379
0
    if (!compilerExternalToolchain.empty() &&
380
0
        !compilerOptionExternalToolchain.empty()) {
381
0
      ret = cmStrCat(std::move(ret), ' ', compilerOptionExternalToolchain,
382
0
                     this->OutputConverter->EscapeForShell(
383
0
                       compilerExternalToolchain, true));
384
0
    }
385
0
    std::string sysroot;
386
    // Some platforms may use separate sysroots for compiling and linking.
387
    // When the build step is link, pass the link sysroot instead.
388
0
    if (this->BuildStep == cmBuildStep::Link) {
389
0
      sysroot = this->LinkerSysroot;
390
0
    } else {
391
0
      sysroot = this->CompilerSysroot;
392
0
    }
393
0
    if (!sysroot.empty() && !compilerOptionSysroot.empty()) {
394
0
      ret = cmStrCat(std::move(ret), ' ', compilerOptionSysroot,
395
0
                     this->OutputConverter->EscapeForShell(sysroot, true));
396
0
    }
397
0
    return ret;
398
0
  }
399
400
0
  auto mapIt = this->VariableMappings.find(variable);
401
0
  if (mapIt != this->VariableMappings.end()) {
402
0
    if (variable.find("_FLAG") == std::string::npos) {
403
0
      return this->ConvertToOutputForExisting(mapIt->second);
404
0
    }
405
0
    return mapIt->second;
406
0
  }
407
0
  return variable;
408
0
}
409
410
void cmRulePlaceholderExpander::ExpandRuleVariables(
411
  cmOutputConverter* outputConverter, std::string& s,
412
  RuleVariables const& replaceValues)
413
0
{
414
0
  this->OutputConverter = outputConverter;
415
0
  this->ReplaceValues = &replaceValues;
416
417
0
  this->ExpandVariables(s);
418
0
}