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