diff options
-rw-r--r-- | Help/manual/cmake.1.rst | 60 | ||||
-rw-r--r-- | Help/release/dev/json_trace.rst | 7 | ||||
-rw-r--r-- | Source/cmMakefile.cxx | 47 | ||||
-rw-r--r-- | Source/cmake.cxx | 73 | ||||
-rw-r--r-- | Source/cmake.h | 17 | ||||
-rw-r--r-- | Source/cmakemain.cxx | 1 |
6 files changed, 196 insertions, 9 deletions
diff --git a/Help/manual/cmake.1.rst b/Help/manual/cmake.1.rst index 6f33866..bae64f7 100644 --- a/Help/manual/cmake.1.rst +++ b/Help/manual/cmake.1.rst @@ -251,6 +251,66 @@ Options Like ``--trace``, but with variables expanded. +``--trace-format=<format>`` + Put cmake in trace mode and sets the trace output format. + + ``<format>`` can be one of the following values. + + ``human`` + Prints each trace line in a human-readable format. This is the + default format. + + ``json`` + Prints each line as a separate JSON document. Each document is + separated by a newline ( ``\n`` ). It is guaranteed that no + newline characters will be present inside a JSON document. + + JSON trace format: + + .. code-block:: json + + { + "file": "/full/path/to/the/CMake/file.txt", + "line": 0, + "cmd": "add_executable", + "args": ["foo", "bar"] + } + + The members are: + + ``file`` + The full path to the CMake source file where the function + was called. + + ``line`` + The line in `file` of the function call. + + ``cmd`` + The name of the function that was called. + + ``args`` + A string list of all function parameters. + + Additionally, the first JSON document outputted contains the + ``version`` key for the current major and minor version of the + + JSON trace format: + + .. code-block:: json + + { + "version": { + "major": 1, + "minor": 0 + } + } + + The members are: + + ``version`` + Indicates the version of the JSON format. The version has a + major and minor components following semantic version conventions. + ``--trace-source=<file>`` Put cmake in trace mode, but output only lines of a specified file. diff --git a/Help/release/dev/json_trace.rst b/Help/release/dev/json_trace.rst new file mode 100644 index 0000000..69a1fb7 --- /dev/null +++ b/Help/release/dev/json_trace.rst @@ -0,0 +1,7 @@ +json-trace +---------- + +* :manual:`cmake(1)` gained a ``--trace-format`` command line option that + can be used to set the ``--trace`` output format. Currently, the old + human readable and the new JSON format are supported. The new JSON format + is easier to parse automatically, than the existing format. diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx index 75f00fc..eff7ddc 100644 --- a/Source/cmMakefile.cxx +++ b/Source/cmMakefile.cxx @@ -7,6 +7,7 @@ #include <algorithm> #include <cassert> #include <cctype> +#include <cstdint> #include <cstdio> #include <cstdlib> #include <cstring> @@ -20,6 +21,8 @@ #include "cmsys/FStream.hxx" #include "cmsys/RegularExpression.hxx" +#include "cm_jsoncpp_value.h" +#include "cm_jsoncpp_writer.h" #include "cm_sys_stat.h" #include "cmAlgorithms.h" @@ -315,21 +318,51 @@ void cmMakefile::PrintCommandTrace(const cmListFileFunction& lff) const } std::ostringstream msg; - msg << full_path << "(" << lff.Line << "): "; - msg << lff.Name.Original << "("; - bool expand = this->GetCMakeInstance()->GetTraceExpand(); + std::vector<std::string> args; std::string temp; + bool expand = this->GetCMakeInstance()->GetTraceExpand(); + + args.reserve(lff.Arguments.size()); for (cmListFileArgument const& arg : lff.Arguments) { if (expand) { temp = arg.Value; this->ExpandVariablesInString(temp); - msg << temp; + args.push_back(temp); } else { - msg << arg.Value; + args.push_back(arg.Value); + } + } + + switch (this->GetCMakeInstance()->GetTraceFormat()) { + case cmake::TraceFormat::TRACE_JSON_V1: { +#ifndef CMAKE_BOOTSTRAP + Json::Value val; + Json::StreamWriterBuilder builder; + builder["indentation"] = ""; + val["file"] = full_path; + val["line"] = static_cast<std::int64_t>(lff.Line); + val["cmd"] = lff.Name.Original; + val["args"] = Json::Value(Json::arrayValue); + for (std::string const& arg : args) { + val["args"].append(arg); + } + msg << Json::writeString(builder, val); +#endif + break; } - msg << " "; + case cmake::TraceFormat::TRACE_HUMAN: + msg << full_path << "(" << lff.Line << "): "; + msg << lff.Name.Original << "("; + + for (std::string const& arg : args) { + msg << arg << " "; + } + msg << ")"; + break; + case cmake::TraceFormat::TRACE_UNDEFINED: + msg << "INTERNAL ERROR: Trace format is TRACE_UNDEFINED"; + break; } - msg << ")"; auto& f = this->GetCMakeInstance()->GetTraceFile(); if (f) { diff --git a/Source/cmake.cxx b/Source/cmake.cxx index fdb3687..b1300c9 100644 --- a/Source/cmake.cxx +++ b/Source/cmake.cxx @@ -755,6 +755,15 @@ void cmake::SetArgs(const std::vector<std::string>& args) std::cout << "Running with expanded trace output on.\n"; this->SetTrace(true); this->SetTraceExpand(true); + } else if (arg.find("--trace-format=", 0) == 0) { + this->SetTrace(true); + const auto traceFormat = + StringToTraceFormat(arg.substr(strlen("--trace-format="))); + if (traceFormat == TraceFormat::TRACE_UNDEFINED) { + cmSystemTools::Error("Invalid format specified for --trace-format"); + return; + } + this->SetTraceFormat(traceFormat); } else if (arg.find("--trace-source=", 0) == 0) { std::string file = arg.substr(strlen("--trace-source=")); cmSystemTools::ConvertToUnixSlashes(file); @@ -895,6 +904,23 @@ cmake::LogLevel cmake::StringToLogLevel(const std::string& levelStr) return (it != levels.cend()) ? it->second : LogLevel::LOG_UNDEFINED; } +cmake::TraceFormat cmake::StringToTraceFormat(const std::string& traceStr) +{ + using TracePair = std::pair<std::string, TraceFormat>; + static const std::vector<TracePair> levels = { + { "human", TraceFormat::TRACE_HUMAN }, + { "json-v1", TraceFormat::TRACE_JSON_V1 }, + }; + + const auto traceStrLowCase = cmSystemTools::LowerCase(traceStr); + + const auto it = std::find_if(levels.cbegin(), levels.cend(), + [&traceStrLowCase](const TracePair& p) { + return p.first == traceStrLowCase; + }); + return (it != levels.cend()) ? it->second : TraceFormat::TRACE_UNDEFINED; +} + void cmake::SetTraceFile(const std::string& file) { this->TraceFile.close(); @@ -909,6 +935,48 @@ void cmake::SetTraceFile(const std::string& file) std::cout << "Trace will be written to " << file << "\n"; } +void cmake::PrintTraceFormatVersion() +{ + if (!this->GetTrace()) { + return; + } + + std::string msg; + + switch (this->GetTraceFormat()) { + case TraceFormat::TRACE_JSON_V1: { +#ifndef CMAKE_BOOTSTRAP + Json::Value val; + Json::Value version; + Json::StreamWriterBuilder builder; + builder["indentation"] = ""; + version["major"] = 1; + version["minor"] = 0; + val["version"] = version; + msg = Json::writeString(builder, val); +#endif + break; + } + case TraceFormat::TRACE_HUMAN: + msg = ""; + break; + case TraceFormat::TRACE_UNDEFINED: + msg = "INTERNAL ERROR: Trace format is TRACE_UNDEFINED"; + break; + } + + if (msg.empty()) { + return; + } + + auto& f = this->GetTraceFile(); + if (f) { + f << msg << '\n'; + } else { + cmSystemTools::Message(msg); + } +} + void cmake::SetDirectoriesFromFile(const std::string& arg) { // Check if the argument refers to a CMakeCache.txt or @@ -1701,6 +1769,11 @@ int cmake::Run(const std::vector<std::string>& args, bool noconfigure) return -1; } + // Log the trace format version to the desired output + if (this->GetTrace()) { + this->PrintTraceFormatVersion(); + } + // If we are given a stamp list file check if it is really out of date. if (!this->CheckStampList.empty() && cmakeCheckStampList(this->CheckStampList)) { diff --git a/Source/cmake.h b/Source/cmake.h index 02de4c1..54fdc03 100644 --- a/Source/cmake.h +++ b/Source/cmake.h @@ -113,6 +113,14 @@ public: LOG_TRACE }; + /** \brief Define supported trace formats **/ + enum TraceFormat + { + TRACE_UNDEFINED, + TRACE_HUMAN, + TRACE_JSON_V1, + }; + struct GeneratorInfo { std::string name; @@ -389,6 +397,7 @@ public: LogLevel GetLogLevel() const { return this->MessageLogLevel; } void SetLogLevel(LogLevel level) { this->MessageLogLevel = level; } static LogLevel StringToLogLevel(const std::string& levelStr); + static TraceFormat StringToTraceFormat(const std::string& levelStr); bool HasCheckInProgress() const { @@ -418,10 +427,12 @@ public: void SetShowLogContext(bool b) { this->LogContext = b; } //! Do we want trace output during the cmake run. - bool GetTrace() { return this->Trace; } + bool GetTrace() const { return this->Trace; } void SetTrace(bool b) { this->Trace = b; } - bool GetTraceExpand() { return this->TraceExpand; } + bool GetTraceExpand() const { return this->TraceExpand; } void SetTraceExpand(bool b) { this->TraceExpand = b; } + TraceFormat GetTraceFormat() const { return this->TraceFormatVar; } + void SetTraceFormat(TraceFormat f) { this->TraceFormatVar = f; } void AddTraceSource(std::string const& file) { this->TraceOnlyThisSources.push_back(file); @@ -432,6 +443,7 @@ public: } cmGeneratedFileStream& GetTraceFile() { return this->TraceFile; } void SetTraceFile(std::string const& file); + void PrintTraceFormatVersion(); bool GetWarnUninitialized() { return this->WarnUninitialized; } void SetWarnUninitialized(bool b) { this->WarnUninitialized = b; } @@ -579,6 +591,7 @@ private: bool DebugOutput = false; bool Trace = false; bool TraceExpand = false; + TraceFormat TraceFormatVar = TRACE_HUMAN; cmGeneratedFileStream TraceFile; bool WarnUninitialized = false; bool WarnUnused = false; diff --git a/Source/cmakemain.cxx b/Source/cmakemain.cxx index 0fc39ba..9dd8831 100644 --- a/Source/cmakemain.cxx +++ b/Source/cmakemain.cxx @@ -81,6 +81,7 @@ const char* cmDocumentationOptions[][2] = { { "--debug-output", "Put cmake in a debug mode." }, { "--trace", "Put cmake in trace mode." }, { "--trace-expand", "Put cmake in trace mode with variable expansion." }, + { "--trace-format=<human|json-v1>", "Set the output format of the trace." }, { "--trace-source=<file>", "Trace only this CMake file/module. Multiple options allowed." }, { "--trace-redirect=<file>", |