JSON: Improve JSON error message formatting

This improves the output of JSON-related error messages. It adds the filename to
the output and excludes the column number.

This is particularly useful when there are multiple JSON files being read that
could be responsible for an error, ie CMakePresets.json and
CMakeUserPresets.json, or multiple instrumentation queries.

Issue: #26717
This commit is contained in:
Martin Duffy 2025-03-12 15:59:29 -04:00
parent d3484a31c6
commit f134468a98
44 changed files with 80 additions and 74 deletions

View File

@ -1668,7 +1668,7 @@ bool cmCTestMultiProcessHandler::InitResourceAllocator(std::string& error)
{
if (!this->ResourceSpec.ReadFromJSONFile(this->ResourceSpecFile)) {
error = cmStrCat("Could not read/parse resource spec file ",
this->ResourceSpecFile, ": ",
this->ResourceSpecFile, ":\n",
this->ResourceSpec.parseState.GetErrorMessage());
return false;
}

View File

@ -1572,7 +1572,7 @@ bool cmCTest::SetArgsFromPreset(std::string const& presetName,
auto result = settingsFile.ReadProjectPresets(workingDirectory);
if (result != true) {
cmSystemTools::Error(cmStrCat("Could not read presets from ",
workingDirectory, ":",
workingDirectory, ":\n",
settingsFile.parseState.GetErrorMessage()));
return false;
}

View File

@ -148,6 +148,16 @@ void cmInstrumentation::ReadJSONQuery(std::string const& file)
auto query = cmInstrumentationQuery();
query.ReadJSON(file, this->errorMsg, this->queries, this->hooks,
this->callbacks);
if (!this->errorMsg.empty()) {
cmSystemTools::Error(cmStrCat(
"Could not load instrumentation queries from ",
cmSystemTools::GetParentDirectory(file), ":\n", this->errorMsg));
}
}
bool cmInstrumentation::HasErrors() const
{
return !this->errorMsg.empty();
}
void cmInstrumentation::WriteJSONQuery(

View File

@ -53,7 +53,7 @@ public:
int CollectTimingAfterBuild(int ppid);
void AddHook(cmInstrumentationQuery::Hook hook);
void AddQuery(cmInstrumentationQuery::Query query);
std::string errorMsg;
bool HasErrors() const;
std::string const& GetCDashDir();
private:
@ -83,5 +83,6 @@ private:
std::vector<std::string> queryFiles;
std::map<std::string, std::string> cdashSnippetsMap;
Json::Value preTestStats;
std::string errorMsg;
bool hasQuery = false;
};

View File

@ -12,12 +12,14 @@
#include "cmsys/FStream.hxx"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
cmJSONState::cmJSONState(std::string const& filename, Json::Value* root)
cmJSONState::cmJSONState(std::string jsonFile, Json::Value* root)
: Filename(std::move(jsonFile))
{
cmsys::ifstream fin(filename.c_str(), std::ios::in | std::ios::binary);
cmsys::ifstream fin(this->Filename.c_str(), std::ios::in | std::ios::binary);
if (!fin) {
this->AddError(cmStrCat("File not found: ", filename));
this->AddError(cmStrCat("File not found: ", this->Filename));
return;
}
// If there's a BOM, toss it.
@ -38,7 +40,7 @@ cmJSONState::cmJSONState(std::string const& filename, Json::Value* root)
Json::CharReaderBuilder::strictMode(&builder.settings_);
std::string errMsg;
if (!Json::parseFromStream(builder, fin, root, &errMsg)) {
errMsg = cmStrCat("JSON Parse Error: ", filename, ":\n", errMsg);
errMsg = cmStrCat("JSON Parse Error: ", this->Filename, ":\n", errMsg);
this->AddError(errMsg);
}
}
@ -72,16 +74,17 @@ void cmJSONState::AddErrorAtOffset(std::string const& errMsg,
std::string cmJSONState::GetErrorMessage(bool showContext)
{
std::string message;
std::string filenameName = cmSystemTools::GetFilenameName(this->Filename);
for (auto const& error : this->errors) {
Location loc = error.GetLocation();
if (!filenameName.empty() && loc.line > 0) {
message = cmStrCat(message, filenameName, ':', loc.line, ": ");
}
message = cmStrCat(message, error.GetErrorMessage(), "\n");
if (showContext) {
Location loc = error.GetLocation();
if (loc.column > 0) {
message = cmStrCat(message, GetJsonContext(loc), "\n");
}
if (showContext && loc.line > 0) {
message = cmStrCat(message, GetJsonContext(loc), "\n");
}
}
message = cmStrCat("\n", message);
message.pop_back();
return message;
}

View File

@ -9,8 +9,6 @@
#include <utility>
#include <vector>
#include "cmStringAlgorithms.h"
namespace Json {
class Value;
}
@ -26,7 +24,7 @@ class cmJSONState
public:
using JsonPair = std::pair<std::string const, Json::Value const*>;
cmJSONState() = default;
cmJSONState(std::string const& filename, Json::Value* root);
cmJSONState(std::string jsonFile, Json::Value* root);
void AddError(std::string const& errMsg);
void AddErrorAtValue(std::string const& errMsg, Json::Value const* value);
void AddErrorAtOffset(std::string const& errMsg, std::ptrdiff_t offset);
@ -46,15 +44,7 @@ public:
Error(std::string errMsg)
: location({ -1, -1 })
, message(std::move(errMsg)) {};
std::string GetErrorMessage() const
{
std::string output = message;
if (location.line > 0) {
output = cmStrCat("Error: @", location.line, ",", location.column,
": ", output);
}
return output;
}
std::string GetErrorMessage() const { return message; }
Location GetLocation() const { return location; }
private:
@ -70,4 +60,5 @@ public:
private:
std::string GetJsonContext(Location loc);
Location LocateInDocument(ptrdiff_t offset);
std::string Filename;
};

View File

@ -1525,8 +1525,8 @@ void cmake::SetArgs(std::vector<std::string> const& args)
auto result = presetsGraph.ReadProjectPresets(this->GetHomeDirectory());
if (result != true) {
std::string errorMsg =
cmStrCat("Could not read presets from ", this->GetHomeDirectory(), ":",
presetsGraph.parseState.GetErrorMessage());
cmStrCat("Could not read presets from ", this->GetHomeDirectory(),
":\n", presetsGraph.parseState.GetErrorMessage());
cmSystemTools::Error(errorMsg);
return;
}
@ -2620,8 +2620,7 @@ int cmake::ActualConfigure()
// actually do the configure
auto startTime = std::chrono::steady_clock::now();
#if !defined(CMAKE_BOOTSTRAP)
if (!this->Instrumentation->errorMsg.empty()) {
cmSystemTools::Error(this->Instrumentation->errorMsg);
if (this->Instrumentation->HasErrors()) {
return 1;
}
auto doConfigure = [this]() -> int {
@ -3735,8 +3734,8 @@ int cmake::Build(int jobs, std::string dir, std::vector<std::string> targets,
auto result = settingsFile.ReadProjectPresets(this->GetHomeDirectory());
if (result != true) {
cmSystemTools::Error(
cmStrCat("Could not read presets from ", this->GetHomeDirectory(), ":",
settingsFile.parseState.GetErrorMessage()));
cmStrCat("Could not read presets from ", this->GetHomeDirectory(),
":\n", settingsFile.parseState.GetErrorMessage()));
return 1;
}
@ -3962,8 +3961,7 @@ int cmake::Build(int jobs, std::string dir, std::vector<std::string> targets,
#if !defined(CMAKE_BOOTSTRAP)
cmInstrumentation instrumentation(dir);
if (!instrumentation.errorMsg.empty()) {
cmSystemTools::Error(instrumentation.errorMsg);
if (instrumentation.HasErrors()) {
return 1;
}
instrumentation.CollectTimingData(
@ -4105,7 +4103,7 @@ int cmake::Workflow(std::string const& presetName,
auto result = settingsFile.ReadProjectPresets(this->GetHomeDirectory());
if (result != true) {
cmSystemTools::Error(cmStrCat("Could not read presets from ",
this->GetHomeDirectory(), ":",
this->GetHomeDirectory(), ":\n",
settingsFile.parseState.GetErrorMessage()));
return 1;
}

View File

@ -505,7 +505,7 @@ bool testFilteredObject()
ASSERT_TRUE(m == expected);
auto error = state.GetErrorMessage();
ASSERT_TRUE(error == "\nFaulty Object Property");
ASSERT_TRUE(error == "Faulty Object Property");
return true;
}

View File

@ -1,2 +1,2 @@
^CMake Error: Could not read presets from .*
Error: @2,15: Invalid extra field \"\$comment\" in root object
CMakePresets.json:2: Invalid extra field \"\$comment\" in root object

View File

@ -1,5 +1,5 @@
^CMake Error: Could not read presets from [^
]*/Tests/RunCMake/CMakePresets/EmptyPenvInInclude:
Error: @3,15: Invalid "include" field
CMakePresets.json:3: Invalid "include" field
"include": \["\$penv\{\}"\],
\^$

View File

@ -1,5 +1,5 @@
^CMake Error: Could not read presets from [^
]*/Tests/RunCMake/CMakePresets/EmptyPresetName:
Error: @5,15: Invalid Preset Name
CMakePresets.json:5: Invalid Preset Name
"name": "",
\^$

View File

@ -1,5 +1,5 @@
^CMake Error: Could not read presets from [^
]*/Tests/RunCMake/CMakePresets/ExtraPresetField:
Error: @8,18: Invalid extra field "invalid" in Preset
CMakePresets.json:8: Invalid extra field "invalid" in Preset
"invalid": true
\^$

View File

@ -1,5 +1,5 @@
^CMake Error: Could not read presets from [^
]*/Tests/RunCMake/CMakePresets/ExtraRootField:
Error: @10,14: Invalid extra field "invalid" in root object
CMakePresets.json:10: Invalid extra field "invalid" in root object
"invalid": true
\^$

View File

@ -1,5 +1,5 @@
^CMake Error: Could not read presets from [^
]*/Tests/RunCMake/CMakePresets/ExtraVariableField:
Error: @11,22: Invalid extra field "invalid" in variable "EXTRA" for preset "ExtraVariableField"
CMakePresets.json:11: Invalid extra field "invalid" in variable "EXTRA" for preset "ExtraVariableField"
"invalid": true
\^$

View File

@ -1,5 +1,5 @@
^CMake Error: Could not read presets from [^
]*/Tests/RunCMake/CMakePresets/HighVersion:
Error: @2,14: Unrecognized "version" 1000: must be >=1 and <=10
CMakePresets.json:2: Unrecognized "version" 1000: must be >=1 and <=10
"version": 1000,
\^$

View File

@ -1,5 +1,5 @@
^CMake Error: Could not read presets from [^
]*/Tests/RunCMake/CMakePresets/InvalidArchitectureStrategy:
Error: @9,21: Invalid preset
CMakePresets.json:9: Invalid preset
"strategy": {}
\^$

View File

@ -1,5 +1,5 @@
^CMake Error: Could not read presets from [^
]*/Tests/RunCMake/CMakePresets/InvalidPresetBinaryDir:
Error: @7,20: "binaryDir" expected a string
CMakePresets.json:7: "binaryDir" expected a string
"binaryDir": \[\]
\^$

View File

@ -1,5 +1,5 @@
^CMake Error: Could not read presets from [^
]*/Tests/RunCMake/CMakePresets/InvalidPresetGenerator:
Error: @6,20: "generator" expected a string
CMakePresets.json:6: "generator" expected a string
"generator": \[\],
\^$

View File

@ -1,5 +1,5 @@
^CMake Error: Could not read presets from [^
]*/Tests/RunCMake/CMakePresets/InvalidPresetName:
Error: @5,15: Invalid Preset Name
CMakePresets.json:5: Invalid Preset Name
"name": \[\],
\^$

View File

@ -1,5 +1,5 @@
^CMake Error: Could not read presets from [^
]*/Tests/RunCMake/CMakePresets/InvalidPresetVendor:
Error: @8,17: Invalid preset
CMakePresets.json:8: Invalid preset
"vendor": true
\^$

View File

@ -1,5 +1,5 @@
^CMake Error: Could not read presets from [^
]*/Tests/RunCMake/CMakePresets/InvalidPresets:
Error: @3,23: Invalid "configurePresets" field
CMakePresets.json:3: Invalid "configurePresets" field
"configurePresets": {}
\^$

View File

@ -1,5 +1,5 @@
^CMake Error: Could not read presets from [^
]*/Tests/RunCMake/CMakePresets/InvalidRoot:
Error: \@1\,1\: Invalid root object
CMakePresets.json:1: Invalid root object
\[\]
\^$

View File

@ -1,5 +1,5 @@
^CMake Error: Could not read presets from [^
]*/Tests/RunCMake/CMakePresets/InvalidToolsetStrategy:
Error: @9,21: Invalid preset
CMakePresets.json:9: Invalid preset
"strategy": {}
\^$

View File

@ -1,5 +1,5 @@
^CMake Error: Could not read presets from [^
]*/Tests/RunCMake/CMakePresets/InvalidVariableValue:
Error: @10,20: "value" expected a string
CMakePresets.json:10: "value" expected a string
"value": \[\]
\^$

View File

@ -1,5 +1,5 @@
^CMake Error: Could not read presets from [^
]*/Tests/RunCMake/CMakePresets/InvalidVariables:
Error: @8,25: Invalid preset
CMakePresets.json:8: Invalid preset
"cacheVariables": \[\]
\^$

View File

@ -1,5 +1,5 @@
^CMake Error: Could not read presets from [^
]*/Tests/RunCMake/CMakePresets/InvalidVendor:
Error: @3,13: Invalid root object
CMakePresets.json:3: Invalid root object
"vendor": true,
\^$

View File

@ -1,5 +1,5 @@
^CMake Error: Could not read presets from [^
]*/Tests/RunCMake/CMakePresets/InvalidVersion:
Error: @2,14: Invalid "version" field
CMakePresets.json:2: Invalid "version" field
"version": "1.0",
\^$

View File

@ -1,5 +1,5 @@
^CMake Error: Could not read presets from [^
]*/Tests/RunCMake/CMakePresets/LowVersion:
Error: @2,14: Unrecognized "version" 0: must be >=1 and <=10
CMakePresets.json:2: Unrecognized "version" 0: must be >=1 and <=10
"version": 0,
\^

View File

@ -1,5 +1,5 @@
^CMake Error: Could not read presets from [^
]*/Tests/RunCMake/CMakePresets/MinimumRequiredInvalid:
Error: @3,27: Invalid "cmakeMinimumRequired"
CMakePresets.json:3: Invalid "cmakeMinimumRequired"
"cmakeMinimumRequired": "3.18",
\^$

View File

@ -1,5 +1,5 @@
^CMake Error: Could not read presets from [^
]*/Tests/RunCMake/CMakePresets/MinimumRequiredMajor:
Error: @4,14: "cmakeMinimumRequired" major version 1000 must be less than [0-9]*
CMakePresets.json:4: "cmakeMinimumRequired" major version 1000 must be less than [0-9]*
"major": 1000
\^$

View File

@ -1,5 +1,5 @@
^CMake Error: Could not read presets from [^
]*/Tests/RunCMake/CMakePresets/MinimumRequiredMinor:
Error: @5,14: "cmakeMinimumRequired" minor version 1000 must be less than [0-9]*
CMakePresets.json:5: "cmakeMinimumRequired" minor version 1000 must be less than [0-9]*
"minor": 1000
\^$

View File

@ -1,5 +1,5 @@
^CMake Error: Could not read presets from [^
]*/Tests/RunCMake/CMakePresets/MinimumRequiredPatch:
Error: @6,14: "cmakeMinimumRequired" patch version 50000000 must be less than [0-9]*
CMakePresets.json:6: "cmakeMinimumRequired" patch version 50000000 must be less than [0-9]*
"patch": 50000000
\^$

View File

@ -1,5 +1,5 @@
^CMake Error: Could not read presets from [^
]*/Tests/RunCMake/CMakePresets/NoPresetName:
Error: @4,5: Missing required field "name" in Preset
CMakePresets.json:4: Missing required field "name" in Preset
{
\^$

View File

@ -1,5 +1,5 @@
^CMake Error: Could not read presets from [^
]*/Tests/RunCMake/CMakePresets/NoVariableValue:
Error: @9,16: Missing required field "value" in variable "VAR" for preset "NoVariableValue"
CMakePresets.json:9: Missing required field "value" in variable "VAR" for preset "NoVariableValue"
"VAR": {}
\^$

View File

@ -1,5 +1,5 @@
^CMake Error: Could not read presets from [^
]*/Tests/RunCMake/CMakePresets/PresetNotObject:
Error: @4,5: Invalid Preset
CMakePresets.json:4: Invalid Preset
\[\]
\^$

View File

@ -1,5 +1,5 @@
^CMake Error: Could not read presets from [^
]*/Tests/RunCMake/CMakePresets/UnknownArchitectureStrategy:
Error: @9,21: Invalid preset
CMakePresets.json:9: Invalid preset
"strategy": "unknown"
\^$

View File

@ -1,5 +1,5 @@
^CMake Error: Could not read presets from [^
]*/Tests/RunCMake/CMakePresets/UnknownToolsetStrategy:
Error: @9,21: Invalid preset
CMakePresets.json:9: Invalid preset
"strategy": "unknown"
\^$

View File

@ -1,5 +1,5 @@
^CMake Error: Could not read presets from [^
]*/Tests/RunCMake/CMakePresets/VariableNotObject:
Error: @9,16: Invalid CMake variable "VAR" for preset "VariableNotObject"
CMakePresets.json:9: Invalid CMake variable "VAR" for preset "VariableNotObject"
"VAR": \[\]
\^$

View File

@ -1,5 +1,5 @@
^Could not read/parse resource spec file [^
]*/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-nofile-ctest-s-build/dynamic-resspec\.json:[ ]
]*/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-nofile-ctest-s-build/dynamic-resspec\.json:
File not found: [^
]*/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-nofile-ctest-s-build/dynamic-resspec\.json
CMake Error at [^

View File

@ -1,5 +1,5 @@
^Could not read/parse resource spec file [^
]*/Tests/RunCMake/CTestResourceAllocation/noexist\.json:[ ]
]*/Tests/RunCMake/CTestResourceAllocation/noexist\.json:
File not found: [^
]*/Tests/RunCMake/CTestResourceAllocation/noexist.json
Errors while running CTest

View File

@ -1,5 +1,5 @@
^Could not read/parse resource spec file [^
]*/Tests/RunCMake/CTestResourceAllocation/invalid\.json:[ ]
]*/Tests/RunCMake/CTestResourceAllocation/invalid\.json:
JSON Parse Error: [^
]*/Tests/RunCMake/CTestResourceAllocation/invalid\.json:
\* Line 1, Column 1

View File

@ -1,4 +1,5 @@
^CMake Error: +
Error: @3,13: Not a valid hook: "bad hook"
^CMake Error: Could not load instrumentation queries from [^
]+:
bad-hook.json:3: Not a valid hook: "bad hook"
"hooks": \["bad hook", "postGenerate", "preCMakeBuild", "postCMakeBuild", "postInstall"\]
\^$

View File

@ -1,4 +1,5 @@
^CMake Error: +
Error: @3,42: Not a valid query: "bad query"
^CMake Error: Could not load instrumentation queries from [^
]+:
bad-query.json:[0-9]+: Not a valid query: "bad query"
"queries": \["staticSystemInformation", "bad query"\]
\^$

View File

@ -1,4 +1,5 @@
^CMake Error: +
Error: @1,1: Missing required field "version" in root object
^CMake Error: Could not load instrumentation queries from [^
]+:
empty.json:[0-9]+: Missing required field "version" in root object
{
\^$