|
|
|
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
|
|
|
file Copyright.txt or https://cmake.org/licensing for details. */
|
|
|
|
|
|
|
|
#include "cmBlockCommand.h"
|
|
|
|
|
|
|
|
#include <cstdint>
|
|
|
|
#include <initializer_list>
|
|
|
|
#include <utility>
|
|
|
|
|
|
|
|
#include <cm/memory>
|
|
|
|
#include <cm/optional>
|
|
|
|
#include <cm/string_view>
|
|
|
|
#include <cmext/enum_set>
|
|
|
|
#include <cmext/string_view>
|
|
|
|
|
|
|
|
#include "cmArgumentParser.h"
|
|
|
|
#include "cmArgumentParserTypes.h"
|
|
|
|
#include "cmExecutionStatus.h"
|
|
|
|
#include "cmFunctionBlocker.h"
|
|
|
|
#include "cmListFileCache.h"
|
|
|
|
#include "cmMakefile.h"
|
|
|
|
#include "cmStringAlgorithms.h"
|
|
|
|
#include "cmSystemTools.h"
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
enum class ScopeType : std::uint8_t
|
|
|
|
{
|
|
|
|
VARIABLES,
|
|
|
|
POLICIES
|
|
|
|
};
|
|
|
|
using ScopeSet = cm::enum_set<ScopeType>;
|
|
|
|
|
|
|
|
class BlockScopePushPop
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
BlockScopePushPop(cmMakefile* m, const ScopeSet& scopes);
|
|
|
|
~BlockScopePushPop() = default;
|
|
|
|
|
|
|
|
BlockScopePushPop(const BlockScopePushPop&) = delete;
|
|
|
|
BlockScopePushPop& operator=(const BlockScopePushPop&) = delete;
|
|
|
|
|
|
|
|
private:
|
|
|
|
std::unique_ptr<cmMakefile::PolicyPushPop> PolicyScope;
|
|
|
|
std::unique_ptr<cmMakefile::VariablePushPop> VariableScope;
|
|
|
|
};
|
|
|
|
|
|
|
|
BlockScopePushPop::BlockScopePushPop(cmMakefile* mf, const ScopeSet& scopes)
|
|
|
|
{
|
|
|
|
if (scopes.contains(ScopeType::POLICIES)) {
|
|
|
|
this->PolicyScope = cm::make_unique<cmMakefile::PolicyPushPop>(mf);
|
|
|
|
}
|
|
|
|
if (scopes.contains(ScopeType::VARIABLES)) {
|
|
|
|
this->VariableScope = cm::make_unique<cmMakefile::VariablePushPop>(mf);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class cmBlockFunctionBlocker : public cmFunctionBlocker
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
cmBlockFunctionBlocker(cmMakefile* mf, const ScopeSet& scopes,
|
|
|
|
std::vector<std::string> variableNames);
|
|
|
|
~cmBlockFunctionBlocker() override;
|
|
|
|
|
|
|
|
cm::string_view StartCommandName() const override { return "block"_s; }
|
|
|
|
cm::string_view EndCommandName() const override { return "endblock"_s; }
|
|
|
|
|
|
|
|
bool EndCommandSupportsArguments() const override { return false; }
|
|
|
|
|
|
|
|
bool ArgumentsMatch(cmListFileFunction const& lff,
|
|
|
|
cmMakefile& mf) const override;
|
|
|
|
|
|
|
|
bool Replay(std::vector<cmListFileFunction> functions,
|
|
|
|
cmExecutionStatus& inStatus) override;
|
|
|
|
|
|
|
|
private:
|
|
|
|
cmMakefile* Makefile;
|
|
|
|
ScopeSet Scopes;
|
|
|
|
BlockScopePushPop BlockScope;
|
|
|
|
std::vector<std::string> VariableNames;
|
|
|
|
};
|
|
|
|
|
|
|
|
cmBlockFunctionBlocker::cmBlockFunctionBlocker(
|
|
|
|
cmMakefile* const mf, const ScopeSet& scopes,
|
|
|
|
std::vector<std::string> variableNames)
|
|
|
|
: Makefile{ mf }
|
|
|
|
, Scopes{ scopes }
|
|
|
|
, BlockScope{ mf, scopes }
|
|
|
|
, VariableNames{ std::move(variableNames) }
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
cmBlockFunctionBlocker::~cmBlockFunctionBlocker()
|
|
|
|
{
|
|
|
|
if (this->Scopes.contains(ScopeType::VARIABLES)) {
|
|
|
|
this->Makefile->RaiseScope(this->VariableNames);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool cmBlockFunctionBlocker::ArgumentsMatch(cmListFileFunction const& lff,
|
|
|
|
cmMakefile&) const
|
|
|
|
{
|
|
|
|
// no arguments expected for endblock()
|
|
|
|
// but this method should not be called because EndCommandHasArguments()
|
|
|
|
// returns false.
|
|
|
|
return lff.Arguments().empty();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool cmBlockFunctionBlocker::Replay(std::vector<cmListFileFunction> functions,
|
|
|
|
cmExecutionStatus& inStatus)
|
|
|
|
{
|
|
|
|
auto& mf = inStatus.GetMakefile();
|
|
|
|
|
|
|
|
// Invoke all the functions that were collected in the block.
|
|
|
|
for (cmListFileFunction const& fn : functions) {
|
|
|
|
cmExecutionStatus status(mf);
|
|
|
|
mf.ExecuteCommand(fn, status);
|
|
|
|
if (status.GetReturnInvoked()) {
|
|
|
|
mf.RaiseScope(status.GetReturnVariables());
|
|
|
|
inStatus.SetReturnInvoked(status.GetReturnVariables());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (status.GetBreakInvoked()) {
|
|
|
|
inStatus.SetBreakInvoked();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (status.GetContinueInvoked()) {
|
|
|
|
inStatus.SetContinueInvoked();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (cmSystemTools::GetFatalErrorOccurred()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // anonymous namespace
|
|
|
|
|
|
|
|
bool cmBlockCommand(std::vector<std::string> const& args,
|
|
|
|
cmExecutionStatus& status)
|
|
|
|
{
|
|
|
|
struct Arguments : public ArgumentParser::ParseResult
|
|
|
|
{
|
|
|
|
cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>> ScopeFor;
|
|
|
|
ArgumentParser::MaybeEmpty<std::vector<std::string>> Propagate;
|
|
|
|
};
|
|
|
|
static auto const parser = cmArgumentParser<Arguments>{}
|
|
|
|
.Bind("SCOPE_FOR"_s, &Arguments::ScopeFor)
|
|
|
|
.Bind("PROPAGATE"_s, &Arguments::Propagate);
|
|
|
|
std::vector<std::string> unrecognizedArguments;
|
|
|
|
auto parsedArgs = parser.Parse(args, &unrecognizedArguments);
|
|
|
|
|
|
|
|
if (!unrecognizedArguments.empty()) {
|
|
|
|
status.SetError(cmStrCat("called with unsupported argument \"",
|
|
|
|
unrecognizedArguments[0], '"'));
|
|
|
|
cmSystemTools::SetFatalErrorOccurred();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (parsedArgs.MaybeReportError(status.GetMakefile())) {
|
|
|
|
cmSystemTools::SetFatalErrorOccurred();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
ScopeSet scopes;
|
|
|
|
|
|
|
|
if (parsedArgs.ScopeFor) {
|
|
|
|
for (auto const& scope : *parsedArgs.ScopeFor) {
|
|
|
|
if (scope == "VARIABLES"_s) {
|
|
|
|
scopes.insert(ScopeType::VARIABLES);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (scope == "POLICIES"_s) {
|
|
|
|
scopes.insert(ScopeType::POLICIES);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
status.SetError(cmStrCat("SCOPE_FOR unsupported scope \"", scope, '"'));
|
|
|
|
cmSystemTools::SetFatalErrorOccurred();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
scopes = { ScopeType::VARIABLES, ScopeType::POLICIES };
|
|
|
|
}
|
|
|
|
if (!scopes.contains(ScopeType::VARIABLES) &&
|
|
|
|
!parsedArgs.Propagate.empty()) {
|
|
|
|
status.SetError(
|
|
|
|
"PROPAGATE cannot be specified without a new scope for VARIABLES");
|
|
|
|
cmSystemTools::SetFatalErrorOccurred();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// create a function blocker
|
|
|
|
auto fb = cm::make_unique<cmBlockFunctionBlocker>(
|
|
|
|
&status.GetMakefile(), scopes, parsedArgs.Propagate);
|
|
|
|
status.GetMakefile().AddFunctionBlocker(std::move(fb));
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|