You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1000 lines
26 KiB
1000 lines
26 KiB
3 years ago
|
/* 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 "cmProperty.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)
|
||
|
{
|
||
|
this->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 this->cmArgumentParser<Result>::Parse(
|
||
|
cmMakeRange(args).advance(Advance), &this->Inputs, keywordsMissingValue,
|
||
|
parsedKeywords);
|
||
|
}
|
||
|
|
||
|
const std::vector<std::string>& 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
|
||
|
{
|
||
|
this->KeywordsMissingValue.clear();
|
||
|
this->ParsedKeywords.clear();
|
||
|
|
||
|
return this->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)
|
||
|
{
|
||
|
cmProp 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(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_PART"_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 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;
|
||
|
|
||
|
const auto 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{};
|
||
|
|
||
|
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 HandleAppendStringCommand(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 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 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;
|
||
|
}
|
||
|
|
||
|
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,
|
||
|
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 HandleHasRelativePartCommand(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) {
|
||
|
status.SetError("HASH must be called with two arguments.");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
std::string inputPath;
|
||
|
if (!getInputPath(args[1], status, inputPath)) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
const auto& 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);
|
||
|
}
|