Imported Upstream version 3.25.0
[platform/upstream/cmake.git] / Source / cmSetPropertyCommand.cxx
1 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2    file Copyright.txt 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 "cmExecutionStatus.h"
10 #include "cmGlobalGenerator.h"
11 #include "cmInstalledFile.h"
12 #include "cmListFileCache.h"
13 #include "cmMakefile.h"
14 #include "cmMessageType.h"
15 #include "cmPolicies.h"
16 #include "cmProperty.h"
17 #include "cmRange.h"
18 #include "cmSourceFile.h"
19 #include "cmSourceFileLocation.h"
20 #include "cmState.h"
21 #include "cmStringAlgorithms.h"
22 #include "cmSystemTools.h"
23 #include "cmTarget.h"
24 #include "cmTest.h"
25 #include "cmValue.h"
26 #include "cmake.h"
27
28 namespace {
29 bool HandleGlobalMode(cmExecutionStatus& status,
30                       const std::set<std::string>& names,
31                       const std::string& propertyName,
32                       const std::string& propertyValue, bool appendAsString,
33                       bool appendMode, bool remove);
34 bool HandleDirectoryMode(cmExecutionStatus& status,
35                          const std::set<std::string>& names,
36                          const std::string& propertyName,
37                          const std::string& propertyValue, bool appendAsString,
38                          bool appendMode, bool remove);
39 bool HandleTargetMode(cmExecutionStatus& status,
40                       const std::set<std::string>& names,
41                       const std::string& propertyName,
42                       const std::string& propertyValue, bool appendAsString,
43                       bool appendMode, bool remove);
44 bool HandleTarget(cmTarget* target, cmMakefile& makefile,
45                   const std::string& propertyName,
46                   const std::string& propertyValue, bool appendAsString,
47                   bool appendMode, bool remove);
48 bool HandleSourceMode(cmExecutionStatus& status,
49                       const std::set<std::string>& names,
50                       const std::string& propertyName,
51                       const std::string& propertyValue, bool appendAsString,
52                       bool appendMode, bool remove,
53                       const std::vector<cmMakefile*>& directory_makefiles,
54                       bool source_file_paths_should_be_absolute);
55 bool HandleSource(cmSourceFile* sf, const std::string& propertyName,
56                   const std::string& propertyValue, bool appendAsString,
57                   bool appendMode, bool remove);
58 bool HandleTestMode(cmExecutionStatus& status, std::set<std::string>& names,
59                     const std::string& propertyName,
60                     const std::string& propertyValue, bool appendAsString,
61                     bool appendMode, bool remove);
62 bool HandleTest(cmTest* test, const std::string& propertyName,
63                 const std::string& propertyValue, bool appendAsString,
64                 bool appendMode, bool remove);
65 bool HandleCacheMode(cmExecutionStatus& status,
66                      const std::set<std::string>& names,
67                      const std::string& propertyName,
68                      const std::string& propertyValue, bool appendAsString,
69                      bool appendMode, bool remove);
70 bool HandleCacheEntry(std::string const& cacheKey, const cmMakefile& makefile,
71                       const std::string& propertyName,
72                       const std::string& propertyValue, bool appendAsString,
73                       bool appendMode, bool remove);
74 bool HandleInstallMode(cmExecutionStatus& status,
75                        const std::set<std::string>& names,
76                        const std::string& propertyName,
77                        const std::string& propertyValue, bool appendAsString,
78                        bool appendMode, bool remove);
79 bool HandleInstall(cmInstalledFile* file, cmMakefile& makefile,
80                    const std::string& propertyName,
81                    const std::string& propertyValue, bool appendAsString,
82                    bool appendMode, bool remove);
83 }
84
85 namespace SetPropertyCommand {
86 bool HandleSourceFileDirectoryScopes(
87   cmExecutionStatus& status, std::vector<std::string>& source_file_directories,
88   std::vector<std::string>& source_file_target_directories,
89   std::vector<cmMakefile*>& directory_makefiles)
90 {
91   std::unordered_set<cmMakefile*> directory_makefiles_set;
92
93   cmMakefile* current_dir_mf = &status.GetMakefile();
94   if (!source_file_directories.empty()) {
95     for (const std::string& dir_path : source_file_directories) {
96       const std::string absolute_dir_path = cmSystemTools::CollapseFullPath(
97         dir_path, current_dir_mf->GetCurrentSourceDirectory());
98       cmMakefile* dir_mf =
99         status.GetMakefile().GetGlobalGenerator()->FindMakefile(
100           absolute_dir_path);
101       if (!dir_mf) {
102         status.SetError(cmStrCat("given non-existent DIRECTORY ", dir_path));
103         return false;
104       }
105       if (directory_makefiles_set.find(dir_mf) ==
106           directory_makefiles_set.end()) {
107         directory_makefiles.push_back(dir_mf);
108         directory_makefiles_set.insert(dir_mf);
109       }
110     }
111   }
112
113   if (!source_file_target_directories.empty()) {
114     for (const std::string& target_name : source_file_target_directories) {
115       cmTarget* target = current_dir_mf->FindTargetToUse(target_name);
116       if (!target) {
117         status.SetError(cmStrCat(
118           "given non-existent target for TARGET_DIRECTORY ", target_name));
119         return false;
120       }
121       cmValue target_source_dir = target->GetProperty("BINARY_DIR");
122       cmMakefile* target_dir_mf =
123         status.GetMakefile().GetGlobalGenerator()->FindMakefile(
124           *target_source_dir);
125
126       if (directory_makefiles_set.find(target_dir_mf) ==
127           directory_makefiles_set.end()) {
128         directory_makefiles.push_back(target_dir_mf);
129         directory_makefiles_set.insert(target_dir_mf);
130       }
131     }
132   }
133
134   if (source_file_directories.empty() &&
135       source_file_target_directories.empty()) {
136     directory_makefiles.push_back(current_dir_mf);
137   }
138   return true;
139 }
140
141 bool HandleSourceFileDirectoryScopeValidation(
142   cmExecutionStatus& status, bool source_file_directory_option_enabled,
143   bool source_file_target_option_enabled,
144   std::vector<std::string>& source_file_directories,
145   std::vector<std::string>& source_file_target_directories)
146 {
147   // Validate source file directory scopes.
148   if (source_file_directory_option_enabled &&
149       source_file_directories.empty()) {
150     std::string errors = "called with incorrect number of arguments "
151                          "no value provided to the DIRECTORY option";
152     status.SetError(errors);
153     return false;
154   }
155   if (source_file_target_option_enabled &&
156       source_file_target_directories.empty()) {
157     std::string errors = "called with incorrect number of arguments "
158                          "no value provided to the TARGET_DIRECTORY option";
159     status.SetError(errors);
160     return false;
161   }
162   return true;
163 }
164
165 bool HandleAndValidateSourceFileDirectoryScopes(
166   cmExecutionStatus& status, bool source_file_directory_option_enabled,
167   bool source_file_target_option_enabled,
168   std::vector<std::string>& source_file_directories,
169   std::vector<std::string>& source_file_target_directories,
170   std::vector<cmMakefile*>& source_file_directory_makefiles)
171 {
172   bool scope_options_valid =
173     SetPropertyCommand::HandleSourceFileDirectoryScopeValidation(
174       status, source_file_directory_option_enabled,
175       source_file_target_option_enabled, source_file_directories,
176       source_file_target_directories);
177   if (!scope_options_valid) {
178     return false;
179   }
180
181   scope_options_valid = SetPropertyCommand::HandleSourceFileDirectoryScopes(
182     status, source_file_directories, source_file_target_directories,
183     source_file_directory_makefiles);
184   return scope_options_valid;
185 }
186
187 std::string MakeSourceFilePathAbsoluteIfNeeded(
188   cmExecutionStatus& status, const std::string& source_file_path,
189   const bool needed)
190 {
191   if (!needed) {
192     return source_file_path;
193   }
194   std::string absolute_file_path = cmSystemTools::CollapseFullPath(
195     source_file_path, status.GetMakefile().GetCurrentSourceDirectory());
196   return absolute_file_path;
197 }
198
199 void MakeSourceFilePathsAbsoluteIfNeeded(
200   cmExecutionStatus& status,
201   std::vector<std::string>& source_files_absolute_paths,
202   std::vector<std::string>::const_iterator files_it_begin,
203   std::vector<std::string>::const_iterator files_it_end, const bool needed)
204 {
205
206   // Make the file paths absolute, so that relative source file paths are
207   // picked up relative to the command calling site, regardless of the
208   // directory scope.
209   std::vector<std::string>::difference_type num_files =
210     files_it_end - files_it_begin;
211   source_files_absolute_paths.reserve(num_files);
212
213   if (!needed) {
214     source_files_absolute_paths.assign(files_it_begin, files_it_end);
215     return;
216   }
217
218   for (; files_it_begin != files_it_end; ++files_it_begin) {
219     const std::string absolute_file_path =
220       MakeSourceFilePathAbsoluteIfNeeded(status, *files_it_begin, true);
221     source_files_absolute_paths.push_back(absolute_file_path);
222   }
223 }
224
225 bool HandleAndValidateSourceFilePropertyGENERATED(
226   cmSourceFile* sf, std::string const& propertyValue, PropertyOp op)
227 {
228   const auto& mf = *sf->GetLocation().GetMakefile();
229   auto policyStatus = mf.GetPolicyStatus(cmPolicies::CMP0118);
230
231   const bool policyWARN = policyStatus == cmPolicies::WARN;
232   const bool policyNEW = policyStatus != cmPolicies::OLD && !policyWARN;
233
234   if (policyWARN) {
235     if (!cmIsOn(propertyValue) && !cmIsOff(propertyValue)) {
236       mf.IssueMessage(
237         MessageType::AUTHOR_WARNING,
238         cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0118),
239                  "\nAttempt to set property 'GENERATED' with the following "
240                  "non-boolean value (which will be interpreted as \"0\"):\n",
241                  propertyValue,
242                  "\nThat exact value will not be retrievable. A value of "
243                  "\"0\" will be returned instead.\n"
244                  "This will be an error under policy CMP0118.\n"));
245     }
246     if (cmIsOff(propertyValue)) {
247       mf.IssueMessage(
248         MessageType::AUTHOR_WARNING,
249         cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0118),
250                  "\nUnsetting property 'GENERATED' will not be allowed under "
251                  "policy CMP0118!\n"));
252     }
253     if (op == PropertyOp::Append || op == PropertyOp::AppendAsString) {
254       mf.IssueMessage(
255         MessageType::AUTHOR_WARNING,
256         cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0118),
257                  "\nAppending to property 'GENERATED' will not be allowed "
258                  "under policy CMP0118!\n"));
259     }
260   } else if (policyNEW) {
261     if (!cmIsOn(propertyValue) && !cmIsOff(propertyValue)) {
262       mf.IssueMessage(
263         MessageType::AUTHOR_ERROR,
264         cmStrCat(
265           "Policy CMP0118 is set to NEW and the following non-boolean value "
266           "given for property 'GENERATED' is therefore not allowed:\n",
267           propertyValue, "\nReplace it with a boolean value!\n"));
268       return true;
269     }
270     if (cmIsOff(propertyValue)) {
271       mf.IssueMessage(
272         MessageType::AUTHOR_ERROR,
273         "Unsetting the 'GENERATED' property is not allowed under CMP0118!\n");
274       return true;
275     }
276     if (op == PropertyOp::Append || op == PropertyOp::AppendAsString) {
277       mf.IssueMessage(MessageType::AUTHOR_ERROR,
278                       "Policy CMP0118 is set to NEW and appending to the "
279                       "'GENERATED' property is therefore not allowed. Only "
280                       "setting it to \"1\" is allowed!\n");
281       return true;
282     }
283   }
284
285   // Set property.
286   if (!policyNEW) {
287     // Do it the traditional way.
288     switch (op) {
289       case PropertyOp::Append:
290         sf->AppendProperty("GENERATED", propertyValue, false);
291         break;
292       case PropertyOp::AppendAsString:
293         sf->AppendProperty("GENERATED", propertyValue, true);
294         break;
295       case PropertyOp::Remove:
296         sf->SetProperty("GENERATED", nullptr);
297         break;
298       case PropertyOp::Set:
299         sf->SetProperty("GENERATED", propertyValue);
300         break;
301     }
302   } else {
303     sf->MarkAsGenerated();
304   }
305   return true;
306 }
307
308 } // END: namespace SetPropertyCommand
309
310 bool cmSetPropertyCommand(std::vector<std::string> const& args,
311                           cmExecutionStatus& status)
312 {
313   if (args.size() < 2) {
314     status.SetError("called with incorrect number of arguments");
315     return false;
316   }
317
318   // Get the scope on which to set the property.
319   std::string const& scopeName = args.front();
320   cmProperty::ScopeType scope;
321   if (scopeName == "GLOBAL") {
322     scope = cmProperty::GLOBAL;
323   } else if (scopeName == "DIRECTORY") {
324     scope = cmProperty::DIRECTORY;
325   } else if (scopeName == "TARGET") {
326     scope = cmProperty::TARGET;
327   } else if (scopeName == "SOURCE") {
328     scope = cmProperty::SOURCE_FILE;
329   } else if (scopeName == "TEST") {
330     scope = cmProperty::TEST;
331   } else if (scopeName == "CACHE") {
332     scope = cmProperty::CACHE;
333   } else if (scopeName == "INSTALL") {
334     scope = cmProperty::INSTALL;
335   } else {
336     status.SetError(cmStrCat("given invalid scope ", scopeName,
337                              ".  "
338                              "Valid scopes are GLOBAL, DIRECTORY, "
339                              "TARGET, SOURCE, TEST, CACHE, INSTALL."));
340     return false;
341   }
342
343   bool appendAsString = false;
344   bool appendMode = false;
345   bool remove = true;
346   std::set<std::string> names;
347   std::string propertyName;
348   std::string propertyValue;
349
350   std::vector<std::string> source_file_directories;
351   std::vector<std::string> source_file_target_directories;
352   bool source_file_directory_option_enabled = false;
353   bool source_file_target_option_enabled = false;
354
355   // Parse the rest of the arguments up to the values.
356   enum Doing
357   {
358     DoingNone,
359     DoingNames,
360     DoingProperty,
361     DoingValues,
362     DoingSourceDirectory,
363     DoingSourceTargetDirectory
364   };
365   Doing doing = DoingNames;
366   const char* sep = "";
367   for (std::string const& arg : cmMakeRange(args).advance(1)) {
368     if (arg == "PROPERTY") {
369       doing = DoingProperty;
370     } else if (arg == "APPEND") {
371       doing = DoingNone;
372       appendMode = true;
373       remove = false;
374       appendAsString = false;
375     } else if (arg == "APPEND_STRING") {
376       doing = DoingNone;
377       appendMode = true;
378       remove = false;
379       appendAsString = true;
380     } else if (doing != DoingProperty && doing != DoingValues &&
381                scope == cmProperty::SOURCE_FILE && arg == "DIRECTORY") {
382       doing = DoingSourceDirectory;
383       source_file_directory_option_enabled = true;
384     } else if (doing != DoingProperty && doing != DoingValues &&
385                scope == cmProperty::SOURCE_FILE && arg == "TARGET_DIRECTORY") {
386       doing = DoingSourceTargetDirectory;
387       source_file_target_option_enabled = true;
388     } else if (doing == DoingNames) {
389       names.insert(arg);
390     } else if (doing == DoingSourceDirectory) {
391       source_file_directories.push_back(arg);
392     } else if (doing == DoingSourceTargetDirectory) {
393       source_file_target_directories.push_back(arg);
394     } else if (doing == DoingProperty) {
395       propertyName = arg;
396       doing = DoingValues;
397     } else if (doing == DoingValues) {
398       propertyValue += sep;
399       sep = ";";
400       propertyValue += arg;
401       remove = false;
402     } else {
403       status.SetError(cmStrCat("given invalid argument \"", arg, "\"."));
404       return false;
405     }
406   }
407
408   // Make sure a property name was found.
409   if (propertyName.empty()) {
410     status.SetError("not given a PROPERTY <name> argument.");
411     return false;
412   }
413
414   std::vector<cmMakefile*> source_file_directory_makefiles;
415   bool file_scopes_handled =
416     SetPropertyCommand::HandleAndValidateSourceFileDirectoryScopes(
417       status, source_file_directory_option_enabled,
418       source_file_target_option_enabled, source_file_directories,
419       source_file_target_directories, source_file_directory_makefiles);
420   if (!file_scopes_handled) {
421     return false;
422   }
423   bool source_file_paths_should_be_absolute =
424     source_file_directory_option_enabled || source_file_target_option_enabled;
425
426   // Dispatch property setting.
427   switch (scope) {
428     case cmProperty::GLOBAL:
429       return HandleGlobalMode(status, names, propertyName, propertyValue,
430                               appendAsString, appendMode, remove);
431     case cmProperty::DIRECTORY:
432       return HandleDirectoryMode(status, names, propertyName, propertyValue,
433                                  appendAsString, appendMode, remove);
434     case cmProperty::TARGET:
435       return HandleTargetMode(status, names, propertyName, propertyValue,
436                               appendAsString, appendMode, remove);
437     case cmProperty::SOURCE_FILE:
438       return HandleSourceMode(status, names, propertyName, propertyValue,
439                               appendAsString, appendMode, remove,
440                               source_file_directory_makefiles,
441                               source_file_paths_should_be_absolute);
442     case cmProperty::TEST:
443       return HandleTestMode(status, names, propertyName, propertyValue,
444                             appendAsString, appendMode, remove);
445     case cmProperty::CACHE:
446       return HandleCacheMode(status, names, propertyName, propertyValue,
447                              appendAsString, appendMode, remove);
448     case cmProperty::INSTALL:
449       return HandleInstallMode(status, names, propertyName, propertyValue,
450                                appendAsString, appendMode, remove);
451
452     case cmProperty::VARIABLE:
453     case cmProperty::CACHED_VARIABLE:
454       break; // should never happen
455   }
456   return true;
457 }
458
459 namespace /* anonymous */ {
460 bool HandleGlobalMode(cmExecutionStatus& status,
461                       const std::set<std::string>& names,
462                       const std::string& propertyName,
463                       const std::string& propertyValue, bool appendAsString,
464                       bool appendMode, bool remove)
465 {
466   if (!names.empty()) {
467     status.SetError("given names for GLOBAL scope.");
468     return false;
469   }
470
471   // Set or append the property.
472   cmake* cm = status.GetMakefile().GetCMakeInstance();
473   if (appendMode) {
474     cm->AppendProperty(propertyName, propertyValue, appendAsString);
475   } else {
476     if (remove) {
477       cm->SetProperty(propertyName, nullptr);
478     } else {
479       cm->SetProperty(propertyName, propertyValue);
480     }
481   }
482
483   return true;
484 }
485
486 bool HandleDirectoryMode(cmExecutionStatus& status,
487                          const std::set<std::string>& names,
488                          const std::string& propertyName,
489                          const std::string& propertyValue, bool appendAsString,
490                          bool appendMode, bool remove)
491 {
492   if (names.size() > 1) {
493     status.SetError("allows at most one name for DIRECTORY scope.");
494     return false;
495   }
496
497   // Default to the current directory.
498   cmMakefile* mf = &status.GetMakefile();
499
500   // Lookup the directory if given.
501   if (!names.empty()) {
502     // Construct the directory name.  Interpret relative paths with
503     // respect to the current directory.
504     std::string dir = cmSystemTools::CollapseFullPath(
505       *names.begin(), status.GetMakefile().GetCurrentSourceDirectory());
506
507     mf = status.GetMakefile().GetGlobalGenerator()->FindMakefile(dir);
508     if (!mf) {
509       // Could not find the directory.
510       status.SetError(
511         "DIRECTORY scope provided but requested directory was not found. "
512         "This could be because the directory argument was invalid or, "
513         "it is valid but has not been processed yet.");
514       return false;
515     }
516   }
517
518   // Set or append the property.
519   if (appendMode) {
520     mf->AppendProperty(propertyName, propertyValue, appendAsString);
521   } else {
522     if (remove) {
523       mf->SetProperty(propertyName, nullptr);
524     } else {
525       mf->SetProperty(propertyName, propertyValue);
526     }
527   }
528
529   return true;
530 }
531
532 bool HandleTargetMode(cmExecutionStatus& status,
533                       const std::set<std::string>& names,
534                       const std::string& propertyName,
535                       const std::string& propertyValue, bool appendAsString,
536                       bool appendMode, bool remove)
537 {
538   for (std::string const& name : names) {
539     if (status.GetMakefile().IsAlias(name)) {
540       status.SetError("can not be used on an ALIAS target.");
541       return false;
542     }
543     if (cmTarget* target = status.GetMakefile().FindTargetToUse(name)) {
544       // Handle the current target.
545       if (!HandleTarget(target, status.GetMakefile(), propertyName,
546                         propertyValue, appendAsString, appendMode, remove)) {
547         return false;
548       }
549     } else {
550       status.SetError(cmStrCat("could not find TARGET ", name,
551                                ".  Perhaps it has not yet been created."));
552       return false;
553     }
554   }
555   return true;
556 }
557
558 bool HandleTarget(cmTarget* target, cmMakefile& makefile,
559                   const std::string& propertyName,
560                   const std::string& propertyValue, bool appendAsString,
561                   bool appendMode, bool remove)
562 {
563   // Set or append the property.
564   if (appendMode) {
565     target->AppendProperty(propertyName, propertyValue,
566                            makefile.GetBacktrace(), appendAsString);
567   } else {
568     if (remove) {
569       target->SetProperty(propertyName, nullptr);
570     } else {
571       target->SetProperty(propertyName, propertyValue);
572     }
573   }
574
575   // Check the resulting value.
576   target->CheckProperty(propertyName, &makefile);
577
578   return true;
579 }
580
581 bool HandleSourceMode(cmExecutionStatus& status,
582                       const std::set<std::string>& names,
583                       const std::string& propertyName,
584                       const std::string& propertyValue, bool appendAsString,
585                       bool appendMode, bool remove,
586                       const std::vector<cmMakefile*>& directory_makefiles,
587                       const bool source_file_paths_should_be_absolute)
588 {
589   std::vector<std::string> files_absolute;
590   std::vector<std::string> unique_files(names.begin(), names.end());
591   SetPropertyCommand::MakeSourceFilePathsAbsoluteIfNeeded(
592     status, files_absolute, unique_files.begin(), unique_files.end(),
593     source_file_paths_should_be_absolute);
594
595   for (auto* const mf : directory_makefiles) {
596     for (std::string const& name : files_absolute) {
597       // Get the source file.
598       if (cmSourceFile* sf = mf->GetOrCreateSource(name)) {
599         if (!HandleSource(sf, propertyName, propertyValue, appendAsString,
600                           appendMode, remove)) {
601           return false;
602         }
603       } else {
604         status.SetError(cmStrCat(
605           "given SOURCE name that could not be found or created: ", name));
606         return false;
607       }
608     }
609   }
610
611   return true;
612 }
613
614 bool HandleSource(cmSourceFile* sf, const std::string& propertyName,
615                   const std::string& propertyValue, bool appendAsString,
616                   bool appendMode, bool remove)
617 {
618   // Special validation and handling of GENERATED flag?
619   if (propertyName == "GENERATED") {
620     SetPropertyCommand::PropertyOp op = (remove)
621       ? SetPropertyCommand::PropertyOp::Remove
622       : (appendAsString)
623         ? SetPropertyCommand::PropertyOp::AppendAsString
624         : (appendMode) ? SetPropertyCommand::PropertyOp::Append
625                        : SetPropertyCommand::PropertyOp::Set;
626     return SetPropertyCommand::HandleAndValidateSourceFilePropertyGENERATED(
627       sf, propertyValue, op);
628   }
629
630   // Set or append the property.
631   if (appendMode) {
632     sf->AppendProperty(propertyName, propertyValue, appendAsString);
633   } else {
634     if (remove) {
635       sf->SetProperty(propertyName, nullptr);
636     } else {
637       sf->SetProperty(propertyName, propertyValue);
638     }
639   }
640   return true;
641 }
642
643 bool HandleTestMode(cmExecutionStatus& status, std::set<std::string>& names,
644                     const std::string& propertyName,
645                     const std::string& propertyValue, bool appendAsString,
646                     bool appendMode, bool remove)
647 {
648   // Look for tests with all names given.
649   std::set<std::string>::iterator next;
650   for (auto ni = names.begin(); ni != names.end(); ni = next) {
651     next = ni;
652     ++next;
653     if (cmTest* test = status.GetMakefile().GetTest(*ni)) {
654       if (HandleTest(test, propertyName, propertyValue, appendAsString,
655                      appendMode, remove)) {
656         names.erase(ni);
657       } else {
658         return false;
659       }
660     }
661   }
662
663   // Names that are still left were not found.
664   if (!names.empty()) {
665     std::ostringstream e;
666     e << "given TEST names that do not exist:\n";
667     for (std::string const& name : names) {
668       e << "  " << name << "\n";
669     }
670     status.SetError(e.str());
671     return false;
672   }
673   return true;
674 }
675
676 bool HandleTest(cmTest* test, const std::string& propertyName,
677                 const std::string& propertyValue, bool appendAsString,
678                 bool appendMode, bool remove)
679 {
680   // Set or append the property.
681   if (appendMode) {
682     test->AppendProperty(propertyName, propertyValue, appendAsString);
683   } else {
684     if (remove) {
685       test->SetProperty(propertyName, nullptr);
686     } else {
687       test->SetProperty(propertyName, propertyValue);
688     }
689   }
690
691   return true;
692 }
693
694 bool HandleCacheMode(cmExecutionStatus& status,
695                      const std::set<std::string>& names,
696                      const std::string& propertyName,
697                      const std::string& propertyValue, bool appendAsString,
698                      bool appendMode, bool remove)
699 {
700   if (propertyName == "ADVANCED") {
701     if (!remove && !cmIsOn(propertyValue) && !cmIsOff(propertyValue)) {
702       status.SetError(cmStrCat("given non-boolean value \"", propertyValue,
703                                R"(" for CACHE property "ADVANCED".  )"));
704       return false;
705     }
706   } else if (propertyName == "TYPE") {
707     if (!cmState::IsCacheEntryType(propertyValue)) {
708       status.SetError(
709         cmStrCat("given invalid CACHE entry TYPE \"", propertyValue, "\""));
710       return false;
711     }
712   } else if (propertyName != "HELPSTRING" && propertyName != "STRINGS" &&
713              propertyName != "VALUE") {
714     status.SetError(
715       cmStrCat("given invalid CACHE property ", propertyName,
716                ".  "
717                "Settable CACHE properties are: "
718                "ADVANCED, HELPSTRING, STRINGS, TYPE, and VALUE."));
719     return false;
720   }
721
722   for (std::string const& name : names) {
723     // Get the source file.
724     cmake* cm = status.GetMakefile().GetCMakeInstance();
725     cmValue existingValue = cm->GetState()->GetCacheEntryValue(name);
726     if (existingValue) {
727       if (!HandleCacheEntry(name, status.GetMakefile(), propertyName,
728                             propertyValue, appendAsString, appendMode,
729                             remove)) {
730         return false;
731       }
732     } else {
733       status.SetError(cmStrCat("could not find CACHE variable ", name,
734                                ".  Perhaps it has not yet been created."));
735       return false;
736     }
737   }
738   return true;
739 }
740
741 bool HandleCacheEntry(std::string const& cacheKey, const cmMakefile& makefile,
742                       const std::string& propertyName,
743                       const std::string& propertyValue, bool appendAsString,
744                       bool appendMode, bool remove)
745 {
746   // Set or append the property.
747   cmState* state = makefile.GetState();
748   if (remove) {
749     state->RemoveCacheEntryProperty(cacheKey, propertyName);
750   }
751   if (appendMode) {
752     state->AppendCacheEntryProperty(cacheKey, propertyName, propertyValue,
753                                     appendAsString);
754   } else {
755     state->SetCacheEntryProperty(cacheKey, propertyName, propertyValue);
756   }
757
758   return true;
759 }
760
761 bool HandleInstallMode(cmExecutionStatus& status,
762                        const std::set<std::string>& names,
763                        const std::string& propertyName,
764                        const std::string& propertyValue, bool appendAsString,
765                        bool appendMode, bool remove)
766 {
767   cmake* cm = status.GetMakefile().GetCMakeInstance();
768
769   for (std::string const& name : names) {
770     if (cmInstalledFile* file =
771           cm->GetOrCreateInstalledFile(&status.GetMakefile(), name)) {
772       if (!HandleInstall(file, status.GetMakefile(), propertyName,
773                          propertyValue, appendAsString, appendMode, remove)) {
774         return false;
775       }
776     } else {
777       status.SetError(cmStrCat(
778         "given INSTALL name that could not be found or created: ", name));
779       return false;
780     }
781   }
782   return true;
783 }
784
785 bool HandleInstall(cmInstalledFile* file, cmMakefile& makefile,
786                    const std::string& propertyName,
787                    const std::string& propertyValue, bool appendAsString,
788                    bool appendMode, bool remove)
789 {
790   // Set or append the property.
791   if (remove) {
792     file->RemoveProperty(propertyName);
793   } else if (appendMode) {
794     file->AppendProperty(&makefile, propertyName, propertyValue,
795                          appendAsString);
796   } else {
797     file->SetProperty(&makefile, propertyName, propertyValue);
798   }
799   return true;
800 }
801 }