Merge topic 'argument-parser'

b783e62533 cmExecuteProcessCommand: Port to cmArgumentParser
9bddb03f31 cmParseArgumentsCommand: Port to cmArgumentParser
45edf1ad66 Retire cmCommandArgumentsHelper
f5acecaa6f cmExportCommand: Port to cmArgumentParser
e6b6bb0618 cmInstallCommand: Port to cmArgumentParser
4336a29edd cmFileCommand: Port to cmArgumentParser
4359fe133b Introduce cmArgumentParser

Acked-by: Kitware Robot <kwrobot@kitware.com>
Acked-by: Leonid Pospelov <pospelovlm@yandex.ru>
Merge-request: !3137
This commit is contained in:
Brad King 2019-04-09 12:27:16 +00:00 committed by Kitware Robot
commit aa0692de67
19 changed files with 799 additions and 1012 deletions

View File

@ -143,6 +143,8 @@ set(SRCS
cmAffinity.cxx
cmAffinity.h
cmArchiveWrite.cxx
cmArgumentParser.cxx
cmArgumentParser.h
cmBase32.cxx
cmCacheManager.cxx
cmCacheManager.h
@ -443,8 +445,6 @@ set(SRCS
cmCMakeMinimumRequired.h
cmCMakePolicyCommand.cxx
cmCMakePolicyCommand.h
cmCommandArgumentsHelper.cxx
cmCommandArgumentsHelper.h
cmConditionEvaluator.cxx
cmConditionEvaluator.h
cmConfigureFileCommand.cxx

View File

@ -0,0 +1,93 @@
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmArgumentParser.h"
#include <algorithm>
#include <type_traits>
namespace ArgumentParser {
auto ActionMap::Emplace(cm::string_view name, Action action)
-> std::pair<iterator, bool>
{
auto const it =
std::lower_bound(this->begin(), this->end(), name,
[](value_type const& elem, cm::string_view const& k) {
return elem.first < k;
});
return (it != this->end() && it->first == name)
? std::make_pair(it, false)
: std::make_pair(this->emplace(it, name, std::move(action)), true);
}
auto ActionMap::Find(cm::string_view name) const -> const_iterator
{
auto const it =
std::lower_bound(this->begin(), this->end(), name,
[](value_type const& elem, cm::string_view const& k) {
return elem.first < k;
});
return (it != this->end() && it->first == name) ? it : this->end();
}
void Instance::Bind(bool& val)
{
val = true;
this->CurrentString = nullptr;
this->CurrentList = nullptr;
this->ExpectValue = false;
}
void Instance::Bind(std::string& val)
{
this->CurrentString = &val;
this->CurrentList = nullptr;
this->ExpectValue = true;
}
void Instance::Bind(StringList& val)
{
this->CurrentString = nullptr;
this->CurrentList = &val;
this->ExpectValue = true;
}
void Instance::Bind(MultiStringList& val)
{
this->CurrentString = nullptr;
this->CurrentList = (val.emplace_back(), &val.back());
this->ExpectValue = false;
}
void Instance::Consume(cm::string_view arg, void* result,
std::vector<std::string>* unparsedArguments,
std::vector<std::string>* keywordsMissingValue)
{
auto const it = this->Bindings.Find(arg);
if (it != this->Bindings.end()) {
it->second(*this, result);
if (this->ExpectValue && keywordsMissingValue != nullptr) {
keywordsMissingValue->emplace_back(arg);
}
return;
}
if (this->CurrentString != nullptr) {
this->CurrentString->assign(std::string(arg));
this->CurrentString = nullptr;
this->CurrentList = nullptr;
} else if (this->CurrentList != nullptr) {
this->CurrentList->emplace_back(arg);
} else if (unparsedArguments != nullptr) {
unparsedArguments->emplace_back(arg);
}
if (this->ExpectValue) {
if (keywordsMissingValue != nullptr) {
keywordsMissingValue->pop_back();
}
this->ExpectValue = false;
}
}
} // namespace ArgumentParser

143
Source/cmArgumentParser.h Normal file
View File

@ -0,0 +1,143 @@
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
#ifndef cmArgumentParser_h
#define cmArgumentParser_h
#include "cmConfigure.h" // IWYU pragma: keep
#include "cm_static_string_view.hxx"
#include "cm_string_view.hxx"
#include <cassert>
#include <functional>
#include <string>
#include <utility>
#include <vector>
namespace ArgumentParser {
using StringList = std::vector<std::string>;
using MultiStringList = std::vector<StringList>;
class Instance;
using Action = std::function<void(Instance&, void*)>;
// using ActionMap = cm::flat_map<cm::string_view, Action>;
class ActionMap : public std::vector<std::pair<cm::string_view, Action>>
{
public:
std::pair<iterator, bool> Emplace(cm::string_view name, Action action);
const_iterator Find(cm::string_view name) const;
};
class Instance
{
public:
Instance(ActionMap const& bindings)
: Bindings(bindings)
{
}
void Bind(bool& val);
void Bind(std::string& val);
void Bind(StringList& val);
void Bind(MultiStringList& val);
void Consume(cm::string_view arg, void* result,
std::vector<std::string>* unparsedArguments,
std::vector<std::string>* keywordsMissingValue);
private:
ActionMap const& Bindings;
std::string* CurrentString = nullptr;
StringList* CurrentList = nullptr;
bool ExpectValue = false;
};
} // namespace ArgumentParser
template <typename Result>
class cmArgumentParser
{
public:
// I *think* this function could be made `constexpr` when the code is
// compiled as C++20. This would allow building a parser at compile time.
template <typename T>
cmArgumentParser& Bind(cm::static_string_view name, T Result::*member)
{
bool const inserted =
this->Bindings
.Emplace(name,
[member](ArgumentParser::Instance& instance, void* result) {
instance.Bind(static_cast<Result*>(result)->*member);
})
.second;
assert(inserted), (void)inserted;
return *this;
}
template <typename Range>
void Parse(Result& result, Range const& args,
std::vector<std::string>* unparsedArguments = nullptr,
std::vector<std::string>* keywordsMissingValue = nullptr) const
{
ArgumentParser::Instance instance(this->Bindings);
for (cm::string_view arg : args) {
instance.Consume(arg, &result, unparsedArguments, keywordsMissingValue);
}
}
template <typename Range>
Result Parse(Range const& args,
std::vector<std::string>* unparsedArguments = nullptr,
std::vector<std::string>* keywordsMissingValue = nullptr) const
{
Result result;
this->Parse(result, args, unparsedArguments, keywordsMissingValue);
return result;
}
private:
ArgumentParser::ActionMap Bindings;
};
template <>
class cmArgumentParser<void>
{
public:
template <typename T>
cmArgumentParser& Bind(cm::static_string_view name, T& ref)
{
bool const inserted = this->Bind(cm::string_view(name), ref);
assert(inserted), (void)inserted;
return *this;
}
template <typename Range>
void Parse(Range const& args,
std::vector<std::string>* unparsedArguments = nullptr,
std::vector<std::string>* keywordsMissingValue = nullptr) const
{
ArgumentParser::Instance instance(this->Bindings);
for (cm::string_view arg : args) {
instance.Consume(arg, nullptr, unparsedArguments, keywordsMissingValue);
}
}
protected:
template <typename T>
bool Bind(cm::string_view name, T& ref)
{
return this->Bindings
.Emplace(name,
[&ref](ArgumentParser::Instance& instance, void*) {
instance.Bind(ref);
})
.second;
}
private:
ArgumentParser::ActionMap Bindings;
};
#endif

View File

@ -1,233 +0,0 @@
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmCommandArgumentsHelper.h"
cmCommandArgument::cmCommandArgument(cmCommandArgumentsHelper* args,
const char* key,
cmCommandArgumentGroup* group)
: Key(key)
, Group(group)
, WasActive(false)
, ArgumentsBeforeEmpty(true)
, CurrentIndex(0)
{
if (args != nullptr) {
args->AddArgument(this);
}
if (this->Group != nullptr) {
this->Group->ContainedArguments.push_back(this);
}
}
void cmCommandArgument::Reset()
{
this->WasActive = false;
this->CurrentIndex = 0;
this->DoReset();
}
void cmCommandArgument::Follows(const cmCommandArgument* arg)
{
this->ArgumentsBeforeEmpty = false;
this->ArgumentsBefore.insert(arg);
}
void cmCommandArgument::FollowsGroup(const cmCommandArgumentGroup* group)
{
if (group != nullptr) {
this->ArgumentsBeforeEmpty = false;
this->ArgumentsBefore.insert(group->ContainedArguments.begin(),
group->ContainedArguments.end());
}
}
bool cmCommandArgument::MayFollow(const cmCommandArgument* current) const
{
if (this->ArgumentsBeforeEmpty) {
return true;
}
return this->ArgumentsBefore.find(current) != this->ArgumentsBefore.end();
}
bool cmCommandArgument::KeyMatches(const std::string& key) const
{
if ((this->Key == nullptr) || (this->Key[0] == '\0')) {
return true;
}
return (key == this->Key);
}
void cmCommandArgument::ApplyOwnGroup()
{
if (this->Group != nullptr) {
for (cmCommandArgument* cargs : this->Group->ContainedArguments) {
if (cargs != this) {
this->ArgumentsBefore.insert(cargs);
}
}
}
}
void cmCommandArgument::Activate()
{
this->WasActive = true;
this->CurrentIndex = 0;
}
bool cmCommandArgument::Consume(const std::string& arg)
{
bool res = this->DoConsume(arg, this->CurrentIndex);
this->CurrentIndex++;
return res;
}
cmCAStringVector::cmCAStringVector(cmCommandArgumentsHelper* args,
const char* key,
cmCommandArgumentGroup* group)
: cmCommandArgument(args, key, group)
, Ignore(nullptr)
{
if ((key == nullptr) || (*key == 0)) {
this->DataStart = 0;
} else {
this->DataStart = 1;
}
}
bool cmCAStringVector::DoConsume(const std::string& arg, unsigned int index)
{
if (index >= this->DataStart) {
if ((this->Ignore == nullptr) || (arg != this->Ignore)) {
this->Vector.push_back(arg);
}
}
return false;
}
void cmCAStringVector::DoReset()
{
this->Vector.clear();
}
cmCAString::cmCAString(cmCommandArgumentsHelper* args, const char* key,
cmCommandArgumentGroup* group)
: cmCommandArgument(args, key, group)
{
if ((key == nullptr) || (*key == 0)) {
this->DataStart = 0;
} else {
this->DataStart = 1;
}
}
bool cmCAString::DoConsume(const std::string& arg, unsigned int index)
{
if (index == this->DataStart) {
this->String = arg;
}
return index >= this->DataStart;
}
void cmCAString::DoReset()
{
this->String.clear();
}
cmCAEnabler::cmCAEnabler(cmCommandArgumentsHelper* args, const char* key,
cmCommandArgumentGroup* group)
: cmCommandArgument(args, key, group)
, Enabled(false)
{
}
bool cmCAEnabler::DoConsume(const std::string&, unsigned int index)
{
if (index == 0) {
this->Enabled = true;
}
return true;
}
void cmCAEnabler::DoReset()
{
this->Enabled = false;
}
cmCADisabler::cmCADisabler(cmCommandArgumentsHelper* args, const char* key,
cmCommandArgumentGroup* group)
: cmCommandArgument(args, key, group)
, Enabled(true)
{
}
bool cmCADisabler::DoConsume(const std::string&, unsigned int index)
{
if (index == 0) {
this->Enabled = false;
}
return true;
}
void cmCADisabler::DoReset()
{
this->Enabled = true;
}
void cmCommandArgumentGroup::Follows(const cmCommandArgument* arg)
{
for (cmCommandArgument* ca : this->ContainedArguments) {
ca->Follows(arg);
}
}
void cmCommandArgumentGroup::FollowsGroup(const cmCommandArgumentGroup* group)
{
for (cmCommandArgument* ca : this->ContainedArguments) {
ca->FollowsGroup(group);
}
}
void cmCommandArgumentsHelper::Parse(const std::vector<std::string>* args,
std::vector<std::string>* unconsumedArgs)
{
if (args == nullptr) {
return;
}
for (cmCommandArgument* ca : this->Arguments) {
ca->ApplyOwnGroup();
ca->Reset();
}
cmCommandArgument* activeArgument = nullptr;
const cmCommandArgument* previousArgument = nullptr;
for (std::string const& it : *args) {
for (cmCommandArgument* ca : this->Arguments) {
if (ca->KeyMatches(it) && (ca->MayFollow(previousArgument))) {
activeArgument = ca;
activeArgument->Activate();
break;
}
}
if (activeArgument) {
bool argDone = activeArgument->Consume(it);
previousArgument = activeArgument;
if (argDone) {
activeArgument = nullptr;
}
} else {
if (unconsumedArgs != nullptr) {
unconsumedArgs->push_back(it);
}
}
}
}
void cmCommandArgumentsHelper::AddArgument(cmCommandArgument* arg)
{
this->Arguments.push_back(arg);
}

View File

@ -1,194 +0,0 @@
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
#ifndef cmCommandArgumentsHelper_h
#define cmCommandArgumentsHelper_h
#include "cmConfigure.h" // IWYU pragma: keep
#include <set>
#include <string>
#include <vector>
class cmCommandArgumentGroup;
class cmCommandArgumentsHelper;
/* cmCommandArgumentsHelper, cmCommandArgumentGroup and cmCommandArgument (i.e.
its derived classes cmCAXXX can be used to simplify the processing of
arguments to cmake commands. Maybe they can also be used to generate
documentation.
For every argument supported by a command one cmCommandArgument is created
and added to cmCommandArgumentsHelper. cmCommand has a cmCommandArgumentsHelper
as member variable so this should be used.
The order of the arguments is defined using the Follows(arg) method. It says
that this argument follows immediateley the given argument. It can be used
with multiple arguments if the argument can follow after different arguments.
Arguments can be arranged in groups using cmCommandArgumentGroup. Every
member of a group can follow any other member of the group. These groups
can also be used to define the order.
Once all arguments and groups are set up, cmCommandArgumentsHelper::Parse()
is called and afterwards the values of the arguments can be evaluated.
For an example see cmExportCommand.cxx.
*/
class cmCommandArgument
{
public:
cmCommandArgument(cmCommandArgumentsHelper* args, const char* key,
cmCommandArgumentGroup* group = nullptr);
virtual ~cmCommandArgument() = default;
/// this argument may follow after arg. 0 means it comes first.
void Follows(const cmCommandArgument* arg);
/// this argument may follow after any of the arguments in the given group
void FollowsGroup(const cmCommandArgumentGroup* group);
/// Returns true if the argument was found in the argument list
bool WasFound() const { return this->WasActive; }
// The following methods are only called from
// cmCommandArgumentsHelper::Parse(), but making this a friend would
// give it access to everything
/// Make the current argument the currently active argument
void Activate();
/// Consume the current string
bool Consume(const std::string& arg);
/// Return true if this argument may follow after the given argument.
bool MayFollow(const cmCommandArgument* current) const;
/** Returns true if the given key matches the key for this argument.
If this argument has an empty key everything matches. */
bool KeyMatches(const std::string& key) const;
/// Make this argument follow all members of the own group
void ApplyOwnGroup();
/// Reset argument, so it's back to its initial state
void Reset();
private:
const char* Key;
std::set<const cmCommandArgument*> ArgumentsBefore;
cmCommandArgumentGroup* Group;
bool WasActive;
bool ArgumentsBeforeEmpty;
unsigned int CurrentIndex;
virtual bool DoConsume(const std::string& arg, unsigned int index) = 0;
virtual void DoReset() = 0;
};
/** cmCAStringVector is to be used for arguments which can consist of more
than one string, e.g. the FILES argument in INSTALL(FILES f1 f2 f3 ...). */
class cmCAStringVector : public cmCommandArgument
{
public:
cmCAStringVector(cmCommandArgumentsHelper* args, const char* key,
cmCommandArgumentGroup* group = nullptr);
/// Return the vector of strings
const std::vector<std::string>& GetVector() const { return this->Vector; }
/** Is there a keyword which should be skipped in
the arguments (e.g. ARGS for ADD_CUSTOM_COMMAND) ? */
void SetIgnore(const char* ignore) { this->Ignore = ignore; }
private:
std::vector<std::string> Vector;
unsigned int DataStart;
const char* Ignore;
bool DoConsume(const std::string& arg, unsigned int index) override;
void DoReset() override;
};
/** cmCAString is to be used for arguments which consist of one value,
e.g. the executable name in ADD_EXECUTABLE(). */
class cmCAString : public cmCommandArgument
{
public:
cmCAString(cmCommandArgumentsHelper* args, const char* key,
cmCommandArgumentGroup* group = nullptr);
/// Return the string
const std::string& GetString() const { return this->String; }
const char* GetCString() const { return this->String.c_str(); }
private:
std::string String;
unsigned int DataStart;
bool DoConsume(const std::string& arg, unsigned int index) override;
void DoReset() override;
};
/** cmCAEnabler is to be used for options which are off by default and can be
enabled using a special argument, e.g. EXCLUDE_FROM_ALL in ADD_EXECUTABLE(). */
class cmCAEnabler : public cmCommandArgument
{
public:
cmCAEnabler(cmCommandArgumentsHelper* args, const char* key,
cmCommandArgumentGroup* group = nullptr);
/// Has it been enabled ?
bool IsEnabled() const { return this->Enabled; }
private:
bool Enabled;
bool DoConsume(const std::string& arg, unsigned int index) override;
void DoReset() override;
};
/** cmCADisable is to be used for options which are on by default and can be
disabled using a special argument.*/
class cmCADisabler : public cmCommandArgument
{
public:
cmCADisabler(cmCommandArgumentsHelper* args, const char* key,
cmCommandArgumentGroup* group = nullptr);
/// Is it still enabled ?
bool IsEnabled() const { return this->Enabled; }
private:
bool Enabled;
bool DoConsume(const std::string& arg, unsigned int index) override;
void DoReset() override;
};
/** Group of arguments, needed for ordering. E.g. WIN32, EXCLUDE_FROM_ALL and
MACSOX_BUNDLE from ADD_EXECUTABLE() are a group.
*/
class cmCommandArgumentGroup
{
friend class cmCommandArgument;
public:
/// All members of this group may follow the given argument
void Follows(const cmCommandArgument* arg);
/// All members of this group may follow all members of the given group
void FollowsGroup(const cmCommandArgumentGroup* group);
private:
std::vector<cmCommandArgument*> ContainedArguments;
};
class cmCommandArgumentsHelper
{
public:
/// Parse the argument list
void Parse(const std::vector<std::string>* args,
std::vector<std::string>* unconsumedArgs);
/// Add an argument.
void AddArgument(cmCommandArgument* arg);
private:
std::vector<cmCommandArgument*> Arguments;
};
#endif

View File

@ -2,12 +2,14 @@
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmExecuteProcessCommand.h"
#include "cm_static_string_view.hxx"
#include "cmsys/Process.h"
#include <algorithm>
#include <ctype.h> /* isspace */
#include <sstream>
#include <stdio.h>
#include "cmAlgorithms.h"
#include "cmArgumentParser.h"
#include "cmMakefile.h"
#include "cmProcessOutput.h"
#include "cmSystemTools.h"
@ -32,157 +34,85 @@ bool cmExecuteProcessCommand::InitialPass(std::vector<std::string> const& args,
this->SetError("called with incorrect number of arguments");
return false;
}
std::vector<std::vector<const char*>> cmds;
std::string arguments;
bool doing_command = false;
size_t command_index = 0;
bool output_quiet = false;
bool error_quiet = false;
bool output_strip_trailing_whitespace = false;
bool error_strip_trailing_whitespace = false;
std::string timeout_string;
std::string input_file;
std::string output_file;
std::string error_file;
std::string output_variable;
std::string error_variable;
std::string result_variable;
std::string results_variable;
std::string working_directory;
cmProcessOutput::Encoding encoding = cmProcessOutput::None;
for (size_t i = 0; i < args.size(); ++i) {
if (args[i] == "COMMAND") {
doing_command = true;
command_index = cmds.size();
cmds.emplace_back();
} else if (args[i] == "OUTPUT_VARIABLE") {
doing_command = false;
if (++i < args.size()) {
output_variable = args[i];
} else {
this->SetError(" called with no value for OUTPUT_VARIABLE.");
return false;
}
} else if (args[i] == "ERROR_VARIABLE") {
doing_command = false;
if (++i < args.size()) {
error_variable = args[i];
} else {
this->SetError(" called with no value for ERROR_VARIABLE.");
return false;
}
} else if (args[i] == "RESULT_VARIABLE") {
doing_command = false;
if (++i < args.size()) {
result_variable = args[i];
} else {
this->SetError(" called with no value for RESULT_VARIABLE.");
return false;
}
} else if (args[i] == "RESULTS_VARIABLE") {
doing_command = false;
if (++i < args.size()) {
results_variable = args[i];
} else {
this->SetError(" called with no value for RESULTS_VARIABLE.");
return false;
}
} else if (args[i] == "WORKING_DIRECTORY") {
doing_command = false;
if (++i < args.size()) {
working_directory = args[i];
} else {
this->SetError(" called with no value for WORKING_DIRECTORY.");
return false;
}
} else if (args[i] == "INPUT_FILE") {
doing_command = false;
if (++i < args.size()) {
input_file = args[i];
} else {
this->SetError(" called with no value for INPUT_FILE.");
return false;
}
} else if (args[i] == "OUTPUT_FILE") {
doing_command = false;
if (++i < args.size()) {
output_file = args[i];
} else {
this->SetError(" called with no value for OUTPUT_FILE.");
return false;
}
} else if (args[i] == "ERROR_FILE") {
doing_command = false;
if (++i < args.size()) {
error_file = args[i];
} else {
this->SetError(" called with no value for ERROR_FILE.");
return false;
}
} else if (args[i] == "TIMEOUT") {
doing_command = false;
if (++i < args.size()) {
timeout_string = args[i];
} else {
this->SetError(" called with no value for TIMEOUT.");
return false;
}
} else if (args[i] == "OUTPUT_QUIET") {
doing_command = false;
output_quiet = true;
} else if (args[i] == "ERROR_QUIET") {
doing_command = false;
error_quiet = true;
} else if (args[i] == "OUTPUT_STRIP_TRAILING_WHITESPACE") {
doing_command = false;
output_strip_trailing_whitespace = true;
} else if (args[i] == "ERROR_STRIP_TRAILING_WHITESPACE") {
doing_command = false;
error_strip_trailing_whitespace = true;
} else if (args[i] == "ENCODING") {
doing_command = false;
if (++i < args.size()) {
encoding = cmProcessOutput::FindEncoding(args[i]);
} else {
this->SetError(" called with no value for ENCODING.");
return false;
}
} else if (doing_command) {
cmds[command_index].push_back(args[i].c_str());
} else {
std::ostringstream e;
e << " given unknown argument \"" << args[i] << "\".";
this->SetError(e.str());
return false;
}
struct Arguments
{
std::vector<std::vector<std::string>> Commands;
std::string OutputVariable;
std::string ErrorVariable;
std::string ResultVariable;
std::string ResultsVariable;
std::string WorkingDirectory;
std::string InputFile;
std::string OutputFile;
std::string ErrorFile;
std::string Timeout;
bool OutputQuiet = false;
bool ErrorQuiet = false;
bool OutputStripTrailingWhitespace = false;
bool ErrorStripTrailingWhitespace = false;
std::string Encoding;
};
static auto const parser =
cmArgumentParser<Arguments>{}
.Bind("COMMAND"_s, &Arguments::Commands)
.Bind("OUTPUT_VARIABLE"_s, &Arguments::OutputVariable)
.Bind("ERROR_VARIABLE"_s, &Arguments::ErrorVariable)
.Bind("RESULT_VARIABLE"_s, &Arguments::ResultVariable)
.Bind("RESULTS_VARIABLE"_s, &Arguments::ResultsVariable)
.Bind("WORKING_DIRECTORY"_s, &Arguments::WorkingDirectory)
.Bind("INPUT_FILE"_s, &Arguments::InputFile)
.Bind("OUTPUT_FILE"_s, &Arguments::OutputFile)
.Bind("ERROR_FILE"_s, &Arguments::ErrorFile)
.Bind("TIMEOUT"_s, &Arguments::Timeout)
.Bind("OUTPUT_QUIET"_s, &Arguments::OutputQuiet)
.Bind("ERROR_QUIET"_s, &Arguments::ErrorQuiet)
.Bind("OUTPUT_STRIP_TRAILING_WHITESPACE"_s,
&Arguments::OutputStripTrailingWhitespace)
.Bind("ERROR_STRIP_TRAILING_WHITESPACE"_s,
&Arguments::ErrorStripTrailingWhitespace)
.Bind("ENCODING"_s, &Arguments::Encoding);
std::vector<std::string> unparsedArguments;
std::vector<std::string> keywordsMissingValue;
Arguments const arguments =
parser.Parse(args, &unparsedArguments, &keywordsMissingValue);
if (!keywordsMissingValue.empty()) {
this->SetError(" called with no value for " +
keywordsMissingValue.front() + ".");
return false;
}
if (!unparsedArguments.empty()) {
this->SetError(" given unknown argument \"" + unparsedArguments.front() +
"\".");
return false;
}
if (!this->Makefile->CanIWriteThisFile(output_file)) {
std::string e = "attempted to output into a file: " + output_file +
" into a source directory.";
this->SetError(e);
if (!this->Makefile->CanIWriteThisFile(arguments.OutputFile)) {
this->SetError("attempted to output into a file: " + arguments.OutputFile +
" into a source directory.");
cmSystemTools::SetFatalErrorOccured();
return false;
}
// Check for commands given.
if (cmds.empty()) {
if (arguments.Commands.empty()) {
this->SetError(" called with no COMMAND argument.");
return false;
}
for (auto& cmd : cmds) {
for (std::vector<std::string> const& cmd : arguments.Commands) {
if (cmd.empty()) {
this->SetError(" given COMMAND argument with no value.");
return false;
}
// Add the null terminating pointer to the command argument list.
cmd.push_back(nullptr);
}
// Parse the timeout string.
double timeout = -1;
if (!timeout_string.empty()) {
if (sscanf(timeout_string.c_str(), "%lg", &timeout) != 1) {
if (!arguments.Timeout.empty()) {
if (sscanf(arguments.Timeout.c_str(), "%lg", &timeout) != 1) {
this->SetError(" called with TIMEOUT value that could not be parsed.");
return false;
}
@ -192,13 +122,17 @@ bool cmExecuteProcessCommand::InitialPass(std::vector<std::string> const& args,
cmsysProcess* cp = cmsysProcess_New();
// Set the command sequence.
for (auto const& cmd : cmds) {
cmsysProcess_AddCommand(cp, cmd.data());
for (std::vector<std::string> const& cmd : arguments.Commands) {
std::vector<const char*> argv(cmd.size() + 1);
std::transform(cmd.begin(), cmd.end(), argv.begin(),
[](std::string const& s) { return s.c_str(); });
argv.back() = nullptr;
cmsysProcess_AddCommand(cp, argv.data());
}
// Set the process working directory.
if (!working_directory.empty()) {
cmsysProcess_SetWorkingDirectory(cp, working_directory.c_str());
if (!arguments.WorkingDirectory.empty()) {
cmsysProcess_SetWorkingDirectory(cp, arguments.WorkingDirectory.c_str());
}
// Always hide the process window.
@ -206,22 +140,24 @@ bool cmExecuteProcessCommand::InitialPass(std::vector<std::string> const& args,
// Check the output variables.
bool merge_output = false;
if (!input_file.empty()) {
cmsysProcess_SetPipeFile(cp, cmsysProcess_Pipe_STDIN, input_file.c_str());
if (!arguments.InputFile.empty()) {
cmsysProcess_SetPipeFile(cp, cmsysProcess_Pipe_STDIN,
arguments.InputFile.c_str());
}
if (!output_file.empty()) {
if (!arguments.OutputFile.empty()) {
cmsysProcess_SetPipeFile(cp, cmsysProcess_Pipe_STDOUT,
output_file.c_str());
arguments.OutputFile.c_str());
}
if (!error_file.empty()) {
if (error_file == output_file) {
if (!arguments.ErrorFile.empty()) {
if (arguments.ErrorFile == arguments.OutputFile) {
merge_output = true;
} else {
cmsysProcess_SetPipeFile(cp, cmsysProcess_Pipe_STDERR,
error_file.c_str());
arguments.ErrorFile.c_str());
}
}
if (!output_variable.empty() && output_variable == error_variable) {
if (!arguments.OutputVariable.empty() &&
arguments.OutputVariable == arguments.ErrorVariable) {
merge_output = true;
}
if (merge_output) {
@ -242,19 +178,20 @@ bool cmExecuteProcessCommand::InitialPass(std::vector<std::string> const& args,
int length;
char* data;
int p;
cmProcessOutput processOutput(encoding);
cmProcessOutput processOutput(
cmProcessOutput::FindEncoding(arguments.Encoding));
std::string strdata;
while ((p = cmsysProcess_WaitForData(cp, &data, &length, nullptr))) {
// Put the output in the right place.
if (p == cmsysProcess_Pipe_STDOUT && !output_quiet) {
if (output_variable.empty()) {
if (p == cmsysProcess_Pipe_STDOUT && !arguments.OutputQuiet) {
if (arguments.OutputVariable.empty()) {
processOutput.DecodeText(data, length, strdata, 1);
cmSystemTools::Stdout(strdata);
} else {
cmExecuteProcessCommandAppend(tempOutput, data, length);
}
} else if (p == cmsysProcess_Pipe_STDERR && !error_quiet) {
if (error_variable.empty()) {
} else if (p == cmsysProcess_Pipe_STDERR && !arguments.ErrorQuiet) {
if (arguments.ErrorVariable.empty()) {
processOutput.DecodeText(data, length, strdata, 2);
cmSystemTools::Stderr(strdata);
} else {
@ -262,13 +199,13 @@ bool cmExecuteProcessCommand::InitialPass(std::vector<std::string> const& args,
}
}
}
if (!output_quiet && output_variable.empty()) {
if (!arguments.OutputQuiet && arguments.OutputVariable.empty()) {
processOutput.DecodeText(std::string(), strdata, 1);
if (!strdata.empty()) {
cmSystemTools::Stdout(strdata);
}
}
if (!error_quiet && error_variable.empty()) {
if (!arguments.ErrorQuiet && arguments.ErrorVariable.empty()) {
processOutput.DecodeText(std::string(), strdata, 2);
if (!strdata.empty()) {
cmSystemTools::Stderr(strdata);
@ -281,46 +218,49 @@ bool cmExecuteProcessCommand::InitialPass(std::vector<std::string> const& args,
processOutput.DecodeText(tempError, tempError);
// Fix the text in the output strings.
cmExecuteProcessCommandFixText(tempOutput, output_strip_trailing_whitespace);
cmExecuteProcessCommandFixText(tempError, error_strip_trailing_whitespace);
cmExecuteProcessCommandFixText(tempOutput,
arguments.OutputStripTrailingWhitespace);
cmExecuteProcessCommandFixText(tempError,
arguments.ErrorStripTrailingWhitespace);
// Store the output obtained.
if (!output_variable.empty() && !tempOutput.empty()) {
this->Makefile->AddDefinition(output_variable, tempOutput.data());
if (!arguments.OutputVariable.empty() && !tempOutput.empty()) {
this->Makefile->AddDefinition(arguments.OutputVariable, tempOutput.data());
}
if (!merge_output && !error_variable.empty() && !tempError.empty()) {
this->Makefile->AddDefinition(error_variable, tempError.data());
if (!merge_output && !arguments.ErrorVariable.empty() &&
!tempError.empty()) {
this->Makefile->AddDefinition(arguments.ErrorVariable, tempError.data());
}
// Store the result of running the process.
if (!result_variable.empty()) {
if (!arguments.ResultVariable.empty()) {
switch (cmsysProcess_GetState(cp)) {
case cmsysProcess_State_Exited: {
int v = cmsysProcess_GetExitValue(cp);
char buf[16];
sprintf(buf, "%d", v);
this->Makefile->AddDefinition(result_variable, buf);
this->Makefile->AddDefinition(arguments.ResultVariable, buf);
} break;
case cmsysProcess_State_Exception:
this->Makefile->AddDefinition(result_variable,
this->Makefile->AddDefinition(arguments.ResultVariable,
cmsysProcess_GetExceptionString(cp));
break;
case cmsysProcess_State_Error:
this->Makefile->AddDefinition(result_variable,
this->Makefile->AddDefinition(arguments.ResultVariable,
cmsysProcess_GetErrorString(cp));
break;
case cmsysProcess_State_Expired:
this->Makefile->AddDefinition(result_variable,
this->Makefile->AddDefinition(arguments.ResultVariable,
"Process terminated due to timeout");
break;
}
}
// Store the result of running the processes.
if (!results_variable.empty()) {
if (!arguments.ResultsVariable.empty()) {
switch (cmsysProcess_GetState(cp)) {
case cmsysProcess_State_Exited: {
std::vector<std::string> res;
for (size_t i = 0; i < cmds.size(); ++i) {
for (size_t i = 0; i < arguments.Commands.size(); ++i) {
switch (cmsysProcess_GetStateByIndex(cp, static_cast<int>(i))) {
case kwsysProcess_StateByIndex_Exited: {
int exitCode =
@ -339,19 +279,19 @@ bool cmExecuteProcessCommand::InitialPass(std::vector<std::string> const& args,
break;
}
}
this->Makefile->AddDefinition(results_variable,
this->Makefile->AddDefinition(arguments.ResultsVariable,
cmJoin(res, ";").c_str());
} break;
case cmsysProcess_State_Exception:
this->Makefile->AddDefinition(results_variable,
this->Makefile->AddDefinition(arguments.ResultsVariable,
cmsysProcess_GetExceptionString(cp));
break;
case cmsysProcess_State_Error:
this->Makefile->AddDefinition(results_variable,
this->Makefile->AddDefinition(arguments.ResultsVariable,
cmsysProcess_GetErrorString(cp));
break;
case cmsysProcess_State_Expired:
this->Makefile->AddDefinition(results_variable,
this->Makefile->AddDefinition(arguments.ResultsVariable,
"Process terminated due to timeout");
break;
}

View File

@ -2,10 +2,13 @@
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmExportCommand.h"
#include "cm_static_string_view.hxx"
#include "cmsys/RegularExpression.hxx"
#include <map>
#include <sstream>
#include <utility>
#include "cmArgumentParser.h"
#include "cmExportBuildAndroidMKGenerator.h"
#include "cmExportBuildFileGenerator.h"
#include "cmExportSetMap.h"
@ -18,6 +21,7 @@
#include "cmSystemTools.h"
#include "cmTarget.h"
class cmExportSet;
class cmExecutionStatus;
#if defined(__HAIKU__)
@ -25,19 +29,6 @@ class cmExecutionStatus;
# include <StorageDefs.h>
#endif
cmExportCommand::cmExportCommand()
: Targets(&Helper, "TARGETS")
, Append(&Helper, "APPEND", &ArgumentGroup)
, ExportSetName(&Helper, "EXPORT", &ArgumentGroup)
, Namespace(&Helper, "NAMESPACE", &ArgumentGroup)
, Filename(&Helper, "FILE", &ArgumentGroup)
, ExportOld(&Helper, "EXPORT_LINK_INTERFACE_LIBRARIES", &ArgumentGroup)
, AndroidMKFile(&Helper, "ANDROID_MK")
{
this->ExportSet = nullptr;
}
// cmExportCommand
bool cmExportCommand::InitialPass(std::vector<std::string> const& args,
cmExecutionStatus&)
{
@ -49,45 +40,62 @@ bool cmExportCommand::InitialPass(std::vector<std::string> const& args,
if (args[0] == "PACKAGE") {
return this->HandlePackage(args);
}
struct Arguments
{
std::string ExportSetName;
std::vector<std::string> Targets;
std::string Namespace;
std::string Filename;
std::string AndroidMKFile;
bool Append = false;
bool ExportOld = false;
};
auto parser = cmArgumentParser<Arguments>{}
.Bind("NAMESPACE"_s, &Arguments::Namespace)
.Bind("FILE"_s, &Arguments::Filename);
if (args[0] == "EXPORT") {
this->ExportSetName.Follows(nullptr);
this->ArgumentGroup.Follows(&this->ExportSetName);
parser.Bind("EXPORT"_s, &Arguments::ExportSetName);
} else {
this->Targets.Follows(nullptr);
this->ArgumentGroup.Follows(&this->Targets);
parser.Bind("TARGETS"_s, &Arguments::Targets);
parser.Bind("ANDROID_MK"_s, &Arguments::AndroidMKFile);
parser.Bind("APPEND"_s, &Arguments::Append);
parser.Bind("EXPORT_LINK_INTERFACE_LIBRARIES"_s, &Arguments::ExportOld);
}
std::vector<std::string> unknownArgs;
this->Helper.Parse(&args, &unknownArgs);
Arguments const arguments = parser.Parse(args, &unknownArgs);
if (!unknownArgs.empty()) {
this->SetError("Unknown arguments.");
this->SetError("Unknown argument: \"" + unknownArgs.front() + "\".");
return false;
}
std::string fname;
bool android = false;
if (this->AndroidMKFile.WasFound()) {
fname = this->AndroidMKFile.GetString();
if (!arguments.AndroidMKFile.empty()) {
fname = arguments.AndroidMKFile;
android = true;
}
if (!this->Filename.WasFound() && fname.empty()) {
if (arguments.Filename.empty() && fname.empty()) {
if (args[0] != "EXPORT") {
this->SetError("FILE <filename> option missing.");
return false;
}
fname = this->ExportSetName.GetString() + ".cmake";
fname = arguments.ExportSetName + ".cmake";
} else if (fname.empty()) {
// Make sure the file has a .cmake extension.
if (cmSystemTools::GetFilenameLastExtension(this->Filename.GetCString()) !=
if (cmSystemTools::GetFilenameLastExtension(arguments.Filename) !=
".cmake") {
std::ostringstream e;
e << "FILE option given filename \"" << this->Filename.GetString()
e << "FILE option given filename \"" << arguments.Filename
<< "\" which does not have an extension of \".cmake\".\n";
this->SetError(e.str());
return false;
}
fname = this->Filename.GetString();
fname = arguments.Filename;
}
// Get the file to write.
@ -109,33 +117,19 @@ bool cmExportCommand::InitialPass(std::vector<std::string> const& args,
cmGlobalGenerator* gg = this->Makefile->GetGlobalGenerator();
cmExportSet* ExportSet = nullptr;
if (args[0] == "EXPORT") {
if (this->Append.IsEnabled()) {
std::ostringstream e;
e << "EXPORT signature does not recognise the APPEND option.";
this->SetError(e.str());
return false;
}
if (this->ExportOld.IsEnabled()) {
std::ostringstream e;
e << "EXPORT signature does not recognise the "
"EXPORT_LINK_INTERFACE_LIBRARIES option.";
this->SetError(e.str());
return false;
}
cmExportSetMap& setMap = gg->GetExportSets();
std::string setName = this->ExportSetName.GetString();
if (setMap.find(setName) == setMap.end()) {
auto const it = setMap.find(arguments.ExportSetName);
if (it == setMap.end()) {
std::ostringstream e;
e << "Export set \"" << setName << "\" not found.";
e << "Export set \"" << arguments.ExportSetName << "\" not found.";
this->SetError(e.str());
return false;
}
this->ExportSet = setMap[setName];
} else if (this->Targets.WasFound()) {
for (std::string const& currentTarget : this->Targets.GetVector()) {
ExportSet = it->second;
} else if (!arguments.Targets.empty()) {
for (std::string const& currentTarget : arguments.Targets) {
if (this->Makefile->IsAlias(currentTarget)) {
std::ostringstream e;
e << "given ALIAS target \"" << currentTarget
@ -159,7 +153,7 @@ bool cmExportCommand::InitialPass(std::vector<std::string> const& args,
}
targets.push_back(currentTarget);
}
if (this->Append.IsEnabled()) {
if (arguments.Append) {
if (cmExportBuildFileGenerator* ebfg =
gg->GetExportedTargetsFile(fname)) {
ebfg->AppendTargets(targets);
@ -179,15 +173,15 @@ bool cmExportCommand::InitialPass(std::vector<std::string> const& args,
ebfg = new cmExportBuildFileGenerator;
}
ebfg->SetExportFile(fname.c_str());
ebfg->SetNamespace(this->Namespace.GetCString());
ebfg->SetAppendMode(this->Append.IsEnabled());
if (this->ExportSet) {
ebfg->SetExportSet(this->ExportSet);
ebfg->SetNamespace(arguments.Namespace);
ebfg->SetAppendMode(arguments.Append);
if (ExportSet != nullptr) {
ebfg->SetExportSet(ExportSet);
} else {
ebfg->SetTargets(targets);
}
this->Makefile->AddExportBuildFileGenerator(ebfg);
ebfg->SetExportOld(this->ExportOld.IsEnabled());
ebfg->SetExportOld(arguments.ExportOld);
// Compute the set of configurations exported.
std::vector<std::string> configurationTypes;
@ -198,7 +192,7 @@ bool cmExportCommand::InitialPass(std::vector<std::string> const& args,
for (std::string const& ct : configurationTypes) {
ebfg->AddConfiguration(ct);
}
if (this->ExportSet) {
if (ExportSet != nullptr) {
gg->AddBuildExportExportSet(ebfg);
} else {
gg->AddBuildExportSet(ebfg);

View File

@ -9,21 +9,12 @@
#include <vector>
#include "cmCommand.h"
#include "cmCommandArgumentsHelper.h"
class cmExecutionStatus;
class cmExportSet;
/** \class cmExportLibraryDependenciesCommand
* \brief Add a test to the lists of tests to run.
*
* cmExportLibraryDependenciesCommand adds a test to the list of tests to run
*
*/
class cmExportCommand : public cmCommand
{
public:
cmExportCommand();
/**
* This is a virtual constructor for the command.
*/
@ -37,21 +28,6 @@ public:
cmExecutionStatus& status) override;
private:
cmCommandArgumentsHelper Helper;
cmCommandArgumentGroup ArgumentGroup;
cmCAStringVector Targets;
cmCAEnabler Append;
cmCAString ExportSetName;
cmCAString Namespace;
cmCAString Filename;
cmCAEnabler ExportOld;
cmCAString AndroidMKFile;
cmExportSet* ExportSet;
friend class cmExportBuildFileGenerator;
std::string ErrorMessage;
bool HandlePackage(std::vector<std::string> const& args);
void StorePackageRegistryWin(std::string const& package, const char* content,
const char* hash);

View File

@ -3,6 +3,7 @@
#include "cmFileCommand.h"
#include "cm_kwiml.h"
#include "cm_static_string_view.hxx"
#include "cmsys/FStream.hxx"
#include "cmsys/Glob.hxx"
#include "cmsys/RegularExpression.hxx"
@ -19,7 +20,7 @@
#include <vector>
#include "cmAlgorithms.h"
#include "cmCommandArgumentsHelper.h"
#include "cmArgumentParser.h"
#include "cmCryptoHash.h"
#include "cmFileCopier.h"
#include "cmFileInstaller.h"
@ -268,36 +269,34 @@ bool cmFileCommand::HandleReadCommand(std::vector<std::string> const& args)
return false;
}
cmCommandArgumentsHelper argHelper;
cmCommandArgumentGroup group;
std::string const& fileNameArg = args[1];
std::string const& variable = args[2];
cmCAString readArg(&argHelper, "READ");
cmCAString fileNameArg(&argHelper, nullptr);
cmCAString resultArg(&argHelper, nullptr);
struct Arguments
{
std::string Offset;
std::string Limit;
bool Hex = false;
};
cmCAString offsetArg(&argHelper, "OFFSET", &group);
cmCAString limitArg(&argHelper, "LIMIT", &group);
cmCAEnabler hexOutputArg(&argHelper, "HEX", &group);
readArg.Follows(nullptr);
fileNameArg.Follows(&readArg);
resultArg.Follows(&fileNameArg);
group.Follows(&resultArg);
argHelper.Parse(&args, nullptr);
static auto const parser = cmArgumentParser<Arguments>{}
.Bind("OFFSET"_s, &Arguments::Offset)
.Bind("LIMIT"_s, &Arguments::Limit)
.Bind("HEX"_s, &Arguments::Hex);
std::string fileName = fileNameArg.GetString();
Arguments const arguments = parser.Parse(cmMakeRange(args).advance(3));
std::string fileName = fileNameArg;
if (!cmsys::SystemTools::FileIsFullPath(fileName)) {
fileName = this->Makefile->GetCurrentSourceDirectory();
fileName += "/" + fileNameArg.GetString();
fileName += "/" + fileNameArg;
}
std::string variable = resultArg.GetString();
// Open the specified file.
#if defined(_WIN32) || defined(__CYGWIN__)
cmsys::ifstream file(
fileName.c_str(),
std::ios::in |
(hexOutputArg.IsEnabled() ? std::ios::binary : std::ios::in));
cmsys::ifstream file(fileName.c_str(),
arguments.Hex ? (std::ios::binary | std::ios::in)
: std::ios::in);
#else
cmsys::ifstream file(fileName.c_str());
#endif
@ -313,21 +312,21 @@ bool cmFileCommand::HandleReadCommand(std::vector<std::string> const& args)
// is there a limit?
long sizeLimit = -1;
if (!limitArg.GetString().empty()) {
sizeLimit = atoi(limitArg.GetCString());
if (!arguments.Limit.empty()) {
sizeLimit = atoi(arguments.Limit.c_str());
}
// is there an offset?
long offset = 0;
if (!offsetArg.GetString().empty()) {
offset = atoi(offsetArg.GetCString());
if (!arguments.Offset.empty()) {
offset = atoi(arguments.Offset.c_str());
}
file.seekg(offset, std::ios::beg); // explicit ios::beg for IBM VisualAge 6
std::string output;
if (hexOutputArg.IsEnabled()) {
if (arguments.Hex) {
// Convert part of the file into hex code
char c;
while ((sizeLimit != 0) && (file.get(c))) {
@ -1272,55 +1271,54 @@ bool cmFileCommand::HandleReadElfCommand(std::vector<std::string> const& args)
return false;
}
cmCommandArgumentsHelper argHelper;
cmCommandArgumentGroup group;
std::string const& fileNameArg = args[1];
cmCAString readArg(&argHelper, "READ_ELF");
cmCAString fileNameArg(&argHelper, nullptr);
struct Arguments
{
std::string RPath;
std::string RunPath;
std::string Error;
};
cmCAString rpathArg(&argHelper, "RPATH", &group);
cmCAString runpathArg(&argHelper, "RUNPATH", &group);
cmCAString errorArg(&argHelper, "CAPTURE_ERROR", &group);
static auto const parser = cmArgumentParser<Arguments>{}
.Bind("RPATH"_s, &Arguments::RPath)
.Bind("RUNPATH"_s, &Arguments::RunPath)
.Bind("CAPTURE_ERROR"_s, &Arguments::Error);
Arguments const arguments = parser.Parse(cmMakeRange(args).advance(2));
readArg.Follows(nullptr);
fileNameArg.Follows(&readArg);
group.Follows(&fileNameArg);
argHelper.Parse(&args, nullptr);
if (!cmSystemTools::FileExists(fileNameArg.GetString(), true)) {
if (!cmSystemTools::FileExists(fileNameArg, true)) {
std::ostringstream e;
e << "READ_ELF given FILE \"" << fileNameArg.GetString()
<< "\" that does not exist.";
e << "READ_ELF given FILE \"" << fileNameArg << "\" that does not exist.";
this->SetError(e.str());
return false;
}
#if defined(CMAKE_USE_ELF_PARSER)
cmELF elf(fileNameArg.GetCString());
cmELF elf(fileNameArg.c_str());
if (!rpathArg.GetString().empty()) {
if (!arguments.RPath.empty()) {
if (cmELF::StringEntry const* se_rpath = elf.GetRPath()) {
std::string rpath(se_rpath->Value);
std::replace(rpath.begin(), rpath.end(), ':', ';');
this->Makefile->AddDefinition(rpathArg.GetString(), rpath.c_str());
this->Makefile->AddDefinition(arguments.RPath, rpath.c_str());
}
}
if (!runpathArg.GetString().empty()) {
if (!arguments.RunPath.empty()) {
if (cmELF::StringEntry const* se_runpath = elf.GetRunPath()) {
std::string runpath(se_runpath->Value);
std::replace(runpath.begin(), runpath.end(), ':', ';');
this->Makefile->AddDefinition(runpathArg.GetString(), runpath.c_str());
this->Makefile->AddDefinition(arguments.RunPath, runpath.c_str());
}
}
return true;
#else
std::string error = "ELF parser not available on this platform.";
if (errorArg.GetString().empty()) {
if (arguments.Error.empty()) {
this->SetError(error);
return false;
}
this->Makefile->AddDefinition(errorArg.GetString(), error.c_str());
this->Makefile->AddDefinition(arguments.Error, error.c_str());
return true;
#endif
}
@ -2597,44 +2595,39 @@ bool cmFileCommand::HandleCreateLinkCommand(
return false;
}
cmCommandArgumentsHelper argHelper;
cmCommandArgumentGroup group;
std::string const& fileName = args[1];
std::string const& newFileName = args[2];
cmCAString linkArg(&argHelper, "CREATE_LINK");
cmCAString fileArg(&argHelper, nullptr);
cmCAString newFileArg(&argHelper, nullptr);
struct Arguments
{
std::string Result;
bool CopyOnError = false;
bool Symbolic = false;
};
cmCAString resultArg(&argHelper, "RESULT", &group);
cmCAEnabler copyOnErrorArg(&argHelper, "COPY_ON_ERROR", &group);
cmCAEnabler symbolicArg(&argHelper, "SYMBOLIC", &group);
linkArg.Follows(nullptr);
fileArg.Follows(&linkArg);
newFileArg.Follows(&fileArg);
group.Follows(&newFileArg);
static auto const parser =
cmArgumentParser<Arguments>{}
.Bind("RESULT"_s, &Arguments::Result)
.Bind("COPY_ON_ERROR"_s, &Arguments::CopyOnError)
.Bind("SYMBOLIC"_s, &Arguments::Symbolic);
std::vector<std::string> unconsumedArgs;
argHelper.Parse(&args, &unconsumedArgs);
Arguments const arguments =
parser.Parse(cmMakeRange(args).advance(3), &unconsumedArgs);
if (!unconsumedArgs.empty()) {
this->SetError("unknown argument: \"" + unconsumedArgs.front() + '\"');
return false;
}
std::string fileName = fileArg.GetString();
std::string newFileName = newFileArg.GetString();
// Output variable for storing the result.
const std::string& resultVar = resultArg.GetString();
// The system error message generated in the operation.
std::string result;
// Check if the paths are distinct.
if (fileName == newFileName) {
result = "CREATE_LINK cannot use same file and newfile";
if (!resultVar.empty()) {
this->Makefile->AddDefinition(resultVar, result.c_str());
if (!arguments.Result.empty()) {
this->Makefile->AddDefinition(arguments.Result, result.c_str());
return true;
}
this->SetError(result);
@ -2642,10 +2635,10 @@ bool cmFileCommand::HandleCreateLinkCommand(
}
// Hard link requires original file to exist.
if (!symbolicArg.IsEnabled() && !cmSystemTools::FileExists(fileName)) {
if (!arguments.Symbolic && !cmSystemTools::FileExists(fileName)) {
result = "Cannot hard link \'" + fileName + "\' as it does not exist.";
if (!resultVar.empty()) {
this->Makefile->AddDefinition(resultVar, result.c_str());
if (!arguments.Result.empty()) {
this->Makefile->AddDefinition(arguments.Result, result.c_str());
return true;
}
this->SetError(result);
@ -2661,8 +2654,8 @@ bool cmFileCommand::HandleCreateLinkCommand(
<< "' because existing path cannot be removed: "
<< cmSystemTools::GetLastSystemError() << "\n";
if (!resultVar.empty()) {
this->Makefile->AddDefinition(resultVar, e.str().c_str());
if (!arguments.Result.empty()) {
this->Makefile->AddDefinition(arguments.Result, e.str().c_str());
return true;
}
this->SetError(e.str());
@ -2673,14 +2666,14 @@ bool cmFileCommand::HandleCreateLinkCommand(
bool completed = false;
// Check if the command requires a symbolic link.
if (symbolicArg.IsEnabled()) {
if (arguments.Symbolic) {
completed = cmSystemTools::CreateSymlink(fileName, newFileName, &result);
} else {
completed = cmSystemTools::CreateLink(fileName, newFileName, &result);
}
// Check if copy-on-error is enabled in the arguments.
if (!completed && copyOnErrorArg.IsEnabled()) {
if (!completed && arguments.CopyOnError) {
completed = cmsys::SystemTools::CopyFileAlways(fileName, newFileName);
if (!completed) {
result = "Copy failed: " + cmSystemTools::GetLastSystemError();
@ -2690,14 +2683,14 @@ bool cmFileCommand::HandleCreateLinkCommand(
// Check if the operation was successful.
if (completed) {
result = "0";
} else if (resultVar.empty()) {
} else if (arguments.Result.empty()) {
// The operation failed and the result is not reported in a variable.
this->SetError(result);
return false;
}
if (!resultVar.empty()) {
this->Makefile->AddDefinition(resultVar, result.c_str());
if (!arguments.Result.empty()) {
this->Makefile->AddDefinition(arguments.Result, result.c_str());
}
return true;

View File

@ -2,6 +2,7 @@
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmInstallCommand.h"
#include "cm_static_string_view.hxx"
#include "cmsys/Glob.hxx"
#include <set>
#include <sstream>
@ -9,7 +10,7 @@
#include <utility>
#include "cmAlgorithms.h"
#include "cmCommandArgumentsHelper.h"
#include "cmArgumentParser.h"
#include "cmExportSet.h"
#include "cmExportSetMap.h"
#include "cmGeneratorExpression.h"
@ -219,49 +220,51 @@ bool cmInstallCommand::HandleScriptMode(std::vector<std::string> const& args)
return true;
}
/*struct InstallPart
{
InstallPart(cmCommandArgumentsHelper* helper, const char* key,
cmCommandArgumentGroup* group);
cmCAStringVector argVector;
cmInstallCommandArguments args;
};*/
bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args)
{
// This is the TARGETS mode.
std::vector<cmTarget*> targets;
cmCommandArgumentsHelper argHelper;
cmCommandArgumentGroup group;
cmCAStringVector genericArgVector(&argHelper, nullptr);
cmCAStringVector archiveArgVector(&argHelper, "ARCHIVE", &group);
cmCAStringVector libraryArgVector(&argHelper, "LIBRARY", &group);
cmCAStringVector runtimeArgVector(&argHelper, "RUNTIME", &group);
cmCAStringVector objectArgVector(&argHelper, "OBJECTS", &group);
cmCAStringVector frameworkArgVector(&argHelper, "FRAMEWORK", &group);
cmCAStringVector bundleArgVector(&argHelper, "BUNDLE", &group);
cmCAStringVector includesArgVector(&argHelper, "INCLUDES", &group);
cmCAStringVector privateHeaderArgVector(&argHelper, "PRIVATE_HEADER",
&group);
cmCAStringVector publicHeaderArgVector(&argHelper, "PUBLIC_HEADER", &group);
cmCAStringVector resourceArgVector(&argHelper, "RESOURCE", &group);
genericArgVector.Follows(nullptr);
group.Follows(&genericArgVector);
struct ArgVectors
{
std::vector<std::string> Archive;
std::vector<std::string> Library;
std::vector<std::string> Runtime;
std::vector<std::string> Object;
std::vector<std::string> Framework;
std::vector<std::string> Bundle;
std::vector<std::string> Includes;
std::vector<std::string> PrivateHeader;
std::vector<std::string> PublicHeader;
std::vector<std::string> Resource;
};
argHelper.Parse(&args, nullptr);
static auto const argHelper =
cmArgumentParser<ArgVectors>{}
.Bind("ARCHIVE"_s, &ArgVectors::Archive)
.Bind("LIBRARY"_s, &ArgVectors::Library)
.Bind("RUNTIME"_s, &ArgVectors::Runtime)
.Bind("OBJECTS"_s, &ArgVectors::Object)
.Bind("FRAMEWORK"_s, &ArgVectors::Framework)
.Bind("BUNDLE"_s, &ArgVectors::Bundle)
.Bind("INCLUDES"_s, &ArgVectors::Includes)
.Bind("PRIVATE_HEADER"_s, &ArgVectors::PrivateHeader)
.Bind("PUBLIC_HEADER"_s, &ArgVectors::PublicHeader)
.Bind("RESOURCE"_s, &ArgVectors::Resource);
std::vector<std::string> genericArgVector;
ArgVectors const argVectors = argHelper.Parse(args, &genericArgVector);
// now parse the generic args (i.e. the ones not specialized on LIBRARY/
// ARCHIVE, RUNTIME etc. (see above)
// These generic args also contain the targets and the export stuff
std::vector<std::string> targetList;
std::string exports;
std::vector<std::string> unknownArgs;
cmInstallCommandArguments genericArgs(this->DefaultComponentName);
cmCAStringVector targetList(&genericArgs.Parser, "TARGETS");
cmCAString exports(&genericArgs.Parser, "EXPORT",
&genericArgs.ArgumentGroup);
targetList.Follows(nullptr);
genericArgs.ArgumentGroup.Follows(&targetList);
genericArgs.Parse(&genericArgVector.GetVector(), &unknownArgs);
genericArgs.Bind("TARGETS"_s, targetList);
genericArgs.Bind("EXPORT"_s, exports);
genericArgs.Parse(genericArgVector, &unknownArgs);
bool success = genericArgs.Finalize();
cmInstallCommandArguments archiveArgs(this->DefaultComponentName);
@ -277,16 +280,16 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args)
// now parse the args for specific parts of the target (e.g. LIBRARY,
// RUNTIME, ARCHIVE etc.
archiveArgs.Parse(&archiveArgVector.GetVector(), &unknownArgs);
libraryArgs.Parse(&libraryArgVector.GetVector(), &unknownArgs);
runtimeArgs.Parse(&runtimeArgVector.GetVector(), &unknownArgs);
objectArgs.Parse(&objectArgVector.GetVector(), &unknownArgs);
frameworkArgs.Parse(&frameworkArgVector.GetVector(), &unknownArgs);
bundleArgs.Parse(&bundleArgVector.GetVector(), &unknownArgs);
privateHeaderArgs.Parse(&privateHeaderArgVector.GetVector(), &unknownArgs);
publicHeaderArgs.Parse(&publicHeaderArgVector.GetVector(), &unknownArgs);
resourceArgs.Parse(&resourceArgVector.GetVector(), &unknownArgs);
includesArgs.Parse(&includesArgVector.GetVector(), &unknownArgs);
archiveArgs.Parse(argVectors.Archive, &unknownArgs);
libraryArgs.Parse(argVectors.Library, &unknownArgs);
runtimeArgs.Parse(argVectors.Runtime, &unknownArgs);
objectArgs.Parse(argVectors.Object, &unknownArgs);
frameworkArgs.Parse(argVectors.Framework, &unknownArgs);
bundleArgs.Parse(argVectors.Bundle, &unknownArgs);
privateHeaderArgs.Parse(argVectors.PrivateHeader, &unknownArgs);
publicHeaderArgs.Parse(argVectors.PublicHeader, &unknownArgs);
resourceArgs.Parse(argVectors.Resource, &unknownArgs);
includesArgs.Parse(&argVectors.Includes, &unknownArgs);
if (!unknownArgs.empty()) {
// Unknown argument.
@ -382,7 +385,7 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args)
}
// Check if there is something to do.
if (targetList.GetVector().empty()) {
if (targetList.empty()) {
return true;
}
@ -390,7 +393,7 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args)
bool dll_platform =
!this->Makefile->GetSafeDefinition("CMAKE_IMPORT_LIBRARY_SUFFIX").empty();
for (std::string const& tgt : targetList.GetVector()) {
for (std::string const& tgt : targetList) {
if (this->Makefile->IsAlias(tgt)) {
std::ostringstream e;
@ -748,7 +751,7 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args)
// Add this install rule to an export if one was specified and
// this is not a namelink-only rule.
if (!exports.GetString().empty() && !namelinkOnly) {
if (!exports.empty() && !namelinkOnly) {
cmTargetExport* te = new cmTargetExport;
te->TargetName = target.GetName();
te->ArchiveGenerator = archiveGenerator;
@ -759,7 +762,7 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args)
te->RuntimeGenerator = runtimeGenerator;
te->ObjectsGenerator = objectGenerator;
this->Makefile->GetGlobalGenerator()
->GetExportSets()[exports.GetString()]
->GetExportSets()[exports]
->AddTargetExport(te);
te->InterfaceIncludeDirectories =
@ -818,11 +821,10 @@ bool cmInstallCommand::HandleFilesMode(std::vector<std::string> const& args)
// This is the FILES mode.
bool programs = (args[0] == "PROGRAMS");
cmInstallCommandArguments ica(this->DefaultComponentName);
cmCAStringVector files(&ica.Parser, programs ? "PROGRAMS" : "FILES");
files.Follows(nullptr);
ica.ArgumentGroup.Follows(&files);
std::vector<std::string> files;
ica.Bind(programs ? "PROGRAMS"_s : "FILES"_s, files);
std::vector<std::string> unknownArgs;
ica.Parse(&args, &unknownArgs);
ica.Parse(args, &unknownArgs);
if (!unknownArgs.empty()) {
// Unknown argument.
@ -840,7 +842,7 @@ bool cmInstallCommand::HandleFilesMode(std::vector<std::string> const& args)
return false;
}
const std::vector<std::string>& filesVector = files.GetVector();
const std::vector<std::string>& filesVector = files;
// Check if there is something to do.
if (filesVector.empty()) {
@ -1271,16 +1273,19 @@ bool cmInstallCommand::HandleExportAndroidMKMode(
#ifdef CMAKE_BUILD_WITH_CMAKE
// This is the EXPORT mode.
cmInstallCommandArguments ica(this->DefaultComponentName);
cmCAString exp(&ica.Parser, "EXPORT_ANDROID_MK");
cmCAString name_space(&ica.Parser, "NAMESPACE", &ica.ArgumentGroup);
cmCAEnabler exportOld(&ica.Parser, "EXPORT_LINK_INTERFACE_LIBRARIES",
&ica.ArgumentGroup);
cmCAString filename(&ica.Parser, "FILE", &ica.ArgumentGroup);
exp.Follows(nullptr);
ica.ArgumentGroup.Follows(&exp);
std::string exp;
std::string name_space;
bool exportOld = false;
std::string filename;
ica.Bind("EXPORT_ANDROID_MK"_s, exp);
ica.Bind("NAMESPACE"_s, name_space);
ica.Bind("EXPORT_LINK_INTERFACE_LIBRARIES"_s, exportOld);
ica.Bind("FILE"_s, filename);
std::vector<std::string> unknownArgs;
ica.Parse(&args, &unknownArgs);
ica.Parse(args, &unknownArgs);
if (!unknownArgs.empty()) {
// Unknown argument.
@ -1304,7 +1309,7 @@ bool cmInstallCommand::HandleExportAndroidMKMode(
}
// Check the file name.
std::string fname = filename.GetString();
std::string fname = filename;
if (fname.find_first_of(":/\\") != std::string::npos) {
std::ostringstream e;
e << args[0] << " given invalid export file name \"" << fname << "\". "
@ -1325,7 +1330,7 @@ bool cmInstallCommand::HandleExportAndroidMKMode(
}
if (fname.find_first_of(":/\\") != std::string::npos) {
std::ostringstream e;
e << args[0] << " given export name \"" << exp.GetString() << "\". "
e << args[0] << " given export name \"" << exp << "\". "
<< "This name cannot be safely converted to a file name. "
<< "Specify a different export name or use the FILE option to set "
<< "a file name explicitly.";
@ -1338,7 +1343,7 @@ bool cmInstallCommand::HandleExportAndroidMKMode(
}
cmExportSet* exportSet =
this->Makefile->GetGlobalGenerator()->GetExportSets()[exp.GetString()];
this->Makefile->GetGlobalGenerator()->GetExportSets()[exp];
cmInstallGenerator::MessageLevel message =
cmInstallGenerator::SelectMessageLevel(this->Makefile);
@ -1347,8 +1352,8 @@ bool cmInstallCommand::HandleExportAndroidMKMode(
cmInstallExportGenerator* exportGenerator = new cmInstallExportGenerator(
exportSet, ica.GetDestination().c_str(), ica.GetPermissions().c_str(),
ica.GetConfigurations(), ica.GetComponent().c_str(), message,
ica.GetExcludeFromAll(), fname.c_str(), name_space.GetCString(),
exportOld.IsEnabled(), true);
ica.GetExcludeFromAll(), fname.c_str(), name_space.c_str(), exportOld,
true);
this->Makefile->AddInstallGenerator(exportGenerator);
return true;
@ -1363,16 +1368,19 @@ bool cmInstallCommand::HandleExportMode(std::vector<std::string> const& args)
{
// This is the EXPORT mode.
cmInstallCommandArguments ica(this->DefaultComponentName);
cmCAString exp(&ica.Parser, "EXPORT");
cmCAString name_space(&ica.Parser, "NAMESPACE", &ica.ArgumentGroup);
cmCAEnabler exportOld(&ica.Parser, "EXPORT_LINK_INTERFACE_LIBRARIES",
&ica.ArgumentGroup);
cmCAString filename(&ica.Parser, "FILE", &ica.ArgumentGroup);
exp.Follows(nullptr);
ica.ArgumentGroup.Follows(&exp);
std::string exp;
std::string name_space;
bool exportOld = false;
std::string filename;
ica.Bind("EXPORT"_s, exp);
ica.Bind("NAMESPACE"_s, name_space);
ica.Bind("EXPORT_LINK_INTERFACE_LIBRARIES"_s, exportOld);
ica.Bind("FILE"_s, filename);
std::vector<std::string> unknownArgs;
ica.Parse(&args, &unknownArgs);
ica.Parse(args, &unknownArgs);
if (!unknownArgs.empty()) {
// Unknown argument.
@ -1396,7 +1404,7 @@ bool cmInstallCommand::HandleExportMode(std::vector<std::string> const& args)
}
// Check the file name.
std::string fname = filename.GetString();
std::string fname = filename;
if (fname.find_first_of(":/\\") != std::string::npos) {
std::ostringstream e;
e << args[0] << " given invalid export file name \"" << fname << "\". "
@ -1418,12 +1426,12 @@ bool cmInstallCommand::HandleExportMode(std::vector<std::string> const& args)
// Construct the file name.
if (fname.empty()) {
fname = exp.GetString();
fname = exp;
fname += ".cmake";
if (fname.find_first_of(":/\\") != std::string::npos) {
std::ostringstream e;
e << args[0] << " given export name \"" << exp.GetString() << "\". "
e << args[0] << " given export name \"" << exp << "\". "
<< "This name cannot be safely converted to a file name. "
<< "Specify a different export name or use the FILE option to set "
<< "a file name explicitly.";
@ -1433,8 +1441,8 @@ bool cmInstallCommand::HandleExportMode(std::vector<std::string> const& args)
}
cmExportSet* exportSet =
this->Makefile->GetGlobalGenerator()->GetExportSets()[exp.GetString()];
if (exportOld.IsEnabled()) {
this->Makefile->GetGlobalGenerator()->GetExportSets()[exp];
if (exportOld) {
for (cmTargetExport* te : *exportSet->GetTargetExports()) {
cmTarget* tgt =
this->Makefile->GetGlobalGenerator()->FindTarget(te->TargetName);
@ -1461,8 +1469,8 @@ bool cmInstallCommand::HandleExportMode(std::vector<std::string> const& args)
cmInstallExportGenerator* exportGenerator = new cmInstallExportGenerator(
exportSet, ica.GetDestination().c_str(), ica.GetPermissions().c_str(),
ica.GetConfigurations(), ica.GetComponent().c_str(), message,
ica.GetExcludeFromAll(), fname.c_str(), name_space.GetCString(),
exportOld.IsEnabled(), false);
ica.GetExcludeFromAll(), fname.c_str(), name_space.c_str(), exportOld,
false);
this->Makefile->AddInstallGenerator(exportGenerator);
return true;

View File

@ -4,6 +4,7 @@
#include "cmRange.h"
#include "cmSystemTools.h"
#include "cm_static_string_view.hxx"
#include <utility>
@ -18,20 +19,19 @@ const std::string cmInstallCommandArguments::EmptyString;
cmInstallCommandArguments::cmInstallCommandArguments(
std::string defaultComponent)
: Destination(&Parser, "DESTINATION", &ArgumentGroup)
, Component(&Parser, "COMPONENT", &ArgumentGroup)
, NamelinkComponent(&Parser, "NAMELINK_COMPONENT", &ArgumentGroup)
, ExcludeFromAll(&Parser, "EXCLUDE_FROM_ALL", &ArgumentGroup)
, Rename(&Parser, "RENAME", &ArgumentGroup)
, Permissions(&Parser, "PERMISSIONS", &ArgumentGroup)
, Configurations(&Parser, "CONFIGURATIONS", &ArgumentGroup)
, Optional(&Parser, "OPTIONAL", &ArgumentGroup)
, NamelinkOnly(&Parser, "NAMELINK_ONLY", &ArgumentGroup)
, NamelinkSkip(&Parser, "NAMELINK_SKIP", &ArgumentGroup)
, Type(&Parser, "TYPE", &ArgumentGroup)
, GenericArguments(nullptr)
, DefaultComponentName(std::move(defaultComponent))
: DefaultComponentName(std::move(defaultComponent))
{
this->Bind("DESTINATION"_s, this->Destination);
this->Bind("COMPONENT"_s, this->Component);
this->Bind("NAMELINK_COMPONENT"_s, this->NamelinkComponent);
this->Bind("EXCLUDE_FROM_ALL"_s, this->ExcludeFromAll);
this->Bind("RENAME"_s, this->Rename);
this->Bind("PERMISSIONS"_s, this->Permissions);
this->Bind("CONFIGURATIONS"_s, this->Configurations);
this->Bind("OPTIONAL"_s, this->Optional);
this->Bind("NAMELINK_ONLY"_s, this->NamelinkOnly);
this->Bind("NAMELINK_SKIP"_s, this->NamelinkSkip);
this->Bind("TYPE"_s, this->Type);
}
const std::string& cmInstallCommandArguments::GetDestination() const
@ -47,8 +47,8 @@ const std::string& cmInstallCommandArguments::GetDestination() const
const std::string& cmInstallCommandArguments::GetComponent() const
{
if (!this->Component.GetString().empty()) {
return this->Component.GetString();
if (!this->Component.empty()) {
return this->Component;
}
if (this->GenericArguments != nullptr) {
return this->GenericArguments->GetComponent();
@ -62,16 +62,16 @@ const std::string& cmInstallCommandArguments::GetComponent() const
const std::string& cmInstallCommandArguments::GetNamelinkComponent() const
{
if (!this->NamelinkComponent.GetString().empty()) {
return this->NamelinkComponent.GetString();
if (!this->NamelinkComponent.empty()) {
return this->NamelinkComponent;
}
return this->GetComponent();
}
const std::string& cmInstallCommandArguments::GetRename() const
{
if (!this->Rename.GetString().empty()) {
return this->Rename.GetString();
if (!this->Rename.empty()) {
return this->Rename;
}
if (this->GenericArguments != nullptr) {
return this->GenericArguments->GetRename();
@ -92,7 +92,7 @@ const std::string& cmInstallCommandArguments::GetPermissions() const
bool cmInstallCommandArguments::GetOptional() const
{
if (this->Optional.IsEnabled()) {
if (this->Optional) {
return true;
}
if (this->GenericArguments != nullptr) {
@ -103,7 +103,7 @@ bool cmInstallCommandArguments::GetOptional() const
bool cmInstallCommandArguments::GetExcludeFromAll() const
{
if (this->ExcludeFromAll.IsEnabled()) {
if (this->ExcludeFromAll) {
return true;
}
if (this->GenericArguments != nullptr) {
@ -114,7 +114,7 @@ bool cmInstallCommandArguments::GetExcludeFromAll() const
bool cmInstallCommandArguments::GetNamelinkOnly() const
{
if (this->NamelinkOnly.IsEnabled()) {
if (this->NamelinkOnly) {
return true;
}
if (this->GenericArguments != nullptr) {
@ -125,7 +125,7 @@ bool cmInstallCommandArguments::GetNamelinkOnly() const
bool cmInstallCommandArguments::GetNamelinkSkip() const
{
if (this->NamelinkSkip.IsEnabled()) {
if (this->NamelinkSkip) {
return true;
}
if (this->GenericArguments != nullptr) {
@ -136,7 +136,7 @@ bool cmInstallCommandArguments::GetNamelinkSkip() const
bool cmInstallCommandArguments::HasNamelinkComponent() const
{
if (!this->NamelinkComponent.GetString().empty()) {
if (!this->NamelinkComponent.empty()) {
return true;
}
if (this->GenericArguments != nullptr) {
@ -147,19 +147,19 @@ bool cmInstallCommandArguments::HasNamelinkComponent() const
const std::string& cmInstallCommandArguments::GetType() const
{
return this->Type.GetString();
return this->Type;
}
const std::vector<std::string>& cmInstallCommandArguments::GetConfigurations()
const
{
if (!this->Configurations.GetVector().empty()) {
return this->Configurations.GetVector();
if (!this->Configurations.empty()) {
return this->Configurations;
}
if (this->GenericArguments != nullptr) {
return this->GenericArguments->GetConfigurations();
}
return this->Configurations.GetVector();
return this->Configurations;
}
bool cmInstallCommandArguments::Finalize()
@ -167,21 +167,15 @@ bool cmInstallCommandArguments::Finalize()
if (!this->CheckPermissions()) {
return false;
}
this->DestinationString = this->Destination.GetString();
this->DestinationString = this->Destination;
cmSystemTools::ConvertToUnixSlashes(this->DestinationString);
return true;
}
void cmInstallCommandArguments::Parse(const std::vector<std::string>* args,
std::vector<std::string>* unconsumedArgs)
{
this->Parser.Parse(args, unconsumedArgs);
}
bool cmInstallCommandArguments::CheckPermissions()
{
this->PermissionsString.clear();
for (std::string const& perm : this->Permissions.GetVector()) {
for (std::string const& perm : this->Permissions) {
if (!cmInstallCommandArguments::CheckPermissions(
perm, this->PermissionsString)) {
return false;

View File

@ -8,9 +8,9 @@
#include <string>
#include <vector>
#include "cmCommandArgumentsHelper.h"
#include "cmArgumentParser.h"
class cmInstallCommandArguments
class cmInstallCommandArguments : public cmArgumentParser<void>
{
public:
cmInstallCommandArguments(std::string defaultComponent);
@ -18,8 +18,6 @@ public:
{
this->GenericArguments = args;
}
void Parse(const std::vector<std::string>* args,
std::vector<std::string>* unconsumedArgs);
// Compute destination path.and check permissions
bool Finalize();
@ -37,30 +35,25 @@ public:
bool HasNamelinkComponent() const;
const std::string& GetType() const;
// once HandleDirectoryMode() is also switched to using
// cmInstallCommandArguments then these two functions can become non-static
// private member functions without arguments
static bool CheckPermissions(const std::string& onePerm, std::string& perm);
cmCommandArgumentsHelper Parser;
cmCommandArgumentGroup ArgumentGroup;
private:
cmCAString Destination;
cmCAString Component;
cmCAString NamelinkComponent;
cmCAEnabler ExcludeFromAll;
cmCAString Rename;
cmCAStringVector Permissions;
cmCAStringVector Configurations;
cmCAEnabler Optional;
cmCAEnabler NamelinkOnly;
cmCAEnabler NamelinkSkip;
cmCAString Type;
std::string Destination;
std::string Component;
std::string NamelinkComponent;
bool ExcludeFromAll = false;
std::string Rename;
std::vector<std::string> Permissions;
std::vector<std::string> Configurations;
bool Optional = false;
bool NamelinkOnly = false;
bool NamelinkSkip = false;
std::string Type;
std::string DestinationString;
std::string PermissionsString;
cmInstallCommandArguments* GenericArguments;
cmInstallCommandArguments* GenericArguments = nullptr;
static const char* PermissionsTable[];
static const std::string EmptyString;
std::string DefaultComponentName;

View File

@ -8,10 +8,12 @@
#include <utility>
#include "cmAlgorithms.h"
#include "cmArgumentParser.h"
#include "cmMakefile.h"
#include "cmMessageType.h"
#include "cmRange.h"
#include "cmSystemTools.h"
#include "cm_string_view.hxx"
class cmExecutionStatus;
@ -28,42 +30,43 @@ static std::string EscapeArg(const std::string& arg)
return escapedArg;
}
namespace {
enum insideValues
static std::string JoinList(std::vector<std::string> const& arg, bool escape)
{
NONE,
SINGLE,
MULTI
};
return escape ? cmJoin(cmMakeRange(arg).transform(EscapeArg), ";")
: cmJoin(cmMakeRange(arg), ";");
}
namespace {
typedef std::map<std::string, bool> options_map;
typedef std::map<std::string, std::string> single_map;
typedef std::map<std::string, std::vector<std::string>> multi_map;
typedef std::set<std::string> options_set;
}
// function to be called every time, a new key word was parsed or all
// parameters where parsed.
static void DetectKeywordsMissingValues(insideValues currentState,
const std::string& currentArgName,
int& argumentsFound,
options_set& keywordsMissingValues)
struct UserArgumentParser : public cmArgumentParser<void>
{
if (currentState == SINGLE ||
(currentState == MULTI && argumentsFound == 0)) {
keywordsMissingValues.insert(currentArgName);
template <typename T, typename H>
void Bind(std::vector<std::string> const& names,
std::map<std::string, T>& ref, H duplicateKey)
{
for (std::string const& key : names) {
auto const it = ref.emplace(key, T{}).first;
bool const inserted = this->cmArgumentParser<void>::Bind(
cm::string_view(it->first), it->second);
if (!inserted) {
duplicateKey(key);
}
}
}
};
argumentsFound = 0;
}
} // namespace
static void PassParsedArguments(const std::string& prefix,
cmMakefile& makefile,
const options_map& options,
const single_map& singleValArgs,
const multi_map& multiValArgs,
const std::vector<std::string>& unparsed,
const options_set& keywordsMissingValues)
static void PassParsedArguments(
const std::string& prefix, cmMakefile& makefile, const options_map& options,
const single_map& singleValArgs, const multi_map& multiValArgs,
const std::vector<std::string>& unparsed,
const options_set& keywordsMissingValues, bool parseFromArgV)
{
for (auto const& iter : options) {
makefile.AddDefinition(prefix + iter.first,
@ -81,7 +84,7 @@ static void PassParsedArguments(const std::string& prefix,
for (auto const& iter : multiValArgs) {
if (!iter.second.empty()) {
makefile.AddDefinition(prefix + iter.first,
cmJoin(cmMakeRange(iter.second), ";").c_str());
JoinList(iter.second, parseFromArgV).c_str());
} else {
makefile.RemoveDefinition(prefix + iter.first);
}
@ -89,7 +92,7 @@ static void PassParsedArguments(const std::string& prefix,
if (!unparsed.empty()) {
makefile.AddDefinition(prefix + "UNPARSED_ARGUMENTS",
cmJoin(cmMakeRange(unparsed), ";").c_str());
JoinList(unparsed, parseFromArgV).c_str());
} else {
makefile.RemoveDefinition(prefix + "UNPARSED_ARGUMENTS");
}
@ -141,6 +144,8 @@ bool cmParseArgumentsCommand::InitialPass(std::vector<std::string> const& args,
// the first argument is the prefix
const std::string prefix = (*argIter++) + "_";
UserArgumentParser parser;
// define the result maps holding key/value pairs for
// options, single values and multi values
options_map options;
@ -150,45 +155,25 @@ bool cmParseArgumentsCommand::InitialPass(std::vector<std::string> const& args,
// anything else is put into a vector of unparsed strings
std::vector<std::string> unparsed;
// remember already defined keywords
std::set<std::string> used_keywords;
const std::string dup_warning = "keyword defined more than once: ";
auto const duplicateKey = [this](std::string const& key) {
this->GetMakefile()->IssueMessage(
MessageType::WARNING, "keyword defined more than once: " + key);
};
// the second argument is a (cmake) list of options without argument
std::vector<std::string> list;
cmSystemTools::ExpandListArgument(*argIter++, list);
for (std::string const& iter : list) {
if (!used_keywords.insert(iter).second) {
this->GetMakefile()->IssueMessage(MessageType::WARNING,
dup_warning + iter);
}
options[iter]; // default initialize
}
parser.Bind(list, options, duplicateKey);
// the third argument is a (cmake) list of single argument options
list.clear();
cmSystemTools::ExpandListArgument(*argIter++, list);
for (std::string const& iter : list) {
if (!used_keywords.insert(iter).second) {
this->GetMakefile()->IssueMessage(MessageType::WARNING,
dup_warning + iter);
}
singleValArgs[iter]; // default initialize
}
parser.Bind(list, singleValArgs, duplicateKey);
// the fourth argument is a (cmake) list of multi argument options
list.clear();
cmSystemTools::ExpandListArgument(*argIter++, list);
for (std::string const& iter : list) {
if (!used_keywords.insert(iter).second) {
this->GetMakefile()->IssueMessage(MessageType::WARNING,
dup_warning + iter);
}
multiValArgs[iter]; // default initialize
}
insideValues insideValues = NONE;
std::string currentArgName;
parser.Bind(list, multiValArgs, duplicateKey);
list.clear();
if (!parseFromArgV) {
@ -223,68 +208,14 @@ bool cmParseArgumentsCommand::InitialPass(std::vector<std::string> const& args,
}
}
options_set keywordsMissingValues;
int multiArgumentsFound = 0;
std::vector<std::string> keywordsMissingValues;
// iterate over the arguments list and fill in the values where applicable
for (std::string const& arg : list) {
const options_map::iterator optIter = options.find(arg);
if (optIter != options.end()) {
DetectKeywordsMissingValues(insideValues, currentArgName,
multiArgumentsFound, keywordsMissingValues);
insideValues = NONE;
optIter->second = true;
continue;
}
parser.Parse(list, &unparsed, &keywordsMissingValues);
const single_map::iterator singleIter = singleValArgs.find(arg);
if (singleIter != singleValArgs.end()) {
DetectKeywordsMissingValues(insideValues, currentArgName,
multiArgumentsFound, keywordsMissingValues);
insideValues = SINGLE;
currentArgName = arg;
continue;
}
const multi_map::iterator multiIter = multiValArgs.find(arg);
if (multiIter != multiValArgs.end()) {
DetectKeywordsMissingValues(insideValues, currentArgName,
multiArgumentsFound, keywordsMissingValues);
insideValues = MULTI;
currentArgName = arg;
continue;
}
switch (insideValues) {
case SINGLE:
singleValArgs[currentArgName] = arg;
insideValues = NONE;
break;
case MULTI:
++multiArgumentsFound;
if (parseFromArgV) {
multiValArgs[currentArgName].push_back(EscapeArg(arg));
} else {
multiValArgs[currentArgName].push_back(arg);
}
break;
default:
multiArgumentsFound = 0;
if (parseFromArgV) {
unparsed.push_back(EscapeArg(arg));
} else {
unparsed.push_back(arg);
}
break;
}
}
DetectKeywordsMissingValues(insideValues, currentArgName,
multiArgumentsFound, keywordsMissingValues);
PassParsedArguments(prefix, *this->Makefile, options, singleValArgs,
multiValArgs, unparsed, keywordsMissingValues);
PassParsedArguments(
prefix, *this->Makefile, options, singleValArgs, multiValArgs, unparsed,
options_set(keywordsMissingValues.begin(), keywordsMissingValues.end()),
parseFromArgV);
return true;
}

View File

@ -5,6 +5,7 @@ include_directories(
)
set(CMakeLib_TESTS
testArgumentParser.cxx
testGeneratedFileStream.cxx
testRST.cxx
testRange.cxx

View File

@ -0,0 +1,148 @@
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmArgumentParser.h"
#include "cm_static_string_view.hxx"
#include "cm_string_view.hxx"
#include <initializer_list>
#include <iostream>
#include <string>
#include <vector>
namespace {
struct Result
{
bool Option1 = false;
bool Option2 = false;
std::string String1;
std::string String2;
std::vector<std::string> List1;
std::vector<std::string> List2;
std::vector<std::string> List3;
std::vector<std::vector<std::string>> Multi1;
std::vector<std::vector<std::string>> Multi2;
std::vector<std::vector<std::string>> Multi3;
};
std::initializer_list<cm::string_view> const args = {
/* clang-format off */
"OPTION_1", // option
"STRING_1", // string arg missing value
"STRING_2", "foo", "bar", // string arg + unparsed value
"LIST_1", // list arg missing values
"LIST_2", "foo", "bar", // list arg with 2 elems
"LIST_3", "bar", // list arg ...
"LIST_3", "foo", // ... with continuation
"MULTI_2", // multi list with 0 lists
"MULTI_3", "foo", "bar", // multi list with first list with two elems
"MULTI_3", "bar", "foo", // multi list with second list with two elems
/* clang-format on */
};
bool verifyResult(Result const& result,
std::vector<std::string> const& unparsedArguments,
std::vector<std::string> const& keywordsMissingValue)
{
static std::vector<std::string> const foobar = { "foo", "bar" };
static std::vector<std::string> const barfoo = { "bar", "foo" };
static std::vector<std::string> const missing = { "STRING_1", "LIST_1" };
#define ASSERT_TRUE(x) \
do { \
if (!(x)) { \
std::cout << "ASSERT_TRUE(" #x ") failed on line " << __LINE__ << "\n"; \
return false; \
} \
} while (false)
ASSERT_TRUE(result.Option1);
ASSERT_TRUE(!result.Option2);
ASSERT_TRUE(result.String1.empty());
ASSERT_TRUE(result.String2 == "foo");
ASSERT_TRUE(result.List1.empty());
ASSERT_TRUE(result.List2 == foobar);
ASSERT_TRUE(result.List3 == barfoo);
ASSERT_TRUE(result.Multi1.empty());
ASSERT_TRUE(result.Multi2.size() == 1);
ASSERT_TRUE(result.Multi2[0].empty());
ASSERT_TRUE(result.Multi3.size() == 2);
ASSERT_TRUE(result.Multi3[0] == foobar);
ASSERT_TRUE(result.Multi3[1] == barfoo);
ASSERT_TRUE(unparsedArguments.size() == 1);
ASSERT_TRUE(unparsedArguments[0] == "bar");
ASSERT_TRUE(keywordsMissingValue == missing);
return true;
}
bool testArgumentParserDynamic()
{
Result result;
std::vector<std::string> unparsedArguments;
std::vector<std::string> keywordsMissingValue;
cmArgumentParser<void>{}
.Bind("OPTION_1"_s, result.Option1)
.Bind("OPTION_2"_s, result.Option2)
.Bind("STRING_1"_s, result.String1)
.Bind("STRING_2"_s, result.String2)
.Bind("LIST_1"_s, result.List1)
.Bind("LIST_2"_s, result.List2)
.Bind("LIST_3"_s, result.List3)
.Bind("MULTI_1"_s, result.Multi1)
.Bind("MULTI_2"_s, result.Multi2)
.Bind("MULTI_3"_s, result.Multi3)
.Parse(args, &unparsedArguments, &keywordsMissingValue);
return verifyResult(result, unparsedArguments, keywordsMissingValue);
}
bool testArgumentParserStatic()
{
static auto const parser = //
cmArgumentParser<Result>{}
.Bind("OPTION_1"_s, &Result::Option1)
.Bind("OPTION_2"_s, &Result::Option2)
.Bind("STRING_1"_s, &Result::String1)
.Bind("STRING_2"_s, &Result::String2)
.Bind("LIST_1"_s, &Result::List1)
.Bind("LIST_2"_s, &Result::List2)
.Bind("LIST_3"_s, &Result::List3)
.Bind("MULTI_1"_s, &Result::Multi1)
.Bind("MULTI_2"_s, &Result::Multi2)
.Bind("MULTI_3"_s, &Result::Multi3);
std::vector<std::string> unparsedArguments;
std::vector<std::string> keywordsMissingValue;
Result const result =
parser.Parse(args, &unparsedArguments, &keywordsMissingValue);
return verifyResult(result, unparsedArguments, keywordsMissingValue);
}
} // namespace
int testArgumentParser(int /*unused*/, char* /*unused*/ [])
{
if (!testArgumentParserDynamic()) {
std::cout << "While executing testArgumentParserDynamic().\n";
return -1;
}
if (!testArgumentParserStatic()) {
std::cout << "While executing testArgumentParserStatic().\n";
return -1;
}
return 0;
}

View File

@ -1,4 +1,4 @@
CMake Error at AppendExport.cmake:[0-9]+ \(export\):
export EXPORT signature does not recognise the APPEND option.
export Unknown argument: "APPEND".
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)

View File

@ -1,5 +1,4 @@
CMake Error at OldIface.cmake:[0-9]+ \(export\):
export EXPORT signature does not recognise the
EXPORT_LINK_INTERFACE_LIBRARIES option.
export Unknown argument: "EXPORT_LINK_INTERFACE_LIBRARIES".
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)

View File

@ -68,6 +68,7 @@
{ symbol: [ "std::__decay_and_strip<cmGeneratorTarget *&>::__type", private, "\"cmConfigure.h\"", public ] },
{ symbol: [ "std::__decay_and_strip<cmFindCommon::PathLabel &>::__type", private, "\"cmConfigure.h\"", public ] },
{ symbol: [ "std::__decay_and_strip<cmSearchPath>::__type", private, "\"cmConfigure.h\"", public ] },
{ symbol: [ "std::__decay_and_strip<cm::string_view>::__type", private, "\"cmConfigure.h\"", public ] },
{ symbol: [ "std::__decay_and_strip<std::basic_string<char> &>::__type", private, "\"cmConfigure.h\"", public ] },
{ symbol: [ "std::__decay_and_strip<const std::basic_string<char> &>::__type", private, "\"cmConfigure.h\"", public ] },
{ symbol: [ "std::__decay_and_strip<cmFindPackageCommand::PathLabel &>::__type", private, "\"cmConfigure.h\"", public ] },

View File

@ -260,6 +260,7 @@ CMAKE_CXX_SOURCES="\
cmAddLibraryCommand \
cmAddSubDirectoryCommand \
cmAddTestCommand \
cmArgumentParser \
cmBreakCommand \
cmBuildCommand \
cmCMakeMinimumRequired \
@ -268,7 +269,6 @@ CMAKE_CXX_SOURCES="\
cmCacheManager \
cmCommand \
cmCommandArgumentParserHelper \
cmCommandArgumentsHelper \
cmCommands \
cmCommonTargetGenerator \
cmComputeComponentGraph \