mirror of
https://gitlab.kitware.com/cmake/cmake.git
synced 2026-09-25 04:09:36 +03:00
Creates the policy scope after the variable scope instead of the other way round. This ensures that the cmStateSnapshot which the policy is registered to is the same as the one created with the variable scope so that reading a policy from the parent snapshot does not incorrectly read policies from the new snapshot.
212 lines
6.2 KiB
C++
212 lines
6.2 KiB
C++
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
|
file LICENSE.rst or https://cmake.org/licensing for details. */
|
|
|
|
#include "cmBlockCommand.h"
|
|
|
|
#include <cstdint>
|
|
#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,
|
|
DIAGNOSTICS,
|
|
};
|
|
using ScopeSet = cm::enum_set<ScopeType>;
|
|
|
|
class BlockScopePushPop
|
|
{
|
|
public:
|
|
BlockScopePushPop(cmMakefile* m, ScopeSet const& scopes);
|
|
~BlockScopePushPop() = default;
|
|
|
|
BlockScopePushPop(BlockScopePushPop const&) = delete;
|
|
BlockScopePushPop& operator=(BlockScopePushPop const&) = delete;
|
|
|
|
private:
|
|
std::unique_ptr<cmMakefile::VariablePushPop> VariableScope;
|
|
std::unique_ptr<cmMakefile::PolicyPushPop> PolicyScope;
|
|
std::unique_ptr<cmMakefile::DiagnosticPushPop> DiagnosticScope;
|
|
};
|
|
|
|
BlockScopePushPop::BlockScopePushPop(cmMakefile* mf, ScopeSet const& scopes)
|
|
{
|
|
if (scopes.contains(ScopeType::VARIABLES)) {
|
|
this->VariableScope = cm::make_unique<cmMakefile::VariablePushPop>(mf);
|
|
}
|
|
if (scopes.contains(ScopeType::POLICIES)) {
|
|
this->PolicyScope = cm::make_unique<cmMakefile::PolicyPushPop>(mf);
|
|
}
|
|
if (scopes.contains(ScopeType::DIAGNOSTICS)) {
|
|
this->DiagnosticScope = cm::make_unique<cmMakefile::DiagnosticPushPop>(mf);
|
|
}
|
|
}
|
|
|
|
class cmBlockFunctionBlocker : public cmFunctionBlocker
|
|
{
|
|
public:
|
|
cmBlockFunctionBlocker(cmMakefile* mf, ScopeSet const& 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, ScopeSet const& 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 (status.HasExitCode()) {
|
|
inStatus.SetExitCode(status.GetExitCode());
|
|
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;
|
|
}
|
|
if (scope == "DIAGNOSTICS"_s) {
|
|
scopes.insert(ScopeType::DIAGNOSTICS);
|
|
continue;
|
|
}
|
|
status.SetError(cmStrCat("SCOPE_FOR unsupported scope \"", scope, '"'));
|
|
cmSystemTools::SetFatalErrorOccurred();
|
|
return false;
|
|
}
|
|
} else {
|
|
scopes = { ScopeType::VARIABLES, ScopeType::POLICIES,
|
|
ScopeType::DIAGNOSTICS };
|
|
}
|
|
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;
|
|
}
|