resolve cyclic dependency with zstd
[platform/upstream/cmake.git] / Source / cmGetPropertyCommand.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 "cmGetPropertyCommand.h"
4
5 #include <cstddef>
6
7 #include "cmExecutionStatus.h"
8 #include "cmGlobalGenerator.h"
9 #include "cmInstalledFile.h"
10 #include "cmMakefile.h"
11 #include "cmMessageType.h"
12 #include "cmPolicies.h"
13 #include "cmProperty.h"
14 #include "cmPropertyDefinition.h"
15 #include "cmSetPropertyCommand.h"
16 #include "cmSourceFile.h"
17 #include "cmState.h"
18 #include "cmStringAlgorithms.h"
19 #include "cmSystemTools.h"
20 #include "cmTarget.h"
21 #include "cmTest.h"
22 #include "cmValue.h"
23 #include "cmake.h"
24
25 namespace {
26 enum OutType
27 {
28   OutValue,
29   OutDefined,
30   OutBriefDoc,
31   OutFullDoc,
32   OutSet
33 };
34
35 // Implementation of each property type.
36 bool HandleGlobalMode(cmExecutionStatus& status, const std::string& name,
37                       OutType infoType, const std::string& variable,
38                       const std::string& propertyName);
39 bool HandleDirectoryMode(cmExecutionStatus& status, const std::string& name,
40                          OutType infoType, const std::string& variable,
41                          const std::string& propertyName);
42 bool HandleTargetMode(cmExecutionStatus& status, const std::string& name,
43                       OutType infoType, const std::string& variable,
44                       const std::string& propertyName);
45 bool HandleSourceMode(cmExecutionStatus& status, const std::string& name,
46                       OutType infoType, const std::string& variable,
47                       const std::string& propertyName,
48                       cmMakefile& directory_makefile,
49                       bool source_file_paths_should_be_absolute);
50 bool HandleTestMode(cmExecutionStatus& status, const std::string& name,
51                     OutType infoType, const std::string& variable,
52                     const std::string& propertyName);
53 bool HandleVariableMode(cmExecutionStatus& status, const std::string& name,
54                         OutType infoType, const std::string& variable,
55                         const std::string& propertyName);
56 bool HandleCacheMode(cmExecutionStatus& status, const std::string& name,
57                      OutType infoType, const std::string& variable,
58                      const std::string& propertyName);
59 bool HandleInstallMode(cmExecutionStatus& status, const std::string& name,
60                        OutType infoType, const std::string& variable,
61                        const std::string& propertyName);
62 }
63
64 bool cmGetPropertyCommand(std::vector<std::string> const& args,
65                           cmExecutionStatus& status)
66 {
67   OutType infoType = OutValue;
68   if (args.size() < 3) {
69     status.SetError("called with incorrect number of arguments");
70     return false;
71   }
72
73   // The cmake variable in which to store the result.
74   std::string const& variable = args[0];
75
76   std::string name;
77   std::string propertyName;
78
79   std::vector<std::string> source_file_directories;
80   std::vector<std::string> source_file_target_directories;
81   bool source_file_directory_option_enabled = false;
82   bool source_file_target_option_enabled = false;
83
84   // Get the scope from which to get the property.
85   cmProperty::ScopeType scope;
86   if (args[1] == "GLOBAL") {
87     scope = cmProperty::GLOBAL;
88   } else if (args[1] == "DIRECTORY") {
89     scope = cmProperty::DIRECTORY;
90   } else if (args[1] == "TARGET") {
91     scope = cmProperty::TARGET;
92   } else if (args[1] == "SOURCE") {
93     scope = cmProperty::SOURCE_FILE;
94   } else if (args[1] == "TEST") {
95     scope = cmProperty::TEST;
96   } else if (args[1] == "VARIABLE") {
97     scope = cmProperty::VARIABLE;
98   } else if (args[1] == "CACHE") {
99     scope = cmProperty::CACHE;
100   } else if (args[1] == "INSTALL") {
101     scope = cmProperty::INSTALL;
102   } else {
103     status.SetError(cmStrCat(
104       "given invalid scope ", args[1],
105       ".  "
106       "Valid scopes are "
107       "GLOBAL, DIRECTORY, TARGET, SOURCE, TEST, VARIABLE, CACHE, INSTALL."));
108     return false;
109   }
110
111   // Parse remaining arguments.
112   enum Doing
113   {
114     DoingNone,
115     DoingName,
116     DoingProperty,
117     DoingType,
118     DoingSourceDirectory,
119     DoingSourceTargetDirectory
120   };
121   Doing doing = DoingName;
122   for (unsigned int i = 2; i < args.size(); ++i) {
123     if (args[i] == "PROPERTY") {
124       doing = DoingProperty;
125     } else if (args[i] == "BRIEF_DOCS") {
126       doing = DoingNone;
127       infoType = OutBriefDoc;
128     } else if (args[i] == "FULL_DOCS") {
129       doing = DoingNone;
130       infoType = OutFullDoc;
131     } else if (args[i] == "SET") {
132       doing = DoingNone;
133       infoType = OutSet;
134     } else if (args[i] == "DEFINED") {
135       doing = DoingNone;
136       infoType = OutDefined;
137     } else if (doing == DoingName) {
138       doing = DoingNone;
139       name = args[i];
140     } else if (doing == DoingNone && scope == cmProperty::SOURCE_FILE &&
141                args[i] == "DIRECTORY") {
142       doing = DoingSourceDirectory;
143       source_file_directory_option_enabled = true;
144     } else if (doing == DoingNone && scope == cmProperty::SOURCE_FILE &&
145                args[i] == "TARGET_DIRECTORY") {
146       doing = DoingSourceTargetDirectory;
147       source_file_target_option_enabled = true;
148     } else if (doing == DoingSourceDirectory) {
149       source_file_directories.push_back(args[i]);
150       doing = DoingNone;
151     } else if (doing == DoingSourceTargetDirectory) {
152       source_file_target_directories.push_back(args[i]);
153       doing = DoingNone;
154     } else if (doing == DoingProperty) {
155       doing = DoingNone;
156       propertyName = args[i];
157     } else {
158       status.SetError(cmStrCat("given invalid argument \"", args[i], "\"."));
159       return false;
160     }
161   }
162
163   // Make sure a property name was found.
164   if (propertyName.empty()) {
165     status.SetError("not given a PROPERTY <name> argument.");
166     return false;
167   }
168
169   std::vector<cmMakefile*> source_file_directory_makefiles;
170   bool file_scopes_handled =
171     SetPropertyCommand::HandleAndValidateSourceFileDirectoryScopes(
172       status, source_file_directory_option_enabled,
173       source_file_target_option_enabled, source_file_directories,
174       source_file_target_directories, source_file_directory_makefiles);
175   if (!file_scopes_handled) {
176     return false;
177   }
178
179   // Compute requested output.
180   if (infoType == OutBriefDoc) {
181     // Lookup brief documentation.
182     std::string output;
183     if (cmPropertyDefinition const* def =
184           status.GetMakefile().GetState()->GetPropertyDefinition(propertyName,
185                                                                  scope)) {
186       output = def->GetShortDescription();
187     }
188     if (output.empty()) {
189       output = "NOTFOUND";
190     }
191     status.GetMakefile().AddDefinition(variable, output);
192   } else if (infoType == OutFullDoc) {
193     // Lookup full documentation.
194     std::string output;
195     if (cmPropertyDefinition const* def =
196           status.GetMakefile().GetState()->GetPropertyDefinition(propertyName,
197                                                                  scope)) {
198       output = def->GetFullDescription();
199     }
200     if (output.empty()) {
201       output = "NOTFOUND";
202     }
203     status.GetMakefile().AddDefinition(variable, output);
204   } else if (infoType == OutDefined) {
205     // Lookup if the property is defined
206     if (status.GetMakefile().GetState()->GetPropertyDefinition(propertyName,
207                                                                scope)) {
208       status.GetMakefile().AddDefinition(variable, "1");
209     } else {
210       status.GetMakefile().AddDefinition(variable, "0");
211     }
212   } else {
213     // Dispatch property getting.
214     cmMakefile& directory_scope_mf = *(source_file_directory_makefiles[0]);
215     bool source_file_paths_should_be_absolute =
216       source_file_directory_option_enabled ||
217       source_file_target_option_enabled;
218
219     switch (scope) {
220       case cmProperty::GLOBAL:
221         return HandleGlobalMode(status, name, infoType, variable,
222                                 propertyName);
223       case cmProperty::DIRECTORY:
224         return HandleDirectoryMode(status, name, infoType, variable,
225                                    propertyName);
226       case cmProperty::TARGET:
227         return HandleTargetMode(status, name, infoType, variable,
228                                 propertyName);
229       case cmProperty::SOURCE_FILE:
230         return HandleSourceMode(status, name, infoType, variable, propertyName,
231                                 directory_scope_mf,
232                                 source_file_paths_should_be_absolute);
233       case cmProperty::TEST:
234         return HandleTestMode(status, name, infoType, variable, propertyName);
235       case cmProperty::VARIABLE:
236         return HandleVariableMode(status, name, infoType, variable,
237                                   propertyName);
238       case cmProperty::CACHE:
239         return HandleCacheMode(status, name, infoType, variable, propertyName);
240       case cmProperty::INSTALL:
241         return HandleInstallMode(status, name, infoType, variable,
242                                  propertyName);
243
244       case cmProperty::CACHED_VARIABLE:
245         break; // should never happen
246     }
247   }
248
249   return true;
250 }
251
252 namespace {
253
254 // Implementation of result storage.
255 template <typename ValueType>
256 bool StoreResult(OutType infoType, cmMakefile& makefile,
257                  const std::string& variable, ValueType value)
258 {
259   if (infoType == OutSet) {
260     makefile.AddDefinition(variable, value ? "1" : "0");
261   } else // if(infoType == OutValue)
262   {
263     if (value) {
264       makefile.AddDefinition(variable, value);
265     } else {
266       makefile.RemoveDefinition(variable);
267     }
268   }
269   return true;
270 }
271 template <>
272 bool StoreResult(OutType infoType, cmMakefile& makefile,
273                  const std::string& variable, std::nullptr_t value)
274 {
275   return StoreResult(infoType, makefile, variable, cmValue(value));
276 }
277
278 bool HandleGlobalMode(cmExecutionStatus& status, const std::string& name,
279                       OutType infoType, const std::string& variable,
280                       const std::string& propertyName)
281 {
282   if (!name.empty()) {
283     status.SetError("given name for GLOBAL scope.");
284     return false;
285   }
286
287   // Get the property.
288   cmake* cm = status.GetMakefile().GetCMakeInstance();
289   return StoreResult(infoType, status.GetMakefile(), variable,
290                      cm->GetState()->GetGlobalProperty(propertyName));
291 }
292
293 bool HandleDirectoryMode(cmExecutionStatus& status, const std::string& name,
294                          OutType infoType, const std::string& variable,
295                          const std::string& propertyName)
296 {
297   // Default to the current directory.
298   cmMakefile* mf = &status.GetMakefile();
299
300   // Lookup the directory if given.
301   if (!name.empty()) {
302     // Construct the directory name.  Interpret relative paths with
303     // respect to the current directory.
304     std::string dir = cmSystemTools::CollapseFullPath(
305       name, status.GetMakefile().GetCurrentSourceDirectory());
306
307     // Lookup the generator.
308     mf = status.GetMakefile().GetGlobalGenerator()->FindMakefile(dir);
309     if (!mf) {
310       // Could not find the directory.
311       status.SetError(
312         "DIRECTORY scope provided but requested directory was not found. "
313         "This could be because the directory argument was invalid or, "
314         "it is valid but has not been processed yet.");
315       return false;
316     }
317   }
318
319   if (propertyName == "DEFINITIONS") {
320     switch (mf->GetPolicyStatus(cmPolicies::CMP0059)) {
321       case cmPolicies::WARN:
322         mf->IssueMessage(MessageType::AUTHOR_WARNING,
323                          cmPolicies::GetPolicyWarning(cmPolicies::CMP0059));
324         CM_FALLTHROUGH;
325       case cmPolicies::OLD:
326         return StoreResult(infoType, status.GetMakefile(), variable,
327                            mf->GetDefineFlagsCMP0059());
328       case cmPolicies::NEW:
329       case cmPolicies::REQUIRED_ALWAYS:
330       case cmPolicies::REQUIRED_IF_USED:
331         break;
332     }
333   }
334
335   // Get the property.
336   return StoreResult(infoType, status.GetMakefile(), variable,
337                      mf->GetProperty(propertyName));
338 }
339
340 bool HandleTargetMode(cmExecutionStatus& status, const std::string& name,
341                       OutType infoType, const std::string& variable,
342                       const std::string& propertyName)
343 {
344   if (name.empty()) {
345     status.SetError("not given name for TARGET scope.");
346     return false;
347   }
348
349   if (cmTarget* target = status.GetMakefile().FindTargetToUse(name)) {
350     if (propertyName == "ALIASED_TARGET" || propertyName == "ALIAS_GLOBAL") {
351       if (status.GetMakefile().IsAlias(name)) {
352         if (propertyName == "ALIASED_TARGET") {
353
354           return StoreResult(infoType, status.GetMakefile(), variable,
355                              target->GetName().c_str());
356         }
357         if (propertyName == "ALIAS_GLOBAL") {
358           return StoreResult(
359             infoType, status.GetMakefile(), variable,
360             status.GetMakefile().GetGlobalGenerator()->IsAlias(name)
361               ? "TRUE"
362               : "FALSE");
363         }
364       }
365       return StoreResult(infoType, status.GetMakefile(), variable, nullptr);
366     }
367     cmValue prop =
368       target->GetComputedProperty(propertyName, status.GetMakefile());
369     if (!prop) {
370       prop = target->GetProperty(propertyName);
371     }
372     return StoreResult(infoType, status.GetMakefile(), variable, prop);
373   }
374   status.SetError(cmStrCat("could not find TARGET ", name,
375                            ".  Perhaps it has not yet been created."));
376   return false;
377 }
378
379 bool HandleSourceMode(cmExecutionStatus& status, const std::string& name,
380                       OutType infoType, const std::string& variable,
381                       const std::string& propertyName,
382                       cmMakefile& directory_makefile,
383                       const bool source_file_paths_should_be_absolute)
384 {
385   if (name.empty()) {
386     status.SetError("not given name for SOURCE scope.");
387     return false;
388   }
389
390   // Get the source file.
391   const std::string source_file_absolute_path =
392     SetPropertyCommand::MakeSourceFilePathAbsoluteIfNeeded(
393       status, name, source_file_paths_should_be_absolute);
394   if (cmSourceFile* sf =
395         directory_makefile.GetOrCreateSource(source_file_absolute_path)) {
396     return StoreResult(infoType, status.GetMakefile(), variable,
397                        sf->GetPropertyForUser(propertyName));
398   }
399   status.SetError(
400     cmStrCat("given SOURCE name that could not be found or created: ",
401              source_file_absolute_path));
402   return false;
403 }
404
405 bool HandleTestMode(cmExecutionStatus& status, const std::string& name,
406                     OutType infoType, const std::string& variable,
407                     const std::string& propertyName)
408 {
409   if (name.empty()) {
410     status.SetError("not given name for TEST scope.");
411     return false;
412   }
413
414   // Loop over all tests looking for matching names.
415   if (cmTest* test = status.GetMakefile().GetTest(name)) {
416     return StoreResult(infoType, status.GetMakefile(), variable,
417                        test->GetProperty(propertyName));
418   }
419
420   // If not found it is an error.
421   status.SetError(cmStrCat("given TEST name that does not exist: ", name));
422   return false;
423 }
424
425 bool HandleVariableMode(cmExecutionStatus& status, const std::string& name,
426                         OutType infoType, const std::string& variable,
427                         const std::string& propertyName)
428 {
429   if (!name.empty()) {
430     status.SetError("given name for VARIABLE scope.");
431     return false;
432   }
433
434   return StoreResult(infoType, status.GetMakefile(), variable,
435                      status.GetMakefile().GetDefinition(propertyName));
436 }
437
438 bool HandleCacheMode(cmExecutionStatus& status, const std::string& name,
439                      OutType infoType, const std::string& variable,
440                      const std::string& propertyName)
441 {
442   if (name.empty()) {
443     status.SetError("not given name for CACHE scope.");
444     return false;
445   }
446
447   cmValue value = nullptr;
448   if (status.GetMakefile().GetState()->GetCacheEntryValue(name)) {
449     value = status.GetMakefile().GetState()->GetCacheEntryProperty(
450       name, propertyName);
451   }
452   StoreResult(infoType, status.GetMakefile(), variable, value);
453   return true;
454 }
455
456 bool HandleInstallMode(cmExecutionStatus& status, const std::string& name,
457                        OutType infoType, const std::string& variable,
458                        const std::string& propertyName)
459 {
460   if (name.empty()) {
461     status.SetError("not given name for INSTALL scope.");
462     return false;
463   }
464
465   // Get the installed file.
466   cmake* cm = status.GetMakefile().GetCMakeInstance();
467
468   if (cmInstalledFile* file =
469         cm->GetOrCreateInstalledFile(&status.GetMakefile(), name)) {
470     std::string value;
471     bool isSet = file->GetProperty(propertyName, value);
472
473     return StoreResult(infoType, status.GetMakefile(), variable,
474                        isSet ? value.c_str() : nullptr);
475   }
476   status.SetError(
477     cmStrCat("given INSTALL name that could not be found or created: ", name));
478   return false;
479 }
480 }