bf94c2dcefacdfc7079b1666b0ecd11377947ed6
[platform/upstream/cmake.git] / Source / cmCMakePathCommand.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 "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:
33   CMakePathArgumentParser()
34     : cmArgumentParser<Result>()
35   {
36   }
37
38   template <typename T>
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>
46   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
57   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:
68   ArgumentParserWithOutputVariable()
69     : CMakePathArgumentParser<Result>()
70   {
71     this->Bind("OUTPUT_VARIABLE"_s, &Result::Output);
72   }
73
74   template <typename T>
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>
83   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
92   const std::vector<std::string>& GetKeywordsMissingValue() const
93   {
94     return this->KeywordsMissingValue;
95   }
96   const std::vector<std::string>& GetParsedKeywords() const
97   {
98     return this->ParsedKeywords;
99   }
100
101   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:
146   NormalizeParser() { this->Bind("NORMALIZE"_s, &NormalizeOption::Normalize); }
147 };
148
149 // retrieve value of input path from specified variable
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 }