/src/CMake/Source/cmEnvironment.cxx
Line | Count | Source |
1 | | /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
2 | | file LICENSE.rst or https://cmake.org/licensing for details. */ |
3 | | #include "cmEnvironment.h" |
4 | | |
5 | | #include <set> |
6 | | #include <sstream> |
7 | | #include <utility> |
8 | | |
9 | | #include <cm/string_view> |
10 | | #include <cm/vector> |
11 | | #include <cmext/algorithm> |
12 | | |
13 | | #include "cmStringAlgorithms.h" |
14 | | #include "cmSystemTools.h" |
15 | | |
16 | | #if defined(_WIN32) |
17 | | # include "cmsys/String.h" |
18 | | #endif |
19 | | |
20 | | bool cmEnvironment::EnvNameLess::operator()(std::string const& lhs, |
21 | | std::string const& rhs) const |
22 | 0 | { |
23 | | #if defined(_WIN32) |
24 | | // Environment variable names are case-insensitive on Windows |
25 | | return cmsysString_strcasecmp(lhs.c_str(), rhs.c_str()) < 0; |
26 | | #else |
27 | 0 | return lhs < rhs; |
28 | 0 | #endif |
29 | 0 | } |
30 | | |
31 | | cmEnvironment::cmEnvironment(std::vector<std::string> const& env) |
32 | 0 | { |
33 | 0 | for (std::string const& var : env) { |
34 | 0 | this->PutEnv(var); |
35 | 0 | } |
36 | 0 | } |
37 | | |
38 | | void cmEnvironment::PutEnv(std::string const& env) |
39 | 0 | { |
40 | 0 | auto const pos = env.find('='); |
41 | 0 | if (pos != std::string::npos) { |
42 | 0 | this->Map[env.substr(0, pos)] = env.substr(pos + 1); |
43 | 0 | } else { |
44 | 0 | this->Map[env] = cm::nullopt; |
45 | 0 | } |
46 | 0 | } |
47 | | |
48 | | void cmEnvironment::UnPutEnv(std::string const& env) |
49 | 0 | { |
50 | 0 | this->Map[env] = cm::nullopt; |
51 | 0 | } |
52 | | |
53 | | void cmEnvironment::Update(cmEnvironment&& other) |
54 | 0 | { |
55 | 0 | for (auto& kv : other.Map) { |
56 | 0 | this->Map[kv.first] = std::move(kv.second); |
57 | 0 | } |
58 | 0 | other.Map.clear(); |
59 | 0 | } |
60 | | |
61 | | std::vector<std::string> cmEnvironment::GetVariables() const |
62 | 0 | { |
63 | 0 | auto result = std::vector<std::string>{}; |
64 | 0 | result.reserve(this->Map.size()); |
65 | 0 | for (auto const& elem : this->Map) { |
66 | 0 | if (elem.second) { |
67 | 0 | result.push_back(elem.first + '=' + *elem.second); |
68 | 0 | } |
69 | 0 | } |
70 | 0 | return result; |
71 | 0 | } |
72 | | |
73 | | std::string cmEnvironment::RecordDifference( |
74 | | cmEnvironment const& original) const |
75 | 0 | { |
76 | 0 | cm::string_view nl; |
77 | 0 | std::ostringstream os; |
78 | 0 | for (auto const& elem : this->Map) { |
79 | 0 | if (!elem.second) { |
80 | | // Signify that this variable is being actively unset |
81 | 0 | os << nl << '#' << elem.first << '='; |
82 | 0 | nl = "\n"; |
83 | 0 | continue; |
84 | 0 | } |
85 | 0 | auto const it = original.Map.find(elem.first); |
86 | 0 | if (it != original.Map.end() && *elem.second == it->second) { |
87 | | // Skip variables that are unchanged |
88 | 0 | continue; |
89 | 0 | } |
90 | 0 | os << nl << elem.first << '=' << *elem.second; |
91 | 0 | nl = "\n"; |
92 | 0 | } |
93 | 0 | return os.str(); |
94 | 0 | } |
95 | | |
96 | | namespace { |
97 | | |
98 | | auto const ValidOperators = std::set<cm::string_view>{ |
99 | | "set", |
100 | | "unset", |
101 | | "string_append", |
102 | | "string_prepend", |
103 | | "path_list_append", |
104 | | "path_list_prepend", |
105 | | "cmake_list_append", |
106 | | "cmake_list_prepend", |
107 | | }; |
108 | | |
109 | | struct ListAppend |
110 | | { |
111 | | public: |
112 | | ListAppend(std::string val, char sep) |
113 | 0 | : value(std::move(val)) |
114 | 0 | , separator(sep) |
115 | 0 | { |
116 | 0 | } |
117 | | |
118 | | void operator()(std::string& output) const |
119 | 0 | { |
120 | 0 | if (!output.empty()) { |
121 | 0 | output += separator; |
122 | 0 | } |
123 | 0 | output += value; |
124 | 0 | } |
125 | | |
126 | | private: |
127 | | std::string value; |
128 | | char separator; |
129 | | }; |
130 | | |
131 | | struct ListPrepend |
132 | | { |
133 | | public: |
134 | | ListPrepend(std::string val, char sep) |
135 | 0 | : value(std::move(val)) |
136 | 0 | , separator(sep) |
137 | 0 | { |
138 | 0 | } |
139 | | |
140 | | void operator()(std::string& output) const |
141 | 0 | { |
142 | 0 | if (!output.empty()) { |
143 | 0 | output.insert(output.begin(), separator); |
144 | 0 | } |
145 | 0 | output.insert(0, value); |
146 | 0 | } |
147 | | |
148 | | private: |
149 | | std::string value; |
150 | | char separator; |
151 | | }; |
152 | | |
153 | | } // namespace |
154 | | |
155 | | bool cmEnvironmentModification::Add(std::vector<std::string> const& envmod) |
156 | 0 | { |
157 | 0 | bool ok = true; |
158 | 0 | for (auto const& entry : envmod) { |
159 | 0 | ok &= this->Add(entry); |
160 | 0 | } |
161 | 0 | return ok; |
162 | 0 | } |
163 | | |
164 | | bool cmEnvironmentModification::Add(std::string const& envmod) |
165 | 0 | { |
166 | | // Split on `=` |
167 | 0 | auto const eq_loc = envmod.find_first_of('='); |
168 | 0 | if (eq_loc == std::string::npos) { |
169 | 0 | cmSystemTools::Error(cmStrCat( |
170 | 0 | "Error: Missing `=` after the variable name in: ", envmod, '\n')); |
171 | 0 | return false; |
172 | 0 | } |
173 | | |
174 | | // Split operation on `:` |
175 | 0 | auto const op_value_start = eq_loc + 1; |
176 | 0 | auto const colon_loc = envmod.find_first_of(':', op_value_start); |
177 | 0 | if (colon_loc == std::string::npos) { |
178 | 0 | cmSystemTools::Error( |
179 | 0 | cmStrCat("Error: Missing `:` after the operation in: ", envmod, '\n')); |
180 | 0 | return false; |
181 | 0 | } |
182 | | |
183 | 0 | auto entry = Entry{}; |
184 | 0 | entry.Name = envmod.substr(0, eq_loc); |
185 | 0 | entry.Op = envmod.substr(op_value_start, colon_loc - op_value_start); |
186 | 0 | entry.Value = envmod.substr(colon_loc + 1); |
187 | |
|
188 | 0 | if (entry.Op == "reset") { |
189 | 0 | cm::erase_if(this->Entries, |
190 | 0 | [&entry](Entry const& e) { return e.Name == entry.Name; }); |
191 | 0 | return true; |
192 | 0 | } |
193 | | |
194 | 0 | if (!cm::contains(ValidOperators, entry.Op)) { |
195 | 0 | cmSystemTools::Error(cmStrCat( |
196 | 0 | "Error: Unrecognized environment manipulation argument: ", entry.Op, |
197 | 0 | '\n')); |
198 | 0 | return false; |
199 | 0 | } |
200 | | |
201 | 0 | this->Entries.push_back(std::move(entry)); |
202 | 0 | return true; |
203 | 0 | } |
204 | | |
205 | | void cmEnvironmentModification::ApplyTo(cmEnvironment& env) |
206 | 0 | { |
207 | 0 | char const path_sep = cmSystemTools::GetSystemPathlistSeparator(); |
208 | |
|
209 | 0 | for (auto const& e : this->Entries) { |
210 | 0 | if (e.Op == "set") { |
211 | 0 | env.PutEnv(e.Name + "=" + e.Value); |
212 | 0 | } else if (e.Op == "unset") { |
213 | 0 | env.UnPutEnv(e.Name); |
214 | 0 | } else if (e.Op == "string_append") { |
215 | 0 | env.Modify(e.Name, [&e](std::string& output) { output += e.Value; }); |
216 | 0 | } else if (e.Op == "string_prepend") { |
217 | 0 | env.Modify(e.Name, |
218 | 0 | [&e](std::string& output) { output.insert(0, e.Value); }); |
219 | 0 | } else if (e.Op == "path_list_append") { |
220 | 0 | env.Modify(e.Name, ListAppend(e.Value, path_sep)); |
221 | 0 | } else if (e.Op == "path_list_prepend") { |
222 | 0 | env.Modify(e.Name, ListPrepend(e.Value, path_sep)); |
223 | 0 | } else if (e.Op == "cmake_list_append") { |
224 | 0 | env.Modify(e.Name, ListAppend(e.Value, ';')); |
225 | 0 | } else if (e.Op == "cmake_list_prepend") { |
226 | 0 | env.Modify(e.Name, ListPrepend(e.Value, ';')); |
227 | 0 | } |
228 | 0 | } |
229 | 0 | } |