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 "cmCMakePathCommand.h"
4
5 #include <algorithm>
6 #include <functional>
7 #include <iomanip>
8 #include <map>
9 #include <sstream>
10 #include <string>
11 #include <utility>
12 #include <vector>
13
14 #include <cm/string_view>
15 #include <cmext/string_view>
16
17 #include "cmArgumentParser.h"
18 #include "cmCMakePath.h"
19 #include "cmExecutionStatus.h"
20 #include "cmMakefile.h"
21 #include "cmRange.h"
22 #include "cmStringAlgorithms.h"
23 #include "cmSubcommandTable.h"
24 #include "cmSystemTools.h"
25 #include "cmValue.h"
26
27 namespace {
28 // Helper classes for argument parsing
29 template <typename Result>
30 class CMakePathArgumentParser : public cmArgumentParser<Result>
31 {
32 public:
CMakePathArgumentParser()33 CMakePathArgumentParser()
34 : cmArgumentParser<Result>()
35 {
36 }
37
38 template <typename T>
Bind(cm::static_string_view name,T Result::* member)39 CMakePathArgumentParser& Bind(cm::static_string_view name, T Result::*member)
40 {
41 this->cmArgumentParser<Result>::Bind(name, member);
42 return *this;
43 }
44
45 template <int Advance = 2>
Parse(std::vector<std::string> const & args,std::vector<std::string> * keywordsMissingValue=nullptr,std::vector<std::string> * parsedKeywords=nullptr) const46 Result Parse(std::vector<std::string> const& args,
47 std::vector<std::string>* keywordsMissingValue = nullptr,
48 std::vector<std::string>* parsedKeywords = nullptr) const
49 {
50 this->Inputs.clear();
51
52 return this->cmArgumentParser<Result>::Parse(
53 cmMakeRange(args).advance(Advance), &this->Inputs, keywordsMissingValue,
54 parsedKeywords);
55 }
56
GetInputs() const57 const std::vector<std::string>& GetInputs() const { return this->Inputs; }
58
59 protected:
60 mutable std::vector<std::string> Inputs;
61 };
62
63 // OUTPUT_VARIABLE is expected
64 template <typename Result>
65 class ArgumentParserWithOutputVariable : public CMakePathArgumentParser<Result>
66 {
67 public:
ArgumentParserWithOutputVariable()68 ArgumentParserWithOutputVariable()
69 : CMakePathArgumentParser<Result>()
70 {
71 this->Bind("OUTPUT_VARIABLE"_s, &Result::Output);
72 }
73
74 template <typename T>
Bind(cm::static_string_view name,T Result::* member)75 ArgumentParserWithOutputVariable& Bind(cm::static_string_view name,
76 T Result::*member)
77 {
78 this->cmArgumentParser<Result>::Bind(name, member);
79 return *this;
80 }
81
82 template <int Advance = 2>
Parse(std::vector<std::string> const & args) const83 Result Parse(std::vector<std::string> const& args) const
84 {
85 this->KeywordsMissingValue.clear();
86 this->ParsedKeywords.clear();
87
88 return this->CMakePathArgumentParser<Result>::template Parse<Advance>(
89 args, &this->KeywordsMissingValue, &this->ParsedKeywords);
90 }
91
GetKeywordsMissingValue() const92 const std::vector<std::string>& GetKeywordsMissingValue() const
93 {
94 return this->KeywordsMissingValue;
95 }
GetParsedKeywords() const96 const std::vector<std::string>& GetParsedKeywords() const
97 {
98 return this->ParsedKeywords;
99 }
100
checkOutputVariable(const Result & arguments,cmExecutionStatus & status) const101 bool checkOutputVariable(const Result& arguments,
102 cmExecutionStatus& status) const
103 {
104 if (std::find(this->GetKeywordsMissingValue().begin(),
105 this->GetKeywordsMissingValue().end(),
106 "OUTPUT_VARIABLE"_s) !=
107 this->GetKeywordsMissingValue().end()) {
108 status.SetError("OUTPUT_VARIABLE requires an argument.");
109 return false;
110 }
111
112 if (std::find(this->GetParsedKeywords().begin(),
113 this->GetParsedKeywords().end(),
114 "OUTPUT_VARIABLE"_s) != this->GetParsedKeywords().end() &&
115 arguments.Output.empty()) {
116 status.SetError("Invalid name for output variable.");
117 return false;
118 }
119
120 return true;
121 }
122
123 private:
124 mutable std::vector<std::string> KeywordsMissingValue;
125 mutable std::vector<std::string> ParsedKeywords;
126 };
127
128 struct OutputVariable
129 {
130 std::string Output;
131 };
132 // Usable when OUTPUT_VARIABLE is the only option
133 class OutputVariableParser
134 : public ArgumentParserWithOutputVariable<OutputVariable>
135 {
136 };
137
138 struct NormalizeOption
139 {
140 bool Normalize = false;
141 };
142 // Usable when NORMALIZE is the only option
143 class NormalizeParser : public CMakePathArgumentParser<NormalizeOption>
144 {
145 public:
NormalizeParser()146 NormalizeParser() { this->Bind("NORMALIZE"_s, &NormalizeOption::Normalize); }
147 };
148
149 // retrieve value of input path from specified variable
getInputPath(const std::string & arg,cmExecutionStatus & status,std::string & path)150 bool getInputPath(const std::string& arg, cmExecutionStatus& status,
151 std::string& path)
152 {
153 cmValue def = status.GetMakefile().GetDefinition(arg);
154 if (!def) {
155 status.SetError("undefined variable for input path.");
156 return false;
157 }
158
159 path = *def;
160 return true;
161 }
162
HandleGetCommand(std::vector<std::string> const & args,cmExecutionStatus & status)163 bool HandleGetCommand(std::vector<std::string> const& args,
164 cmExecutionStatus& status)
165 {
166 static std::map<cm::string_view,
167 std::function<cmCMakePath(const cmCMakePath&, bool)>> const
168 actions{ { "ROOT_NAME"_s,
169 [](const cmCMakePath& path, bool) -> cmCMakePath {
170 return path.GetRootName();
171 } },
172 { "ROOT_DIRECTORY"_s,
173 [](const cmCMakePath& path, bool) -> cmCMakePath {
174 return path.GetRootDirectory();
175 } },
176 { "ROOT_PATH"_s,
177 [](const cmCMakePath& path, bool) -> cmCMakePath {
178 return path.GetRootPath();
179 } },
180 { "FILENAME"_s,
181 [](const cmCMakePath& path, bool) -> cmCMakePath {
182 return path.GetFileName();
183 } },
184 { "EXTENSION"_s,
185 [](const cmCMakePath& path, bool last_only) -> cmCMakePath {
186 if (last_only) {
187 return path.GetExtension();
188 }
189 return path.GetWideExtension();
190 } },
191 { "STEM"_s,
192 [](const cmCMakePath& path, bool last_only) -> cmCMakePath {
193 if (last_only) {
194 return path.GetStem();
195 }
196 return path.GetNarrowStem();
197 } },
198 { "RELATIVE_PART"_s,
199 [](const cmCMakePath& path, bool) -> cmCMakePath {
200 return path.GetRelativePath();
201 } },
202 { "PARENT_PATH"_s,
203 [](const cmCMakePath& path, bool) -> cmCMakePath {
204 return path.GetParentPath();
205 } } };
206
207 if (args.size() < 4) {
208 status.SetError("GET must be called with at least three arguments.");
209 return false;
210 }
211
212 const auto& action = args[2];
213
214 if (actions.find(action) == actions.end()) {
215 status.SetError(
216 cmStrCat("GET called with an unknown action: ", action, "."));
217 return false;
218 }
219
220 struct Arguments
221 {
222 bool LastOnly = false;
223 };
224
225 CMakePathArgumentParser<Arguments> parser;
226 if ((action == "EXTENSION"_s || action == "STEM"_s)) {
227 parser.Bind("LAST_ONLY"_s, &Arguments::LastOnly);
228 }
229
230 Arguments const arguments = parser.Parse<3>(args);
231
232 if (parser.GetInputs().size() != 1) {
233 status.SetError("GET called with unexpected arguments.");
234 return false;
235 }
236 if (parser.GetInputs().front().empty()) {
237 status.SetError("Invalid name for output variable.");
238 return false;
239 }
240
241 std::string path;
242 if (!getInputPath(args[1], status, path)) {
243 return false;
244 }
245
246 auto result = actions.at(action)(path, arguments.LastOnly);
247
248 status.GetMakefile().AddDefinition(parser.GetInputs().front(),
249 result.String());
250
251 return true;
252 }
253
HandleSetCommand(std::vector<std::string> const & args,cmExecutionStatus & status)254 bool HandleSetCommand(std::vector<std::string> const& args,
255 cmExecutionStatus& status)
256 {
257 if (args.size() < 3 || args.size() > 4) {
258 status.SetError("SET must be called with two or three arguments.");
259 return false;
260 }
261
262 if (args[1].empty()) {
263 status.SetError("Invalid name for path variable.");
264 return false;
265 }
266
267 static NormalizeParser const parser;
268
269 const auto arguments = parser.Parse(args);
270
271 if (parser.GetInputs().size() != 1) {
272 status.SetError("SET called with unexpected arguments.");
273 return false;
274 }
275
276 auto path =
277 cmCMakePath(parser.GetInputs().front(), cmCMakePath::native_format);
278
279 if (arguments.Normalize) {
280 path = path.Normal();
281 }
282
283 status.GetMakefile().AddDefinition(args[1], path.GenericString());
284
285 return true;
286 }
287
HandleAppendCommand(std::vector<std::string> const & args,cmExecutionStatus & status)288 bool HandleAppendCommand(std::vector<std::string> const& args,
289 cmExecutionStatus& status)
290 {
291 if (args[1].empty()) {
292 status.SetError("Invalid name for path variable.");
293 return false;
294 }
295
296 static OutputVariableParser const parser{};
297
298 const auto arguments = parser.Parse(args);
299
300 if (!parser.checkOutputVariable(arguments, status)) {
301 return false;
302 }
303
304 cmCMakePath path(status.GetMakefile().GetSafeDefinition(args[1]));
305 for (const auto& input : parser.GetInputs()) {
306 path /= input;
307 }
308
309 status.GetMakefile().AddDefinition(
310 arguments.Output.empty() ? args[1] : arguments.Output, path.String());
311
312 return true;
313 }
314
HandleAppendStringCommand(std::vector<std::string> const & args,cmExecutionStatus & status)315 bool HandleAppendStringCommand(std::vector<std::string> const& args,
316 cmExecutionStatus& status)
317 {
318 static OutputVariableParser const parser{};
319
320 const auto arguments = parser.Parse(args);
321
322 if (!parser.checkOutputVariable(arguments, status)) {
323 return false;
324 }
325
326 std::string inputPath;
327 if (!getInputPath(args[1], status, inputPath)) {
328 return false;
329 }
330
331 cmCMakePath path(inputPath);
332 for (const auto& input : parser.GetInputs()) {
333 path += input;
334 }
335
336 status.GetMakefile().AddDefinition(
337 arguments.Output.empty() ? args[1] : arguments.Output, path.String());
338
339 return true;
340 }
341
HandleRemoveFilenameCommand(std::vector<std::string> const & args,cmExecutionStatus & status)342 bool HandleRemoveFilenameCommand(std::vector<std::string> const& args,
343 cmExecutionStatus& status)
344 {
345 static OutputVariableParser const parser{};
346
347 const auto arguments = parser.Parse(args);
348
349 if (!parser.checkOutputVariable(arguments, status)) {
350 return false;
351 }
352
353 if (!parser.GetInputs().empty()) {
354 status.SetError("REMOVE_FILENAME called with unexpected arguments.");
355 return false;
356 }
357
358 std::string inputPath;
359 if (!getInputPath(args[1], status, inputPath)) {
360 return false;
361 }
362
363 cmCMakePath path(inputPath);
364 path.RemoveFileName();
365
366 status.GetMakefile().AddDefinition(
367 arguments.Output.empty() ? args[1] : arguments.Output, path.String());
368
369 return true;
370 }
371
HandleReplaceFilenameCommand(std::vector<std::string> const & args,cmExecutionStatus & status)372 bool HandleReplaceFilenameCommand(std::vector<std::string> const& args,
373 cmExecutionStatus& status)
374 {
375 static OutputVariableParser const parser{};
376
377 const auto arguments = parser.Parse(args);
378
379 if (!parser.checkOutputVariable(arguments, status)) {
380 return false;
381 }
382
383 if (parser.GetInputs().size() > 1) {
384 status.SetError("REPLACE_FILENAME called with unexpected arguments.");
385 return false;
386 }
387
388 std::string inputPath;
389 if (!getInputPath(args[1], status, inputPath)) {
390 return false;
391 }
392
393 cmCMakePath path(inputPath);
394 path.ReplaceFileName(
395 parser.GetInputs().empty() ? "" : parser.GetInputs().front());
396
397 status.GetMakefile().AddDefinition(
398 arguments.Output.empty() ? args[1] : arguments.Output, path.String());
399
400 return true;
401 }
402
HandleRemoveExtensionCommand(std::vector<std::string> const & args,cmExecutionStatus & status)403 bool HandleRemoveExtensionCommand(std::vector<std::string> const& args,
404 cmExecutionStatus& status)
405 {
406 struct Arguments
407 {
408 std::string Output;
409 bool LastOnly = false;
410 };
411
412 static auto const parser =
413 ArgumentParserWithOutputVariable<Arguments>{}.Bind("LAST_ONLY"_s,
414 &Arguments::LastOnly);
415
416 Arguments const arguments = parser.Parse(args);
417
418 if (!parser.checkOutputVariable(arguments, status)) {
419 return false;
420 }
421
422 if (!parser.GetInputs().empty()) {
423 status.SetError("REMOVE_EXTENSION called with unexpected arguments.");
424 return false;
425 }
426
427 std::string inputPath;
428 if (!getInputPath(args[1], status, inputPath)) {
429 return false;
430 }
431
432 cmCMakePath path(inputPath);
433
434 if (arguments.LastOnly) {
435 path.RemoveExtension();
436 } else {
437 path.RemoveWideExtension();
438 }
439
440 status.GetMakefile().AddDefinition(
441 arguments.Output.empty() ? args[1] : arguments.Output, path.String());
442
443 return true;
444 }
445
HandleReplaceExtensionCommand(std::vector<std::string> const & args,cmExecutionStatus & status)446 bool HandleReplaceExtensionCommand(std::vector<std::string> const& args,
447 cmExecutionStatus& status)
448 {
449 struct Arguments
450 {
451 std::string Output;
452 bool LastOnly = false;
453 };
454
455 static auto const parser =
456 ArgumentParserWithOutputVariable<Arguments>{}.Bind("LAST_ONLY"_s,
457 &Arguments::LastOnly);
458
459 Arguments const arguments = parser.Parse(args);
460
461 if (!parser.checkOutputVariable(arguments, status)) {
462 return false;
463 }
464
465 if (parser.GetInputs().size() > 1) {
466 status.SetError("REPLACE_EXTENSION called with unexpected arguments.");
467 return false;
468 }
469
470 std::string inputPath;
471 if (!getInputPath(args[1], status, inputPath)) {
472 return false;
473 }
474
475 cmCMakePath path(inputPath);
476 cmCMakePath extension(
477 parser.GetInputs().empty() ? "" : parser.GetInputs().front());
478
479 if (arguments.LastOnly) {
480 path.ReplaceExtension(extension);
481 } else {
482 path.ReplaceWideExtension(extension);
483 }
484
485 status.GetMakefile().AddDefinition(
486 arguments.Output.empty() ? args[1] : arguments.Output, path.String());
487
488 return true;
489 }
490
HandleNormalPathCommand(std::vector<std::string> const & args,cmExecutionStatus & status)491 bool HandleNormalPathCommand(std::vector<std::string> const& args,
492 cmExecutionStatus& status)
493 {
494 static OutputVariableParser const parser{};
495
496 const auto arguments = parser.Parse(args);
497
498 if (!parser.checkOutputVariable(arguments, status)) {
499 return false;
500 }
501
502 if (!parser.GetInputs().empty()) {
503 status.SetError("NORMAL_PATH called with unexpected arguments.");
504 return false;
505 }
506
507 std::string inputPath;
508 if (!getInputPath(args[1], status, inputPath)) {
509 return false;
510 }
511
512 auto path = cmCMakePath(inputPath).Normal();
513
514 status.GetMakefile().AddDefinition(
515 arguments.Output.empty() ? args[1] : arguments.Output, path.String());
516
517 return true;
518 }
519
HandleTransformPathCommand(std::vector<std::string> const & args,cmExecutionStatus & status,const std::function<cmCMakePath (const cmCMakePath &,const std::string & base)> & transform,bool normalizeOption=false)520 bool HandleTransformPathCommand(
521 std::vector<std::string> const& args, cmExecutionStatus& status,
522 const std::function<cmCMakePath(const cmCMakePath&,
523 const std::string& base)>& transform,
524 bool normalizeOption = false)
525 {
526 struct Arguments
527 {
528 std::string Output;
529 std::string BaseDirectory;
530 bool Normalize = false;
531 };
532
533 auto parser = ArgumentParserWithOutputVariable<Arguments>{}.Bind(
534 "BASE_DIRECTORY"_s, &Arguments::BaseDirectory);
535 if (normalizeOption) {
536 parser.Bind("NORMALIZE"_s, &Arguments::Normalize);
537 }
538
539 Arguments arguments = parser.Parse(args);
540
541 if (!parser.checkOutputVariable(arguments, status)) {
542 return false;
543 }
544
545 if (!parser.GetInputs().empty()) {
546 status.SetError(cmStrCat(args[0], " called with unexpected arguments."));
547 return false;
548 }
549
550 if (std::find(parser.GetKeywordsMissingValue().begin(),
551 parser.GetKeywordsMissingValue().end(), "BASE_DIRECTORY"_s) !=
552 parser.GetKeywordsMissingValue().end()) {
553 status.SetError("BASE_DIRECTORY requires an argument.");
554 return false;
555 }
556
557 if (std::find(parser.GetParsedKeywords().begin(),
558 parser.GetParsedKeywords().end(),
559 "BASE_DIRECTORY"_s) == parser.GetParsedKeywords().end()) {
560 arguments.BaseDirectory = status.GetMakefile().GetCurrentSourceDirectory();
561 }
562
563 std::string inputPath;
564 if (!getInputPath(args[1], status, inputPath)) {
565 return false;
566 }
567
568 auto path = transform(cmCMakePath(inputPath), arguments.BaseDirectory);
569 if (arguments.Normalize) {
570 path = path.Normal();
571 }
572
573 status.GetMakefile().AddDefinition(
574 arguments.Output.empty() ? args[1] : arguments.Output, path.String());
575
576 return true;
577 }
578
HandleRelativePathCommand(std::vector<std::string> const & args,cmExecutionStatus & status)579 bool HandleRelativePathCommand(std::vector<std::string> const& args,
580 cmExecutionStatus& status)
581 {
582 return HandleTransformPathCommand(
583 args, status,
584 [](const cmCMakePath& path, const std::string& base) -> cmCMakePath {
585 return path.Relative(base);
586 });
587 }
588
HandleAbsolutePathCommand(std::vector<std::string> const & args,cmExecutionStatus & status)589 bool HandleAbsolutePathCommand(std::vector<std::string> const& args,
590 cmExecutionStatus& status)
591 {
592 return HandleTransformPathCommand(
593 args, status,
594 [](const cmCMakePath& path, const std::string& base) -> cmCMakePath {
595 return path.Absolute(base);
596 },
597 true);
598 }
599
HandleNativePathCommand(std::vector<std::string> const & args,cmExecutionStatus & status)600 bool HandleNativePathCommand(std::vector<std::string> const& args,
601 cmExecutionStatus& status)
602 {
603 if (args.size() < 3 || args.size() > 4) {
604 status.SetError("NATIVE_PATH must be called with two or three arguments.");
605 return false;
606 }
607
608 static NormalizeParser const parser;
609
610 const auto arguments = parser.Parse(args);
611
612 if (parser.GetInputs().size() != 1) {
613 status.SetError("NATIVE_PATH called with unexpected arguments.");
614 return false;
615 }
616 if (parser.GetInputs().front().empty()) {
617 status.SetError("Invalid name for output variable.");
618 return false;
619 }
620
621 std::string inputPath;
622 if (!getInputPath(args[1], status, inputPath)) {
623 return false;
624 }
625
626 cmCMakePath path(inputPath);
627 if (arguments.Normalize) {
628 path = path.Normal();
629 }
630
631 status.GetMakefile().AddDefinition(parser.GetInputs().front(),
632 path.NativeString());
633
634 return true;
635 }
636
HandleConvertCommand(std::vector<std::string> const & args,cmExecutionStatus & status)637 bool HandleConvertCommand(std::vector<std::string> const& args,
638 cmExecutionStatus& status)
639 {
640 #if defined(_WIN32) && !defined(__CYGWIN__)
641 const auto pathSep = ";"_s;
642 #else
643 const auto pathSep = ":"_s;
644 #endif
645 const auto cmakePath = "TO_CMAKE_PATH_LIST"_s;
646 const auto nativePath = "TO_NATIVE_PATH_LIST"_s;
647
648 if (args.size() < 4 || args.size() > 5) {
649 status.SetError("CONVERT must be called with three or four arguments.");
650 return false;
651 }
652
653 const auto& action = args[2];
654
655 if (action != cmakePath && action != nativePath) {
656 status.SetError(
657 cmStrCat("CONVERT called with an unknown action: ", action, "."));
658 return false;
659 }
660
661 if (args[3].empty()) {
662 status.SetError("Invalid name for output variable.");
663 return false;
664 }
665
666 static NormalizeParser const parser;
667
668 const auto arguments = parser.Parse<4>(args);
669
670 if (!parser.GetInputs().empty()) {
671 status.SetError("CONVERT called with unexpected arguments.");
672 return false;
673 }
674
675 std::vector<std::string> paths;
676
677 if (action == cmakePath) {
678 paths = cmSystemTools::SplitString(args[1], pathSep.front());
679 } else {
680 cmExpandList(args[1], paths);
681 }
682
683 for (auto& path : paths) {
684 auto p = cmCMakePath(path,
685 action == cmakePath ? cmCMakePath::native_format
686 : cmCMakePath::generic_format);
687 if (arguments.Normalize) {
688 p = p.Normal();
689 }
690 if (action == cmakePath) {
691 path = p.GenericString();
692 } else {
693 path = p.NativeString();
694 }
695 }
696
697 auto value = cmJoin(paths, action == cmakePath ? ";"_s : pathSep);
698 status.GetMakefile().AddDefinition(args[3], value);
699
700 return true;
701 }
702
HandleCompareCommand(std::vector<std::string> const & args,cmExecutionStatus & status)703 bool HandleCompareCommand(std::vector<std::string> const& args,
704 cmExecutionStatus& status)
705 {
706 if (args.size() != 5) {
707 status.SetError("COMPARE must be called with four arguments.");
708 return false;
709 }
710
711 static std::map<cm::string_view,
712 std::function<bool(const cmCMakePath&,
713 const cmCMakePath&)>> const operators{
714 { "EQUAL"_s,
715 [](const cmCMakePath& path1, const cmCMakePath& path2) -> bool {
716 return path1 == path2;
717 } },
718 { "NOT_EQUAL"_s,
719 [](const cmCMakePath& path1, const cmCMakePath& path2) -> bool {
720 return path1 != path2;
721 } }
722 };
723
724 const auto op = operators.find(args[2]);
725 if (op == operators.end()) {
726 status.SetError(cmStrCat(
727 "COMPARE called with an unknown comparison operator: ", args[2], "."));
728 return false;
729 }
730
731 if (args[4].empty()) {
732 status.SetError("Invalid name for output variable.");
733 return false;
734 }
735
736 cmCMakePath path1(args[1]);
737 cmCMakePath path2(args[3]);
738 auto result = op->second(path1, path2);
739
740 status.GetMakefile().AddDefinitionBool(args[4], result);
741
742 return true;
743 }
744
HandleHasItemCommand(std::vector<std::string> const & args,cmExecutionStatus & status,const std::function<bool (const cmCMakePath &)> & has_item)745 bool HandleHasItemCommand(
746 std::vector<std::string> const& args, cmExecutionStatus& status,
747 const std::function<bool(const cmCMakePath&)>& has_item)
748 {
749 if (args.size() != 3) {
750 status.SetError(
751 cmStrCat(args.front(), " must be called with two arguments."));
752 return false;
753 }
754
755 std::string inputPath;
756 if (!getInputPath(args[1], status, inputPath)) {
757 return false;
758 }
759
760 if (args[2].empty()) {
761 status.SetError("Invalid name for output variable.");
762 return false;
763 }
764
765 cmCMakePath path(inputPath);
766 auto result = has_item(path);
767
768 status.GetMakefile().AddDefinitionBool(args[2], result);
769
770 return true;
771 }
772
HandleHasRootNameCommand(std::vector<std::string> const & args,cmExecutionStatus & status)773 bool HandleHasRootNameCommand(std::vector<std::string> const& args,
774 cmExecutionStatus& status)
775 {
776 return HandleHasItemCommand(
777 args, status,
778 [](const cmCMakePath& path) -> bool { return path.HasRootName(); });
779 }
780
HandleHasRootDirectoryCommand(std::vector<std::string> const & args,cmExecutionStatus & status)781 bool HandleHasRootDirectoryCommand(std::vector<std::string> const& args,
782 cmExecutionStatus& status)
783 {
784 return HandleHasItemCommand(
785 args, status,
786 [](const cmCMakePath& path) -> bool { return path.HasRootDirectory(); });
787 }
788
HandleHasRootPathCommand(std::vector<std::string> const & args,cmExecutionStatus & status)789 bool HandleHasRootPathCommand(std::vector<std::string> const& args,
790 cmExecutionStatus& status)
791 {
792 return HandleHasItemCommand(
793 args, status,
794 [](const cmCMakePath& path) -> bool { return path.HasRootPath(); });
795 }
796
HandleHasFilenameCommand(std::vector<std::string> const & args,cmExecutionStatus & status)797 bool HandleHasFilenameCommand(std::vector<std::string> const& args,
798 cmExecutionStatus& status)
799 {
800 return HandleHasItemCommand(
801 args, status,
802 [](const cmCMakePath& path) -> bool { return path.HasFileName(); });
803 }
804
HandleHasExtensionCommand(std::vector<std::string> const & args,cmExecutionStatus & status)805 bool HandleHasExtensionCommand(std::vector<std::string> const& args,
806 cmExecutionStatus& status)
807 {
808 return HandleHasItemCommand(
809 args, status,
810 [](const cmCMakePath& path) -> bool { return path.HasExtension(); });
811 }
812
HandleHasStemCommand(std::vector<std::string> const & args,cmExecutionStatus & status)813 bool HandleHasStemCommand(std::vector<std::string> const& args,
814 cmExecutionStatus& status)
815 {
816 return HandleHasItemCommand(
817 args, status,
818 [](const cmCMakePath& path) -> bool { return path.HasStem(); });
819 }
820
HandleHasRelativePartCommand(std::vector<std::string> const & args,cmExecutionStatus & status)821 bool HandleHasRelativePartCommand(std::vector<std::string> const& args,
822 cmExecutionStatus& status)
823 {
824 return HandleHasItemCommand(
825 args, status,
826 [](const cmCMakePath& path) -> bool { return path.HasRelativePath(); });
827 }
828
HandleHasParentPathCommand(std::vector<std::string> const & args,cmExecutionStatus & status)829 bool HandleHasParentPathCommand(std::vector<std::string> const& args,
830 cmExecutionStatus& status)
831 {
832 return HandleHasItemCommand(
833 args, status,
834 [](const cmCMakePath& path) -> bool { return path.HasParentPath(); });
835 }
836
HandleIsAbsoluteCommand(std::vector<std::string> const & args,cmExecutionStatus & status)837 bool HandleIsAbsoluteCommand(std::vector<std::string> const& args,
838 cmExecutionStatus& status)
839 {
840 if (args.size() != 3) {
841 status.SetError("IS_ABSOLUTE must be called with two arguments.");
842 return false;
843 }
844
845 std::string inputPath;
846 if (!getInputPath(args[1], status, inputPath)) {
847 return false;
848 }
849
850 if (args[2].empty()) {
851 status.SetError("Invalid name for output variable.");
852 return false;
853 }
854
855 bool isAbsolute = cmCMakePath(inputPath).IsAbsolute();
856
857 status.GetMakefile().AddDefinitionBool(args[2], isAbsolute);
858
859 return true;
860 }
861
HandleIsRelativeCommand(std::vector<std::string> const & args,cmExecutionStatus & status)862 bool HandleIsRelativeCommand(std::vector<std::string> const& args,
863 cmExecutionStatus& status)
864 {
865 if (args.size() != 3) {
866 status.SetError("IS_RELATIVE must be called with two arguments.");
867 return false;
868 }
869
870 std::string inputPath;
871 if (!getInputPath(args[1], status, inputPath)) {
872 return false;
873 }
874
875 if (args[2].empty()) {
876 status.SetError("Invalid name for output variable.");
877 return false;
878 }
879
880 bool isRelative = cmCMakePath(inputPath).IsRelative();
881
882 status.GetMakefile().AddDefinitionBool(args[2], isRelative);
883
884 return true;
885 }
886
HandleIsPrefixCommand(std::vector<std::string> const & args,cmExecutionStatus & status)887 bool HandleIsPrefixCommand(std::vector<std::string> const& args,
888 cmExecutionStatus& status)
889 {
890 if (args.size() < 4 || args.size() > 5) {
891 status.SetError("IS_PREFIX must be called with three or four arguments.");
892 return false;
893 }
894
895 static NormalizeParser const parser;
896
897 const auto arguments = parser.Parse(args);
898
899 if (parser.GetInputs().size() != 2) {
900 status.SetError("IS_PREFIX called with unexpected arguments.");
901 return false;
902 }
903
904 std::string inputPath;
905 if (!getInputPath(args[1], status, inputPath)) {
906 return false;
907 }
908
909 const auto& input = parser.GetInputs().front();
910 const auto& output = parser.GetInputs().back();
911
912 if (output.empty()) {
913 status.SetError("Invalid name for output variable.");
914 return false;
915 }
916
917 bool isPrefix;
918 if (arguments.Normalize) {
919 isPrefix =
920 cmCMakePath(inputPath).Normal().IsPrefix(cmCMakePath(input).Normal());
921 } else {
922 isPrefix = cmCMakePath(inputPath).IsPrefix(input);
923 }
924
925 status.GetMakefile().AddDefinitionBool(output, isPrefix);
926
927 return true;
928 }
929
HandleHashCommand(std::vector<std::string> const & args,cmExecutionStatus & status)930 bool HandleHashCommand(std::vector<std::string> const& args,
931 cmExecutionStatus& status)
932 {
933 if (args.size() != 3) {
934 status.SetError("HASH must be called with two arguments.");
935 return false;
936 }
937
938 std::string inputPath;
939 if (!getInputPath(args[1], status, inputPath)) {
940 return false;
941 }
942
943 const auto& output = args[2];
944
945 if (output.empty()) {
946 status.SetError("Invalid name for output variable.");
947 return false;
948 }
949
950 auto hash = hash_value(cmCMakePath(inputPath).Normal());
951
952 std::ostringstream out;
953 out << std::setbase(16) << hash;
954
955 status.GetMakefile().AddDefinition(output, out.str());
956
957 return true;
958 }
959 } // anonymous namespace
960
cmCMakePathCommand(std::vector<std::string> const & args,cmExecutionStatus & status)961 bool cmCMakePathCommand(std::vector<std::string> const& args,
962 cmExecutionStatus& status)
963 {
964 if (args.size() < 2) {
965 status.SetError("must be called with at least two arguments.");
966 return false;
967 }
968
969 static cmSubcommandTable const subcommand{
970 { "GET"_s, HandleGetCommand },
971 { "SET"_s, HandleSetCommand },
972 { "APPEND"_s, HandleAppendCommand },
973 { "APPEND_STRING"_s, HandleAppendStringCommand },
974 { "REMOVE_FILENAME"_s, HandleRemoveFilenameCommand },
975 { "REPLACE_FILENAME"_s, HandleReplaceFilenameCommand },
976 { "REMOVE_EXTENSION"_s, HandleRemoveExtensionCommand },
977 { "REPLACE_EXTENSION"_s, HandleReplaceExtensionCommand },
978 { "NORMAL_PATH"_s, HandleNormalPathCommand },
979 { "RELATIVE_PATH"_s, HandleRelativePathCommand },
980 { "ABSOLUTE_PATH"_s, HandleAbsolutePathCommand },
981 { "NATIVE_PATH"_s, HandleNativePathCommand },
982 { "CONVERT"_s, HandleConvertCommand },
983 { "COMPARE"_s, HandleCompareCommand },
984 { "HAS_ROOT_NAME"_s, HandleHasRootNameCommand },
985 { "HAS_ROOT_DIRECTORY"_s, HandleHasRootDirectoryCommand },
986 { "HAS_ROOT_PATH"_s, HandleHasRootPathCommand },
987 { "HAS_FILENAME"_s, HandleHasFilenameCommand },
988 { "HAS_EXTENSION"_s, HandleHasExtensionCommand },
989 { "HAS_STEM"_s, HandleHasStemCommand },
990 { "HAS_RELATIVE_PART"_s, HandleHasRelativePartCommand },
991 { "HAS_PARENT_PATH"_s, HandleHasParentPathCommand },
992 { "IS_ABSOLUTE"_s, HandleIsAbsoluteCommand },
993 { "IS_RELATIVE"_s, HandleIsRelativeCommand },
994 { "IS_PREFIX"_s, HandleIsPrefixCommand },
995 { "HASH"_s, HandleHashCommand }
996 };
997
998 return subcommand(args[0], args, status);
999 }
1000