resolve cyclic dependency with zstd
[platform/upstream/cmake.git] / Source / cmSetCommand.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 "cmSetCommand.h"
4
5 #include "cmExecutionStatus.h"
6 #include "cmMakefile.h"
7 #include "cmMessageType.h"
8 #include "cmRange.h"
9 #include "cmState.h"
10 #include "cmStateTypes.h"
11 #include "cmStringAlgorithms.h"
12 #include "cmSystemTools.h"
13 #include "cmValue.h"
14
15 // cmSetCommand
16 bool cmSetCommand(std::vector<std::string> const& args,
17                   cmExecutionStatus& status)
18 {
19   if (args.empty()) {
20     status.SetError("called with incorrect number of arguments");
21     return false;
22   }
23
24   // watch for ENV signatures
25   auto const& variable = args[0]; // VAR is always first
26   if (cmHasLiteralPrefix(variable, "ENV{") && variable.size() > 5) {
27     // what is the variable name
28     auto const& varName = variable.substr(4, variable.size() - 5);
29     std::string putEnvArg = varName + "=";
30
31     // what is the current value if any
32     std::string currValue;
33     const bool currValueSet = cmSystemTools::GetEnv(varName, currValue);
34
35     // will it be set to something, then set it
36     if (args.size() > 1 && !args[1].empty()) {
37       // but only if it is different from current value
38       if (!currValueSet || currValue != args[1]) {
39         putEnvArg += args[1];
40         cmSystemTools::PutEnv(putEnvArg);
41       }
42       // if there's extra arguments, warn user
43       // that they are ignored by this command.
44       if (args.size() > 2) {
45         std::string m = "Only the first value argument is used when setting "
46                         "an environment variable.  Argument '" +
47           args[2] + "' and later are unused.";
48         status.GetMakefile().IssueMessage(MessageType::AUTHOR_WARNING, m);
49       }
50       return true;
51     }
52
53     // if it will be cleared, then clear it if it isn't already clear
54     if (currValueSet) {
55       cmSystemTools::PutEnv(putEnvArg);
56     }
57     return true;
58   }
59
60   // SET (VAR) // Removes the definition of VAR.
61   if (args.size() == 1) {
62     status.GetMakefile().RemoveDefinition(variable);
63     return true;
64   }
65   // SET (VAR PARENT_SCOPE) // Removes the definition of VAR
66   // in the parent scope.
67   if (args.size() == 2 && args.back() == "PARENT_SCOPE") {
68     status.GetMakefile().RaiseScope(variable, nullptr);
69     return true;
70   }
71
72   // here are the remaining options
73   //  SET (VAR value )
74   //  SET (VAR value PARENT_SCOPE)
75   //  SET (VAR CACHE TYPE "doc String" [FORCE])
76   //  SET (VAR value CACHE TYPE "doc string" [FORCE])
77   std::string value;  // optional
78   bool cache = false; // optional
79   bool force = false; // optional
80   bool parentScope = false;
81   cmStateEnums::CacheEntryType type =
82     cmStateEnums::STRING;          // required if cache
83   const char* docstring = nullptr; // required if cache
84
85   unsigned int ignoreLastArgs = 0;
86   // look for PARENT_SCOPE argument
87   if (args.size() > 1 && args.back() == "PARENT_SCOPE") {
88     parentScope = true;
89     ignoreLastArgs++;
90   } else {
91     // look for FORCE argument
92     if (args.size() > 4 && args.back() == "FORCE") {
93       force = true;
94       ignoreLastArgs++;
95     }
96
97     // check for cache signature
98     if (args.size() > 3 &&
99         args[args.size() - 3 - (force ? 1 : 0)] == "CACHE") {
100       cache = true;
101       ignoreLastArgs += 3;
102     }
103   }
104
105   // collect any values into a single semi-colon separated value list
106   value = cmJoin(cmMakeRange(args).advance(1).retreat(ignoreLastArgs), ";");
107
108   if (parentScope) {
109     status.GetMakefile().RaiseScope(variable, value.c_str());
110     return true;
111   }
112
113   // we should be nice and try to catch some simple screwups if the last or
114   // next to last args are CACHE then they screwed up.  If they used FORCE
115   // without CACHE they screwed up
116   if ((args.back() == "CACHE") ||
117       (args.size() > 1 && args[args.size() - 2] == "CACHE") ||
118       (force && !cache)) {
119     status.SetError("given invalid arguments for CACHE mode.");
120     return false;
121   }
122
123   if (cache) {
124     std::string::size_type cacheStart = args.size() - 3 - (force ? 1 : 0);
125     if (!cmState::StringToCacheEntryType(args[cacheStart + 1], type)) {
126       std::string m = "implicitly converting '" + args[cacheStart + 1] +
127         "' to 'STRING' type.";
128       status.GetMakefile().IssueMessage(MessageType::AUTHOR_WARNING, m);
129       // Setting this may not be required, since it's
130       // initialized as a string. Keeping this here to
131       // ensure that the type is actually converting to a string.
132       type = cmStateEnums::STRING;
133     }
134     docstring = args[cacheStart + 2].c_str();
135   }
136
137   // see if this is already in the cache
138   cmState* state = status.GetMakefile().GetState();
139   cmValue existingValue = state->GetCacheEntryValue(variable);
140   if (existingValue &&
141       (state->GetCacheEntryType(variable) != cmStateEnums::UNINITIALIZED)) {
142     // if the set is trying to CACHE the value but the value
143     // is already in the cache and the type is not internal
144     // then leave now without setting any definitions in the cache
145     // or the makefile
146     if (cache && type != cmStateEnums::INTERNAL && !force) {
147       return true;
148     }
149   }
150
151   // if it is meant to be in the cache then define it in the cache
152   if (cache) {
153     status.GetMakefile().AddCacheDefinition(variable, value, docstring, type,
154                                             force);
155   } else {
156     // add the definition
157     status.GetMakefile().AddDefinition(variable, value);
158   }
159   return true;
160 }