cmMessageCommand.cxx 6.37 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.  */
Ken Martin's avatar
Ken Martin committed
3 4
#include "cmMessageCommand.h"

5
#include <cassert>
6 7 8 9 10
#include <utility>

#include <cm/string_view>

#include "cm_static_string_view.hxx"
11

12
#include "cmExecutionStatus.h"
13
#include "cmMakefile.h"
14
#include "cmMessageType.h"
15
#include "cmMessenger.h"
16
#include "cmRange.h"
17
#include "cmStringAlgorithms.h"
18
#include "cmSystemTools.h"
19 20
#include "cmake.h"

21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
namespace {

enum class CheckingType
{
  UNDEFINED,
  CHECK_START,
  CHECK_PASS,
  CHECK_FAIL
};

std::string IndentText(std::string text, cmMakefile& mf)
{
  auto indent =
    cmJoin(cmExpandedList(mf.GetSafeDefinition("CMAKE_MESSAGE_INDENT")), "");

  const auto showContext = mf.GetCMakeInstance()->GetShowLogContext() ||
    mf.IsOn("CMAKE_MESSAGE_CONTEXT_SHOW");
  if (showContext) {
    auto context = cmJoin(
      cmExpandedList(mf.GetSafeDefinition("CMAKE_MESSAGE_CONTEXT")), ".");
    if (!context.empty()) {
      indent.insert(0u, cmStrCat("["_s, context, "] "_s));
    }
  }

  if (!indent.empty()) {
    cmSystemTools::ReplaceString(text, "\n", "\n" + indent);
    text.insert(0u, indent);
  }
  return text;
}

void ReportCheckResult(cm::string_view what, std::string result,
                       cmMakefile& mf)
{
  if (mf.GetCMakeInstance()->HasCheckInProgress()) {
    auto text = mf.GetCMakeInstance()->GetTopCheckInProgressMessage() + " - " +
      std::move(result);
    mf.DisplayStatus(IndentText(std::move(text), mf), -1);
  } else {
    mf.GetMessenger()->DisplayMessage(
      MessageType::AUTHOR_WARNING,
      cmStrCat("Ignored "_s, what, " without CHECK_START"_s),
      mf.GetBacktrace());
  }
}

} // anonymous namespace

Ken Martin's avatar
Ken Martin committed
70
// cmLibraryCommand
71 72
bool cmMessageCommand(std::vector<std::string> const& args,
                      cmExecutionStatus& status)
Ken Martin's avatar
Ken Martin committed
73
{
74
  if (args.empty()) {
75
    status.SetError("called with incorrect number of arguments");
Ken Martin's avatar
Ken Martin committed
76
    return false;
77
  }
78 79 80

  auto& mf = status.GetMakefile();

81
  auto i = args.cbegin();
82

83 84
  auto type = MessageType::MESSAGE;
  auto fatal = false;
85
  auto level = cmake::LogLevel::LOG_UNDEFINED;
86
  auto checkingType = CheckingType::UNDEFINED;
87
  if (*i == "SEND_ERROR") {
88
    type = MessageType::FATAL_ERROR;
89
    level = cmake::LogLevel::LOG_ERROR;
90
    ++i;
91
  } else if (*i == "FATAL_ERROR") {
92
    fatal = true;
93
    type = MessageType::FATAL_ERROR;
94
    level = cmake::LogLevel::LOG_ERROR;
95
    ++i;
96
  } else if (*i == "WARNING") {
97
    type = MessageType::WARNING;
98
    level = cmake::LogLevel::LOG_WARNING;
99
    ++i;
100
  } else if (*i == "AUTHOR_WARNING") {
101 102
    if (mf.IsSet("CMAKE_SUPPRESS_DEVELOPER_ERRORS") &&
        !mf.IsOn("CMAKE_SUPPRESS_DEVELOPER_ERRORS")) {
103
      fatal = true;
104
      type = MessageType::AUTHOR_ERROR;
105
      level = cmake::LogLevel::LOG_ERROR;
106
    } else if (!mf.IsOn("CMAKE_SUPPRESS_DEVELOPER_WARNINGS")) {
107
      type = MessageType::AUTHOR_WARNING;
108
      level = cmake::LogLevel::LOG_WARNING;
109
    } else {
110
      return true;
111
    }
112
    ++i;
113 114 115 116 117 118 119 120 121 122 123 124
  } else if (*i == "CHECK_START") {
    level = cmake::LogLevel::LOG_STATUS;
    checkingType = CheckingType::CHECK_START;
    ++i;
  } else if (*i == "CHECK_PASS") {
    level = cmake::LogLevel::LOG_STATUS;
    checkingType = CheckingType::CHECK_PASS;
    ++i;
  } else if (*i == "CHECK_FAIL") {
    level = cmake::LogLevel::LOG_STATUS;
    checkingType = CheckingType::CHECK_FAIL;
    ++i;
125
  } else if (*i == "STATUS") {
126 127 128 129 130 131 132 133 134 135
    level = cmake::LogLevel::LOG_STATUS;
    ++i;
  } else if (*i == "VERBOSE") {
    level = cmake::LogLevel::LOG_VERBOSE;
    ++i;
  } else if (*i == "DEBUG") {
    level = cmake::LogLevel::LOG_DEBUG;
    ++i;
  } else if (*i == "TRACE") {
    level = cmake::LogLevel::LOG_TRACE;
136
    ++i;
137
  } else if (*i == "DEPRECATION") {
138
    if (mf.IsOn("CMAKE_ERROR_DEPRECATED")) {
139
      fatal = true;
140
      type = MessageType::DEPRECATION_ERROR;
141
      level = cmake::LogLevel::LOG_ERROR;
142 143
    } else if (!mf.IsSet("CMAKE_WARN_DEPRECATED") ||
               mf.IsOn("CMAKE_WARN_DEPRECATED")) {
144
      type = MessageType::DEPRECATION_WARNING;
145
      level = cmake::LogLevel::LOG_WARNING;
146
    } else {
147
      return true;
148
    }
149
    ++i;
150 151 152 153 154 155 156 157 158 159 160
  } else if (*i == "NOTICE") {
    // `NOTICE` message type is going to be output to stderr
    level = cmake::LogLevel::LOG_NOTICE;
    ++i;
  } else {
    // Messages w/o any type are `NOTICE`s
    level = cmake::LogLevel::LOG_NOTICE;
  }
  assert("Message log level expected to be set" &&
         level != cmake::LogLevel::LOG_UNDEFINED);

161
  auto desiredLevel = mf.GetCMakeInstance()->GetLogLevel();
162 163 164
  assert("Expected a valid log level here" &&
         desiredLevel != cmake::LogLevel::LOG_UNDEFINED);

165 166 167 168 169 170 171 172 173
  // Command line option takes precedence over the cache variable
  if (!mf.GetCMakeInstance()->WasLogLevelSetViaCLI()) {
    const auto desiredLevelFromCache =
      cmake::StringToLogLevel(mf.GetSafeDefinition("CMAKE_MESSAGE_LOG_LEVEL"));
    if (desiredLevelFromCache != cmake::LogLevel::LOG_UNDEFINED) {
      desiredLevel = desiredLevelFromCache;
    }
  }

174 175 176
  if (desiredLevel < level) {
    // Suppress the message
    return true;
177
  }
178

179
  auto message = cmJoin(cmMakeRange(i, args.cend()), "");
180

181 182 183 184
  switch (level) {
    case cmake::LogLevel::LOG_ERROR:
    case cmake::LogLevel::LOG_WARNING:
      // we've overridden the message type, above, so display it directly
185
      mf.GetMessenger()->DisplayMessage(type, message, mf.GetBacktrace());
186 187 188
      break;

    case cmake::LogLevel::LOG_NOTICE:
189
      cmSystemTools::Message(IndentText(message, mf));
190 191 192
      break;

    case cmake::LogLevel::LOG_STATUS:
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
      switch (checkingType) {
        case CheckingType::CHECK_START:
          mf.DisplayStatus(IndentText(message, mf), -1);
          mf.GetCMakeInstance()->PushCheckInProgressMessage(message);
          break;

        case CheckingType::CHECK_PASS:
          ReportCheckResult("CHECK_PASS"_s, message, mf);
          break;

        case CheckingType::CHECK_FAIL:
          ReportCheckResult("CHECK_FAIL"_s, message, mf);
          break;

        default:
          mf.DisplayStatus(IndentText(message, mf), -1);
          break;
      }
      break;

213 214 215
    case cmake::LogLevel::LOG_VERBOSE:
    case cmake::LogLevel::LOG_DEBUG:
    case cmake::LogLevel::LOG_TRACE:
216
      mf.DisplayStatus(IndentText(message, mf), -1);
217 218 219 220 221 222
      break;

    default:
      assert("Unexpected log level! Review the `cmMessageCommand.cxx`." &&
             false);
      break;
223
  }
224

225
  if (fatal) {
226
    cmSystemTools::SetFatalErrorOccured();
227
  }
228
  return true;
Ken Martin's avatar
Ken Martin committed
229
}