| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
| file Copyright.txt or https://cmake.org/licensing for details. */ |
| #include "cmIfCommand.h" |
| |
| #include "cmConditionEvaluator.h" |
| #include "cmExecutionStatus.h" |
| #include "cmExpandedCommandArgument.h" |
| #include "cmMakefile.h" |
| #include "cmOutputConverter.h" |
| #include "cmSystemTools.h" |
| #include "cmake.h" |
| |
| #include <memory> // IWYU pragma: keep |
| |
| static std::string cmIfCommandError( |
| std::vector<cmExpandedCommandArgument> const& args) |
| { |
| std::string err = "given arguments:\n "; |
| for (cmExpandedCommandArgument const& i : args) { |
| err += " "; |
| err += cmOutputConverter::EscapeForCMake(i.GetValue()); |
| } |
| err += "\n"; |
| return err; |
| } |
| |
| //========================================================================= |
| bool cmIfFunctionBlocker::IsFunctionBlocked(const cmListFileFunction& lff, |
| cmMakefile& mf, |
| cmExecutionStatus& inStatus) |
| { |
| // we start by recording all the functions |
| if (lff.Name.Lower == "if") { |
| this->ScopeDepth++; |
| } else if (lff.Name.Lower == "endif") { |
| this->ScopeDepth--; |
| // if this is the endif for this if statement, then start executing |
| if (!this->ScopeDepth) { |
| // Remove the function blocker for this scope or bail. |
| std::unique_ptr<cmFunctionBlocker> fb( |
| mf.RemoveFunctionBlocker(this, lff)); |
| if (!fb.get()) { |
| return false; |
| } |
| |
| // execute the functions for the true parts of the if statement |
| cmExecutionStatus status; |
| int scopeDepth = 0; |
| for (cmListFileFunction const& func : this->Functions) { |
| // keep track of scope depth |
| if (func.Name.Lower == "if") { |
| scopeDepth++; |
| } |
| if (func.Name.Lower == "endif") { |
| scopeDepth--; |
| } |
| // watch for our state change |
| if (scopeDepth == 0 && func.Name.Lower == "else") { |
| |
| if (this->ElseSeen) { |
| cmListFileBacktrace bt = mf.GetBacktrace(func); |
| mf.GetCMakeInstance()->IssueMessage( |
| cmake::FATAL_ERROR, |
| "A duplicate ELSE command was found inside an IF block.", bt); |
| cmSystemTools::SetFatalErrorOccured(); |
| return true; |
| } |
| |
| this->IsBlocking = this->HasRun; |
| this->HasRun = true; |
| this->ElseSeen = true; |
| |
| // if trace is enabled, print a (trivially) evaluated "else" |
| // statement |
| if (!this->IsBlocking && mf.GetCMakeInstance()->GetTrace()) { |
| mf.PrintCommandTrace(func); |
| } |
| } else if (scopeDepth == 0 && func.Name.Lower == "elseif") { |
| if (this->ElseSeen) { |
| cmListFileBacktrace bt = mf.GetBacktrace(func); |
| mf.GetCMakeInstance()->IssueMessage( |
| cmake::FATAL_ERROR, |
| "An ELSEIF command was found after an ELSE command.", bt); |
| cmSystemTools::SetFatalErrorOccured(); |
| return true; |
| } |
| |
| if (this->HasRun) { |
| this->IsBlocking = true; |
| } else { |
| // if trace is enabled, print the evaluated "elseif" statement |
| if (mf.GetCMakeInstance()->GetTrace()) { |
| mf.PrintCommandTrace(func); |
| } |
| |
| std::string errorString; |
| |
| std::vector<cmExpandedCommandArgument> expandedArguments; |
| mf.ExpandArguments(func.Arguments, expandedArguments); |
| |
| cmake::MessageType messType; |
| |
| cmListFileContext conditionContext = |
| cmListFileContext::FromCommandContext( |
| func, this->GetStartingContext().FilePath); |
| |
| cmConditionEvaluator conditionEvaluator(mf, conditionContext, |
| mf.GetBacktrace(func)); |
| |
| bool isTrue = conditionEvaluator.IsTrue(expandedArguments, |
| errorString, messType); |
| |
| if (!errorString.empty()) { |
| std::string err = cmIfCommandError(expandedArguments); |
| err += errorString; |
| cmListFileBacktrace bt = mf.GetBacktrace(func); |
| mf.GetCMakeInstance()->IssueMessage(messType, err, bt); |
| if (messType == cmake::FATAL_ERROR) { |
| cmSystemTools::SetFatalErrorOccured(); |
| return true; |
| } |
| } |
| |
| if (isTrue) { |
| this->IsBlocking = false; |
| this->HasRun = true; |
| } |
| } |
| } |
| |
| // should we execute? |
| else if (!this->IsBlocking) { |
| status.Clear(); |
| mf.ExecuteCommand(func, status); |
| if (status.GetReturnInvoked()) { |
| inStatus.SetReturnInvoked(); |
| return true; |
| } |
| if (status.GetBreakInvoked()) { |
| inStatus.SetBreakInvoked(); |
| return true; |
| } |
| if (status.GetContinueInvoked()) { |
| inStatus.SetContinueInvoked(); |
| return true; |
| } |
| } |
| } |
| return true; |
| } |
| } |
| |
| // record the command |
| this->Functions.push_back(lff); |
| |
| // always return true |
| return true; |
| } |
| |
| //========================================================================= |
| bool cmIfFunctionBlocker::ShouldRemove(const cmListFileFunction& lff, |
| cmMakefile&) |
| { |
| if (lff.Name.Lower == "endif") { |
| // if the endif has arguments, then make sure |
| // they match the arguments of the matching if |
| if (lff.Arguments.empty() || lff.Arguments == this->Args) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| //========================================================================= |
| bool cmIfCommand::InvokeInitialPass( |
| const std::vector<cmListFileArgument>& args, cmExecutionStatus&) |
| { |
| std::string errorString; |
| |
| std::vector<cmExpandedCommandArgument> expandedArguments; |
| this->Makefile->ExpandArguments(args, expandedArguments); |
| |
| cmake::MessageType status; |
| |
| cmConditionEvaluator conditionEvaluator( |
| *(this->Makefile), this->Makefile->GetExecutionContext(), |
| this->Makefile->GetBacktrace()); |
| |
| bool isTrue = |
| conditionEvaluator.IsTrue(expandedArguments, errorString, status); |
| |
| if (!errorString.empty()) { |
| std::string err = "if " + cmIfCommandError(expandedArguments); |
| err += errorString; |
| if (status == cmake::FATAL_ERROR) { |
| this->Makefile->IssueMessage(cmake::FATAL_ERROR, err); |
| cmSystemTools::SetFatalErrorOccured(); |
| return true; |
| } |
| this->Makefile->IssueMessage(status, err); |
| } |
| |
| cmIfFunctionBlocker* f = new cmIfFunctionBlocker(); |
| // if is isn't true block the commands |
| f->ScopeDepth = 1; |
| f->IsBlocking = !isTrue; |
| if (isTrue) { |
| f->HasRun = true; |
| } |
| f->Args = args; |
| this->Makefile->AddFunctionBlocker(f); |
| |
| return true; |
| } |