summaryrefslogtreecommitdiff
path: root/Source/cmCMakePathCommand.cxx
diff options
context:
space:
mode:
authorMarc Chevrier <marc.chevrier@gmail.com>2020-07-23 16:54:12 +0200
committerMarc Chevrier <marc.chevrier@gmail.com>2020-09-06 10:52:25 +0200
commiteb583b0a660ba68e8e3b5f820301fde333619283 (patch)
treed88a52fdc1af9843ee4adceb1a37c66148d48c2e /Source/cmCMakePathCommand.cxx
parent212e953d352c2ca20cf6280492633d21fbacdbc9 (diff)
downloadcmake-eb583b0a660ba68e8e3b5f820301fde333619283.tar.gz
cmake_path command: path management
Fixes: #19568, #20922
Diffstat (limited to 'Source/cmCMakePathCommand.cxx')
-rw-r--r--Source/cmCMakePathCommand.cxx1019
1 files changed, 1019 insertions, 0 deletions
diff --git a/Source/cmCMakePathCommand.cxx b/Source/cmCMakePathCommand.cxx
new file mode 100644
index 0000000000..720f582f9d
--- /dev/null
+++ b/Source/cmCMakePathCommand.cxx
@@ -0,0 +1,1019 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#include "cmCMakePathCommand.h"
+
+#include <algorithm>
+#include <functional>
+#include <iomanip>
+#include <map>
+#include <sstream>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <cm/string_view>
+#include <cmext/string_view>
+
+#include "cmArgumentParser.h"
+#include "cmCMakePath.h"
+#include "cmExecutionStatus.h"
+#include "cmMakefile.h"
+#include "cmRange.h"
+#include "cmStringAlgorithms.h"
+#include "cmSubcommandTable.h"
+#include "cmSystemTools.h"
+
+namespace {
+// Helper classes for argument parsing
+template <typename Result>
+class CMakePathArgumentParser : public cmArgumentParser<Result>
+{
+public:
+ CMakePathArgumentParser()
+ : cmArgumentParser<Result>()
+ {
+ }
+
+ template <typename T>
+ CMakePathArgumentParser& Bind(cm::static_string_view name, T Result::*member)
+ {
+ cmArgumentParser<Result>::Bind(name, member);
+ return *this;
+ }
+
+ template <int Advance = 2>
+ Result Parse(std::vector<std::string> const& args,
+ std::vector<std::string>* keywordsMissingValue = nullptr,
+ std::vector<std::string>* parsedKeywords = nullptr) const
+ {
+ this->Inputs.clear();
+
+ return cmArgumentParser<Result>::Parse(cmMakeRange(args).advance(Advance),
+ &this->Inputs, keywordsMissingValue,
+ parsedKeywords);
+ }
+
+ const std::vector<std::string>& GetInputs() const { return Inputs; }
+
+protected:
+ mutable std::vector<std::string> Inputs;
+};
+
+// OUTPUT_VARIABLE is expected
+template <typename Result>
+class ArgumentParserWithOutputVariable : public CMakePathArgumentParser<Result>
+{
+public:
+ ArgumentParserWithOutputVariable()
+ : CMakePathArgumentParser<Result>()
+ {
+ this->Bind("OUTPUT_VARIABLE"_s, &Result::Output);
+ }
+
+ template <typename T>
+ ArgumentParserWithOutputVariable& Bind(cm::static_string_view name,
+ T Result::*member)
+ {
+ cmArgumentParser<Result>::Bind(name, member);
+ return *this;
+ }
+
+ template <int Advance = 2>
+ Result Parse(std::vector<std::string> const& args) const
+ {
+ this->KeywordsMissingValue.clear();
+ this->ParsedKeywords.clear();
+
+ return CMakePathArgumentParser<Result>::template Parse<Advance>(
+ args, &this->KeywordsMissingValue, &this->ParsedKeywords);
+ }
+
+ const std::vector<std::string>& GetKeywordsMissingValue() const
+ {
+ return this->KeywordsMissingValue;
+ }
+ const std::vector<std::string>& GetParsedKeywords() const
+ {
+ return this->ParsedKeywords;
+ }
+
+ bool checkOutputVariable(const Result& arguments,
+ cmExecutionStatus& status) const
+ {
+ if (std::find(this->GetKeywordsMissingValue().begin(),
+ this->GetKeywordsMissingValue().end(),
+ "OUTPUT_VARIABLE"_s) !=
+ this->GetKeywordsMissingValue().end()) {
+ status.SetError("OUTPUT_VARIABLE requires an argument.");
+ return false;
+ }
+
+ if (std::find(this->GetParsedKeywords().begin(),
+ this->GetParsedKeywords().end(),
+ "OUTPUT_VARIABLE"_s) != this->GetParsedKeywords().end() &&
+ arguments.Output.empty()) {
+ status.SetError("Invalid name for output variable.");
+ return false;
+ }
+
+ return true;
+ }
+
+private:
+ mutable std::vector<std::string> KeywordsMissingValue;
+ mutable std::vector<std::string> ParsedKeywords;
+};
+
+struct OutputVariable
+{
+ std::string Output;
+};
+// Usable when OUTPUT_VARIABLE is the only option
+class OutputVariableParser
+ : public ArgumentParserWithOutputVariable<OutputVariable>
+{
+};
+
+struct NormalizeOption
+{
+ bool Normalize = false;
+};
+// Usable when NORMALIZE is the only option
+class NormalizeParser : public CMakePathArgumentParser<NormalizeOption>
+{
+public:
+ NormalizeParser() { this->Bind("NORMALIZE"_s, &NormalizeOption::Normalize); }
+};
+
+// retrieve value of input path from specified variable
+bool getInputPath(const std::string& arg, cmExecutionStatus& status,
+ std::string& path)
+{
+ auto def = status.GetMakefile().GetDefinition(arg);
+ if (def == nullptr) {
+ status.SetError("undefined variable for input path.");
+ return false;
+ }
+
+ path = *def;
+ return true;
+}
+
+bool HandleGetCommand(std::vector<std::string> const& args,
+ cmExecutionStatus& status)
+{
+ static std::map<cm::string_view,
+ std::function<cmCMakePath(const cmCMakePath&, bool)>> const
+ actions{ { "ROOT_NAME"_s,
+ [](const cmCMakePath& path, bool) -> cmCMakePath {
+ return path.GetRootName();
+ } },
+ { "ROOT_DIRECTORY"_s,
+ [](const cmCMakePath& path, bool) -> cmCMakePath {
+ return path.GetRootDirectory();
+ } },
+ { "ROOT_PATH"_s,
+ [](const cmCMakePath& path, bool) -> cmCMakePath {
+ return path.GetRootPath();
+ } },
+ { "FILENAME"_s,
+ [](const cmCMakePath& path, bool) -> cmCMakePath {
+ return path.GetFileName();
+ } },
+ { "EXTENSION"_s,
+ [](const cmCMakePath& path, bool last_only) -> cmCMakePath {
+ if (last_only) {
+ return path.GetExtension();
+ }
+ return path.GetWideExtension();
+ } },
+ { "STEM"_s,
+ [](const cmCMakePath& path, bool last_only) -> cmCMakePath {
+ if (last_only) {
+ return path.GetStem();
+ }
+ return path.GetNarrowStem();
+ } },
+ { "RELATIVE_PATH"_s,
+ [](const cmCMakePath& path, bool) -> cmCMakePath {
+ return path.GetRelativePath();
+ } },
+ { "PARENT_PATH"_s,
+ [](const cmCMakePath& path, bool) -> cmCMakePath {
+ return path.GetParentPath();
+ } } };
+
+ if (args.size() < 4) {
+ status.SetError("GET must be called with at least three arguments.");
+ return false;
+ }
+
+ const auto& action = args[2];
+
+ if (actions.find(action) == actions.end()) {
+ status.SetError(
+ cmStrCat("GET called with an unknown action: ", action, "."));
+ return false;
+ }
+
+ struct Arguments
+ {
+ bool LastOnly = false;
+ };
+
+ CMakePathArgumentParser<Arguments> parser;
+ if ((action == "EXTENSION"_s || action == "STEM"_s)) {
+ parser.Bind("LAST_ONLY"_s, &Arguments::LastOnly);
+ }
+
+ Arguments const arguments = parser.Parse<3>(args);
+
+ if (parser.GetInputs().size() != 1) {
+ status.SetError("GET called with unexpected arguments.");
+ return false;
+ }
+ if (parser.GetInputs().front().empty()) {
+ status.SetError("Invalid name for output variable.");
+ return false;
+ }
+
+ std::string path;
+ if (!getInputPath(args[1], status, path)) {
+ return false;
+ }
+
+ auto result = actions.at(action)(path, arguments.LastOnly);
+
+ status.GetMakefile().AddDefinition(parser.GetInputs().front(),
+ result.String());
+
+ return true;
+}
+
+bool HandleAppendCommand(std::vector<std::string> const& args,
+ cmExecutionStatus& status)
+{
+ static OutputVariableParser const parser{};
+
+ const auto arguments = parser.Parse(args);
+
+ if (!parser.checkOutputVariable(arguments, status)) {
+ return false;
+ }
+
+ cmCMakePath path(status.GetMakefile().GetSafeDefinition(args[1]));
+ for (const auto& input : parser.GetInputs()) {
+ path /= input;
+ }
+
+ status.GetMakefile().AddDefinition(
+ arguments.Output.empty() ? args[1] : arguments.Output, path.String());
+
+ return true;
+}
+
+bool HandleConcatCommand(std::vector<std::string> const& args,
+ cmExecutionStatus& status)
+{
+ static OutputVariableParser const parser{};
+
+ const auto arguments = parser.Parse(args);
+
+ if (!parser.checkOutputVariable(arguments, status)) {
+ return false;
+ }
+
+ std::string inputPath;
+ if (!getInputPath(args[1], status, inputPath)) {
+ return false;
+ }
+
+ cmCMakePath path(inputPath);
+ for (const auto& input : parser.GetInputs()) {
+ path += input;
+ }
+
+ status.GetMakefile().AddDefinition(
+ arguments.Output.empty() ? args[1] : arguments.Output, path.String());
+
+ return true;
+}
+
+bool HandleRemoveFilenameCommand(std::vector<std::string> const& args,
+ cmExecutionStatus& status)
+{
+ static OutputVariableParser const parser{};
+
+ const auto arguments = parser.Parse(args);
+
+ if (!parser.checkOutputVariable(arguments, status)) {
+ return false;
+ }
+
+ if (!parser.GetInputs().empty()) {
+ status.SetError("REMOVE_FILENAME called with unexpected arguments.");
+ return false;
+ }
+
+ std::string inputPath;
+ if (!getInputPath(args[1], status, inputPath)) {
+ return false;
+ }
+
+ cmCMakePath path(inputPath);
+ path.RemoveFileName();
+
+ status.GetMakefile().AddDefinition(
+ arguments.Output.empty() ? args[1] : arguments.Output, path.String());
+
+ return true;
+}
+
+bool HandleReplaceFilenameCommand(std::vector<std::string> const& args,
+ cmExecutionStatus& status)
+{
+ static OutputVariableParser const parser{};
+
+ const auto arguments = parser.Parse(args);
+
+ if (!parser.checkOutputVariable(arguments, status)) {
+ return false;
+ }
+
+ if (parser.GetInputs().size() > 1) {
+ status.SetError("REPLACE_FILENAME called with unexpected arguments.");
+ return false;
+ }
+
+ std::string inputPath;
+ if (!getInputPath(args[1], status, inputPath)) {
+ return false;
+ }
+
+ cmCMakePath path(inputPath);
+ path.ReplaceFileName(
+ parser.GetInputs().empty() ? "" : parser.GetInputs().front());
+
+ status.GetMakefile().AddDefinition(
+ arguments.Output.empty() ? args[1] : arguments.Output, path.String());
+
+ return true;
+}
+
+bool HandleRemoveExtensionCommand(std::vector<std::string> const& args,
+ cmExecutionStatus& status)
+{
+ struct Arguments
+ {
+ std::string Output;
+ bool LastOnly = false;
+ };
+
+ static auto const parser =
+ ArgumentParserWithOutputVariable<Arguments>{}.Bind("LAST_ONLY"_s,
+ &Arguments::LastOnly);
+
+ Arguments const arguments = parser.Parse(args);
+
+ if (!parser.checkOutputVariable(arguments, status)) {
+ return false;
+ }
+
+ if (!parser.GetInputs().empty()) {
+ status.SetError("REMOVE_EXTENSION called with unexpected arguments.");
+ return false;
+ }
+
+ std::string inputPath;
+ if (!getInputPath(args[1], status, inputPath)) {
+ return false;
+ }
+
+ cmCMakePath path(inputPath);
+
+ if (arguments.LastOnly) {
+ path.RemoveExtension();
+ } else {
+ path.RemoveWideExtension();
+ }
+
+ status.GetMakefile().AddDefinition(
+ arguments.Output.empty() ? args[1] : arguments.Output, path.String());
+
+ return true;
+}
+
+bool HandleReplaceExtensionCommand(std::vector<std::string> const& args,
+ cmExecutionStatus& status)
+{
+ struct Arguments
+ {
+ std::string Output;
+ bool LastOnly = false;
+ };
+
+ static auto const parser =
+ ArgumentParserWithOutputVariable<Arguments>{}.Bind("LAST_ONLY"_s,
+ &Arguments::LastOnly);
+
+ Arguments const arguments = parser.Parse(args);
+
+ if (!parser.checkOutputVariable(arguments, status)) {
+ return false;
+ }
+
+ if (parser.GetInputs().size() > 1) {
+ status.SetError("REPLACE_EXTENSION called with unexpected arguments.");
+ return false;
+ }
+
+ std::string inputPath;
+ if (!getInputPath(args[1], status, inputPath)) {
+ return false;
+ }
+
+ cmCMakePath path(inputPath);
+ cmCMakePath extension(
+ parser.GetInputs().empty() ? "" : parser.GetInputs().front());
+
+ if (arguments.LastOnly) {
+ path.ReplaceExtension(extension);
+ } else {
+ path.ReplaceWideExtension(extension);
+ }
+
+ status.GetMakefile().AddDefinition(
+ arguments.Output.empty() ? args[1] : arguments.Output, path.String());
+
+ return true;
+}
+
+bool HandleNormalPathCommand(std::vector<std::string> const& args,
+ cmExecutionStatus& status)
+{
+ static OutputVariableParser const parser{};
+
+ const auto arguments = parser.Parse(args);
+
+ if (!parser.checkOutputVariable(arguments, status)) {
+ return false;
+ }
+
+ if (!parser.GetInputs().empty()) {
+ status.SetError("NORMAL_PATH called with unexpected arguments.");
+ return false;
+ }
+
+ std::string inputPath;
+ if (!getInputPath(args[1], status, inputPath)) {
+ return false;
+ }
+
+ auto path = cmCMakePath(inputPath).Normal();
+
+ status.GetMakefile().AddDefinition(
+ arguments.Output.empty() ? args[1] : arguments.Output, path.String());
+
+ return true;
+}
+
+bool HandleTransformPathCommand(
+ std::vector<std::string> const& args, cmExecutionStatus& status,
+ const std::function<cmCMakePath(const cmCMakePath&,
+ const std::string& base)>& transform,
+ bool normalizeOption = false)
+{
+ struct Arguments
+ {
+ std::string Output;
+ std::string BaseDirectory;
+ bool Normalize = false;
+ };
+
+ auto parser = ArgumentParserWithOutputVariable<Arguments>{}.Bind(
+ "BASE_DIRECTORY"_s, &Arguments::BaseDirectory);
+ if (normalizeOption) {
+ parser.Bind("NORMALIZE"_s, &Arguments::Normalize);
+ }
+
+ Arguments arguments = parser.Parse(args);
+
+ if (!parser.checkOutputVariable(arguments, status)) {
+ return false;
+ }
+
+ if (!parser.GetInputs().empty()) {
+ status.SetError(cmStrCat(args[0], " called with unexpected arguments."));
+ return false;
+ }
+
+ if (std::find(parser.GetKeywordsMissingValue().begin(),
+ parser.GetKeywordsMissingValue().end(), "BASE_DIRECTORY"_s) !=
+ parser.GetKeywordsMissingValue().end()) {
+ status.SetError("BASE_DIRECTORY requires an argument.");
+ return false;
+ }
+
+ if (std::find(parser.GetParsedKeywords().begin(),
+ parser.GetParsedKeywords().end(),
+ "BASE_DIRECTORY"_s) == parser.GetParsedKeywords().end()) {
+ arguments.BaseDirectory = status.GetMakefile().GetCurrentSourceDirectory();
+ }
+
+ std::string inputPath;
+ if (!getInputPath(args[1], status, inputPath)) {
+ return false;
+ }
+
+ auto path = transform(cmCMakePath(inputPath), arguments.BaseDirectory);
+ if (arguments.Normalize) {
+ path = path.Normal();
+ }
+
+ status.GetMakefile().AddDefinition(
+ arguments.Output.empty() ? args[1] : arguments.Output, path.String());
+
+ return true;
+}
+
+bool HandleRelativePathCommand(std::vector<std::string> const& args,
+ cmExecutionStatus& status)
+{
+ return HandleTransformPathCommand(
+ args, status,
+ [](const cmCMakePath& path, const std::string& base) -> cmCMakePath {
+ return path.Relative(base);
+ });
+}
+
+bool HandleProximatePathCommand(std::vector<std::string> const& args,
+ cmExecutionStatus& status)
+{
+ return HandleTransformPathCommand(
+ args, status,
+ [](const cmCMakePath& path, const std::string& base) -> cmCMakePath {
+ return path.Proximate(base);
+ });
+}
+
+bool HandleAbsolutePathCommand(std::vector<std::string> const& args,
+ cmExecutionStatus& status)
+{
+ return HandleTransformPathCommand(
+ args, status,
+ [](const cmCMakePath& path, const std::string& base) -> cmCMakePath {
+ return path.Absolute(base);
+ },
+ true);
+}
+
+bool HandleCMakePathCommand(std::vector<std::string> const& args,
+ cmExecutionStatus& status)
+{
+ if (args.size() < 3 || args.size() > 4) {
+ status.SetError("CMAKE_PATH must be called with two or three arguments.");
+ return false;
+ }
+
+ static NormalizeParser const parser;
+
+ const auto arguments = parser.Parse(args);
+
+ if (parser.GetInputs().size() != 1) {
+ status.SetError("CMAKE_PATH called with unexpected arguments.");
+ return false;
+ }
+
+ if (args[1].empty()) {
+ status.SetError("Invalid name for output variable.");
+ return false;
+ }
+
+ auto path =
+ cmCMakePath(parser.GetInputs().front(), cmCMakePath::native_format);
+
+ if (arguments.Normalize) {
+ path = path.Normal();
+ }
+
+ status.GetMakefile().AddDefinition(args[1], path.GenericString());
+
+ return true;
+}
+
+bool HandleNativePathCommand(std::vector<std::string> const& args,
+ cmExecutionStatus& status)
+{
+ if (args.size() < 3 || args.size() > 4) {
+ status.SetError("NATIVE_PATH must be called with two or three arguments.");
+ return false;
+ }
+
+ static NormalizeParser const parser;
+
+ const auto arguments = parser.Parse(args);
+
+ if (parser.GetInputs().size() != 1) {
+ status.SetError("NATIVE_PATH called with unexpected arguments.");
+ return false;
+ }
+ if (parser.GetInputs().front().empty()) {
+ status.SetError("Invalid name for output variable.");
+ return false;
+ }
+
+ std::string inputPath;
+ if (!getInputPath(args[1], status, inputPath)) {
+ return false;
+ }
+
+ cmCMakePath path(inputPath);
+ if (arguments.Normalize) {
+ path = path.Normal();
+ }
+
+ status.GetMakefile().AddDefinition(parser.GetInputs().front(),
+ path.NativeString());
+
+ return true;
+}
+
+bool HandleConvertCommand(std::vector<std::string> const& args,
+ cmExecutionStatus& status)
+{
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ const auto pathSep = ";"_s;
+#else
+ const auto pathSep = ":"_s;
+#endif
+ const auto cmakePath = "TO_CMAKE_PATH_LIST"_s;
+ const auto nativePath = "TO_NATIVE_PATH_LIST"_s;
+
+ if (args.size() < 4 || args.size() > 5) {
+ status.SetError("CONVERT must be called with three or four arguments.");
+ return false;
+ }
+
+ const auto& action = args[2];
+
+ if (action != cmakePath && action != nativePath) {
+ status.SetError(
+ cmStrCat("CONVERT called with an unknown action: ", action, "."));
+ return false;
+ }
+
+ if (args[3].empty()) {
+ status.SetError("Invalid name for output variable.");
+ return false;
+ }
+
+ static NormalizeParser const parser;
+
+ const auto arguments = parser.Parse<4>(args);
+
+ if (!parser.GetInputs().empty()) {
+ status.SetError("CONVERT called with unexpected arguments.");
+ return false;
+ }
+
+ std::vector<std::string> paths;
+
+ if (action == cmakePath) {
+ paths = cmSystemTools::SplitString(args[1], pathSep.front());
+ } else {
+ cmExpandList(args[1], paths);
+ }
+
+ for (auto& path : paths) {
+ auto p = cmCMakePath(path,
+ action == cmakePath ? cmCMakePath::native_format
+ : cmCMakePath::generic_format);
+ if (arguments.Normalize) {
+ p = p.Normal();
+ }
+ if (action == cmakePath) {
+ path = p.GenericString();
+ } else {
+ path = p.NativeString();
+ }
+ }
+
+ auto value = cmJoin(paths, action == cmakePath ? ";"_s : pathSep);
+ status.GetMakefile().AddDefinition(args[3], value);
+
+ return true;
+}
+
+bool HandleCompareCommand(std::vector<std::string> const& args,
+ cmExecutionStatus& status)
+{
+ if (args.size() != 5) {
+ status.SetError("COMPARE must be called with four arguments.");
+ return false;
+ }
+
+ static std::map<cm::string_view,
+ std::function<bool(const cmCMakePath&,
+ const cmCMakePath&)>> const operators{
+ { "EQUAL"_s,
+ [](const cmCMakePath& path1, const cmCMakePath& path2) -> bool {
+ return path1 == path2;
+ } },
+ { "NOT_EQUAL"_s,
+ [](const cmCMakePath& path1, const cmCMakePath& path2) -> bool {
+ return path1 != path2;
+ } }
+ };
+
+ const auto op = operators.find(args[2]);
+ if (op == operators.end()) {
+ status.SetError(cmStrCat(
+ "COMPARE called with an unknown comparison operator: ", args[2], "."));
+ return false;
+ }
+
+ if (args[4].empty()) {
+ status.SetError("Invalid name for output variable.");
+ return false;
+ }
+
+ std::string inputPath;
+ if (!getInputPath(args[1], status, inputPath)) {
+ return false;
+ }
+
+ cmCMakePath path1(inputPath);
+ cmCMakePath path2(args[3]);
+ auto result = op->second(path1, path2);
+
+ status.GetMakefile().AddDefinitionBool(args[4], result);
+
+ return true;
+}
+
+bool HandleHasItemCommand(
+ std::vector<std::string> const& args, cmExecutionStatus& status,
+ const std::function<bool(const cmCMakePath&)>& has_item)
+{
+ if (args.size() != 3) {
+ status.SetError(
+ cmStrCat(args.front(), " must be called with two arguments."));
+ return false;
+ }
+
+ std::string inputPath;
+ if (!getInputPath(args[1], status, inputPath)) {
+ return false;
+ }
+
+ if (args[2].empty()) {
+ status.SetError("Invalid name for output variable.");
+ return false;
+ }
+
+ cmCMakePath path(inputPath);
+ auto result = has_item(path);
+
+ status.GetMakefile().AddDefinitionBool(args[2], result);
+
+ return true;
+}
+
+bool HandleHasRootNameCommand(std::vector<std::string> const& args,
+ cmExecutionStatus& status)
+{
+ return HandleHasItemCommand(
+ args, status,
+ [](const cmCMakePath& path) -> bool { return path.HasRootName(); });
+}
+
+bool HandleHasRootDirectoryCommand(std::vector<std::string> const& args,
+ cmExecutionStatus& status)
+{
+ return HandleHasItemCommand(
+ args, status,
+ [](const cmCMakePath& path) -> bool { return path.HasRootDirectory(); });
+}
+
+bool HandleHasRootPathCommand(std::vector<std::string> const& args,
+ cmExecutionStatus& status)
+{
+ return HandleHasItemCommand(
+ args, status,
+ [](const cmCMakePath& path) -> bool { return path.HasRootPath(); });
+}
+
+bool HandleHasFilenameCommand(std::vector<std::string> const& args,
+ cmExecutionStatus& status)
+{
+ return HandleHasItemCommand(
+ args, status,
+ [](const cmCMakePath& path) -> bool { return path.HasFileName(); });
+}
+
+bool HandleHasExtensionCommand(std::vector<std::string> const& args,
+ cmExecutionStatus& status)
+{
+ return HandleHasItemCommand(
+ args, status,
+ [](const cmCMakePath& path) -> bool { return path.HasExtension(); });
+}
+
+bool HandleHasStemCommand(std::vector<std::string> const& args,
+ cmExecutionStatus& status)
+{
+ return HandleHasItemCommand(
+ args, status,
+ [](const cmCMakePath& path) -> bool { return path.HasStem(); });
+}
+
+bool HandleHasRelativePathCommand(std::vector<std::string> const& args,
+ cmExecutionStatus& status)
+{
+ return HandleHasItemCommand(
+ args, status,
+ [](const cmCMakePath& path) -> bool { return path.HasRelativePath(); });
+}
+
+bool HandleHasParentPathCommand(std::vector<std::string> const& args,
+ cmExecutionStatus& status)
+{
+ return HandleHasItemCommand(
+ args, status,
+ [](const cmCMakePath& path) -> bool { return path.HasParentPath(); });
+}
+
+bool HandleIsAbsoluteCommand(std::vector<std::string> const& args,
+ cmExecutionStatus& status)
+{
+ if (args.size() != 3) {
+ status.SetError("IS_ABSOLUTE must be called with two arguments.");
+ return false;
+ }
+
+ std::string inputPath;
+ if (!getInputPath(args[1], status, inputPath)) {
+ return false;
+ }
+
+ if (args[2].empty()) {
+ status.SetError("Invalid name for output variable.");
+ return false;
+ }
+
+ bool isAbsolute = cmCMakePath(inputPath).IsAbsolute();
+
+ status.GetMakefile().AddDefinitionBool(args[2], isAbsolute);
+
+ return true;
+}
+
+bool HandleIsRelativeCommand(std::vector<std::string> const& args,
+ cmExecutionStatus& status)
+{
+ if (args.size() != 3) {
+ status.SetError("IS_RELATIVE must be called with two arguments.");
+ return false;
+ }
+
+ std::string inputPath;
+ if (!getInputPath(args[1], status, inputPath)) {
+ return false;
+ }
+
+ if (args[2].empty()) {
+ status.SetError("Invalid name for output variable.");
+ return false;
+ }
+
+ bool isRelative = cmCMakePath(inputPath).IsRelative();
+
+ status.GetMakefile().AddDefinitionBool(args[2], isRelative);
+
+ return true;
+}
+
+bool HandleIsPrefixCommand(std::vector<std::string> const& args,
+ cmExecutionStatus& status)
+{
+ if (args.size() < 4 || args.size() > 5) {
+ status.SetError("IS_PREFIX must be called with three or four arguments.");
+ return false;
+ }
+
+ static NormalizeParser const parser;
+
+ const auto arguments = parser.Parse(args);
+
+ if (parser.GetInputs().size() != 2) {
+ status.SetError("IS_PREFIX called with unexpected arguments.");
+ return false;
+ }
+
+ std::string inputPath;
+ if (!getInputPath(args[1], status, inputPath)) {
+ return false;
+ }
+
+ const auto& input = parser.GetInputs().front();
+ const auto& output = parser.GetInputs().back();
+
+ if (output.empty()) {
+ status.SetError("Invalid name for output variable.");
+ return false;
+ }
+
+ bool isPrefix;
+ if (arguments.Normalize) {
+ isPrefix =
+ cmCMakePath(inputPath).Normal().IsPrefix(cmCMakePath(input).Normal());
+ } else {
+ isPrefix = cmCMakePath(inputPath).IsPrefix(input);
+ }
+
+ status.GetMakefile().AddDefinitionBool(output, isPrefix);
+
+ return true;
+}
+
+bool HandleHashCommand(std::vector<std::string> const& args,
+ cmExecutionStatus& status)
+{
+ if (args.size() < 3 || args.size() > 4) {
+ status.SetError("HASH must be called with two or three arguments.");
+ return false;
+ }
+
+ static NormalizeParser const parser;
+
+ const auto arguments = parser.Parse(args);
+
+ if (parser.GetInputs().size() != 1) {
+ status.SetError("HASH called with unexpected arguments.");
+ return false;
+ }
+
+ std::string inputPath;
+ if (!getInputPath(args[1], status, inputPath)) {
+ return false;
+ }
+
+ const auto& output = parser.GetInputs().front();
+
+ if (output.empty()) {
+ status.SetError("Invalid name for output variable.");
+ return false;
+ }
+
+ auto hash = hash_value(arguments.Normalize ? cmCMakePath(inputPath).Normal()
+ : cmCMakePath(inputPath));
+
+ std::ostringstream out;
+ out << std::setbase(16) << hash;
+
+ status.GetMakefile().AddDefinition(output, out.str());
+
+ return true;
+}
+} // anonymous namespace
+
+bool cmCMakePathCommand(std::vector<std::string> const& args,
+ cmExecutionStatus& status)
+{
+ if (args.size() < 2) {
+ status.SetError("must be called with at least two arguments.");
+ return false;
+ }
+
+ static cmSubcommandTable const subcommand{
+ { "GET"_s, HandleGetCommand },
+ { "APPEND"_s, HandleAppendCommand },
+ { "CONCAT"_s, HandleConcatCommand },
+ { "REMOVE_FILENAME"_s, HandleRemoveFilenameCommand },
+ { "REPLACE_FILENAME"_s, HandleReplaceFilenameCommand },
+ { "REMOVE_EXTENSION"_s, HandleRemoveExtensionCommand },
+ { "REPLACE_EXTENSION"_s, HandleReplaceExtensionCommand },
+ { "NORMAL_PATH"_s, HandleNormalPathCommand },
+ { "RELATIVE_PATH"_s, HandleRelativePathCommand },
+ { "PROXIMATE_PATH"_s, HandleProximatePathCommand },
+ { "ABSOLUTE_PATH"_s, HandleAbsolutePathCommand },
+ { "CMAKE_PATH"_s, HandleCMakePathCommand },
+ { "NATIVE_PATH"_s, HandleNativePathCommand },
+ { "CONVERT"_s, HandleConvertCommand },
+ { "COMPARE"_s, HandleCompareCommand },
+ { "HAS_ROOT_NAME"_s, HandleHasRootNameCommand },
+ { "HAS_ROOT_DIRECTORY"_s, HandleHasRootDirectoryCommand },
+ { "HAS_ROOT_PATH"_s, HandleHasRootPathCommand },
+ { "HAS_FILENAME"_s, HandleHasFilenameCommand },
+ { "HAS_EXTENSION"_s, HandleHasExtensionCommand },
+ { "HAS_STEM"_s, HandleHasStemCommand },
+ { "HAS_RELATIVE_PATH"_s, HandleHasRelativePathCommand },
+ { "HAS_PARENT_PATH"_s, HandleHasParentPathCommand },
+ { "IS_ABSOLUTE"_s, HandleIsAbsoluteCommand },
+ { "IS_RELATIVE"_s, HandleIsRelativeCommand },
+ { "IS_PREFIX"_s, HandleIsPrefixCommand },
+ { "HASH"_s, HandleHashCommand }
+ };
+
+ return subcommand(args[0], args, status);
+}