/src/CMake/Source/cmSetPropertyCommand.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 "cmSetPropertyCommand.h" |
4 | | |
5 | | #include <set> |
6 | | #include <sstream> |
7 | | #include <unordered_set> |
8 | | |
9 | | #include <cm/optional> |
10 | | #include <cm/string_view> |
11 | | #include <cmext/string_view> |
12 | | |
13 | | #include "cmDiagnostics.h" |
14 | | #include "cmExecutionStatus.h" |
15 | | #include "cmFileSet.h" |
16 | | #include "cmGlobalGenerator.h" |
17 | | #include "cmInstalledFile.h" |
18 | | #include "cmListFileCache.h" |
19 | | #include "cmMakefile.h" |
20 | | #include "cmPolicies.h" |
21 | | #include "cmProperty.h" |
22 | | #include "cmRange.h" |
23 | | #include "cmSourceFile.h" |
24 | | #include "cmSourceFileLocation.h" |
25 | | #include "cmState.h" |
26 | | #include "cmStringAlgorithms.h" |
27 | | #include "cmSystemTools.h" |
28 | | #include "cmTarget.h" |
29 | | #include "cmTest.h" |
30 | | #include "cmValue.h" |
31 | | #include "cmake.h" |
32 | | |
33 | | namespace { |
34 | | bool HandleGlobalMode(cmExecutionStatus& status, |
35 | | std::set<std::string> const& names, |
36 | | std::string const& propertyName, |
37 | | std::string const& propertyValue, bool appendAsString, |
38 | | bool appendMode, bool remove); |
39 | | bool HandleDirectoryMode(cmExecutionStatus& status, |
40 | | std::set<std::string> const& names, |
41 | | std::string const& propertyName, |
42 | | std::string const& propertyValue, bool appendAsString, |
43 | | bool appendMode, bool remove); |
44 | | bool HandleTargetMode(cmExecutionStatus& status, |
45 | | std::set<std::string> const& names, |
46 | | std::string const& propertyName, |
47 | | std::string const& propertyValue, bool appendAsString, |
48 | | bool appendMode, bool remove); |
49 | | bool HandleTarget(cmTarget* target, cmMakefile& makefile, |
50 | | std::string const& propertyName, |
51 | | std::string const& propertyValue, bool appendAsString, |
52 | | bool appendMode, bool remove); |
53 | | bool HandleFileSetMode(cmExecutionStatus& status, |
54 | | std::set<std::string> const& names, |
55 | | std::string const& propertyName, |
56 | | std::string const& propertyValue, bool appendAsString, |
57 | | bool appendMode, bool remove, cmTarget* target); |
58 | | bool HandleFileSet(cmFileSet* fileSet, std::string const& propertyName, |
59 | | std::string const& propertyValue, bool appendAsString, |
60 | | bool appendMode, bool remove); |
61 | | bool HandleSourceMode(cmExecutionStatus& status, |
62 | | std::set<std::string> const& names, |
63 | | std::string const& propertyName, |
64 | | std::string const& propertyValue, bool appendAsString, |
65 | | bool appendMode, bool remove, |
66 | | std::vector<cmMakefile*> const& directory_makefiles, |
67 | | bool source_file_paths_should_be_absolute); |
68 | | bool HandleSource(cmSourceFile* sf, std::string const& propertyName, |
69 | | std::string const& propertyValue, bool appendAsString, |
70 | | bool appendMode, bool remove); |
71 | | bool HandleTestMode(cmExecutionStatus& status, std::set<std::string>& names, |
72 | | std::string const& propertyName, |
73 | | std::string const& propertyValue, bool appendAsString, |
74 | | bool appendMode, bool remove, |
75 | | cmMakefile* test_directory_makefile); |
76 | | bool HandleTest(cmTest* test, std::string const& propertyName, |
77 | | std::string const& propertyValue, bool appendAsString, |
78 | | bool appendMode, bool remove); |
79 | | bool HandleCacheMode(cmExecutionStatus& status, |
80 | | std::set<std::string> const& names, |
81 | | std::string const& propertyName, |
82 | | std::string const& propertyValue, bool appendAsString, |
83 | | bool appendMode, bool remove); |
84 | | bool HandleCacheEntry(std::string const& cacheKey, cmMakefile const& makefile, |
85 | | std::string const& propertyName, |
86 | | std::string const& propertyValue, bool appendAsString, |
87 | | bool appendMode, bool remove); |
88 | | bool HandleInstallMode(cmExecutionStatus& status, |
89 | | std::set<std::string> const& names, |
90 | | std::string const& propertyName, |
91 | | std::string const& propertyValue, bool appendAsString, |
92 | | bool appendMode, bool remove); |
93 | | bool HandleInstall(cmInstalledFile* file, cmMakefile& makefile, |
94 | | std::string const& propertyName, |
95 | | std::string const& propertyValue, bool appendAsString, |
96 | | bool appendMode, bool remove); |
97 | | } |
98 | | |
99 | | namespace SetPropertyCommand { |
100 | | bool HandleFileSetTargetScopes(cmExecutionStatus& status, |
101 | | std::string& file_set_target_name, |
102 | | cmTarget*& file_set_target) |
103 | 0 | { |
104 | 0 | file_set_target = status.GetMakefile().FindTargetToUse(file_set_target_name); |
105 | 0 | if (!file_set_target) { |
106 | 0 | status.SetError( |
107 | 0 | cmStrCat("given non-existent TARGET ", file_set_target_name)); |
108 | 0 | return false; |
109 | 0 | } |
110 | 0 | return true; |
111 | 0 | } |
112 | | |
113 | | bool HandleFileSetTargetScopeValidation(cmExecutionStatus& status, |
114 | | bool file_set_target_option_enabled, |
115 | | std::string& file_set_target_name) |
116 | 0 | { |
117 | 0 | if (!file_set_target_option_enabled) { |
118 | 0 | status.SetError("required TARGET option is missing"); |
119 | 0 | return false; |
120 | 0 | } |
121 | | |
122 | | // Validate file set target scopes. |
123 | 0 | if (file_set_target_name.empty()) { |
124 | 0 | status.SetError("called with incorrect number of arguments " |
125 | 0 | "no value provided to the TARGET option"); |
126 | 0 | return false; |
127 | 0 | } |
128 | 0 | return true; |
129 | 0 | } |
130 | | |
131 | | bool HandleAndValidateFileSetTargetScopes(cmExecutionStatus& status, |
132 | | bool file_set_target_option_enabled, |
133 | | std::string& file_set_target_name, |
134 | | cmTarget*& file_set_target) |
135 | 0 | { |
136 | 0 | bool scope_options_valid = |
137 | 0 | SetPropertyCommand::HandleFileSetTargetScopeValidation( |
138 | 0 | status, file_set_target_option_enabled, file_set_target_name); |
139 | 0 | if (!scope_options_valid) { |
140 | 0 | return false; |
141 | 0 | } |
142 | | |
143 | 0 | scope_options_valid = SetPropertyCommand::HandleFileSetTargetScopes( |
144 | 0 | status, file_set_target_name, file_set_target); |
145 | 0 | return scope_options_valid; |
146 | 0 | } |
147 | | |
148 | | bool HandleSourceFileDirectoryScopes( |
149 | | cmExecutionStatus& status, std::vector<std::string>& source_file_directories, |
150 | | std::vector<std::string>& source_file_target_directories, |
151 | | std::vector<cmMakefile*>& directory_makefiles) |
152 | 0 | { |
153 | 0 | std::unordered_set<cmMakefile*> directory_makefiles_set; |
154 | |
|
155 | 0 | cmMakefile* current_dir_mf = &status.GetMakefile(); |
156 | 0 | if (!source_file_directories.empty()) { |
157 | 0 | for (std::string const& dir_path : source_file_directories) { |
158 | 0 | std::string const absolute_dir_path = cmSystemTools::CollapseFullPath( |
159 | 0 | dir_path, current_dir_mf->GetCurrentSourceDirectory()); |
160 | 0 | cmMakefile* dir_mf = |
161 | 0 | status.GetMakefile().GetGlobalGenerator()->FindMakefile( |
162 | 0 | absolute_dir_path); |
163 | 0 | if (!dir_mf) { |
164 | 0 | status.SetError(cmStrCat("given non-existent DIRECTORY ", dir_path)); |
165 | 0 | return false; |
166 | 0 | } |
167 | 0 | if (directory_makefiles_set.find(dir_mf) == |
168 | 0 | directory_makefiles_set.end()) { |
169 | 0 | directory_makefiles.push_back(dir_mf); |
170 | 0 | directory_makefiles_set.insert(dir_mf); |
171 | 0 | } |
172 | 0 | } |
173 | 0 | } |
174 | | |
175 | 0 | if (!source_file_target_directories.empty()) { |
176 | 0 | for (std::string const& target_name : source_file_target_directories) { |
177 | 0 | cmTarget* target = current_dir_mf->FindTargetToUse(target_name); |
178 | 0 | if (!target) { |
179 | 0 | status.SetError(cmStrCat( |
180 | 0 | "given non-existent target for TARGET_DIRECTORY ", target_name)); |
181 | 0 | return false; |
182 | 0 | } |
183 | 0 | cmValue target_source_dir = target->GetProperty("BINARY_DIR"); |
184 | 0 | cmMakefile* target_dir_mf = |
185 | 0 | status.GetMakefile().GetGlobalGenerator()->FindMakefile( |
186 | 0 | *target_source_dir); |
187 | |
|
188 | 0 | if (directory_makefiles_set.find(target_dir_mf) == |
189 | 0 | directory_makefiles_set.end()) { |
190 | 0 | directory_makefiles.push_back(target_dir_mf); |
191 | 0 | directory_makefiles_set.insert(target_dir_mf); |
192 | 0 | } |
193 | 0 | } |
194 | 0 | } |
195 | | |
196 | 0 | if (source_file_directories.empty() && |
197 | 0 | source_file_target_directories.empty()) { |
198 | 0 | directory_makefiles.push_back(current_dir_mf); |
199 | 0 | } |
200 | 0 | return true; |
201 | 0 | } |
202 | | |
203 | | bool HandleSourceFileDirectoryScopeValidation( |
204 | | cmExecutionStatus& status, bool source_file_directory_option_enabled, |
205 | | bool source_file_target_option_enabled, |
206 | | std::vector<std::string>& source_file_directories, |
207 | | std::vector<std::string>& source_file_target_directories) |
208 | 0 | { |
209 | | // Validate source file directory scopes. |
210 | 0 | if (source_file_directory_option_enabled && |
211 | 0 | source_file_directories.empty()) { |
212 | 0 | std::string errors = "called with incorrect number of arguments " |
213 | 0 | "no value provided to the DIRECTORY option"; |
214 | 0 | status.SetError(errors); |
215 | 0 | return false; |
216 | 0 | } |
217 | 0 | if (source_file_target_option_enabled && |
218 | 0 | source_file_target_directories.empty()) { |
219 | 0 | std::string errors = "called with incorrect number of arguments " |
220 | 0 | "no value provided to the TARGET_DIRECTORY option"; |
221 | 0 | status.SetError(errors); |
222 | 0 | return false; |
223 | 0 | } |
224 | 0 | return true; |
225 | 0 | } |
226 | | |
227 | | bool HandleAndValidateSourceFileDirectoryScopes( |
228 | | cmExecutionStatus& status, bool source_file_directory_option_enabled, |
229 | | bool source_file_target_option_enabled, |
230 | | std::vector<std::string>& source_file_directories, |
231 | | std::vector<std::string>& source_file_target_directories, |
232 | | std::vector<cmMakefile*>& source_file_directory_makefiles) |
233 | 0 | { |
234 | 0 | bool scope_options_valid = |
235 | 0 | SetPropertyCommand::HandleSourceFileDirectoryScopeValidation( |
236 | 0 | status, source_file_directory_option_enabled, |
237 | 0 | source_file_target_option_enabled, source_file_directories, |
238 | 0 | source_file_target_directories); |
239 | 0 | if (!scope_options_valid) { |
240 | 0 | return false; |
241 | 0 | } |
242 | | |
243 | 0 | scope_options_valid = SetPropertyCommand::HandleSourceFileDirectoryScopes( |
244 | 0 | status, source_file_directories, source_file_target_directories, |
245 | 0 | source_file_directory_makefiles); |
246 | 0 | return scope_options_valid; |
247 | 0 | } |
248 | | |
249 | | bool HandleTestDirectoryScopes(cmExecutionStatus& status, |
250 | | std::string& test_directory, |
251 | | cmMakefile*& directory_makefile) |
252 | 0 | { |
253 | 0 | cmMakefile* current_dir_mf = &status.GetMakefile(); |
254 | 0 | if (!test_directory.empty()) { |
255 | 0 | std::string const absolute_dir_path = cmSystemTools::CollapseFullPath( |
256 | 0 | test_directory, current_dir_mf->GetCurrentSourceDirectory()); |
257 | 0 | cmMakefile* dir_mf = |
258 | 0 | status.GetMakefile().GetGlobalGenerator()->FindMakefile( |
259 | 0 | absolute_dir_path); |
260 | 0 | if (!dir_mf) { |
261 | 0 | status.SetError( |
262 | 0 | cmStrCat("given non-existent DIRECTORY ", test_directory)); |
263 | 0 | return false; |
264 | 0 | } |
265 | 0 | directory_makefile = dir_mf; |
266 | 0 | } else { |
267 | 0 | directory_makefile = current_dir_mf; |
268 | 0 | } |
269 | 0 | return true; |
270 | 0 | } |
271 | | |
272 | | bool HandleTestDirectoryScopeValidation(cmExecutionStatus& status, |
273 | | bool test_directory_option_enabled, |
274 | | std::string& test_directory) |
275 | 0 | { |
276 | | // Validate source file directory scopes. |
277 | 0 | if (test_directory_option_enabled && test_directory.empty()) { |
278 | 0 | std::string errors = "called with incorrect number of arguments " |
279 | 0 | "no value provided to the DIRECTORY option"; |
280 | 0 | status.SetError(errors); |
281 | 0 | return false; |
282 | 0 | } |
283 | 0 | return true; |
284 | 0 | } |
285 | | |
286 | | bool HandleAndValidateTestDirectoryScopes(cmExecutionStatus& status, |
287 | | bool test_directory_option_enabled, |
288 | | std::string& test_directory, |
289 | | cmMakefile*& test_directory_makefile) |
290 | 0 | { |
291 | 0 | bool scope_options_valid = |
292 | 0 | SetPropertyCommand::HandleTestDirectoryScopeValidation( |
293 | 0 | status, test_directory_option_enabled, test_directory); |
294 | 0 | if (!scope_options_valid) { |
295 | 0 | return false; |
296 | 0 | } |
297 | | |
298 | 0 | scope_options_valid = SetPropertyCommand::HandleTestDirectoryScopes( |
299 | 0 | status, test_directory, test_directory_makefile); |
300 | 0 | return scope_options_valid; |
301 | 0 | } |
302 | | |
303 | | std::string MakeSourceFilePathAbsoluteIfNeeded( |
304 | | cmExecutionStatus& status, std::string const& source_file_path, |
305 | | bool const needed) |
306 | 0 | { |
307 | 0 | if (!needed) { |
308 | 0 | return source_file_path; |
309 | 0 | } |
310 | 0 | std::string absolute_file_path = cmSystemTools::CollapseFullPath( |
311 | 0 | source_file_path, status.GetMakefile().GetCurrentSourceDirectory()); |
312 | 0 | return absolute_file_path; |
313 | 0 | } |
314 | | |
315 | | void MakeSourceFilePathsAbsoluteIfNeeded( |
316 | | cmExecutionStatus& status, |
317 | | std::vector<std::string>& source_files_absolute_paths, |
318 | | std::vector<std::string>::const_iterator files_it_begin, |
319 | | std::vector<std::string>::const_iterator files_it_end, bool const needed) |
320 | 0 | { |
321 | | |
322 | | // Make the file paths absolute, so that relative source file paths are |
323 | | // picked up relative to the command calling site, regardless of the |
324 | | // directory scope. |
325 | 0 | std::vector<std::string>::difference_type num_files = |
326 | 0 | files_it_end - files_it_begin; |
327 | 0 | source_files_absolute_paths.reserve(num_files); |
328 | |
|
329 | 0 | if (!needed) { |
330 | 0 | source_files_absolute_paths.assign(files_it_begin, files_it_end); |
331 | 0 | return; |
332 | 0 | } |
333 | | |
334 | 0 | for (; files_it_begin != files_it_end; ++files_it_begin) { |
335 | 0 | std::string const absolute_file_path = |
336 | 0 | MakeSourceFilePathAbsoluteIfNeeded(status, *files_it_begin, true); |
337 | 0 | source_files_absolute_paths.push_back(absolute_file_path); |
338 | 0 | } |
339 | 0 | } |
340 | | |
341 | | bool HandleAndValidateSourceFilePropertyGENERATED( |
342 | | cmSourceFile* sf, std::string const& propertyValue, PropertyOp op) |
343 | 0 | { |
344 | 0 | auto const& mf = *sf->GetLocation().GetMakefile(); |
345 | |
|
346 | 0 | auto isProblematic = [&mf, &propertyValue, |
347 | 0 | op](cm::string_view policy) -> bool { |
348 | 0 | if (!cmIsOn(propertyValue) && !cmIsOff(propertyValue)) { |
349 | 0 | mf.IssueDiagnostic( |
350 | 0 | cmDiagnostics::CMD_AUTHOR, |
351 | 0 | cmStrCat("Policy ", policy, |
352 | 0 | " is set to NEW and the following non-boolean value given " |
353 | 0 | "for property 'GENERATED' is therefore not allowed:\n", |
354 | 0 | propertyValue, "\nReplace it with a boolean value!\n")); |
355 | 0 | return true; |
356 | 0 | } |
357 | 0 | if (cmIsOff(propertyValue)) { |
358 | 0 | mf.IssueDiagnostic( |
359 | 0 | cmDiagnostics::CMD_AUTHOR, |
360 | 0 | cmStrCat("Unsetting the 'GENERATED' property is not allowed under ", |
361 | 0 | policy, "!\n")); |
362 | 0 | return true; |
363 | 0 | } |
364 | 0 | if (op == PropertyOp::Append || op == PropertyOp::AppendAsString) { |
365 | 0 | mf.IssueDiagnostic( |
366 | 0 | cmDiagnostics::CMD_AUTHOR, |
367 | 0 | cmStrCat( |
368 | 0 | "Policy ", policy, |
369 | 0 | " is set to NEW and appending to the 'GENERATED' property is " |
370 | 0 | "therefore not allowed. Only setting it to \"1\" is allowed!\n")); |
371 | 0 | return true; |
372 | 0 | } |
373 | 0 | return false; |
374 | 0 | }; |
375 | |
|
376 | 0 | auto const cmp0163PolicyStatus = mf.GetPolicyStatus(cmPolicies::CMP0163); |
377 | 0 | bool const cmp0163PolicyNEW = cmp0163PolicyStatus != cmPolicies::OLD && |
378 | 0 | cmp0163PolicyStatus != cmPolicies::WARN; |
379 | 0 | if (cmp0163PolicyNEW) { |
380 | 0 | if (!isProblematic("CMP0163")) { |
381 | 0 | sf->MarkAsGenerated(); |
382 | 0 | } |
383 | 0 | return true; |
384 | 0 | } |
385 | | |
386 | 0 | auto const cmp0118PolicyStatus = mf.GetPolicyStatus(cmPolicies::CMP0118); |
387 | 0 | bool const cmp0118PolicyWARN = cmp0118PolicyStatus == cmPolicies::WARN; |
388 | 0 | bool const cmp0118PolicyNEW = |
389 | 0 | cmp0118PolicyStatus != cmPolicies::OLD && !cmp0118PolicyWARN; |
390 | |
|
391 | 0 | if (cmp0118PolicyNEW) { |
392 | 0 | if (!isProblematic("CMP0118")) { |
393 | 0 | sf->MarkAsGenerated(); |
394 | 0 | } |
395 | 0 | return true; |
396 | 0 | } |
397 | | |
398 | 0 | if (cmp0118PolicyWARN) { |
399 | 0 | if (!cmIsOn(propertyValue) && !cmIsOff(propertyValue)) { |
400 | 0 | mf.IssuePolicyWarning( |
401 | 0 | cmPolicies::CMP0118, {}, |
402 | 0 | cmStrCat("Attempt to set property 'GENERATED' with the following " |
403 | 0 | "non-boolean value (which will be interpreted as \"0\"):\n"_s, |
404 | 0 | propertyValue, |
405 | 0 | "\nThat exact value will not be retrievable. A value of " |
406 | 0 | "\"0\" will be returned instead.\n" |
407 | 0 | "This will be an error under policy CMP0118."_s)); |
408 | 0 | } |
409 | 0 | if (cmIsOff(propertyValue)) { |
410 | 0 | mf.IssuePolicyWarning(cmPolicies::CMP0118, {}, |
411 | 0 | "Unsetting property 'GENERATED' " |
412 | 0 | "will not be allowed under policy CMP0118!"_s); |
413 | 0 | } |
414 | 0 | if (op == PropertyOp::Append || op == PropertyOp::AppendAsString) { |
415 | 0 | mf.IssuePolicyWarning(cmPolicies::CMP0118, {}, |
416 | 0 | "Appending to property 'GENERATED' " |
417 | 0 | "will not be allowed under policy CMP0118!"_s); |
418 | 0 | } |
419 | 0 | } |
420 | | |
421 | | // Set property the traditional way. |
422 | 0 | switch (op) { |
423 | 0 | case PropertyOp::Append: |
424 | 0 | sf->AppendProperty("GENERATED", propertyValue, false); |
425 | 0 | break; |
426 | 0 | case PropertyOp::AppendAsString: |
427 | 0 | sf->AppendProperty("GENERATED", propertyValue, true); |
428 | 0 | break; |
429 | 0 | case PropertyOp::Remove: |
430 | 0 | sf->RemoveProperty("GENERATED"); |
431 | 0 | break; |
432 | 0 | case PropertyOp::Set: |
433 | 0 | sf->SetProperty("GENERATED", propertyValue); |
434 | 0 | break; |
435 | 0 | } |
436 | 0 | return true; |
437 | 0 | } |
438 | | |
439 | | } // END: namespace SetPropertyCommand |
440 | | |
441 | | bool cmSetPropertyCommand(std::vector<std::string> const& args, |
442 | | cmExecutionStatus& status) |
443 | 0 | { |
444 | 0 | if (args.size() < 2) { |
445 | 0 | status.SetError("called with incorrect number of arguments"); |
446 | 0 | return false; |
447 | 0 | } |
448 | | |
449 | | // Get the scope on which to set the property. |
450 | 0 | std::string const& scopeName = args.front(); |
451 | 0 | cmProperty::ScopeType scope; |
452 | 0 | if (scopeName == "GLOBAL") { |
453 | 0 | scope = cmProperty::GLOBAL; |
454 | 0 | } else if (scopeName == "DIRECTORY") { |
455 | 0 | scope = cmProperty::DIRECTORY; |
456 | 0 | } else if (scopeName == "TARGET") { |
457 | 0 | scope = cmProperty::TARGET; |
458 | 0 | } else if (scopeName == "FILE_SET") { |
459 | 0 | scope = cmProperty::FILE_SET; |
460 | 0 | } else if (scopeName == "SOURCE") { |
461 | 0 | scope = cmProperty::SOURCE_FILE; |
462 | 0 | } else if (scopeName == "TEST") { |
463 | 0 | scope = cmProperty::TEST; |
464 | 0 | } else if (scopeName == "CACHE") { |
465 | 0 | scope = cmProperty::CACHE; |
466 | 0 | } else if (scopeName == "INSTALL") { |
467 | 0 | scope = cmProperty::INSTALL; |
468 | 0 | } else { |
469 | 0 | status.SetError(cmStrCat("given invalid scope ", scopeName, |
470 | 0 | ". Valid scopes are GLOBAL, DIRECTORY, TARGET, " |
471 | 0 | "FILE_SET, SOURCE, TEST, CACHE, INSTALL.")); |
472 | 0 | return false; |
473 | 0 | } |
474 | | |
475 | 0 | bool appendAsString = false; |
476 | 0 | bool appendMode = false; |
477 | 0 | bool remove = true; |
478 | 0 | std::set<std::string> names; |
479 | 0 | std::string propertyName; |
480 | 0 | std::string propertyValue; |
481 | |
|
482 | 0 | std::string file_set_target_name; |
483 | 0 | bool file_set_target_option_enabled = false; |
484 | |
|
485 | 0 | std::vector<std::string> source_file_directories; |
486 | 0 | std::vector<std::string> source_file_target_directories; |
487 | 0 | bool source_file_directory_option_enabled = false; |
488 | 0 | bool source_file_target_option_enabled = false; |
489 | |
|
490 | 0 | std::string test_directory; |
491 | 0 | bool test_directory_option_enabled = false; |
492 | | |
493 | | // Parse the rest of the arguments up to the values. |
494 | 0 | enum Doing |
495 | 0 | { |
496 | 0 | DoingNone, |
497 | 0 | DoingNames, |
498 | 0 | DoingProperty, |
499 | 0 | DoingValues, |
500 | 0 | DoingFileSetTarget, |
501 | 0 | DoingSourceDirectory, |
502 | 0 | DoingSourceTargetDirectory, |
503 | 0 | DoingTestDirectory, |
504 | 0 | }; |
505 | 0 | Doing doing = DoingNames; |
506 | 0 | char const* sep = ""; |
507 | 0 | for (std::string const& arg : cmMakeRange(args).advance(1)) { |
508 | 0 | if (arg == "PROPERTY") { |
509 | 0 | doing = DoingProperty; |
510 | 0 | } else if (arg == "APPEND") { |
511 | 0 | doing = DoingNone; |
512 | 0 | appendMode = true; |
513 | 0 | remove = false; |
514 | 0 | appendAsString = false; |
515 | 0 | } else if (arg == "APPEND_STRING") { |
516 | 0 | doing = DoingNone; |
517 | 0 | appendMode = true; |
518 | 0 | remove = false; |
519 | 0 | appendAsString = true; |
520 | 0 | } else if (doing != DoingProperty && doing != DoingValues && |
521 | 0 | scope == cmProperty::FILE_SET && arg == "TARGET") { |
522 | 0 | doing = DoingFileSetTarget; |
523 | 0 | file_set_target_option_enabled = true; |
524 | 0 | } else if (doing != DoingProperty && doing != DoingValues && |
525 | 0 | scope == cmProperty::SOURCE_FILE && arg == "DIRECTORY") { |
526 | 0 | doing = DoingSourceDirectory; |
527 | 0 | source_file_directory_option_enabled = true; |
528 | 0 | } else if (doing != DoingProperty && doing != DoingValues && |
529 | 0 | scope == cmProperty::SOURCE_FILE && arg == "TARGET_DIRECTORY") { |
530 | 0 | doing = DoingSourceTargetDirectory; |
531 | 0 | source_file_target_option_enabled = true; |
532 | 0 | } else if (doing != DoingProperty && doing != DoingValues && |
533 | 0 | scope == cmProperty::TEST && arg == "DIRECTORY") { |
534 | 0 | doing = DoingTestDirectory; |
535 | 0 | test_directory_option_enabled = true; |
536 | 0 | } else if (doing == DoingNames) { |
537 | 0 | names.insert(arg); |
538 | 0 | } else if (doing == DoingFileSetTarget) { |
539 | 0 | file_set_target_name = arg; |
540 | 0 | file_set_target_option_enabled = true; |
541 | 0 | doing = DoingNone; |
542 | 0 | } else if (doing == DoingSourceDirectory) { |
543 | 0 | source_file_directories.push_back(arg); |
544 | 0 | } else if (doing == DoingSourceTargetDirectory) { |
545 | 0 | source_file_target_directories.push_back(arg); |
546 | 0 | } else if (doing == DoingTestDirectory) { |
547 | 0 | test_directory = arg; |
548 | 0 | doing = DoingNone; |
549 | 0 | } else if (doing == DoingProperty) { |
550 | 0 | propertyName = arg; |
551 | 0 | doing = DoingValues; |
552 | 0 | } else if (doing == DoingValues) { |
553 | 0 | propertyValue += sep; |
554 | 0 | sep = ";"; |
555 | 0 | propertyValue += arg; |
556 | 0 | remove = false; |
557 | 0 | } else { |
558 | 0 | status.SetError(cmStrCat("given invalid argument \"", arg, "\".")); |
559 | 0 | return false; |
560 | 0 | } |
561 | 0 | } |
562 | | |
563 | | // Make sure a property name was found. |
564 | 0 | if (propertyName.empty()) { |
565 | 0 | status.SetError("not given a PROPERTY <name> argument."); |
566 | 0 | return false; |
567 | 0 | } |
568 | | |
569 | | // Dispatch property setting. |
570 | 0 | switch (scope) { |
571 | 0 | case cmProperty::GLOBAL: |
572 | 0 | return HandleGlobalMode(status, names, propertyName, propertyValue, |
573 | 0 | appendAsString, appendMode, remove); |
574 | 0 | case cmProperty::DIRECTORY: |
575 | 0 | return HandleDirectoryMode(status, names, propertyName, propertyValue, |
576 | 0 | appendAsString, appendMode, remove); |
577 | 0 | case cmProperty::TARGET: |
578 | 0 | return HandleTargetMode(status, names, propertyName, propertyValue, |
579 | 0 | appendAsString, appendMode, remove); |
580 | 0 | case cmProperty::FILE_SET: { |
581 | 0 | cmTarget* file_set_target; |
582 | 0 | if (!SetPropertyCommand::HandleAndValidateFileSetTargetScopes( |
583 | 0 | status, file_set_target_option_enabled, file_set_target_name, |
584 | 0 | file_set_target)) { |
585 | 0 | return false; |
586 | 0 | } |
587 | 0 | return HandleFileSetMode(status, names, propertyName, propertyValue, |
588 | 0 | appendAsString, appendMode, remove, |
589 | 0 | file_set_target); |
590 | 0 | } |
591 | 0 | case cmProperty::SOURCE_FILE: { |
592 | 0 | std::vector<cmMakefile*> source_file_directory_makefiles; |
593 | 0 | if (!SetPropertyCommand::HandleAndValidateSourceFileDirectoryScopes( |
594 | 0 | status, source_file_directory_option_enabled, |
595 | 0 | source_file_target_option_enabled, source_file_directories, |
596 | 0 | source_file_target_directories, source_file_directory_makefiles)) { |
597 | 0 | return false; |
598 | 0 | } |
599 | 0 | bool source_file_paths_should_be_absolute = |
600 | 0 | source_file_directory_option_enabled || |
601 | 0 | source_file_target_option_enabled; |
602 | 0 | return HandleSourceMode(status, names, propertyName, propertyValue, |
603 | 0 | appendAsString, appendMode, remove, |
604 | 0 | source_file_directory_makefiles, |
605 | 0 | source_file_paths_should_be_absolute); |
606 | 0 | } |
607 | 0 | case cmProperty::TEST: { |
608 | 0 | cmMakefile* test_directory_makefile; |
609 | 0 | if (!SetPropertyCommand::HandleAndValidateTestDirectoryScopes( |
610 | 0 | status, test_directory_option_enabled, test_directory, |
611 | 0 | test_directory_makefile)) { |
612 | 0 | return false; |
613 | 0 | } |
614 | 0 | return HandleTestMode(status, names, propertyName, propertyValue, |
615 | 0 | appendAsString, appendMode, remove, |
616 | 0 | test_directory_makefile); |
617 | 0 | } |
618 | 0 | case cmProperty::CACHE: |
619 | 0 | return HandleCacheMode(status, names, propertyName, propertyValue, |
620 | 0 | appendAsString, appendMode, remove); |
621 | 0 | case cmProperty::INSTALL: |
622 | 0 | return HandleInstallMode(status, names, propertyName, propertyValue, |
623 | 0 | appendAsString, appendMode, remove); |
624 | | |
625 | 0 | case cmProperty::VARIABLE: |
626 | 0 | case cmProperty::CACHED_VARIABLE: |
627 | 0 | break; // should never happen |
628 | 0 | } |
629 | 0 | return true; |
630 | 0 | } |
631 | | |
632 | | namespace /* anonymous */ { |
633 | | bool HandleGlobalMode(cmExecutionStatus& status, |
634 | | std::set<std::string> const& names, |
635 | | std::string const& propertyName, |
636 | | std::string const& propertyValue, bool appendAsString, |
637 | | bool appendMode, bool remove) |
638 | 0 | { |
639 | 0 | if (!names.empty()) { |
640 | 0 | status.SetError("given names for GLOBAL scope."); |
641 | 0 | return false; |
642 | 0 | } |
643 | | |
644 | | // Set or append the property. |
645 | 0 | cmake* cm = status.GetMakefile().GetCMakeInstance(); |
646 | 0 | if (appendMode) { |
647 | 0 | cm->AppendProperty(propertyName, propertyValue, appendAsString); |
648 | 0 | } else { |
649 | 0 | if (remove) { |
650 | 0 | cm->SetProperty(propertyName, nullptr); |
651 | 0 | } else { |
652 | 0 | cm->SetProperty(propertyName, propertyValue); |
653 | 0 | } |
654 | 0 | } |
655 | |
|
656 | 0 | return true; |
657 | 0 | } |
658 | | |
659 | | bool HandleDirectoryMode(cmExecutionStatus& status, |
660 | | std::set<std::string> const& names, |
661 | | std::string const& propertyName, |
662 | | std::string const& propertyValue, bool appendAsString, |
663 | | bool appendMode, bool remove) |
664 | 0 | { |
665 | 0 | if (names.size() > 1) { |
666 | 0 | status.SetError("allows at most one name for DIRECTORY scope."); |
667 | 0 | return false; |
668 | 0 | } |
669 | | |
670 | | // Default to the current directory. |
671 | 0 | cmMakefile* mf = &status.GetMakefile(); |
672 | | |
673 | | // Lookup the directory if given. |
674 | 0 | if (!names.empty()) { |
675 | | // Construct the directory name. Interpret relative paths with |
676 | | // respect to the current directory. |
677 | 0 | std::string dir = cmSystemTools::CollapseFullPath( |
678 | 0 | *names.begin(), status.GetMakefile().GetCurrentSourceDirectory()); |
679 | |
|
680 | 0 | mf = status.GetMakefile().GetGlobalGenerator()->FindMakefile(dir); |
681 | 0 | if (!mf) { |
682 | | // Could not find the directory. |
683 | 0 | status.SetError( |
684 | 0 | "DIRECTORY scope provided but requested directory was not found. " |
685 | 0 | "This could be because the directory argument was invalid or, " |
686 | 0 | "it is valid but has not been processed yet."); |
687 | 0 | return false; |
688 | 0 | } |
689 | 0 | } |
690 | | |
691 | | // Set or append the property. |
692 | 0 | if (appendMode) { |
693 | 0 | mf->AppendProperty(propertyName, propertyValue, appendAsString); |
694 | 0 | } else { |
695 | 0 | if (remove) { |
696 | 0 | mf->SetProperty(propertyName, nullptr); |
697 | 0 | } else { |
698 | 0 | mf->SetProperty(propertyName, propertyValue); |
699 | 0 | } |
700 | 0 | } |
701 | |
|
702 | 0 | return true; |
703 | 0 | } |
704 | | |
705 | | bool HandleTargetMode(cmExecutionStatus& status, |
706 | | std::set<std::string> const& names, |
707 | | std::string const& propertyName, |
708 | | std::string const& propertyValue, bool appendAsString, |
709 | | bool appendMode, bool remove) |
710 | 0 | { |
711 | 0 | for (std::string const& name : names) { |
712 | 0 | if (status.GetMakefile().IsAlias(name)) { |
713 | 0 | status.SetError("can not be used on an ALIAS target."); |
714 | 0 | return false; |
715 | 0 | } |
716 | | |
717 | 0 | if (cmTarget* target = status.GetMakefile().FindTargetToUse(name)) { |
718 | 0 | if (target->IsSymbolic()) { |
719 | 0 | status.SetError("can not be used on a SYMBOLIC target."); |
720 | 0 | return false; |
721 | 0 | } |
722 | | |
723 | | // Handle the current target. |
724 | 0 | if (!HandleTarget(target, status.GetMakefile(), propertyName, |
725 | 0 | propertyValue, appendAsString, appendMode, remove)) { |
726 | 0 | return false; |
727 | 0 | } |
728 | 0 | } else { |
729 | 0 | status.SetError(cmStrCat("could not find TARGET ", name, |
730 | 0 | ". Perhaps it has not yet been created.")); |
731 | 0 | return false; |
732 | 0 | } |
733 | 0 | } |
734 | 0 | return true; |
735 | 0 | } |
736 | | |
737 | | bool HandleTarget(cmTarget* target, cmMakefile& makefile, |
738 | | std::string const& propertyName, |
739 | | std::string const& propertyValue, bool appendAsString, |
740 | | bool appendMode, bool remove) |
741 | 0 | { |
742 | | // Set or append the property. |
743 | 0 | if (appendMode) { |
744 | 0 | target->AppendProperty(propertyName, propertyValue, |
745 | 0 | makefile.GetBacktrace(), appendAsString); |
746 | 0 | } else { |
747 | 0 | if (remove) { |
748 | 0 | target->SetProperty(propertyName, nullptr); |
749 | 0 | } else { |
750 | 0 | target->SetProperty(propertyName, propertyValue); |
751 | 0 | } |
752 | 0 | } |
753 | | |
754 | | // Check the resulting value. |
755 | 0 | target->CheckProperty(propertyName, &makefile); |
756 | |
|
757 | 0 | return true; |
758 | 0 | } |
759 | | |
760 | | bool HandleFileSetMode(cmExecutionStatus& status, |
761 | | std::set<std::string> const& names, |
762 | | std::string const& propertyName, |
763 | | std::string const& propertyValue, bool appendAsString, |
764 | | bool appendMode, bool remove, cmTarget* target) |
765 | 0 | { |
766 | 0 | for (std::string const& name : names) { |
767 | 0 | if (cmFileSet* fileSet = target->GetFileSet(name)) { |
768 | | // Handle the current file set. |
769 | 0 | if (!HandleFileSet(fileSet, propertyName, propertyValue, appendAsString, |
770 | 0 | appendMode, remove)) { |
771 | 0 | return false; |
772 | 0 | } |
773 | 0 | } else { |
774 | 0 | status.SetError(cmStrCat("could not find FILE_SET ", name, |
775 | 0 | " for TARGET ", target->GetName(), |
776 | 0 | ". Perhaps it has not yet been created.")); |
777 | 0 | return false; |
778 | 0 | } |
779 | 0 | } |
780 | 0 | return true; |
781 | 0 | } |
782 | | |
783 | | bool HandleFileSet(cmFileSet* fileSet, std::string const& propertyName, |
784 | | std::string const& propertyValue, bool appendAsString, |
785 | | bool appendMode, bool remove) |
786 | 0 | { |
787 | | // Set or append the property. |
788 | 0 | if (appendMode) { |
789 | 0 | fileSet->AppendProperty(propertyName, propertyValue, appendAsString); |
790 | 0 | } else { |
791 | 0 | if (remove) { |
792 | 0 | fileSet->SetProperty(propertyName, nullptr); |
793 | 0 | } else { |
794 | 0 | fileSet->SetProperty(propertyName, propertyValue); |
795 | 0 | } |
796 | 0 | } |
797 | 0 | return true; |
798 | 0 | } |
799 | | |
800 | | bool HandleSourceMode(cmExecutionStatus& status, |
801 | | std::set<std::string> const& names, |
802 | | std::string const& propertyName, |
803 | | std::string const& propertyValue, bool appendAsString, |
804 | | bool appendMode, bool remove, |
805 | | std::vector<cmMakefile*> const& directory_makefiles, |
806 | | bool const source_file_paths_should_be_absolute) |
807 | 0 | { |
808 | 0 | std::vector<std::string> files_absolute; |
809 | 0 | std::vector<std::string> unique_files(names.begin(), names.end()); |
810 | 0 | SetPropertyCommand::MakeSourceFilePathsAbsoluteIfNeeded( |
811 | 0 | status, files_absolute, unique_files.begin(), unique_files.end(), |
812 | 0 | source_file_paths_should_be_absolute); |
813 | |
|
814 | 0 | for (auto* const mf : directory_makefiles) { |
815 | 0 | for (std::string const& name : files_absolute) { |
816 | | // Get the source file. |
817 | 0 | if (cmSourceFile* sf = mf->GetOrCreateSource(name)) { |
818 | 0 | if (!HandleSource(sf, propertyName, propertyValue, appendAsString, |
819 | 0 | appendMode, remove)) { |
820 | 0 | return false; |
821 | 0 | } |
822 | 0 | } else { |
823 | 0 | status.SetError(cmStrCat( |
824 | 0 | "given SOURCE name that could not be found or created: ", name)); |
825 | 0 | return false; |
826 | 0 | } |
827 | 0 | } |
828 | 0 | } |
829 | | |
830 | 0 | return true; |
831 | 0 | } |
832 | | |
833 | | bool HandleSource(cmSourceFile* sf, std::string const& propertyName, |
834 | | std::string const& propertyValue, bool appendAsString, |
835 | | bool appendMode, bool remove) |
836 | 0 | { |
837 | | // Special validation and handling of GENERATED flag? |
838 | 0 | if (propertyName == "GENERATED") { |
839 | 0 | SetPropertyCommand::PropertyOp op = (remove) |
840 | 0 | ? SetPropertyCommand::PropertyOp::Remove |
841 | 0 | : (appendAsString) ? SetPropertyCommand::PropertyOp::AppendAsString |
842 | 0 | : (appendMode) ? SetPropertyCommand::PropertyOp::Append |
843 | 0 | : SetPropertyCommand::PropertyOp::Set; |
844 | 0 | return SetPropertyCommand::HandleAndValidateSourceFilePropertyGENERATED( |
845 | 0 | sf, propertyValue, op); |
846 | 0 | } |
847 | | |
848 | | // Set or append the property. |
849 | 0 | if (appendMode) { |
850 | 0 | sf->AppendProperty(propertyName, propertyValue, appendAsString); |
851 | 0 | } else { |
852 | 0 | if (remove) { |
853 | 0 | sf->RemoveProperty(propertyName); |
854 | 0 | } else { |
855 | 0 | sf->SetProperty(propertyName, propertyValue); |
856 | 0 | } |
857 | 0 | } |
858 | 0 | return true; |
859 | 0 | } |
860 | | |
861 | | bool HandleTestMode(cmExecutionStatus& status, std::set<std::string>& names, |
862 | | std::string const& propertyName, |
863 | | std::string const& propertyValue, bool appendAsString, |
864 | | bool appendMode, bool remove, cmMakefile* test_makefile) |
865 | 0 | { |
866 | | // Look for tests with all names given. |
867 | 0 | std::set<std::string>::iterator next; |
868 | 0 | for (auto ni = names.begin(); ni != names.end(); ni = next) { |
869 | 0 | next = ni; |
870 | 0 | ++next; |
871 | 0 | if (cmTest* test = test_makefile->GetTest(*ni)) { |
872 | 0 | if (HandleTest(test, propertyName, propertyValue, appendAsString, |
873 | 0 | appendMode, remove)) { |
874 | 0 | names.erase(ni); |
875 | 0 | } else { |
876 | 0 | return false; |
877 | 0 | } |
878 | 0 | } |
879 | 0 | } |
880 | | |
881 | | // Names that are still left were not found. |
882 | 0 | if (!names.empty()) { |
883 | 0 | std::ostringstream e; |
884 | 0 | e << "given TEST names that do not exist:\n"; |
885 | 0 | for (std::string const& name : names) { |
886 | 0 | e << " " << name << "\n"; |
887 | 0 | } |
888 | 0 | status.SetError(e.str()); |
889 | 0 | return false; |
890 | 0 | } |
891 | 0 | return true; |
892 | 0 | } |
893 | | |
894 | | bool HandleTest(cmTest* test, std::string const& propertyName, |
895 | | std::string const& propertyValue, bool appendAsString, |
896 | | bool appendMode, bool remove) |
897 | 0 | { |
898 | | // Set or append the property. |
899 | 0 | if (appendMode) { |
900 | 0 | test->AppendProperty(propertyName, propertyValue, appendAsString); |
901 | 0 | } else { |
902 | 0 | if (remove) { |
903 | 0 | test->SetProperty(propertyName, nullptr); |
904 | 0 | } else { |
905 | 0 | test->SetProperty(propertyName, propertyValue); |
906 | 0 | } |
907 | 0 | } |
908 | |
|
909 | 0 | return true; |
910 | 0 | } |
911 | | |
912 | | bool HandleCacheMode(cmExecutionStatus& status, |
913 | | std::set<std::string> const& names, |
914 | | std::string const& propertyName, |
915 | | std::string const& propertyValue, bool appendAsString, |
916 | | bool appendMode, bool remove) |
917 | 0 | { |
918 | 0 | if (propertyName == "ADVANCED") { |
919 | 0 | if (!remove && !cmIsOn(propertyValue) && !cmIsOff(propertyValue)) { |
920 | 0 | status.SetError(cmStrCat("given non-boolean value \"", propertyValue, |
921 | 0 | R"(" for CACHE property "ADVANCED". )")); |
922 | 0 | return false; |
923 | 0 | } |
924 | 0 | } else if (propertyName == "TYPE") { |
925 | 0 | if (!cmState::IsCacheEntryType(propertyValue)) { |
926 | 0 | status.SetError( |
927 | 0 | cmStrCat("given invalid CACHE entry TYPE \"", propertyValue, '"')); |
928 | 0 | return false; |
929 | 0 | } |
930 | 0 | } else if (propertyName != "HELPSTRING" && propertyName != "STRINGS" && |
931 | 0 | propertyName != "VALUE") { |
932 | 0 | status.SetError( |
933 | 0 | cmStrCat("given invalid CACHE property ", propertyName, |
934 | 0 | ". " |
935 | 0 | "Settable CACHE properties are: " |
936 | 0 | "ADVANCED, HELPSTRING, STRINGS, TYPE, and VALUE.")); |
937 | 0 | return false; |
938 | 0 | } |
939 | | |
940 | 0 | for (std::string const& name : names) { |
941 | | // Get the source file. |
942 | 0 | cmake* cm = status.GetMakefile().GetCMakeInstance(); |
943 | 0 | cmValue existingValue = cm->GetState()->GetCacheEntryValue(name); |
944 | 0 | if (existingValue) { |
945 | 0 | if (!HandleCacheEntry(name, status.GetMakefile(), propertyName, |
946 | 0 | propertyValue, appendAsString, appendMode, |
947 | 0 | remove)) { |
948 | 0 | return false; |
949 | 0 | } |
950 | 0 | } else { |
951 | 0 | status.SetError(cmStrCat("could not find CACHE variable ", name, |
952 | 0 | ". Perhaps it has not yet been created.")); |
953 | 0 | return false; |
954 | 0 | } |
955 | 0 | } |
956 | 0 | return true; |
957 | 0 | } |
958 | | |
959 | | bool HandleCacheEntry(std::string const& cacheKey, cmMakefile const& makefile, |
960 | | std::string const& propertyName, |
961 | | std::string const& propertyValue, bool appendAsString, |
962 | | bool appendMode, bool remove) |
963 | 0 | { |
964 | | // Set or append the property. |
965 | 0 | cmState* state = makefile.GetState(); |
966 | 0 | if (remove) { |
967 | 0 | state->RemoveCacheEntryProperty(cacheKey, propertyName); |
968 | 0 | } |
969 | 0 | if (appendMode) { |
970 | 0 | state->AppendCacheEntryProperty(cacheKey, propertyName, propertyValue, |
971 | 0 | appendAsString); |
972 | 0 | } else { |
973 | 0 | state->SetCacheEntryProperty(cacheKey, propertyName, propertyValue); |
974 | 0 | } |
975 | |
|
976 | 0 | return true; |
977 | 0 | } |
978 | | |
979 | | bool HandleInstallMode(cmExecutionStatus& status, |
980 | | std::set<std::string> const& names, |
981 | | std::string const& propertyName, |
982 | | std::string const& propertyValue, bool appendAsString, |
983 | | bool appendMode, bool remove) |
984 | 0 | { |
985 | 0 | cmake* cm = status.GetMakefile().GetCMakeInstance(); |
986 | |
|
987 | 0 | for (std::string const& name : names) { |
988 | 0 | if (cmInstalledFile* file = |
989 | 0 | cm->GetOrCreateInstalledFile(&status.GetMakefile(), name)) { |
990 | 0 | if (!HandleInstall(file, status.GetMakefile(), propertyName, |
991 | 0 | propertyValue, appendAsString, appendMode, remove)) { |
992 | 0 | return false; |
993 | 0 | } |
994 | 0 | } else { |
995 | 0 | status.SetError(cmStrCat( |
996 | 0 | "given INSTALL name that could not be found or created: ", name)); |
997 | 0 | return false; |
998 | 0 | } |
999 | 0 | } |
1000 | 0 | return true; |
1001 | 0 | } |
1002 | | |
1003 | | bool HandleInstall(cmInstalledFile* file, cmMakefile& makefile, |
1004 | | std::string const& propertyName, |
1005 | | std::string const& propertyValue, bool appendAsString, |
1006 | | bool appendMode, bool remove) |
1007 | 0 | { |
1008 | | // Set or append the property. |
1009 | 0 | if (remove) { |
1010 | 0 | file->RemoveProperty(propertyName); |
1011 | 0 | } else if (appendMode) { |
1012 | 0 | file->AppendProperty(&makefile, propertyName, propertyValue, |
1013 | 0 | appendAsString); |
1014 | 0 | } else { |
1015 | 0 | file->SetProperty(&makefile, propertyName, propertyValue); |
1016 | 0 | } |
1017 | 0 | return true; |
1018 | 0 | } |
1019 | | } |