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