Browse Source

trace: Add JSON output format

Add a new `--trace-format=` flag, to enable the new JSON trace
output format. This new format is easier to parse by machines
than the existing format. This new format also removes the
ambiguity of the whitespace in the "old" format (e.g. is that
whitespace part of a file path, or does it seperate arguments)
pull/332/head
Daniel Mensinger 6 years ago
parent
commit
482497e0de
No known key found for this signature in database GPG Key ID: 54DD94C131E277D4
  1. 60
      Help/manual/cmake.1.rst
  2. 7
      Help/release/dev/json_trace.rst
  3. 47
      Source/cmMakefile.cxx
  4. 73
      Source/cmake.cxx
  5. 17
      Source/cmake.h
  6. 1
      Source/cmakemain.cxx

60
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.

7
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.

47
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) {

73
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)) {

17
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;

1
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>",

Loading…
Cancel
Save