Coverage Report

Created: 2026-03-12 06:35

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->RustSources) {
160
0
    if (variable == "RUST_SOURCES") {
161
0
      return this->ReplaceValues->RustSources;
162
0
    }
163
0
  }
164
0
  if (this->ReplaceValues->RustObjectDeps) {
165
0
    if (variable == "RUST_OBJECT_DEPS") {
166
0
      return this->ReplaceValues->RustObjectDeps;
167
0
    }
168
0
  }
169
0
  if (this->ReplaceValues->TargetPDB) {
170
0
    if (variable == "TARGET_PDB") {
171
0
      return this->ReplaceValues->TargetPDB;
172
0
    }
173
0
  }
174
0
  if (this->ReplaceValues->TargetCompilePDB) {
175
0
    if (variable == "TARGET_COMPILE_PDB") {
176
0
      return this->ReplaceValues->TargetCompilePDB;
177
0
    }
178
0
  }
179
0
  if (this->ReplaceValues->DependencyFile) {
180
0
    if (variable == "DEP_FILE") {
181
0
      return this->ReplaceValues->DependencyFile;
182
0
    }
183
0
  }
184
0
  if (this->ReplaceValues->DependencyTarget) {
185
0
    if (variable == "DEP_TARGET") {
186
0
      return this->ReplaceValues->DependencyTarget;
187
0
    }
188
0
  }
189
0
  if (this->ReplaceValues->Fatbinary) {
190
0
    if (variable == "FATBINARY") {
191
0
      return this->ReplaceValues->Fatbinary;
192
0
    }
193
0
  }
194
0
  if (this->ReplaceValues->RegisterFile) {
195
0
    if (variable == "REGISTER_FILE") {
196
0
      return this->ReplaceValues->RegisterFile;
197
0
    }
198
0
  }
199
200
0
  if (this->ReplaceValues->Target) {
201
0
    if (variable == "TARGET_QUOTED") {
202
0
      std::string targetQuoted = this->ReplaceValues->Target;
203
0
      if (!targetQuoted.empty() && targetQuoted.front() != '\"') {
204
0
        targetQuoted = '\"';
205
0
        targetQuoted += this->ReplaceValues->Target;
206
0
        targetQuoted += '\"';
207
0
      }
208
0
      return targetQuoted;
209
0
    }
210
0
    if (variable == "TARGET_UNQUOTED") {
211
0
      std::string unquoted = this->ReplaceValues->Target;
212
0
      std::string::size_type sz = unquoted.size();
213
0
      if (sz > 2 && unquoted.front() == '\"' && unquoted.back() == '\"') {
214
0
        unquoted = unquoted.substr(1, sz - 2);
215
0
      }
216
0
      return unquoted;
217
0
    }
218
0
    if (this->ReplaceValues->LanguageCompileFlags) {
219
0
      if (variable == "LANGUAGE_COMPILE_FLAGS") {
220
0
        return this->ReplaceValues->LanguageCompileFlags;
221
0
      }
222
0
    }
223
0
    if (this->ReplaceValues->Target) {
224
0
      if (variable == "TARGET") {
225
0
        return this->ReplaceValues->Target;
226
0
      }
227
0
    }
228
0
    if (variable == "TARGET_IMPLIB") {
229
0
      return this->TargetImpLib;
230
0
    }
231
0
    if (variable == "TARGET_VERSION_MAJOR") {
232
0
      if (this->ReplaceValues->TargetVersionMajor) {
233
0
        return this->ReplaceValues->TargetVersionMajor;
234
0
      }
235
0
      return "0";
236
0
    }
237
0
    if (variable == "TARGET_VERSION_MINOR") {
238
0
      if (this->ReplaceValues->TargetVersionMinor) {
239
0
        return this->ReplaceValues->TargetVersionMinor;
240
0
      }
241
0
      return "0";
242
0
    }
243
0
    if (this->ReplaceValues->Target) {
244
0
      if (variable == "TARGET_BASE") {
245
        // Strip the last extension off the target name.
246
0
        std::string targetBase = this->ReplaceValues->Target;
247
0
        std::string::size_type pos = targetBase.rfind('.');
248
0
        if (pos != std::string::npos) {
249
0
          return targetBase.substr(0, pos);
250
0
        }
251
0
        return targetBase;
252
0
      }
253
0
    }
254
0
  }
255
0
  if (variable == "TARGET_SONAME" || variable == "SONAME_FLAG" ||
256
0
      variable == "TARGET_INSTALLNAME_DIR") {
257
    // All these variables depend on TargetSOName
258
0
    if (this->ReplaceValues->TargetSOName) {
259
0
      if (variable == "TARGET_SONAME") {
260
0
        return this->ReplaceValues->TargetSOName;
261
0
      }
262
0
      if (variable == "SONAME_FLAG" && this->ReplaceValues->SONameFlag) {
263
0
        return this->ReplaceValues->SONameFlag;
264
0
      }
265
0
      if (this->ReplaceValues->TargetInstallNameDir &&
266
0
          variable == "TARGET_INSTALLNAME_DIR") {
267
0
        return this->ReplaceValues->TargetInstallNameDir;
268
0
      }
269
0
    }
270
0
    return "";
271
0
  }
272
0
  if (this->ReplaceValues->LinkLibraries) {
273
0
    if (variable == "LINK_LIBRARIES") {
274
0
      return this->ReplaceValues->LinkLibraries;
275
0
    }
276
0
  }
277
0
  if (this->ReplaceValues->Language) {
278
0
    if (variable == "LANGUAGE") {
279
0
      return this->ReplaceValues->Language;
280
0
    }
281
0
  }
282
0
  if (this->ReplaceValues->CMTargetName) {
283
0
    if (variable == "TARGET_NAME") {
284
0
      return this->ReplaceValues->CMTargetName;
285
0
    }
286
0
  }
287
0
  if (this->ReplaceValues->CMTargetType) {
288
0
    if (variable == "TARGET_TYPE") {
289
0
      return this->ReplaceValues->CMTargetType;
290
0
    }
291
0
  }
292
0
  if (this->ReplaceValues->Output) {
293
0
    if (variable == "OUTPUT") {
294
0
      return this->ReplaceValues->Output;
295
0
    }
296
0
  }
297
0
  if (variable == "CMAKE_COMMAND") {
298
0
    return this->OutputConverter->ConvertToOutputFormat(
299
0
      cmSystemTools::GetCMakeCommand(), cmOutputConverter::SHELL);
300
0
  }
301
0
  if (variable == "ROLE") {
302
0
    if (this->ReplaceValues->Role) {
303
0
      return this->ReplaceValues->Role;
304
0
    }
305
0
    return "";
306
0
  }
307
0
  if (variable == "CONFIG") {
308
0
    if (this->ReplaceValues->Config) {
309
0
      return this->ReplaceValues->Config;
310
0
    }
311
0
    return "";
312
0
  }
313
314
0
  auto compIt = this->Compilers.find(variable);
315
316
0
  if (compIt != this->Compilers.end()) {
317
0
    std::string const& compilerPath =
318
0
      this->VariableMappings["CMAKE_" + compIt->second + "_COMPILER"];
319
0
    std::string ret = this->ConvertToOutputForExisting(compilerPath);
320
0
    std::string const& compilerArg1 =
321
0
      this->VariableMappings["CMAKE_" + compIt->second + "_COMPILER_ARG1"];
322
0
    std::string const& compilerTarget =
323
0
      this->VariableMappings["CMAKE_" + compIt->second + "_COMPILER_TARGET"];
324
0
    std::string const& compilerOptionTarget =
325
0
      this->VariableMappings["CMAKE_" + compIt->second +
326
0
                             "_COMPILE_OPTIONS_TARGET"];
327
0
    std::string const& compilerExternalToolchain =
328
0
      this->VariableMappings["CMAKE_" + compIt->second +
329
0
                             "_COMPILER_EXTERNAL_TOOLCHAIN"];
330
0
    std::string const& compilerOptionExternalToolchain =
331
0
      this->VariableMappings["CMAKE_" + compIt->second +
332
0
                             "_COMPILE_OPTIONS_EXTERNAL_TOOLCHAIN"];
333
0
    std::string const& compilerOptionSysroot =
334
0
      this->VariableMappings["CMAKE_" + compIt->second +
335
0
                             "_COMPILE_OPTIONS_SYSROOT"];
336
337
0
    if (compIt->second == this->ReplaceValues->Language &&
338
0
        this->ReplaceValues->Launcher) {
339
      // Add launcher as part of expansion so that it always appears
340
      // immediately before the command itself, regardless of whether the
341
      // overall rule template contains other content at the front.
342
0
      ret = cmStrCat(this->ReplaceValues->Launcher, ' ', ret);
343
0
    }
344
345
    // if there are required arguments to the compiler add it
346
    // to the compiler string
347
0
    if (!compilerArg1.empty()) {
348
0
      ret += " ";
349
0
      ret += compilerArg1;
350
0
    }
351
0
    if (!compilerTarget.empty() && !compilerOptionTarget.empty()) {
352
0
      ret += " ";
353
0
      ret += compilerOptionTarget;
354
0
      ret += compilerTarget;
355
0
    }
356
0
    if (!compilerExternalToolchain.empty() &&
357
0
        !compilerOptionExternalToolchain.empty()) {
358
0
      ret += " ";
359
0
      ret += compilerOptionExternalToolchain;
360
0
      ret +=
361
0
        this->OutputConverter->EscapeForShell(compilerExternalToolchain, true);
362
0
    }
363
0
    std::string sysroot;
364
    // Some platforms may use separate sysroots for compiling and linking.
365
    // When the build step is link, pass the link sysroot instead.
366
0
    if (this->BuildStep == cmBuildStep::Link) {
367
0
      sysroot = this->LinkerSysroot;
368
0
    } else {
369
0
      sysroot = this->CompilerSysroot;
370
0
    }
371
0
    if (!sysroot.empty() && !compilerOptionSysroot.empty()) {
372
0
      ret += " ";
373
0
      ret += compilerOptionSysroot;
374
0
      ret += this->OutputConverter->EscapeForShell(sysroot, true);
375
0
    }
376
0
    return ret;
377
0
  }
378
379
0
  auto mapIt = this->VariableMappings.find(variable);
380
0
  if (mapIt != this->VariableMappings.end()) {
381
0
    if (variable.find("_FLAG") == std::string::npos) {
382
0
      return this->ConvertToOutputForExisting(mapIt->second);
383
0
    }
384
0
    return mapIt->second;
385
0
  }
386
0
  return variable;
387
0
}
388
389
void cmRulePlaceholderExpander::ExpandRuleVariables(
390
  cmOutputConverter* outputConverter, std::string& s,
391
  RuleVariables const& replaceValues)
392
0
{
393
0
  this->OutputConverter = outputConverter;
394
0
  this->ReplaceValues = &replaceValues;
395
396
0
  this->ExpandVariables(s);
397
0
}