Coverage Report

Created: 2026-06-15 07:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmSetPropertyCommand.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 "cmSetPropertyCommand.h"
4
5
#include <set>
6
#include <sstream>
7
#include <unordered_set>
8
9
#include <cm/optional>
10
#include <cm/string_view>
11
#include <cmext/string_view>
12
13
#include "cmDiagnostics.h"
14
#include "cmExecutionStatus.h"
15
#include "cmFileSet.h"
16
#include "cmGlobalGenerator.h"
17
#include "cmInstalledFile.h"
18
#include "cmListFileCache.h"
19
#include "cmMakefile.h"
20
#include "cmPolicies.h"
21
#include "cmProperty.h"
22
#include "cmRange.h"
23
#include "cmSourceFile.h"
24
#include "cmSourceFileLocation.h"
25
#include "cmState.h"
26
#include "cmStringAlgorithms.h"
27
#include "cmSystemTools.h"
28
#include "cmTarget.h"
29
#include "cmTest.h"
30
#include "cmValue.h"
31
#include "cmake.h"
32
33
namespace {
34
bool HandleGlobalMode(cmExecutionStatus& status,
35
                      std::set<std::string> const& names,
36
                      std::string const& propertyName,
37
                      std::string const& propertyValue, bool appendAsString,
38
                      bool appendMode, bool remove);
39
bool HandleDirectoryMode(cmExecutionStatus& status,
40
                         std::set<std::string> const& names,
41
                         std::string const& propertyName,
42
                         std::string const& propertyValue, bool appendAsString,
43
                         bool appendMode, bool remove);
44
bool HandleTargetMode(cmExecutionStatus& status,
45
                      std::set<std::string> const& names,
46
                      std::string const& propertyName,
47
                      std::string const& propertyValue, bool appendAsString,
48
                      bool appendMode, bool remove);
49
bool HandleTarget(cmTarget* target, cmMakefile& makefile,
50
                  std::string const& propertyName,
51
                  std::string const& propertyValue, bool appendAsString,
52
                  bool appendMode, bool remove);
53
bool HandleFileSetMode(cmExecutionStatus& status,
54
                       std::set<std::string> const& names,
55
                       std::string const& propertyName,
56
                       std::string const& propertyValue, bool appendAsString,
57
                       bool appendMode, bool remove, cmTarget* target);
58
bool HandleFileSet(cmFileSet* fileSet, std::string const& propertyName,
59
                   std::string const& propertyValue, bool appendAsString,
60
                   bool appendMode, bool remove);
61
bool HandleSourceMode(cmExecutionStatus& status,
62
                      std::set<std::string> const& names,
63
                      std::string const& propertyName,
64
                      std::string const& propertyValue, bool appendAsString,
65
                      bool appendMode, bool remove,
66
                      std::vector<cmMakefile*> const& directory_makefiles,
67
                      bool source_file_paths_should_be_absolute);
68
bool HandleSource(cmSourceFile* sf, std::string const& propertyName,
69
                  std::string const& propertyValue, bool appendAsString,
70
                  bool appendMode, bool remove);
71
bool HandleTestMode(cmExecutionStatus& status, std::set<std::string>& names,
72
                    std::string const& propertyName,
73
                    std::string const& propertyValue, bool appendAsString,
74
                    bool appendMode, bool remove,
75
                    cmMakefile* test_directory_makefile);
76
bool HandleTest(cmTest* test, std::string const& propertyName,
77
                std::string const& propertyValue, bool appendAsString,
78
                bool appendMode, bool remove);
79
bool HandleCacheMode(cmExecutionStatus& status,
80
                     std::set<std::string> const& names,
81
                     std::string const& propertyName,
82
                     std::string const& propertyValue, bool appendAsString,
83
                     bool appendMode, bool remove);
84
bool HandleCacheEntry(std::string const& cacheKey, cmMakefile const& makefile,
85
                      std::string const& propertyName,
86
                      std::string const& propertyValue, bool appendAsString,
87
                      bool appendMode, bool remove);
88
bool HandleInstallMode(cmExecutionStatus& status,
89
                       std::set<std::string> const& names,
90
                       std::string const& propertyName,
91
                       std::string const& propertyValue, bool appendAsString,
92
                       bool appendMode, bool remove);
93
bool HandleInstall(cmInstalledFile* file, cmMakefile& makefile,
94
                   std::string const& propertyName,
95
                   std::string const& propertyValue, bool appendAsString,
96
                   bool appendMode, bool remove);
97
}
98
99
namespace SetPropertyCommand {
100
bool HandleFileSetTargetScopes(cmExecutionStatus& status,
101
                               std::string& file_set_target_name,
102
                               cmTarget*& file_set_target)
103
0
{
104
0
  file_set_target = status.GetMakefile().FindTargetToUse(file_set_target_name);
105
0
  if (!file_set_target) {
106
0
    status.SetError(
107
0
      cmStrCat("given non-existent TARGET ", file_set_target_name));
108
0
    return false;
109
0
  }
110
0
  return true;
111
0
}
112
113
bool HandleFileSetTargetScopeValidation(cmExecutionStatus& status,
114
                                        bool file_set_target_option_enabled,
115
                                        std::string& file_set_target_name)
116
0
{
117
0
  if (!file_set_target_option_enabled) {
118
0
    status.SetError("required TARGET option is missing");
119
0
    return false;
120
0
  }
121
122
  // Validate file set target scopes.
123
0
  if (file_set_target_name.empty()) {
124
0
    status.SetError("called with incorrect number of arguments "
125
0
                    "no value provided to the TARGET option");
126
0
    return false;
127
0
  }
128
0
  return true;
129
0
}
130
131
bool HandleAndValidateFileSetTargetScopes(cmExecutionStatus& status,
132
                                          bool file_set_target_option_enabled,
133
                                          std::string& file_set_target_name,
134
                                          cmTarget*& file_set_target)
135
0
{
136
0
  bool scope_options_valid =
137
0
    SetPropertyCommand::HandleFileSetTargetScopeValidation(
138
0
      status, file_set_target_option_enabled, file_set_target_name);
139
0
  if (!scope_options_valid) {
140
0
    return false;
141
0
  }
142
143
0
  scope_options_valid = SetPropertyCommand::HandleFileSetTargetScopes(
144
0
    status, file_set_target_name, file_set_target);
145
0
  return scope_options_valid;
146
0
}
147
148
bool HandleSourceFileDirectoryScopes(
149
  cmExecutionStatus& status, std::vector<std::string>& source_file_directories,
150
  std::vector<std::string>& source_file_target_directories,
151
  std::vector<cmMakefile*>& directory_makefiles)
152
0
{
153
0
  std::unordered_set<cmMakefile*> directory_makefiles_set;
154
155
0
  cmMakefile* current_dir_mf = &status.GetMakefile();
156
0
  if (!source_file_directories.empty()) {
157
0
    for (std::string const& dir_path : source_file_directories) {
158
0
      std::string const absolute_dir_path = cmSystemTools::CollapseFullPath(
159
0
        dir_path, current_dir_mf->GetCurrentSourceDirectory());
160
0
      cmMakefile* dir_mf =
161
0
        status.GetMakefile().GetGlobalGenerator()->FindMakefile(
162
0
          absolute_dir_path);
163
0
      if (!dir_mf) {
164
0
        status.SetError(cmStrCat("given non-existent DIRECTORY ", dir_path));
165
0
        return false;
166
0
      }
167
0
      if (directory_makefiles_set.find(dir_mf) ==
168
0
          directory_makefiles_set.end()) {
169
0
        directory_makefiles.push_back(dir_mf);
170
0
        directory_makefiles_set.insert(dir_mf);
171
0
      }
172
0
    }
173
0
  }
174
175
0
  if (!source_file_target_directories.empty()) {
176
0
    for (std::string const& target_name : source_file_target_directories) {
177
0
      cmTarget* target = current_dir_mf->FindTargetToUse(target_name);
178
0
      if (!target) {
179
0
        status.SetError(cmStrCat(
180
0
          "given non-existent target for TARGET_DIRECTORY ", target_name));
181
0
        return false;
182
0
      }
183
0
      cmValue target_source_dir = target->GetProperty("BINARY_DIR");
184
0
      cmMakefile* target_dir_mf =
185
0
        status.GetMakefile().GetGlobalGenerator()->FindMakefile(
186
0
          *target_source_dir);
187
188
0
      if (directory_makefiles_set.find(target_dir_mf) ==
189
0
          directory_makefiles_set.end()) {
190
0
        directory_makefiles.push_back(target_dir_mf);
191
0
        directory_makefiles_set.insert(target_dir_mf);
192
0
      }
193
0
    }
194
0
  }
195
196
0
  if (source_file_directories.empty() &&
197
0
      source_file_target_directories.empty()) {
198
0
    directory_makefiles.push_back(current_dir_mf);
199
0
  }
200
0
  return true;
201
0
}
202
203
bool HandleSourceFileDirectoryScopeValidation(
204
  cmExecutionStatus& status, bool source_file_directory_option_enabled,
205
  bool source_file_target_option_enabled,
206
  std::vector<std::string>& source_file_directories,
207
  std::vector<std::string>& source_file_target_directories)
208
0
{
209
  // Validate source file directory scopes.
210
0
  if (source_file_directory_option_enabled &&
211
0
      source_file_directories.empty()) {
212
0
    std::string errors = "called with incorrect number of arguments "
213
0
                         "no value provided to the DIRECTORY option";
214
0
    status.SetError(errors);
215
0
    return false;
216
0
  }
217
0
  if (source_file_target_option_enabled &&
218
0
      source_file_target_directories.empty()) {
219
0
    std::string errors = "called with incorrect number of arguments "
220
0
                         "no value provided to the TARGET_DIRECTORY option";
221
0
    status.SetError(errors);
222
0
    return false;
223
0
  }
224
0
  return true;
225
0
}
226
227
bool HandleAndValidateSourceFileDirectoryScopes(
228
  cmExecutionStatus& status, bool source_file_directory_option_enabled,
229
  bool source_file_target_option_enabled,
230
  std::vector<std::string>& source_file_directories,
231
  std::vector<std::string>& source_file_target_directories,
232
  std::vector<cmMakefile*>& source_file_directory_makefiles)
233
0
{
234
0
  bool scope_options_valid =
235
0
    SetPropertyCommand::HandleSourceFileDirectoryScopeValidation(
236
0
      status, source_file_directory_option_enabled,
237
0
      source_file_target_option_enabled, source_file_directories,
238
0
      source_file_target_directories);
239
0
  if (!scope_options_valid) {
240
0
    return false;
241
0
  }
242
243
0
  scope_options_valid = SetPropertyCommand::HandleSourceFileDirectoryScopes(
244
0
    status, source_file_directories, source_file_target_directories,
245
0
    source_file_directory_makefiles);
246
0
  return scope_options_valid;
247
0
}
248
249
bool HandleTestDirectoryScopes(cmExecutionStatus& status,
250
                               std::string& test_directory,
251
                               cmMakefile*& directory_makefile)
252
0
{
253
0
  cmMakefile* current_dir_mf = &status.GetMakefile();
254
0
  if (!test_directory.empty()) {
255
0
    std::string const absolute_dir_path = cmSystemTools::CollapseFullPath(
256
0
      test_directory, current_dir_mf->GetCurrentSourceDirectory());
257
0
    cmMakefile* dir_mf =
258
0
      status.GetMakefile().GetGlobalGenerator()->FindMakefile(
259
0
        absolute_dir_path);
260
0
    if (!dir_mf) {
261
0
      status.SetError(
262
0
        cmStrCat("given non-existent DIRECTORY ", test_directory));
263
0
      return false;
264
0
    }
265
0
    directory_makefile = dir_mf;
266
0
  } else {
267
0
    directory_makefile = current_dir_mf;
268
0
  }
269
0
  return true;
270
0
}
271
272
bool HandleTestDirectoryScopeValidation(cmExecutionStatus& status,
273
                                        bool test_directory_option_enabled,
274
                                        std::string& test_directory)
275
0
{
276
  // Validate source file directory scopes.
277
0
  if (test_directory_option_enabled && test_directory.empty()) {
278
0
    std::string errors = "called with incorrect number of arguments "
279
0
                         "no value provided to the DIRECTORY option";
280
0
    status.SetError(errors);
281
0
    return false;
282
0
  }
283
0
  return true;
284
0
}
285
286
bool HandleAndValidateTestDirectoryScopes(cmExecutionStatus& status,
287
                                          bool test_directory_option_enabled,
288
                                          std::string& test_directory,
289
                                          cmMakefile*& test_directory_makefile)
290
0
{
291
0
  bool scope_options_valid =
292
0
    SetPropertyCommand::HandleTestDirectoryScopeValidation(
293
0
      status, test_directory_option_enabled, test_directory);
294
0
  if (!scope_options_valid) {
295
0
    return false;
296
0
  }
297
298
0
  scope_options_valid = SetPropertyCommand::HandleTestDirectoryScopes(
299
0
    status, test_directory, test_directory_makefile);
300
0
  return scope_options_valid;
301
0
}
302
303
std::string MakeSourceFilePathAbsoluteIfNeeded(
304
  cmExecutionStatus& status, std::string const& source_file_path,
305
  bool const needed)
306
0
{
307
0
  if (!needed) {
308
0
    return source_file_path;
309
0
  }
310
0
  std::string absolute_file_path = cmSystemTools::CollapseFullPath(
311
0
    source_file_path, status.GetMakefile().GetCurrentSourceDirectory());
312
0
  return absolute_file_path;
313
0
}
314
315
void MakeSourceFilePathsAbsoluteIfNeeded(
316
  cmExecutionStatus& status,
317
  std::vector<std::string>& source_files_absolute_paths,
318
  std::vector<std::string>::const_iterator files_it_begin,
319
  std::vector<std::string>::const_iterator files_it_end, bool const needed)
320
0
{
321
322
  // Make the file paths absolute, so that relative source file paths are
323
  // picked up relative to the command calling site, regardless of the
324
  // directory scope.
325
0
  std::vector<std::string>::difference_type num_files =
326
0
    files_it_end - files_it_begin;
327
0
  source_files_absolute_paths.reserve(num_files);
328
329
0
  if (!needed) {
330
0
    source_files_absolute_paths.assign(files_it_begin, files_it_end);
331
0
    return;
332
0
  }
333
334
0
  for (; files_it_begin != files_it_end; ++files_it_begin) {
335
0
    std::string const absolute_file_path =
336
0
      MakeSourceFilePathAbsoluteIfNeeded(status, *files_it_begin, true);
337
0
    source_files_absolute_paths.push_back(absolute_file_path);
338
0
  }
339
0
}
340
341
bool HandleAndValidateSourceFilePropertyGENERATED(
342
  cmSourceFile* sf, std::string const& propertyValue, PropertyOp op)
343
0
{
344
0
  auto const& mf = *sf->GetLocation().GetMakefile();
345
346
0
  auto isProblematic = [&mf, &propertyValue,
347
0
                        op](cm::string_view policy) -> bool {
348
0
    if (!cmIsOn(propertyValue) && !cmIsOff(propertyValue)) {
349
0
      mf.IssueDiagnostic(
350
0
        cmDiagnostics::CMD_AUTHOR,
351
0
        cmStrCat("Policy ", policy,
352
0
                 " is set to NEW and the following non-boolean value given "
353
0
                 "for property 'GENERATED' is therefore not allowed:\n",
354
0
                 propertyValue, "\nReplace it with a boolean value!\n"));
355
0
      return true;
356
0
    }
357
0
    if (cmIsOff(propertyValue)) {
358
0
      mf.IssueDiagnostic(
359
0
        cmDiagnostics::CMD_AUTHOR,
360
0
        cmStrCat("Unsetting the 'GENERATED' property is not allowed under ",
361
0
                 policy, "!\n"));
362
0
      return true;
363
0
    }
364
0
    if (op == PropertyOp::Append || op == PropertyOp::AppendAsString) {
365
0
      mf.IssueDiagnostic(
366
0
        cmDiagnostics::CMD_AUTHOR,
367
0
        cmStrCat(
368
0
          "Policy ", policy,
369
0
          " is set to NEW and appending to the 'GENERATED' property is "
370
0
          "therefore not allowed. Only setting it to \"1\" is allowed!\n"));
371
0
      return true;
372
0
    }
373
0
    return false;
374
0
  };
375
376
0
  auto const cmp0163PolicyStatus = mf.GetPolicyStatus(cmPolicies::CMP0163);
377
0
  bool const cmp0163PolicyNEW = cmp0163PolicyStatus != cmPolicies::OLD &&
378
0
    cmp0163PolicyStatus != cmPolicies::WARN;
379
0
  if (cmp0163PolicyNEW) {
380
0
    if (!isProblematic("CMP0163")) {
381
0
      sf->MarkAsGenerated();
382
0
    }
383
0
    return true;
384
0
  }
385
386
0
  auto const cmp0118PolicyStatus = mf.GetPolicyStatus(cmPolicies::CMP0118);
387
0
  bool const cmp0118PolicyWARN = cmp0118PolicyStatus == cmPolicies::WARN;
388
0
  bool const cmp0118PolicyNEW =
389
0
    cmp0118PolicyStatus != cmPolicies::OLD && !cmp0118PolicyWARN;
390
391
0
  if (cmp0118PolicyNEW) {
392
0
    if (!isProblematic("CMP0118")) {
393
0
      sf->MarkAsGenerated();
394
0
    }
395
0
    return true;
396
0
  }
397
398
0
  if (cmp0118PolicyWARN) {
399
0
    if (!cmIsOn(propertyValue) && !cmIsOff(propertyValue)) {
400
0
      mf.IssuePolicyWarning(
401
0
        cmPolicies::CMP0118, {},
402
0
        cmStrCat("Attempt to set property 'GENERATED' with the following "
403
0
                 "non-boolean value (which will be interpreted as \"0\"):\n"_s,
404
0
                 propertyValue,
405
0
                 "\nThat exact value will not be retrievable. A value of "
406
0
                 "\"0\" will be returned instead.\n"
407
0
                 "This will be an error under policy CMP0118."_s));
408
0
    }
409
0
    if (cmIsOff(propertyValue)) {
410
0
      mf.IssuePolicyWarning(cmPolicies::CMP0118, {},
411
0
                            "Unsetting property 'GENERATED' "
412
0
                            "will not be allowed under policy CMP0118!"_s);
413
0
    }
414
0
    if (op == PropertyOp::Append || op == PropertyOp::AppendAsString) {
415
0
      mf.IssuePolicyWarning(cmPolicies::CMP0118, {},
416
0
                            "Appending to property 'GENERATED' "
417
0
                            "will not be allowed under policy CMP0118!"_s);
418
0
    }
419
0
  }
420
421
  // Set property the traditional way.
422
0
  switch (op) {
423
0
    case PropertyOp::Append:
424
0
      sf->AppendProperty("GENERATED", propertyValue, false);
425
0
      break;
426
0
    case PropertyOp::AppendAsString:
427
0
      sf->AppendProperty("GENERATED", propertyValue, true);
428
0
      break;
429
0
    case PropertyOp::Remove:
430
0
      sf->RemoveProperty("GENERATED");
431
0
      break;
432
0
    case PropertyOp::Set:
433
0
      sf->SetProperty("GENERATED", propertyValue);
434
0
      break;
435
0
  }
436
0
  return true;
437
0
}
438
439
} // END: namespace SetPropertyCommand
440
441
bool cmSetPropertyCommand(std::vector<std::string> const& args,
442
                          cmExecutionStatus& status)
443
0
{
444
0
  if (args.size() < 2) {
445
0
    status.SetError("called with incorrect number of arguments");
446
0
    return false;
447
0
  }
448
449
  // Get the scope on which to set the property.
450
0
  std::string const& scopeName = args.front();
451
0
  cmProperty::ScopeType scope;
452
0
  if (scopeName == "GLOBAL") {
453
0
    scope = cmProperty::GLOBAL;
454
0
  } else if (scopeName == "DIRECTORY") {
455
0
    scope = cmProperty::DIRECTORY;
456
0
  } else if (scopeName == "TARGET") {
457
0
    scope = cmProperty::TARGET;
458
0
  } else if (scopeName == "FILE_SET") {
459
0
    scope = cmProperty::FILE_SET;
460
0
  } else if (scopeName == "SOURCE") {
461
0
    scope = cmProperty::SOURCE_FILE;
462
0
  } else if (scopeName == "TEST") {
463
0
    scope = cmProperty::TEST;
464
0
  } else if (scopeName == "CACHE") {
465
0
    scope = cmProperty::CACHE;
466
0
  } else if (scopeName == "INSTALL") {
467
0
    scope = cmProperty::INSTALL;
468
0
  } else {
469
0
    status.SetError(cmStrCat("given invalid scope ", scopeName,
470
0
                             ".  Valid scopes are GLOBAL, DIRECTORY, TARGET, "
471
0
                             "FILE_SET, SOURCE, TEST, CACHE, INSTALL."));
472
0
    return false;
473
0
  }
474
475
0
  bool appendAsString = false;
476
0
  bool appendMode = false;
477
0
  bool remove = true;
478
0
  std::set<std::string> names;
479
0
  std::string propertyName;
480
0
  std::string propertyValue;
481
482
0
  std::string file_set_target_name;
483
0
  bool file_set_target_option_enabled = false;
484
485
0
  std::vector<std::string> source_file_directories;
486
0
  std::vector<std::string> source_file_target_directories;
487
0
  bool source_file_directory_option_enabled = false;
488
0
  bool source_file_target_option_enabled = false;
489
490
0
  std::string test_directory;
491
0
  bool test_directory_option_enabled = false;
492
493
  // Parse the rest of the arguments up to the values.
494
0
  enum Doing
495
0
  {
496
0
    DoingNone,
497
0
    DoingNames,
498
0
    DoingProperty,
499
0
    DoingValues,
500
0
    DoingFileSetTarget,
501
0
    DoingSourceDirectory,
502
0
    DoingSourceTargetDirectory,
503
0
    DoingTestDirectory,
504
0
  };
505
0
  Doing doing = DoingNames;
506
0
  char const* sep = "";
507
0
  for (std::string const& arg : cmMakeRange(args).advance(1)) {
508
0
    if (arg == "PROPERTY") {
509
0
      doing = DoingProperty;
510
0
    } else if (arg == "APPEND") {
511
0
      doing = DoingNone;
512
0
      appendMode = true;
513
0
      remove = false;
514
0
      appendAsString = false;
515
0
    } else if (arg == "APPEND_STRING") {
516
0
      doing = DoingNone;
517
0
      appendMode = true;
518
0
      remove = false;
519
0
      appendAsString = true;
520
0
    } else if (doing != DoingProperty && doing != DoingValues &&
521
0
               scope == cmProperty::FILE_SET && arg == "TARGET") {
522
0
      doing = DoingFileSetTarget;
523
0
      file_set_target_option_enabled = true;
524
0
    } else if (doing != DoingProperty && doing != DoingValues &&
525
0
               scope == cmProperty::SOURCE_FILE && arg == "DIRECTORY") {
526
0
      doing = DoingSourceDirectory;
527
0
      source_file_directory_option_enabled = true;
528
0
    } else if (doing != DoingProperty && doing != DoingValues &&
529
0
               scope == cmProperty::SOURCE_FILE && arg == "TARGET_DIRECTORY") {
530
0
      doing = DoingSourceTargetDirectory;
531
0
      source_file_target_option_enabled = true;
532
0
    } else if (doing != DoingProperty && doing != DoingValues &&
533
0
               scope == cmProperty::TEST && arg == "DIRECTORY") {
534
0
      doing = DoingTestDirectory;
535
0
      test_directory_option_enabled = true;
536
0
    } else if (doing == DoingNames) {
537
0
      names.insert(arg);
538
0
    } else if (doing == DoingFileSetTarget) {
539
0
      file_set_target_name = arg;
540
0
      file_set_target_option_enabled = true;
541
0
      doing = DoingNone;
542
0
    } else if (doing == DoingSourceDirectory) {
543
0
      source_file_directories.push_back(arg);
544
0
    } else if (doing == DoingSourceTargetDirectory) {
545
0
      source_file_target_directories.push_back(arg);
546
0
    } else if (doing == DoingTestDirectory) {
547
0
      test_directory = arg;
548
0
      doing = DoingNone;
549
0
    } else if (doing == DoingProperty) {
550
0
      propertyName = arg;
551
0
      doing = DoingValues;
552
0
    } else if (doing == DoingValues) {
553
0
      propertyValue += sep;
554
0
      sep = ";";
555
0
      propertyValue += arg;
556
0
      remove = false;
557
0
    } else {
558
0
      status.SetError(cmStrCat("given invalid argument \"", arg, "\"."));
559
0
      return false;
560
0
    }
561
0
  }
562
563
  // Make sure a property name was found.
564
0
  if (propertyName.empty()) {
565
0
    status.SetError("not given a PROPERTY <name> argument.");
566
0
    return false;
567
0
  }
568
569
  // Dispatch property setting.
570
0
  switch (scope) {
571
0
    case cmProperty::GLOBAL:
572
0
      return HandleGlobalMode(status, names, propertyName, propertyValue,
573
0
                              appendAsString, appendMode, remove);
574
0
    case cmProperty::DIRECTORY:
575
0
      return HandleDirectoryMode(status, names, propertyName, propertyValue,
576
0
                                 appendAsString, appendMode, remove);
577
0
    case cmProperty::TARGET:
578
0
      return HandleTargetMode(status, names, propertyName, propertyValue,
579
0
                              appendAsString, appendMode, remove);
580
0
    case cmProperty::FILE_SET: {
581
0
      cmTarget* file_set_target;
582
0
      if (!SetPropertyCommand::HandleAndValidateFileSetTargetScopes(
583
0
            status, file_set_target_option_enabled, file_set_target_name,
584
0
            file_set_target)) {
585
0
        return false;
586
0
      }
587
0
      return HandleFileSetMode(status, names, propertyName, propertyValue,
588
0
                               appendAsString, appendMode, remove,
589
0
                               file_set_target);
590
0
    }
591
0
    case cmProperty::SOURCE_FILE: {
592
0
      std::vector<cmMakefile*> source_file_directory_makefiles;
593
0
      if (!SetPropertyCommand::HandleAndValidateSourceFileDirectoryScopes(
594
0
            status, source_file_directory_option_enabled,
595
0
            source_file_target_option_enabled, source_file_directories,
596
0
            source_file_target_directories, source_file_directory_makefiles)) {
597
0
        return false;
598
0
      }
599
0
      bool source_file_paths_should_be_absolute =
600
0
        source_file_directory_option_enabled ||
601
0
        source_file_target_option_enabled;
602
0
      return HandleSourceMode(status, names, propertyName, propertyValue,
603
0
                              appendAsString, appendMode, remove,
604
0
                              source_file_directory_makefiles,
605
0
                              source_file_paths_should_be_absolute);
606
0
    }
607
0
    case cmProperty::TEST: {
608
0
      cmMakefile* test_directory_makefile;
609
0
      if (!SetPropertyCommand::HandleAndValidateTestDirectoryScopes(
610
0
            status, test_directory_option_enabled, test_directory,
611
0
            test_directory_makefile)) {
612
0
        return false;
613
0
      }
614
0
      return HandleTestMode(status, names, propertyName, propertyValue,
615
0
                            appendAsString, appendMode, remove,
616
0
                            test_directory_makefile);
617
0
    }
618
0
    case cmProperty::CACHE:
619
0
      return HandleCacheMode(status, names, propertyName, propertyValue,
620
0
                             appendAsString, appendMode, remove);
621
0
    case cmProperty::INSTALL:
622
0
      return HandleInstallMode(status, names, propertyName, propertyValue,
623
0
                               appendAsString, appendMode, remove);
624
625
0
    case cmProperty::VARIABLE:
626
0
    case cmProperty::CACHED_VARIABLE:
627
0
      break; // should never happen
628
0
  }
629
0
  return true;
630
0
}
631
632
namespace /* anonymous */ {
633
bool HandleGlobalMode(cmExecutionStatus& status,
634
                      std::set<std::string> const& names,
635
                      std::string const& propertyName,
636
                      std::string const& propertyValue, bool appendAsString,
637
                      bool appendMode, bool remove)
638
0
{
639
0
  if (!names.empty()) {
640
0
    status.SetError("given names for GLOBAL scope.");
641
0
    return false;
642
0
  }
643
644
  // Set or append the property.
645
0
  cmake* cm = status.GetMakefile().GetCMakeInstance();
646
0
  if (appendMode) {
647
0
    cm->AppendProperty(propertyName, propertyValue, appendAsString);
648
0
  } else {
649
0
    if (remove) {
650
0
      cm->SetProperty(propertyName, nullptr);
651
0
    } else {
652
0
      cm->SetProperty(propertyName, propertyValue);
653
0
    }
654
0
  }
655
656
0
  return true;
657
0
}
658
659
bool HandleDirectoryMode(cmExecutionStatus& status,
660
                         std::set<std::string> const& names,
661
                         std::string const& propertyName,
662
                         std::string const& propertyValue, bool appendAsString,
663
                         bool appendMode, bool remove)
664
0
{
665
0
  if (names.size() > 1) {
666
0
    status.SetError("allows at most one name for DIRECTORY scope.");
667
0
    return false;
668
0
  }
669
670
  // Default to the current directory.
671
0
  cmMakefile* mf = &status.GetMakefile();
672
673
  // Lookup the directory if given.
674
0
  if (!names.empty()) {
675
    // Construct the directory name.  Interpret relative paths with
676
    // respect to the current directory.
677
0
    std::string dir = cmSystemTools::CollapseFullPath(
678
0
      *names.begin(), status.GetMakefile().GetCurrentSourceDirectory());
679
680
0
    mf = status.GetMakefile().GetGlobalGenerator()->FindMakefile(dir);
681
0
    if (!mf) {
682
      // Could not find the directory.
683
0
      status.SetError(
684
0
        "DIRECTORY scope provided but requested directory was not found. "
685
0
        "This could be because the directory argument was invalid or, "
686
0
        "it is valid but has not been processed yet.");
687
0
      return false;
688
0
    }
689
0
  }
690
691
  // Set or append the property.
692
0
  if (appendMode) {
693
0
    mf->AppendProperty(propertyName, propertyValue, appendAsString);
694
0
  } else {
695
0
    if (remove) {
696
0
      mf->SetProperty(propertyName, nullptr);
697
0
    } else {
698
0
      mf->SetProperty(propertyName, propertyValue);
699
0
    }
700
0
  }
701
702
0
  return true;
703
0
}
704
705
bool HandleTargetMode(cmExecutionStatus& status,
706
                      std::set<std::string> const& names,
707
                      std::string const& propertyName,
708
                      std::string const& propertyValue, bool appendAsString,
709
                      bool appendMode, bool remove)
710
0
{
711
0
  for (std::string const& name : names) {
712
0
    if (status.GetMakefile().IsAlias(name)) {
713
0
      status.SetError("can not be used on an ALIAS target.");
714
0
      return false;
715
0
    }
716
717
0
    if (cmTarget* target = status.GetMakefile().FindTargetToUse(name)) {
718
0
      if (target->IsSymbolic()) {
719
0
        status.SetError("can not be used on a SYMBOLIC target.");
720
0
        return false;
721
0
      }
722
723
      // Handle the current target.
724
0
      if (!HandleTarget(target, status.GetMakefile(), propertyName,
725
0
                        propertyValue, appendAsString, appendMode, remove)) {
726
0
        return false;
727
0
      }
728
0
    } else {
729
0
      status.SetError(cmStrCat("could not find TARGET ", name,
730
0
                               ".  Perhaps it has not yet been created."));
731
0
      return false;
732
0
    }
733
0
  }
734
0
  return true;
735
0
}
736
737
bool HandleTarget(cmTarget* target, cmMakefile& makefile,
738
                  std::string const& propertyName,
739
                  std::string const& propertyValue, bool appendAsString,
740
                  bool appendMode, bool remove)
741
0
{
742
  // Set or append the property.
743
0
  if (appendMode) {
744
0
    target->AppendProperty(propertyName, propertyValue,
745
0
                           makefile.GetBacktrace(), appendAsString);
746
0
  } else {
747
0
    if (remove) {
748
0
      target->SetProperty(propertyName, nullptr);
749
0
    } else {
750
0
      target->SetProperty(propertyName, propertyValue);
751
0
    }
752
0
  }
753
754
  // Check the resulting value.
755
0
  target->CheckProperty(propertyName, &makefile);
756
757
0
  return true;
758
0
}
759
760
bool HandleFileSetMode(cmExecutionStatus& status,
761
                       std::set<std::string> const& names,
762
                       std::string const& propertyName,
763
                       std::string const& propertyValue, bool appendAsString,
764
                       bool appendMode, bool remove, cmTarget* target)
765
0
{
766
0
  for (std::string const& name : names) {
767
0
    if (cmFileSet* fileSet = target->GetFileSet(name)) {
768
      // Handle the current file set.
769
0
      if (!HandleFileSet(fileSet, propertyName, propertyValue, appendAsString,
770
0
                         appendMode, remove)) {
771
0
        return false;
772
0
      }
773
0
    } else {
774
0
      status.SetError(cmStrCat("could not find FILE_SET ", name,
775
0
                               " for TARGET ", target->GetName(),
776
0
                               ".  Perhaps it has not yet been created."));
777
0
      return false;
778
0
    }
779
0
  }
780
0
  return true;
781
0
}
782
783
bool HandleFileSet(cmFileSet* fileSet, std::string const& propertyName,
784
                   std::string const& propertyValue, bool appendAsString,
785
                   bool appendMode, bool remove)
786
0
{
787
  // Set or append the property.
788
0
  if (appendMode) {
789
0
    fileSet->AppendProperty(propertyName, propertyValue, appendAsString);
790
0
  } else {
791
0
    if (remove) {
792
0
      fileSet->SetProperty(propertyName, nullptr);
793
0
    } else {
794
0
      fileSet->SetProperty(propertyName, propertyValue);
795
0
    }
796
0
  }
797
0
  return true;
798
0
}
799
800
bool HandleSourceMode(cmExecutionStatus& status,
801
                      std::set<std::string> const& names,
802
                      std::string const& propertyName,
803
                      std::string const& propertyValue, bool appendAsString,
804
                      bool appendMode, bool remove,
805
                      std::vector<cmMakefile*> const& directory_makefiles,
806
                      bool const source_file_paths_should_be_absolute)
807
0
{
808
0
  std::vector<std::string> files_absolute;
809
0
  std::vector<std::string> unique_files(names.begin(), names.end());
810
0
  SetPropertyCommand::MakeSourceFilePathsAbsoluteIfNeeded(
811
0
    status, files_absolute, unique_files.begin(), unique_files.end(),
812
0
    source_file_paths_should_be_absolute);
813
814
0
  for (auto* const mf : directory_makefiles) {
815
0
    for (std::string const& name : files_absolute) {
816
      // Get the source file.
817
0
      if (cmSourceFile* sf = mf->GetOrCreateSource(name)) {
818
0
        if (!HandleSource(sf, propertyName, propertyValue, appendAsString,
819
0
                          appendMode, remove)) {
820
0
          return false;
821
0
        }
822
0
      } else {
823
0
        status.SetError(cmStrCat(
824
0
          "given SOURCE name that could not be found or created: ", name));
825
0
        return false;
826
0
      }
827
0
    }
828
0
  }
829
830
0
  return true;
831
0
}
832
833
bool HandleSource(cmSourceFile* sf, std::string const& propertyName,
834
                  std::string const& propertyValue, bool appendAsString,
835
                  bool appendMode, bool remove)
836
0
{
837
  // Special validation and handling of GENERATED flag?
838
0
  if (propertyName == "GENERATED") {
839
0
    SetPropertyCommand::PropertyOp op = (remove)
840
0
      ? SetPropertyCommand::PropertyOp::Remove
841
0
      : (appendAsString) ? SetPropertyCommand::PropertyOp::AppendAsString
842
0
      : (appendMode)     ? SetPropertyCommand::PropertyOp::Append
843
0
                         : SetPropertyCommand::PropertyOp::Set;
844
0
    return SetPropertyCommand::HandleAndValidateSourceFilePropertyGENERATED(
845
0
      sf, propertyValue, op);
846
0
  }
847
848
  // Set or append the property.
849
0
  if (appendMode) {
850
0
    sf->AppendProperty(propertyName, propertyValue, appendAsString);
851
0
  } else {
852
0
    if (remove) {
853
0
      sf->RemoveProperty(propertyName);
854
0
    } else {
855
0
      sf->SetProperty(propertyName, propertyValue);
856
0
    }
857
0
  }
858
0
  return true;
859
0
}
860
861
bool HandleTestMode(cmExecutionStatus& status, std::set<std::string>& names,
862
                    std::string const& propertyName,
863
                    std::string const& propertyValue, bool appendAsString,
864
                    bool appendMode, bool remove, cmMakefile* test_makefile)
865
0
{
866
  // Look for tests with all names given.
867
0
  std::set<std::string>::iterator next;
868
0
  for (auto ni = names.begin(); ni != names.end(); ni = next) {
869
0
    next = ni;
870
0
    ++next;
871
0
    if (cmTest* test = test_makefile->GetTest(*ni)) {
872
0
      if (HandleTest(test, propertyName, propertyValue, appendAsString,
873
0
                     appendMode, remove)) {
874
0
        names.erase(ni);
875
0
      } else {
876
0
        return false;
877
0
      }
878
0
    }
879
0
  }
880
881
  // Names that are still left were not found.
882
0
  if (!names.empty()) {
883
0
    std::ostringstream e;
884
0
    e << "given TEST names that do not exist:\n";
885
0
    for (std::string const& name : names) {
886
0
      e << "  " << name << "\n";
887
0
    }
888
0
    status.SetError(e.str());
889
0
    return false;
890
0
  }
891
0
  return true;
892
0
}
893
894
bool HandleTest(cmTest* test, std::string const& propertyName,
895
                std::string const& propertyValue, bool appendAsString,
896
                bool appendMode, bool remove)
897
0
{
898
  // Set or append the property.
899
0
  if (appendMode) {
900
0
    test->AppendProperty(propertyName, propertyValue, appendAsString);
901
0
  } else {
902
0
    if (remove) {
903
0
      test->SetProperty(propertyName, nullptr);
904
0
    } else {
905
0
      test->SetProperty(propertyName, propertyValue);
906
0
    }
907
0
  }
908
909
0
  return true;
910
0
}
911
912
bool HandleCacheMode(cmExecutionStatus& status,
913
                     std::set<std::string> const& names,
914
                     std::string const& propertyName,
915
                     std::string const& propertyValue, bool appendAsString,
916
                     bool appendMode, bool remove)
917
0
{
918
0
  if (propertyName == "ADVANCED") {
919
0
    if (!remove && !cmIsOn(propertyValue) && !cmIsOff(propertyValue)) {
920
0
      status.SetError(cmStrCat("given non-boolean value \"", propertyValue,
921
0
                               R"(" for CACHE property "ADVANCED".  )"));
922
0
      return false;
923
0
    }
924
0
  } else if (propertyName == "TYPE") {
925
0
    if (!cmState::IsCacheEntryType(propertyValue)) {
926
0
      status.SetError(
927
0
        cmStrCat("given invalid CACHE entry TYPE \"", propertyValue, '"'));
928
0
      return false;
929
0
    }
930
0
  } else if (propertyName != "HELPSTRING" && propertyName != "STRINGS" &&
931
0
             propertyName != "VALUE") {
932
0
    status.SetError(
933
0
      cmStrCat("given invalid CACHE property ", propertyName,
934
0
               ".  "
935
0
               "Settable CACHE properties are: "
936
0
               "ADVANCED, HELPSTRING, STRINGS, TYPE, and VALUE."));
937
0
    return false;
938
0
  }
939
940
0
  for (std::string const& name : names) {
941
    // Get the source file.
942
0
    cmake* cm = status.GetMakefile().GetCMakeInstance();
943
0
    cmValue existingValue = cm->GetState()->GetCacheEntryValue(name);
944
0
    if (existingValue) {
945
0
      if (!HandleCacheEntry(name, status.GetMakefile(), propertyName,
946
0
                            propertyValue, appendAsString, appendMode,
947
0
                            remove)) {
948
0
        return false;
949
0
      }
950
0
    } else {
951
0
      status.SetError(cmStrCat("could not find CACHE variable ", name,
952
0
                               ".  Perhaps it has not yet been created."));
953
0
      return false;
954
0
    }
955
0
  }
956
0
  return true;
957
0
}
958
959
bool HandleCacheEntry(std::string const& cacheKey, cmMakefile const& makefile,
960
                      std::string const& propertyName,
961
                      std::string const& propertyValue, bool appendAsString,
962
                      bool appendMode, bool remove)
963
0
{
964
  // Set or append the property.
965
0
  cmState* state = makefile.GetState();
966
0
  if (remove) {
967
0
    state->RemoveCacheEntryProperty(cacheKey, propertyName);
968
0
  }
969
0
  if (appendMode) {
970
0
    state->AppendCacheEntryProperty(cacheKey, propertyName, propertyValue,
971
0
                                    appendAsString);
972
0
  } else {
973
0
    state->SetCacheEntryProperty(cacheKey, propertyName, propertyValue);
974
0
  }
975
976
0
  return true;
977
0
}
978
979
bool HandleInstallMode(cmExecutionStatus& status,
980
                       std::set<std::string> const& names,
981
                       std::string const& propertyName,
982
                       std::string const& propertyValue, bool appendAsString,
983
                       bool appendMode, bool remove)
984
0
{
985
0
  cmake* cm = status.GetMakefile().GetCMakeInstance();
986
987
0
  for (std::string const& name : names) {
988
0
    if (cmInstalledFile* file =
989
0
          cm->GetOrCreateInstalledFile(&status.GetMakefile(), name)) {
990
0
      if (!HandleInstall(file, status.GetMakefile(), propertyName,
991
0
                         propertyValue, appendAsString, appendMode, remove)) {
992
0
        return false;
993
0
      }
994
0
    } else {
995
0
      status.SetError(cmStrCat(
996
0
        "given INSTALL name that could not be found or created: ", name));
997
0
      return false;
998
0
    }
999
0
  }
1000
0
  return true;
1001
0
}
1002
1003
bool HandleInstall(cmInstalledFile* file, cmMakefile& makefile,
1004
                   std::string const& propertyName,
1005
                   std::string const& propertyValue, bool appendAsString,
1006
                   bool appendMode, bool remove)
1007
0
{
1008
  // Set or append the property.
1009
0
  if (remove) {
1010
0
    file->RemoveProperty(propertyName);
1011
0
  } else if (appendMode) {
1012
0
    file->AppendProperty(&makefile, propertyName, propertyValue,
1013
0
                         appendAsString);
1014
0
  } else {
1015
0
    file->SetProperty(&makefile, propertyName, propertyValue);
1016
0
  }
1017
0
  return true;
1018
0
}
1019
}