| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying | |
| file LICENSE.rst or https://cmake.org/licensing for details. */ | |
| #include "cmConfigure.h" // IWYU pragma: keep | |
| #include "cmCMakePresetsErrors.h" | |
| #include <functional> | |
| #include <utility> | |
| #include <vector> | |
| #include <cm3p/json/value.h> | |
| #include "cmJSONHelpers.h" | |
| #include "cmJSONState.h" | |
| #include "cmStringAlgorithms.h" | |
| namespace cmCMakePresetsErrors { | |
| namespace { | |
| std::string CapitalizePresetKind(std::string kind) | |
| { | |
| if (!kind.empty() && kind.front() >= 'a' && kind.front() <= 'z') { | |
| kind.front() = static_cast<char>(kind.front() - 'a' + 'A'); | |
| } | |
| return kind; | |
| } | |
| } | |
| Json::Value const* getPreset(cmJSONState* state) | |
| { | |
| if (state->parseStack.size() < 2) { | |
| return nullptr; | |
| } | |
| std::string firstKey = state->parseStack[0].first; | |
| if (firstKey == "configurePresets" || firstKey == "packagePresets" || | |
| firstKey == "buildPresets" || firstKey == "testPresets") { | |
| return state->parseStack[1].second; | |
| } | |
| return nullptr; | |
| } | |
| std::string getPresetName(cmJSONState* state) | |
| { | |
| Json::Value const* preset = getPreset(state); | |
| if (preset && preset->isMember("name")) { | |
| Json::Value const& name = preset->operator[]("name"); | |
| if (name.isConvertibleTo(Json::ValueType::stringValue)) { | |
| return name.asString(); | |
| } | |
| } | |
| return ""; | |
| } | |
| std::string getVariableName(cmJSONState* state) | |
| { | |
| std::string var = state->key_after("cacheVariables"); | |
| if (var.empty()) { | |
| var = state->key_after("variables"); | |
| } | |
| std::string errMsg = | |
| var.empty() ? "variable" : cmStrCat("variable \"", var, '"'); | |
| errMsg = | |
| cmStrCat(std::move(errMsg), " for preset \"", getPresetName(state), '"'); | |
| return errMsg; | |
| } | |
| void FILE_NOT_FOUND(std::string const& filename, cmJSONState* state) | |
| { | |
| state->AddError(cmStrCat("File not found: ", filename)); | |
| } | |
| void INVALID_ROOT(Json::Value const* value, cmJSONState* state) | |
| { | |
| state->AddErrorAtValue("Invalid root object", value); | |
| } | |
| void NO_VERSION(Json::Value const* value, cmJSONState* state) | |
| { | |
| state->AddErrorAtValue("No \"version\" field", value); | |
| } | |
| void INVALID_VERSION(Json::Value const* value, cmJSONState* state) | |
| { | |
| state->AddErrorAtValue("Invalid \"version\" field", value); | |
| } | |
| void UNRECOGNIZED_VERSION(Json::Value const* value, cmJSONState* state) | |
| { | |
| state->AddErrorAtValue("Unrecognized \"version\" field", value); | |
| } | |
| void INVALID_PRESETS(Json::Value const* value, cmJSONState* state) | |
| { | |
| state->AddErrorAtValue(cmStrCat("Invalid \"", state->key(), "\" field"), | |
| value); | |
| } | |
| void INVALID_PRESET(Json::Value const* value, cmJSONState* state) | |
| { | |
| state->AddErrorAtValue("Invalid preset", value); | |
| } | |
| void INVALID_PRESET_NAMED(std::string const& presetName, | |
| std::string const& kind, cmJSONState* state, | |
| std::string const& detail) | |
| { | |
| std::string err_msg = | |
| cmStrCat("Invalid ", kind, " preset: \"", presetName, '"'); | |
| if (!detail.empty()) { | |
| err_msg = cmStrCat(std::move(err_msg), ": ", detail); | |
| } | |
| state->AddError(err_msg); | |
| } | |
| void INVALID_VARIABLE(Json::Value const* value, cmJSONState* state) | |
| { | |
| std::string var = cmCMakePresetsErrors::getVariableName(state); | |
| state->AddErrorAtValue(cmStrCat("Invalid CMake ", var), value); | |
| } | |
| void DUPLICATE_PRESETS(std::string const& presetName, cmJSONState* state) | |
| { | |
| state->AddError(cmStrCat("Duplicate preset: \"", presetName, '"')); | |
| } | |
| void CYCLIC_PRESET_INHERITANCE(std::string const& presetName, | |
| std::string const& kind, cmJSONState* state) | |
| { | |
| state->AddError(cmStrCat("Cyclic preset inheritance for ", kind, | |
| " preset \"", presetName, '"')); | |
| } | |
| void INHERITED_PRESET_UNREACHABLE_FROM_FILE(std::string const& presetName, | |
| std::string const& inheritedPreset, | |
| std::string const& kind, | |
| cmJSONState* state) | |
| { | |
| state->AddError(cmStrCat(CapitalizePresetKind(kind), " preset \"", | |
| presetName, "\" inherits preset \"", | |
| inheritedPreset, "\", but \"", inheritedPreset, | |
| "\" is not reachable from the preset's file")); | |
| } | |
| void CONFIGURE_PRESET_UNREACHABLE_FROM_FILE(std::string const& presetName, | |
| std::string const& kind, | |
| std::string const& configurePreset, | |
| cmJSONState* state) | |
| { | |
| state->AddError(cmStrCat(CapitalizePresetKind(kind), " preset \"", | |
| presetName, "\" references configure preset \"", | |
| configurePreset, "\", but \"", configurePreset, | |
| "\" is not reachable from the preset's file")); | |
| } | |
| void INVALID_MACRO_EXPANSION(std::string const& presetName, cmJSONState* state) | |
| { | |
| state->AddError(cmStrCat("Invalid macro expansion in \"", presetName, '"')); | |
| } | |
| void BUILD_TEST_PRESETS_UNSUPPORTED(Json::Value const*, cmJSONState* state) | |
| { | |
| state->AddError("File version must be 2 or higher for build and test preset " | |
| "support"); | |
| } | |
| void PACKAGE_PRESETS_UNSUPPORTED(Json::Value const*, cmJSONState* state) | |
| { | |
| state->AddError( | |
| "File version must be 6 or higher for package preset support"); | |
| } | |
| void WORKFLOW_PRESETS_UNSUPPORTED(Json::Value const*, cmJSONState* state) | |
| { | |
| state->AddError( | |
| "File version must be 6 or higher for workflow preset support"); | |
| } | |
| void INCLUDE_UNSUPPORTED(Json::Value const*, cmJSONState* state) | |
| { | |
| state->AddError("File version must be 4 or higher for include support"); | |
| } | |
| void INVALID_INCLUDE(Json::Value const* value, cmJSONState* state) | |
| { | |
| state->AddErrorAtValue("Invalid \"include\" field", value); | |
| } | |
| void CONFIGURE_PRESET_NOT_FOUND(std::string const& presetName, | |
| std::string const& kind, | |
| std::string const& configurePreset, | |
| cmJSONState* state) | |
| { | |
| state->AddError(cmStrCat( | |
| CapitalizePresetKind(kind), " preset \"", presetName, | |
| "\" references unknown configure preset \"", configurePreset, '"')); | |
| } | |
| void INSTALL_PREFIX_UNSUPPORTED(Json::Value const* value, cmJSONState* state) | |
| { | |
| state->AddErrorAtValue( | |
| "File version must be 3 or higher for installDir preset " | |
| "support", | |
| value); | |
| } | |
| void CONDITION_UNSUPPORTED(cmJSONState* state) | |
| { | |
| state->AddError("File version must be 3 or higher for condition support"); | |
| } | |
| void TOOLCHAIN_FILE_UNSUPPORTED(cmJSONState* state) | |
| { | |
| state->AddError("File version must be 3 or higher for toolchainFile preset " | |
| "support"); | |
| } | |
| void GRAPHVIZ_FILE_UNSUPPORTED(cmJSONState* state) | |
| { | |
| state->AddError( | |
| "File version must be 10 or higher for graphviz preset support"); | |
| } | |
| void JOBS_PROC_UNSUPPORTED(cmJSONState* state) | |
| { | |
| state->AddError("File version must be 11 or higher for " | |
| "processor-count-based jobs preset support"); | |
| } | |
| void PASSTHROUGH_ARGS_UNSUPPORTED(cmJSONState* state) | |
| { | |
| state->AddError("File version must be 12 or higher for " | |
| "testPassthroughArguments preset support"); | |
| } | |
| void CYCLIC_INCLUDE(std::string const& file, cmJSONState* state) | |
| { | |
| state->AddError(cmStrCat("Cyclic include among presets files: ", file)); | |
| } | |
| void TEST_OUTPUT_TRUNCATION_UNSUPPORTED(cmJSONState* state) | |
| { | |
| state->AddError("File version must be 5 or higher for testOutputTruncation " | |
| "preset support"); | |
| } | |
| namespace { | |
| std::string WorkflowStepLabel(cm::string_view stepType, | |
| std::string const& stepName) | |
| { | |
| return cmStrCat("of type \"", stepType, "\" named \"", stepName, '"'); | |
| } | |
| } | |
| void INVALID_WORKFLOW_STEPS(cm::string_view stepType, | |
| std::string const& stepName, cmJSONState* state) | |
| { | |
| state->AddError( | |
| cmStrCat("Invalid workflow step ", WorkflowStepLabel(stepType, stepName))); | |
| } | |
| void WORKFLOW_STEP_CONFIGURE_PRESET_MISMATCH( | |
| std::string const& kind, std::string const& workflowStep, | |
| std::string const& stepConfigurePreset, | |
| std::string const& workflowConfigurePreset, cmJSONState* state) | |
| { | |
| state->AddError(cmStrCat( | |
| CapitalizePresetKind(kind), " workflow step \"", workflowStep, | |
| "\" uses configure preset \"", stepConfigurePreset, | |
| "\", but workflow configure preset is \"", workflowConfigurePreset, '"')); | |
| } | |
| void INVALID_TEST_FILTER_INCLUDE_INDEX(Json::Value const* value, | |
| cmJSONState* state) | |
| { | |
| state->AddErrorAtValue( | |
| "Invalid \"filter.include.index\" field: expected a string or object", | |
| value); | |
| } | |
| void NO_WORKFLOW_STEPS(std::string const& presetName, cmJSONState* state) | |
| { | |
| state->AddError( | |
| cmStrCat("No workflow steps specified for \"", presetName, '"')); | |
| } | |
| void FIRST_WORKFLOW_STEP_NOT_CONFIGURE(cm::string_view stepType, | |
| std::string const& stepName, | |
| cmJSONState* state) | |
| { | |
| state->AddError(cmStrCat("First workflow step ", | |
| WorkflowStepLabel(stepType, stepName), | |
| " must be a configure step")); | |
| } | |
| void CONFIGURE_WORKFLOW_STEP_NOT_FIRST(cm::string_view stepType, | |
| std::string const& stepName, | |
| cmJSONState* state) | |
| { | |
| state->AddError(cmStrCat("Workflow step ", | |
| WorkflowStepLabel(stepType, stepName), | |
| " must be the first step")); | |
| } | |
| void WORKFLOW_STEP_UNREACHABLE_FROM_FILE(cm::string_view stepType, | |
| std::string const& stepName, | |
| cmJSONState* state) | |
| { | |
| state->AddError(cmStrCat("Workflow step ", | |
| WorkflowStepLabel(stepType, stepName), | |
| " is unreachable from preset's file")); | |
| } | |
| void CTEST_JUNIT_UNSUPPORTED(cmJSONState* state) | |
| { | |
| state->AddError( | |
| "File version must be 6 or higher for CTest JUnit output support"); | |
| } | |
| void TRACE_UNSUPPORTED(cmJSONState* state) | |
| { | |
| state->AddError("File version must be 7 or higher for trace preset support"); | |
| } | |
| JsonErrors::ErrorGenerator UNRECOGNIZED_VERSION_RANGE(int min, int max) | |
| { | |
| return [min, max](Json::Value const* value, cmJSONState* state) -> void { | |
| state->AddErrorAtValue(cmStrCat("Unrecognized \"version\" ", | |
| value->asString(), ": must be >=", min, | |
| " and <=", max), | |
| value); | |
| }; | |
| } | |
| JsonErrors::ErrorGenerator UNRECOGNIZED_CMAKE_VERSION( | |
| std::string const& version, int current, int required) | |
| { | |
| return [version, current, required](Json::Value const* value, | |
| cmJSONState* state) -> void { | |
| state->AddErrorAtValue(cmStrCat("\"cmakeMinimumRequired\" ", version, | |
| " version ", required, | |
| " must be less than ", current), | |
| value); | |
| }; | |
| } | |
| void INVALID_PRESET_NAME(Json::Value const* value, cmJSONState* state) | |
| { | |
| std::string errMsg = "Invalid Preset Name"; | |
| if (value && value->isConvertibleTo(Json::ValueType::stringValue) && | |
| !value->asString().empty()) { | |
| errMsg = cmStrCat(std::move(errMsg), ": ", value->asString()); | |
| } | |
| state->AddErrorAtValue(errMsg, value); | |
| } | |
| void INVALID_CONDITION(Json::Value const* value, cmJSONState* state) | |
| { | |
| state->AddErrorAtValue( | |
| cmStrCat("Invalid condition for preset \"", getPresetName(state), '"'), | |
| value); | |
| } | |
| JsonErrors::ErrorGenerator INVALID_CONDITION_OBJECT( | |
| JsonErrors::ObjectError errorType, Json::Value::Members const& extraFields) | |
| { | |
| return JsonErrors::INVALID_NAMED_OBJECT( | |
| [](Json::Value const*, cmJSONState* state) -> std::string { | |
| return cmStrCat(" condition for preset \"", getPresetName(state), '"'); | |
| })(errorType, extraFields); | |
| } | |
| JsonErrors::ErrorGenerator INVALID_VARIABLE_OBJECT( | |
| JsonErrors::ObjectError errorType, Json::Value::Members const& extraFields) | |
| { | |
| return JsonErrors::INVALID_NAMED_OBJECT( | |
| [](Json::Value const*, cmJSONState* state) -> std::string { | |
| return getVariableName(state); | |
| })(errorType, extraFields); | |
| } | |
| JsonErrors::ErrorGenerator INVALID_PRESET_OBJECT( | |
| JsonErrors::ObjectError errorType, Json::Value::Members const& extraFields) | |
| { | |
| return JsonErrors::INVALID_NAMED_OBJECT( | |
| [](Json::Value const*, cmJSONState*) -> std::string { return "Preset"; })( | |
| errorType, extraFields); | |
| } | |
| JsonErrors::ErrorGenerator INVALID_ROOT_OBJECT( | |
| JsonErrors::ObjectError errorType, Json::Value::Members const& extraFields) | |
| { | |
| return JsonErrors::INVALID_NAMED_OBJECT( | |
| [](Json::Value const*, cmJSONState*) -> std::string { | |
| return "root object"; | |
| })(errorType, extraFields); | |
| } | |
| void PRESET_MISSING_FIELD(std::string const& presetName, | |
| std::string const& missingField, cmJSONState* state) | |
| { | |
| state->AddError(cmStrCat("Preset \"", presetName, "\" missing field \"", | |
| missingField, '"')); | |
| } | |
| void SCHEMA_UNSUPPORTED(cmJSONState* state) | |
| { | |
| state->AddError("File version must be 8 or higher for $schema support"); | |
| } | |
| void DIAGNOSTIC_UNSUPPORTED(cm::string_view name, cm::string_view context, | |
| int version, cmJSONState* state) | |
| { | |
| state->AddError(cmStrCat("File version must be ", std::to_string(version), | |
| " or higher for ", context, '.', name, " support")); | |
| } | |
| void DIAGNOSTIC_REMOVED(cm::string_view name, cm::string_view context, | |
| int version, cmJSONState* state) | |
| { | |
| state->AddError(cmStrCat("File version must be ", std::to_string(version), | |
| " or lower for ", context, '.', name, " support")); | |
| } | |
| } |