cmIfCommand.cxx 6.1 KB
Newer Older
1 2
/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
   file Copyright.txt or https://cmake.org/licensing for details.  */
3
#include "cmIfCommand.h"
Brad King's avatar
Brad King committed
4

5
#include "cm_memory.hxx"
6 7
#include "cm_static_string_view.hxx"
#include "cm_string_view.hxx"
8

9 10 11
#include "cmConditionEvaluator.h"
#include "cmExecutionStatus.h"
#include "cmExpandedCommandArgument.h"
12 13
#include "cmFunctionBlocker.h"
#include "cmListFileCache.h"
14
#include "cmMakefile.h"
15
#include "cmMessageType.h"
16
#include "cmOutputConverter.h"
17
#include "cmSystemTools.h"
18
#include "cmake.h"
19

20
#include <string>
21 22
#include <utility>

23
static std::string cmIfCommandError(
24
  std::vector<cmExpandedCommandArgument> const& args)
25 26
{
  std::string err = "given arguments:\n ";
27
  for (cmExpandedCommandArgument const& i : args) {
28
    err += " ";
29
    err += cmOutputConverter::EscapeForCMake(i.GetValue());
30
  }
31 32 33 34
  err += "\n";
  return err;
}

35 36 37
class cmIfFunctionBlocker : public cmFunctionBlocker
{
public:
38 39 40
  cm::string_view StartCommandName() const override { return "if"_s; }
  cm::string_view EndCommandName() const override { return "endif"_s; }

41 42 43
  bool ArgumentsMatch(cmListFileFunction const& lff,
                      cmMakefile&) const override;

44
  bool Replay(std::vector<cmListFileFunction> functions,
45
              cmExecutionStatus& inStatus) override;
46 47 48 49 50 51 52

  std::vector<cmListFileArgument> Args;
  bool IsBlocking;
  bool HasRun = false;
  bool ElseSeen = false;
};

53 54
bool cmIfFunctionBlocker::ArgumentsMatch(cmListFileFunction const& lff,
                                         cmMakefile&) const
55
{
56
  return lff.Arguments.empty() || lff.Arguments == this->Args;
57 58
}

59 60
bool cmIfFunctionBlocker::Replay(std::vector<cmListFileFunction> functions,
                                 cmExecutionStatus& inStatus)
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
{
  cmMakefile& mf = inStatus.GetMakefile();
  // execute the functions for the true parts of the if statement
  int scopeDepth = 0;
  for (cmListFileFunction const& func : 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(
          MessageType::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(
          MessageType::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);

        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 == MessageType::FATAL_ERROR) {
            cmSystemTools::SetFatalErrorOccured();
            return true;
          }
        }

        if (isTrue) {
          this->IsBlocking = false;
          this->HasRun = true;
        }
      }
    }

    // should we execute?
    else if (!this->IsBlocking) {
149
      cmExecutionStatus status(mf);
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
      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;
}

168
//=========================================================================
169 170
bool cmIfCommand(std::vector<cmListFileArgument> const& args,
                 cmExecutionStatus& inStatus)
171
{
172
  cmMakefile& makefile = inStatus.GetMakefile();
173
  std::string errorString;
174

175
  std::vector<cmExpandedCommandArgument> expandedArguments;
176
  makefile.ExpandArguments(args, expandedArguments);
177

178
  MessageType status;
179

180
  cmConditionEvaluator conditionEvaluator(
181
    makefile, makefile.GetExecutionContext(), makefile.GetBacktrace());
182

183 184
  bool isTrue =
    conditionEvaluator.IsTrue(expandedArguments, errorString, status);
185

186
  if (!errorString.empty()) {
187
    std::string err = "if " + cmIfCommandError(expandedArguments);
188
    err += errorString;
189
    if (status == MessageType::FATAL_ERROR) {
190
      makefile.IssueMessage(MessageType::FATAL_ERROR, err);
191
      cmSystemTools::SetFatalErrorOccured();
192
      return true;
193
    }
194
    makefile.IssueMessage(status, err);
195
  }
196

197 198 199 200 201 202 203 204
  {
    auto fb = cm::make_unique<cmIfFunctionBlocker>();
    // if is isn't true block the commands
    fb->IsBlocking = !isTrue;
    if (isTrue) {
      fb->HasRun = true;
    }
    fb->Args = args;
205
    makefile.AddFunctionBlocker(std::move(fb));
206
  }
207

208 209
  return true;
}