Coverage Report

Created: 2026-02-09 06:05

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