| /* 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 <functional> | |
| #include <iomanip> | |
| #include <map> | |
| #include <sstream> | |
| #include <string> | |
| #include <utility> | |
| #include <vector> | |
| #include <cm/optional> | |
| #include <cm/string_view> | |
| #include <cmext/string_view> | |
| #include "cmArgumentParser.h" | |
| #include "cmArgumentParserTypes.h" | |
| #include "cmCMakePath.h" | |
| #include "cmExecutionStatus.h" | |
| #include "cmList.h" | |
| #include "cmMakefile.h" | |
| #include "cmRange.h" | |
| #include "cmStringAlgorithms.h" | |
| #include "cmSubcommandTable.h" | |
| #include "cmSystemTools.h" | |
| #include "cmValue.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) | |
| { | |
| this->cmArgumentParser<Result>::Bind(name, member); | |
| return *this; | |
| } | |
| template <int Advance = 2> | |
| Result Parse(std::vector<std::string> const& args) const | |
| { | |
| this->Inputs.clear(); | |
| return this->cmArgumentParser<Result>::Parse( | |
| cmMakeRange(args).advance(Advance), &this->Inputs); | |
| } | |
| std::vector<std::string> const& GetInputs() const { return this->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) | |
| { | |
| this->cmArgumentParser<Result>::Bind(name, member); | |
| return *this; | |
| } | |
| template <int Advance = 2> | |
| Result Parse(std::vector<std::string> const& args) const | |
| { | |
| return this->CMakePathArgumentParser<Result>::template Parse<Advance>( | |
| args); | |
| } | |
| }; | |
| struct OutputVariable : public ArgumentParser::ParseResult | |
| { | |
| cm::optional<ArgumentParser::NonEmpty<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(std::string const& arg, cmExecutionStatus& status, | |
| std::string& path) | |
| { | |
| cmValue def = status.GetMakefile().GetDefinition(arg); | |
| if (!def) { | |
| 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(cmCMakePath const&, bool)>> const | |
| actions{ { "ROOT_NAME"_s, | |
| [](cmCMakePath const& path, bool) -> cmCMakePath { | |
| return path.GetRootName(); | |
| } }, | |
| { "ROOT_DIRECTORY"_s, | |
| [](cmCMakePath const& path, bool) -> cmCMakePath { | |
| return path.GetRootDirectory(); | |
| } }, | |
| { "ROOT_PATH"_s, | |
| [](cmCMakePath const& path, bool) -> cmCMakePath { | |
| return path.GetRootPath(); | |
| } }, | |
| { "FILENAME"_s, | |
| [](cmCMakePath const& path, bool) -> cmCMakePath { | |
| return path.GetFileName(); | |
| } }, | |
| { "EXTENSION"_s, | |
| [](cmCMakePath const& path, bool last_only) -> cmCMakePath { | |
| if (last_only) { | |
| return path.GetExtension(); | |
| } | |
| return path.GetWideExtension(); | |
| } }, | |
| { "STEM"_s, | |
| [](cmCMakePath const& path, bool last_only) -> cmCMakePath { | |
| if (last_only) { | |
| return path.GetStem(); | |
| } | |
| return path.GetNarrowStem(); | |
| } }, | |
| { "RELATIVE_PART"_s, | |
| [](cmCMakePath const& path, bool) -> cmCMakePath { | |
| return path.GetRelativePath(); | |
| } }, | |
| { "PARENT_PATH"_s, | |
| [](cmCMakePath const& path, bool) -> cmCMakePath { | |
| return path.GetParentPath(); | |
| } } }; | |
| if (args.size() < 4) { | |
| status.SetError("GET must be called with at least three arguments."); | |
| return false; | |
| } | |
| auto const& 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 HandleSetCommand(std::vector<std::string> const& args, | |
| cmExecutionStatus& status) | |
| { | |
| if (args.size() < 3 || args.size() > 4) { | |
| status.SetError("SET must be called with two or three arguments."); | |
| return false; | |
| } | |
| if (args[1].empty()) { | |
| status.SetError("Invalid name for path variable."); | |
| return false; | |
| } | |
| static NormalizeParser const parser; | |
| auto const arguments = parser.Parse(args); | |
| if (parser.GetInputs().size() != 1) { | |
| status.SetError("SET called with unexpected arguments."); | |
| 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 HandleAppendCommand(std::vector<std::string> const& args, | |
| cmExecutionStatus& status) | |
| { | |
| if (args[1].empty()) { | |
| status.SetError("Invalid name for path variable."); | |
| return false; | |
| } | |
| static OutputVariableParser const parser{}; | |
| auto const arguments = parser.Parse(args); | |
| if (arguments.MaybeReportError(status.GetMakefile())) { | |
| return true; | |
| } | |
| cmCMakePath path(status.GetMakefile().GetSafeDefinition(args[1])); | |
| for (auto const& input : parser.GetInputs()) { | |
| path /= input; | |
| } | |
| status.GetMakefile().AddDefinition( | |
| arguments.Output ? *arguments.Output : args[1], path.String()); | |
| return true; | |
| } | |
| bool HandleAppendStringCommand(std::vector<std::string> const& args, | |
| cmExecutionStatus& status) | |
| { | |
| static OutputVariableParser const parser{}; | |
| auto const arguments = parser.Parse(args); | |
| if (arguments.MaybeReportError(status.GetMakefile())) { | |
| return true; | |
| } | |
| std::string inputPath; | |
| if (!getInputPath(args[1], status, inputPath)) { | |
| return false; | |
| } | |
| cmCMakePath path(inputPath); | |
| for (auto const& input : parser.GetInputs()) { | |
| path += input; | |
| } | |
| status.GetMakefile().AddDefinition( | |
| arguments.Output ? *arguments.Output : args[1], path.String()); | |
| return true; | |
| } | |
| bool HandleRemoveFilenameCommand(std::vector<std::string> const& args, | |
| cmExecutionStatus& status) | |
| { | |
| static OutputVariableParser const parser{}; | |
| auto const arguments = parser.Parse(args); | |
| if (arguments.MaybeReportError(status.GetMakefile())) { | |
| return true; | |
| } | |
| 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 ? *arguments.Output : args[1], path.String()); | |
| return true; | |
| } | |
| bool HandleReplaceFilenameCommand(std::vector<std::string> const& args, | |
| cmExecutionStatus& status) | |
| { | |
| static OutputVariableParser const parser{}; | |
| auto const arguments = parser.Parse(args); | |
| if (arguments.MaybeReportError(status.GetMakefile())) { | |
| return true; | |
| } | |
| 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 ? *arguments.Output : args[1], path.String()); | |
| return true; | |
| } | |
| bool HandleRemoveExtensionCommand(std::vector<std::string> const& args, | |
| cmExecutionStatus& status) | |
| { | |
| struct Arguments : public ArgumentParser::ParseResult | |
| { | |
| cm::optional<ArgumentParser::NonEmpty<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 (arguments.MaybeReportError(status.GetMakefile())) { | |
| return true; | |
| } | |
| 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 ? *arguments.Output : args[1], path.String()); | |
| return true; | |
| } | |
| bool HandleReplaceExtensionCommand(std::vector<std::string> const& args, | |
| cmExecutionStatus& status) | |
| { | |
| struct Arguments : public ArgumentParser::ParseResult | |
| { | |
| cm::optional<ArgumentParser::NonEmpty<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 (arguments.MaybeReportError(status.GetMakefile())) { | |
| return true; | |
| } | |
| 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 ? *arguments.Output : args[1], path.String()); | |
| return true; | |
| } | |
| bool HandleNormalPathCommand(std::vector<std::string> const& args, | |
| cmExecutionStatus& status) | |
| { | |
| static OutputVariableParser const parser{}; | |
| auto const arguments = parser.Parse(args); | |
| if (arguments.MaybeReportError(status.GetMakefile())) { | |
| return true; | |
| } | |
| 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 ? *arguments.Output : args[1], path.String()); | |
| return true; | |
| } | |
| bool HandleTransformPathCommand( | |
| std::vector<std::string> const& args, cmExecutionStatus& status, | |
| std::function<cmCMakePath(cmCMakePath const&, | |
| std::string const& base)> const& transform, | |
| bool normalizeOption = false) | |
| { | |
| struct Arguments : public ArgumentParser::ParseResult | |
| { | |
| cm::optional<ArgumentParser::NonEmpty<std::string>> Output; | |
| cm::optional<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 (arguments.MaybeReportError(status.GetMakefile())) { | |
| return true; | |
| } | |
| if (!parser.GetInputs().empty()) { | |
| status.SetError(cmStrCat(args[0], " called with unexpected arguments.")); | |
| return false; | |
| } | |
| std::string baseDirectory; | |
| if (arguments.BaseDirectory) { | |
| baseDirectory = *arguments.BaseDirectory; | |
| } else { | |
| baseDirectory = status.GetMakefile().GetCurrentSourceDirectory(); | |
| } | |
| std::string inputPath; | |
| if (!getInputPath(args[1], status, inputPath)) { | |
| return false; | |
| } | |
| auto path = transform(cmCMakePath(inputPath), baseDirectory); | |
| if (arguments.Normalize) { | |
| path = path.Normal(); | |
| } | |
| status.GetMakefile().AddDefinition( | |
| arguments.Output ? *arguments.Output : args[1], path.String()); | |
| return true; | |
| } | |
| bool HandleRelativePathCommand(std::vector<std::string> const& args, | |
| cmExecutionStatus& status) | |
| { | |
| return HandleTransformPathCommand( | |
| args, status, | |
| [](cmCMakePath const& path, std::string const& base) -> cmCMakePath { | |
| return path.Relative(base); | |
| }); | |
| } | |
| bool HandleAbsolutePathCommand(std::vector<std::string> const& args, | |
| cmExecutionStatus& status) | |
| { | |
| return HandleTransformPathCommand( | |
| args, status, | |
| [](cmCMakePath const& path, std::string const& base) -> cmCMakePath { | |
| return path.Absolute(base); | |
| }, | |
| 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; | |
| auto const 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__) | |
| auto const pathSep = ";"_s; | |
| #else | |
| auto const pathSep = ":"_s; | |
| #endif | |
| auto const cmakePath = "TO_CMAKE_PATH_LIST"_s; | |
| auto const 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; | |
| } | |
| auto const& 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; | |
| auto const arguments = parser.Parse<4>(args); | |
| if (!parser.GetInputs().empty()) { | |
| status.SetError("CONVERT called with unexpected arguments."); | |
| return false; | |
| } | |
| cmList paths; | |
| if (action == cmakePath) { | |
| paths = cmSystemTools::SplitString(args[1], pathSep.front()); | |
| } else { | |
| paths.assign(args[1]); | |
| } | |
| 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 = action == cmakePath ? paths.to_string() : paths.join(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(cmCMakePath const&, | |
| cmCMakePath const&)>> const operators{ | |
| { "EQUAL"_s, | |
| [](cmCMakePath const& path1, cmCMakePath const& path2) -> bool { | |
| return path1 == path2; | |
| } }, | |
| { "NOT_EQUAL"_s, | |
| [](cmCMakePath const& path1, cmCMakePath const& path2) -> bool { | |
| return path1 != path2; | |
| } } | |
| }; | |
| auto const 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; | |
| } | |
| cmCMakePath path1(args[1]); | |
| 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, | |
| std::function<bool(cmCMakePath const&)> const& 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, | |
| [](cmCMakePath const& path) -> bool { return path.HasRootName(); }); | |
| } | |
| bool HandleHasRootDirectoryCommand(std::vector<std::string> const& args, | |
| cmExecutionStatus& status) | |
| { | |
| return HandleHasItemCommand( | |
| args, status, | |
| [](cmCMakePath const& path) -> bool { return path.HasRootDirectory(); }); | |
| } | |
| bool HandleHasRootPathCommand(std::vector<std::string> const& args, | |
| cmExecutionStatus& status) | |
| { | |
| return HandleHasItemCommand( | |
| args, status, | |
| [](cmCMakePath const& path) -> bool { return path.HasRootPath(); }); | |
| } | |
| bool HandleHasFilenameCommand(std::vector<std::string> const& args, | |
| cmExecutionStatus& status) | |
| { | |
| return HandleHasItemCommand( | |
| args, status, | |
| [](cmCMakePath const& path) -> bool { return path.HasFileName(); }); | |
| } | |
| bool HandleHasExtensionCommand(std::vector<std::string> const& args, | |
| cmExecutionStatus& status) | |
| { | |
| return HandleHasItemCommand( | |
| args, status, | |
| [](cmCMakePath const& path) -> bool { return path.HasExtension(); }); | |
| } | |
| bool HandleHasStemCommand(std::vector<std::string> const& args, | |
| cmExecutionStatus& status) | |
| { | |
| return HandleHasItemCommand( | |
| args, status, | |
| [](cmCMakePath const& path) -> bool { return path.HasStem(); }); | |
| } | |
| bool HandleHasRelativePartCommand(std::vector<std::string> const& args, | |
| cmExecutionStatus& status) | |
| { | |
| return HandleHasItemCommand( | |
| args, status, | |
| [](cmCMakePath const& path) -> bool { return path.HasRelativePath(); }); | |
| } | |
| bool HandleHasParentPathCommand(std::vector<std::string> const& args, | |
| cmExecutionStatus& status) | |
| { | |
| return HandleHasItemCommand( | |
| args, status, | |
| [](cmCMakePath const& 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; | |
| auto const 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; | |
| } | |
| auto const& input = parser.GetInputs().front(); | |
| auto const& 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) { | |
| status.SetError("HASH must be called with two arguments."); | |
| return false; | |
| } | |
| std::string inputPath; | |
| if (!getInputPath(args[1], status, inputPath)) { | |
| return false; | |
| } | |
| auto const& output = args[2]; | |
| if (output.empty()) { | |
| status.SetError("Invalid name for output variable."); | |
| return false; | |
| } | |
| auto hash = hash_value(cmCMakePath(inputPath).Normal()); | |
| 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 }, | |
| { "SET"_s, HandleSetCommand }, | |
| { "APPEND"_s, HandleAppendCommand }, | |
| { "APPEND_STRING"_s, HandleAppendStringCommand }, | |
| { "REMOVE_FILENAME"_s, HandleRemoveFilenameCommand }, | |
| { "REPLACE_FILENAME"_s, HandleReplaceFilenameCommand }, | |
| { "REMOVE_EXTENSION"_s, HandleRemoveExtensionCommand }, | |
| { "REPLACE_EXTENSION"_s, HandleReplaceExtensionCommand }, | |
| { "NORMAL_PATH"_s, HandleNormalPathCommand }, | |
| { "RELATIVE_PATH"_s, HandleRelativePathCommand }, | |
| { "ABSOLUTE_PATH"_s, HandleAbsolutePathCommand }, | |
| { "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_PART"_s, HandleHasRelativePartCommand }, | |
| { "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); | |
| } |