Coverage Report

Created: 2026-04-29 07:01

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