diff --git a/DEPENDENCIES b/DEPENDENCIES index 9df0ec45e..921adaebc 100644 --- a/DEPENDENCIES +++ b/DEPENDENCIES @@ -1,4 +1,4 @@ vendorpull https://github.com/sourcemeta/vendorpull 1dcbac42809cf87cb5b045106b863e17ad84ba02 -core https://github.com/sourcemeta/core 39431ca30471bb248effafb841f937962bf8291c -blaze https://github.com/sourcemeta/blaze b10fca7344e3a11048587a7b904e5710fbb448d3 +core https://github.com/sourcemeta/core 8018e9d85ef6fc0fd9ccd11c2ae438789214b00a +blaze https://github.com/sourcemeta/blaze bc1f434acafd38803f58a941a756a6f788e556e2 bootstrap https://github.com/twbs/bootstrap 1a6fdfae6be09b09eaced8f0e442ca6f7680a61e diff --git a/config.cmake.in b/config.cmake.in index 7b5987308..e24ed3748 100644 --- a/config.cmake.in +++ b/config.cmake.in @@ -10,7 +10,7 @@ endif() include(CMakeFindDependencyMacro) find_dependency(Core COMPONENTS numeric regex uri json jsonpointer io) -find_dependency(Blaze COMPONENTS foundation alterschema) +find_dependency(Blaze COMPONENTS foundation bundle alterschema) foreach(component ${JSONBINPACK_COMPONENTS}) if(component STREQUAL "runtime") diff --git a/test/e2e/CMakeLists.txt b/test/e2e/CMakeLists.txt index 91cde3ed4..f1eec4d63 100644 --- a/test/e2e/CMakeLists.txt +++ b/test/e2e/CMakeLists.txt @@ -3,6 +3,7 @@ sourcemeta_add_default_options(PRIVATE jsonbinpack_e2e_test_runner) target_link_libraries(jsonbinpack_e2e_test_runner PRIVATE sourcemeta::core::json) target_link_libraries(jsonbinpack_e2e_test_runner PRIVATE sourcemeta::jsonbinpack::compiler) target_link_libraries(jsonbinpack_e2e_test_runner PRIVATE sourcemeta::jsonbinpack::runtime) +target_link_libraries(jsonbinpack_e2e_test_runner PRIVATE sourcemeta::blaze::format) set_target_properties(jsonbinpack_e2e_test_runner PROPERTIES FOLDER "JSON BinPack/E2E") diff --git a/test/e2e/runner.cc b/test/e2e/runner.cc index 2c7897bbb..6c56b0419 100644 --- a/test/e2e/runner.cc +++ b/test/e2e/runner.cc @@ -1,6 +1,7 @@ #include #include +#include #include #include diff --git a/vendor/blaze/CMakeLists.txt b/vendor/blaze/CMakeLists.txt index 3369d4d8a..aae7a5136 100644 --- a/vendor/blaze/CMakeLists.txt +++ b/vendor/blaze/CMakeLists.txt @@ -15,6 +15,9 @@ option(BLAZE_CODEGEN "Build the Blaze codegen library" ON) option(BLAZE_DOCUMENTATION "Build the Blaze documentation generator library" ON) option(BLAZE_EDITOR "Build the Blaze editor schema compatibility library" ON) option(BLAZE_FOUNDATION "Build the Blaze foundation JSON Schema library" ON) +option(BLAZE_FRAME "Build the Blaze frame library" ON) +option(BLAZE_BUNDLE "Build the Blaze bundle library" ON) +option(BLAZE_FORMAT "Build the Blaze format library" ON) option(BLAZE_TESTS "Build the Blaze tests" OFF) option(BLAZE_BENCHMARK "Build the Blaze benchmarks" OFF) option(BLAZE_CONTRIB "Build the Blaze contrib programs" OFF) @@ -51,6 +54,18 @@ if(BLAZE_FOUNDATION) add_subdirectory(src/foundation) endif() +if(BLAZE_FRAME) + add_subdirectory(src/frame) +endif() + +if(BLAZE_BUNDLE) + add_subdirectory(src/bundle) +endif() + +if(BLAZE_FORMAT) + add_subdirectory(src/format) +endif() + if(BLAZE_COMPILER) add_subdirectory(src/compiler) endif() @@ -137,6 +152,18 @@ if(BLAZE_TESTS) add_subdirectory(test/foundation) endif() + if(BLAZE_FRAME) + add_subdirectory(test/frame) + endif() + + if(BLAZE_BUNDLE) + add_subdirectory(test/bundle) + endif() + + if(BLAZE_FORMAT) + add_subdirectory(test/format) + endif() + if(BLAZE_COMPILER) add_subdirectory(test/compiler) endif() diff --git a/vendor/blaze/DEPENDENCIES b/vendor/blaze/DEPENDENCIES index 425adb78d..30df177fd 100644 --- a/vendor/blaze/DEPENDENCIES +++ b/vendor/blaze/DEPENDENCIES @@ -1,5 +1,5 @@ vendorpull https://github.com/sourcemeta/vendorpull 1dcbac42809cf87cb5b045106b863e17ad84ba02 -core https://github.com/sourcemeta/core 31404273bf927fc26457a1052b27b56519c0329c +core https://github.com/sourcemeta/core 8018e9d85ef6fc0fd9ccd11c2ae438789214b00a jsonschema-test-suite https://github.com/json-schema-org/JSON-Schema-Test-Suite c7257e92580678a086f0b9243a1903ed88bd27f7 jsonschema-2020-12 https://github.com/json-schema-org/json-schema-spec 769daad75a9553562333a8937a187741cb708c72 jsonschema-2019-09 https://github.com/json-schema-org/json-schema-spec 41014ea723120ce70b314d72f863c6929d9f3cfd diff --git a/vendor/blaze/config.cmake.in b/vendor/blaze/config.cmake.in index 695683bcd..499cd43c2 100644 --- a/vendor/blaze/config.cmake.in +++ b/vendor/blaze/config.cmake.in @@ -5,6 +5,7 @@ list(APPEND BLAZE_COMPONENTS ${Blaze_FIND_COMPONENTS}) list(APPEND BLAZE_COMPONENTS ${blaze_FIND_COMPONENTS}) if(NOT BLAZE_COMPONENTS) list(APPEND BLAZE_COMPONENTS foundation) + list(APPEND BLAZE_COMPONENTS frame) list(APPEND BLAZE_COMPONENTS compiler) list(APPEND BLAZE_COMPONENTS evaluator) list(APPEND BLAZE_COMPONENTS output) @@ -14,6 +15,8 @@ if(NOT BLAZE_COMPONENTS) list(APPEND BLAZE_COMPONENTS codegen) list(APPEND BLAZE_COMPONENTS documentation) list(APPEND BLAZE_COMPONENTS editor) + list(APPEND BLAZE_COMPONENTS bundle) + list(APPEND BLAZE_COMPONENTS format) endif() include(CMakeFindDependencyMacro) @@ -22,8 +25,13 @@ find_dependency(Core COMPONENTS regex uri uritemplate json jsonpointer io yaml c foreach(component ${BLAZE_COMPONENTS}) if(component STREQUAL "foundation") include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_blaze_foundation.cmake") + elseif(component STREQUAL "frame") + include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_blaze_foundation.cmake") + include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_blaze_frame.cmake") elseif(component STREQUAL "compiler") include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_blaze_foundation.cmake") + include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_blaze_frame.cmake") + include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_blaze_bundle.cmake") include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_blaze_evaluator.cmake") include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_blaze_compiler.cmake") elseif(component STREQUAL "evaluator") @@ -31,24 +39,32 @@ foreach(component ${BLAZE_COMPONENTS}) include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_blaze_evaluator.cmake") elseif(component STREQUAL "test") include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_blaze_foundation.cmake") + include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_blaze_frame.cmake") + include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_blaze_bundle.cmake") include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_blaze_evaluator.cmake") include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_blaze_compiler.cmake") include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_blaze_test.cmake") elseif(component STREQUAL "output") include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_blaze_foundation.cmake") + include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_blaze_frame.cmake") include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_blaze_evaluator.cmake") include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_blaze_output.cmake") elseif(component STREQUAL "configuration") include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_blaze_foundation.cmake") + include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_blaze_frame.cmake") + include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_blaze_bundle.cmake") include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_blaze_configuration.cmake") elseif(component STREQUAL "alterschema") include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_blaze_foundation.cmake") + include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_blaze_frame.cmake") include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_blaze_evaluator.cmake") include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_blaze_compiler.cmake") include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_blaze_output.cmake") include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_blaze_alterschema.cmake") elseif(component STREQUAL "codegen") include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_blaze_foundation.cmake") + include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_blaze_frame.cmake") + include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_blaze_bundle.cmake") include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_blaze_evaluator.cmake") include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_blaze_compiler.cmake") include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_blaze_output.cmake") @@ -56,10 +72,20 @@ foreach(component ${BLAZE_COMPONENTS}) include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_blaze_codegen.cmake") elseif(component STREQUAL "documentation") include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_blaze_foundation.cmake") + include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_blaze_frame.cmake") include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_blaze_documentation.cmake") elseif(component STREQUAL "editor") include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_blaze_foundation.cmake") + include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_blaze_frame.cmake") include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_blaze_editor.cmake") + elseif(component STREQUAL "bundle") + include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_blaze_foundation.cmake") + include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_blaze_frame.cmake") + include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_blaze_bundle.cmake") + elseif(component STREQUAL "format") + include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_blaze_foundation.cmake") + include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_blaze_frame.cmake") + include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_blaze_format.cmake") else() message(FATAL_ERROR "Unknown Blaze component: ${component}") endif() diff --git a/vendor/blaze/src/alterschema/CMakeLists.txt b/vendor/blaze/src/alterschema/CMakeLists.txt index 045650fe0..37c5b5a15 100644 --- a/vendor/blaze/src/alterschema/CMakeLists.txt +++ b/vendor/blaze/src/alterschema/CMakeLists.txt @@ -1,7 +1,7 @@ sourcemeta_library(NAMESPACE sourcemeta PROJECT blaze NAME alterschema FOLDER "Blaze/AlterSchema" PRIVATE_HEADERS error.h transformer.h - SOURCES alterschema.cc schema_rule.cc transformer.cc + SOURCES alterschema.cc schema_rule.cc transformer.cc wrap.cc # Canonicalizer canonicalizer/additional_items_implicit.h canonicalizer/comment_drop.h @@ -170,6 +170,8 @@ endif() target_link_libraries(sourcemeta_blaze_alterschema PUBLIC sourcemeta::blaze::foundation) +target_link_libraries(sourcemeta_blaze_alterschema PUBLIC + sourcemeta::blaze::frame) target_link_libraries(sourcemeta_blaze_alterschema PUBLIC sourcemeta::blaze::compiler) target_link_libraries(sourcemeta_blaze_alterschema PRIVATE diff --git a/vendor/blaze/src/alterschema/alterschema.cc b/vendor/blaze/src/alterschema/alterschema.cc index 30b5417cd..2f1a6cb76 100644 --- a/vendor/blaze/src/alterschema/alterschema.cc +++ b/vendor/blaze/src/alterschema/alterschema.cc @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include diff --git a/vendor/blaze/src/alterschema/include/sourcemeta/blaze/alterschema.h b/vendor/blaze/src/alterschema/include/sourcemeta/blaze/alterschema.h index 0c2abcfe4..e1fa7ea6b 100644 --- a/vendor/blaze/src/alterschema/include/sourcemeta/blaze/alterschema.h +++ b/vendor/blaze/src/alterschema/include/sourcemeta/blaze/alterschema.h @@ -19,6 +19,7 @@ #include #include +#include #include // std::uint8_t #include // std::optional, std::nullopt @@ -127,6 +128,46 @@ class SOURCEMETA_BLAZE_ALTERSCHEMA_EXPORT SchemaRule final #pragma warning(default : 4251 4275) #endif +/// @ingroup alterschema +/// +/// Wrap a schema to only access one of its subschemas. This is useful if you +/// want to perform validation on only a specific part of the schema without +/// having to reinvent the wheel. For example: +/// +/// ```cpp +/// #include +/// #include +/// #include +/// #include +/// +/// const sourcemeta::core::JSON document = +/// sourcemeta::core::parse_json(R"JSON({ +/// "$schema": "https://json-schema.org/draft/2020-12/schema", +/// "items": { "type": "string" } +/// })JSON"); +/// +/// sourcemeta::blaze::SchemaFrame frame{ +/// sourcemeta::blaze::SchemaFrame::Mode::References}; +/// frame.analyse(document, sourcemeta::blaze::schema_walker, +/// sourcemeta::blaze::schema_resolver); +/// +/// const auto location{frame.traverse( +/// sourcemeta::core::WeakPointer{"items"}, +/// sourcemeta::blaze::SchemaFrame::LocationType::Subschema)}; +/// +/// sourcemeta::core::WeakPointer base; +/// const sourcemeta::core::JSON result = +/// sourcemeta::blaze::wrap(document, frame, location.value().get(), +/// sourcemeta::blaze::schema_resolver, base); +/// +/// sourcemeta::core::prettify(result, std::cerr); +/// std::cerr << "\n"; +/// ``` +SOURCEMETA_BLAZE_ALTERSCHEMA_EXPORT +auto wrap(const sourcemeta::core::JSON &schema, const SchemaFrame &frame, + const SchemaFrame::Location &location, const SchemaResolver &resolver, + sourcemeta::core::WeakPointer &base) -> sourcemeta::core::JSON; + } // namespace sourcemeta::blaze #endif diff --git a/vendor/blaze/src/alterschema/include/sourcemeta/blaze/alterschema_transformer.h b/vendor/blaze/src/alterschema/include/sourcemeta/blaze/alterschema_transformer.h index bd46c3cfc..1dec5e413 100644 --- a/vendor/blaze/src/alterschema/include/sourcemeta/blaze/alterschema_transformer.h +++ b/vendor/blaze/src/alterschema/include/sourcemeta/blaze/alterschema_transformer.h @@ -6,6 +6,7 @@ #endif #include +#include #include #include diff --git a/vendor/blaze/src/alterschema/schema_rule.cc b/vendor/blaze/src/alterschema/schema_rule.cc index 5d50a1597..4c5a9937b 100644 --- a/vendor/blaze/src/alterschema/schema_rule.cc +++ b/vendor/blaze/src/alterschema/schema_rule.cc @@ -2,6 +2,7 @@ #include #include #include +#include #include #include diff --git a/vendor/blaze/src/alterschema/transformer.cc b/vendor/blaze/src/alterschema/transformer.cc index 08b417776..68e1f54cf 100644 --- a/vendor/blaze/src/alterschema/transformer.cc +++ b/vendor/blaze/src/alterschema/transformer.cc @@ -1,5 +1,6 @@ #include #include +#include #include #include // std::erase_if diff --git a/vendor/blaze/src/alterschema/wrap.cc b/vendor/blaze/src/alterschema/wrap.cc new file mode 100644 index 000000000..9235cd89f --- /dev/null +++ b/vendor/blaze/src/alterschema/wrap.cc @@ -0,0 +1,109 @@ +#include + +#include +#include + +#include +#include +#include + +#include // std::any_of +#include // assert +#include // std::cref +#include // std::string_view +#include // std::move + +namespace sourcemeta::blaze { + +auto wrap(const sourcemeta::core::JSON &schema, const SchemaFrame &frame, + const SchemaFrame::Location &location, const SchemaResolver &resolver, + sourcemeta::core::WeakPointer &base) -> sourcemeta::core::JSON { + assert(frame.mode() == SchemaFrame::Mode::References); + assert(location.type != SchemaFrame::LocationType::Pointer); + + const auto &pointer{location.pointer}; + if (pointer.empty()) { + auto copy = schema; + if (copy.is_object()) { + copy.assign("$schema", sourcemeta::core::JSON{location.dialect}); + } + + return copy; + } + + assert(sourcemeta::core::try_get(schema, pointer)); + const auto has_internal_references{ + std::any_of(frame.references().cbegin(), frame.references().cend(), + [&pointer](const auto &reference) { + return reference.first.second.starts_with(pointer); + })}; + + if (!has_internal_references) { + auto subschema{sourcemeta::core::get(schema, pointer)}; + if (subschema.is_object()) { + subschema.assign("$schema", sourcemeta::core::JSON{location.dialect}); + } + + return subschema; + } + + auto copy = schema; + copy.assign("$schema", sourcemeta::core::JSON{location.dialect}); + + auto result{sourcemeta::core::JSON::make_object()}; + // JSON Schema 2020-12 is the first dialect that truly supports + // cross-dialect references In practice, others do, but we can + // play it safe here + result.assign_assume_new( + "$schema", + sourcemeta::core::JSON{"https://json-schema.org/draft/2020-12/schema"}); + // We need to make sure the schema we are wrapping always has an identifier, + // at least an artificial one, otherwise a standalone instance of `$schema` + // outside of the root of a schema resource is not valid according to + // JSON Schema + // However, note that we use a relative URI so that references to + // other schemas whose top-level identifiers are relative URIs don't + // get affected. Otherwise, we would cause unintended base resolution. + constexpr std::string_view WRAPPER_IDENTIFIER{"__sourcemeta-core-wrap__"}; + const auto maybe_id{identify(copy, resolver, location.dialect)}; + const auto id{maybe_id.empty() ? WRAPPER_IDENTIFIER : maybe_id}; + + sourcemeta::core::URI uri{id}; + + try { + reidentify(copy, id, resolver, location.dialect); + + // Otherwise we will get an error with the `WRAPPER_IDENTIFIER`, which will + // be confusing to end users + } catch (const SchemaReferenceObjectResourceError &) { + throw SchemaError( + "Cannot process a JSON Schema Draft 7 or older with a top-level " + "`$ref` (which overrides sibling keywords) without introducing " + "undefined behavior"); + } + + result.assign_assume_new("$defs", sourcemeta::core::JSON::make_object()); + result.at("$defs").assign_assume_new("schema", std::move(copy)); + + // Add a reference to the schema + if (!uri.fragment().has_value() || uri.fragment().value().empty()) { + uri.fragment(sourcemeta::core::to_string(pointer)); + result.assign_assume_new("$ref", sourcemeta::core::JSON{uri.recompose()}); + } else { + static const sourcemeta::core::JSON::String DEFS{"$defs"}; + static const sourcemeta::core::JSON::String SCHEMA{"schema"}; + result.assign_assume_new( + "$ref", + sourcemeta::core::JSON{ + sourcemeta::core::to_uri(sourcemeta::core::WeakPointer{ + std::cref(DEFS), std::cref(SCHEMA)} + .concat(pointer)) + .recompose()}); + } + + static const sourcemeta::core::JSON::String REF{"$ref"}; + base.push_back(REF); + return result; +} + +} // namespace sourcemeta::blaze diff --git a/vendor/blaze/src/bundle/CMakeLists.txt b/vendor/blaze/src/bundle/CMakeLists.txt new file mode 100644 index 000000000..a1dd3ec9d --- /dev/null +++ b/vendor/blaze/src/bundle/CMakeLists.txt @@ -0,0 +1,18 @@ +sourcemeta_library(NAMESPACE sourcemeta PROJECT blaze NAME bundle + FOLDER "Blaze/Bundle" + SOURCES bundle.cc helpers.h) + +if(BLAZE_INSTALL) + sourcemeta_library_install(NAMESPACE sourcemeta PROJECT blaze NAME bundle) +endif() + +target_link_libraries(sourcemeta_blaze_bundle PUBLIC + sourcemeta::core::json) +target_link_libraries(sourcemeta_blaze_bundle PUBLIC + sourcemeta::core::jsonpointer) +target_link_libraries(sourcemeta_blaze_bundle PUBLIC + sourcemeta::blaze::foundation) +target_link_libraries(sourcemeta_blaze_bundle PUBLIC + sourcemeta::blaze::frame) +target_link_libraries(sourcemeta_blaze_bundle PRIVATE + sourcemeta::core::uri) diff --git a/vendor/blaze/src/foundation/bundle.cc b/vendor/blaze/src/bundle/bundle.cc similarity index 99% rename from vendor/blaze/src/foundation/bundle.cc rename to vendor/blaze/src/bundle/bundle.cc index 16e416cf3..ad28457d7 100644 --- a/vendor/blaze/src/foundation/bundle.cc +++ b/vendor/blaze/src/bundle/bundle.cc @@ -1,4 +1,7 @@ +#include + #include +#include #include "helpers.h" diff --git a/vendor/blaze/src/bundle/helpers.h b/vendor/blaze/src/bundle/helpers.h new file mode 100644 index 000000000..adbf7d681 --- /dev/null +++ b/vendor/blaze/src/bundle/helpers.h @@ -0,0 +1,89 @@ +#ifndef SOURCEMETA_BLAZE_BUNDLE_HELPERS_H +#define SOURCEMETA_BLAZE_BUNDLE_HELPERS_H + +#include + +#include // assert +#include // std::string_view + +namespace sourcemeta::blaze { + +inline auto id_keyword(const SchemaBaseDialect base_dialect) + -> std::string_view { + switch (base_dialect) { + case SchemaBaseDialect::JSON_Schema_2020_12: + case SchemaBaseDialect::JSON_Schema_2020_12_Hyper: + case SchemaBaseDialect::JSON_Schema_2019_09: + case SchemaBaseDialect::JSON_Schema_2019_09_Hyper: + case SchemaBaseDialect::JSON_Schema_Draft_7: + case SchemaBaseDialect::JSON_Schema_Draft_7_Hyper: + case SchemaBaseDialect::JSON_Schema_Draft_6: + case SchemaBaseDialect::JSON_Schema_Draft_6_Hyper: + return "$id"; + case SchemaBaseDialect::JSON_Schema_Draft_4: + case SchemaBaseDialect::JSON_Schema_Draft_4_Hyper: + case SchemaBaseDialect::JSON_Schema_Draft_3: + case SchemaBaseDialect::JSON_Schema_Draft_3_Hyper: + case SchemaBaseDialect::JSON_Schema_Draft_2_Hyper: + case SchemaBaseDialect::JSON_Schema_Draft_1_Hyper: + case SchemaBaseDialect::JSON_Schema_Draft_0_Hyper: + return "id"; + } + + assert(false); + return "$id"; +} + +inline auto definitions_keyword(const SchemaBaseDialect base_dialect) + -> std::string_view { + switch (base_dialect) { + case SchemaBaseDialect::JSON_Schema_2020_12: + case SchemaBaseDialect::JSON_Schema_2020_12_Hyper: + case SchemaBaseDialect::JSON_Schema_2019_09: + case SchemaBaseDialect::JSON_Schema_2019_09_Hyper: + return "$defs"; + case SchemaBaseDialect::JSON_Schema_Draft_7: + case SchemaBaseDialect::JSON_Schema_Draft_7_Hyper: + case SchemaBaseDialect::JSON_Schema_Draft_6: + case SchemaBaseDialect::JSON_Schema_Draft_6_Hyper: + case SchemaBaseDialect::JSON_Schema_Draft_4: + case SchemaBaseDialect::JSON_Schema_Draft_4_Hyper: + case SchemaBaseDialect::JSON_Schema_Draft_3: + case SchemaBaseDialect::JSON_Schema_Draft_3_Hyper: + return "definitions"; + case SchemaBaseDialect::JSON_Schema_Draft_2_Hyper: + case SchemaBaseDialect::JSON_Schema_Draft_1_Hyper: + case SchemaBaseDialect::JSON_Schema_Draft_0_Hyper: + return ""; + } + + assert(false); + return "$defs"; +} + +// In older drafts, the presence of `$ref` would override any sibling keywords +// See +// https://json-schema.org/draft-07/draft-handrews-json-schema-01#rfc.section.8.3 +inline auto +ref_overrides_adjacent_keywords(const SchemaBaseDialect base_dialect) -> bool { + switch (base_dialect) { + case SchemaBaseDialect::JSON_Schema_Draft_7: + case SchemaBaseDialect::JSON_Schema_Draft_7_Hyper: + case SchemaBaseDialect::JSON_Schema_Draft_6: + case SchemaBaseDialect::JSON_Schema_Draft_6_Hyper: + case SchemaBaseDialect::JSON_Schema_Draft_4: + case SchemaBaseDialect::JSON_Schema_Draft_4_Hyper: + case SchemaBaseDialect::JSON_Schema_Draft_3: + case SchemaBaseDialect::JSON_Schema_Draft_3_Hyper: + case SchemaBaseDialect::JSON_Schema_Draft_2_Hyper: + case SchemaBaseDialect::JSON_Schema_Draft_1_Hyper: + case SchemaBaseDialect::JSON_Schema_Draft_0_Hyper: + return true; + default: + return false; + } +} + +} // namespace sourcemeta::blaze + +#endif diff --git a/vendor/blaze/src/foundation/include/sourcemeta/blaze/foundation_bundle.h b/vendor/blaze/src/bundle/include/sourcemeta/blaze/bundle.h similarity index 89% rename from vendor/blaze/src/foundation/include/sourcemeta/blaze/foundation_bundle.h rename to vendor/blaze/src/bundle/include/sourcemeta/blaze/bundle.h index b439736e0..9a21f9293 100644 --- a/vendor/blaze/src/foundation/include/sourcemeta/blaze/foundation_bundle.h +++ b/vendor/blaze/src/bundle/include/sourcemeta/blaze/bundle.h @@ -1,24 +1,32 @@ -#ifndef SOURCEMETA_BLAZE_FOUNDATION_BUNDLE_H -#define SOURCEMETA_BLAZE_FOUNDATION_BUNDLE_H +#ifndef SOURCEMETA_BLAZE_BUNDLE_H_ +#define SOURCEMETA_BLAZE_BUNDLE_H_ -#ifndef SOURCEMETA_BLAZE_FOUNDATION_EXPORT -#include +/// @defgroup bundle Bundle +/// @brief Bundle JSON Schemas by inlining their external references. +/// +/// This functionality is included as follows: +/// +/// ```cpp +/// #include +/// ``` + +#ifndef SOURCEMETA_BLAZE_BUNDLE_EXPORT +#include #endif #include #include -// NOLINTBEGIN(misc-include-cleaner) -#include -#include -// NOLINTEND(misc-include-cleaner) +#include +#include #include // std::function +#include // std::optional, std::nullopt #include // std::string_view namespace sourcemeta::blaze { -/// @ingroup foundation +/// @ingroup bundle /// A callback to get dependency information /// - Origin URI (empty if none) /// - Pointer (reference keyword from the origin) @@ -28,13 +36,14 @@ using DependencyCallback = std::function; -/// @ingroup foundation +/// @ingroup bundle /// /// This function recursively traverses and reports the external references in a /// schema. For example: /// /// ```cpp /// #include +/// #include /// #include /// /// // A custom resolver that knows about an additional schema @@ -66,7 +75,7 @@ using DependencyCallback = /// // Do something with the information /// }); /// ``` -SOURCEMETA_BLAZE_FOUNDATION_EXPORT +SOURCEMETA_BLAZE_BUNDLE_EXPORT auto dependencies(const sourcemeta::core::JSON &schema, const SchemaWalker &walker, const SchemaResolver &resolver, const DependencyCallback &callback, @@ -75,7 +84,7 @@ auto dependencies(const sourcemeta::core::JSON &schema, const SchemaFrame::Paths &paths = { sourcemeta::core::empty_weak_pointer}) -> void; -/// @ingroup foundation +/// @ingroup bundle /// /// This function bundles a JSON Schema (starting from Draft 4) by embedding /// every remote reference into the top level schema resource, handling circular @@ -83,6 +92,7 @@ auto dependencies(const sourcemeta::core::JSON &schema, /// /// ```cpp /// #include +/// #include /// #include /// #include /// @@ -124,7 +134,7 @@ auto dependencies(const sourcemeta::core::JSON &schema, /// /// assert(document == expected); /// ``` -SOURCEMETA_BLAZE_FOUNDATION_EXPORT +SOURCEMETA_BLAZE_BUNDLE_EXPORT auto bundle(sourcemeta::core::JSON &schema, const SchemaWalker &walker, const SchemaResolver &resolver, std::string_view default_dialect = "", @@ -134,7 +144,7 @@ auto bundle(sourcemeta::core::JSON &schema, const SchemaWalker &walker, const SchemaFrame::Paths &paths = { sourcemeta::core::empty_weak_pointer}) -> void; -/// @ingroup foundation +/// @ingroup bundle /// /// This function bundles a JSON Schema (starting from Draft 4) by embedding /// every remote reference into the top level schema resource, handling circular @@ -143,6 +153,7 @@ auto bundle(sourcemeta::core::JSON &schema, const SchemaWalker &walker, /// /// ```cpp /// #include +/// #include /// #include /// #include /// @@ -185,7 +196,7 @@ auto bundle(sourcemeta::core::JSON &schema, const SchemaWalker &walker, /// /// assert(result == expected); /// ``` -SOURCEMETA_BLAZE_FOUNDATION_EXPORT +SOURCEMETA_BLAZE_BUNDLE_EXPORT auto bundle( const sourcemeta::core::JSON &schema, const SchemaWalker &walker, const SchemaResolver &resolver, std::string_view default_dialect = "", diff --git a/vendor/blaze/src/codegen/CMakeLists.txt b/vendor/blaze/src/codegen/CMakeLists.txt index a570b15b4..cb512d2c5 100644 --- a/vendor/blaze/src/codegen/CMakeLists.txt +++ b/vendor/blaze/src/codegen/CMakeLists.txt @@ -16,5 +16,9 @@ target_link_libraries(sourcemeta_blaze_codegen PUBLIC sourcemeta::core::json) target_link_libraries(sourcemeta_blaze_codegen PUBLIC sourcemeta::blaze::foundation) +target_link_libraries(sourcemeta_blaze_codegen PUBLIC + sourcemeta::blaze::frame) +target_link_libraries(sourcemeta_blaze_codegen PRIVATE + sourcemeta::blaze::bundle) target_link_libraries(sourcemeta_blaze_codegen PRIVATE sourcemeta::blaze::alterschema) diff --git a/vendor/blaze/src/codegen/codegen.cc b/vendor/blaze/src/codegen/codegen.cc index c7831daae..583566305 100644 --- a/vendor/blaze/src/codegen/codegen.cc +++ b/vendor/blaze/src/codegen/codegen.cc @@ -1,5 +1,7 @@ #include +#include #include +#include #include // std::ranges::sort #include // assert diff --git a/vendor/blaze/src/codegen/codegen_default_compiler.h b/vendor/blaze/src/codegen/codegen_default_compiler.h index 98eced569..cbb4a517a 100644 --- a/vendor/blaze/src/codegen/codegen_default_compiler.h +++ b/vendor/blaze/src/codegen/codegen_default_compiler.h @@ -4,6 +4,7 @@ #include #include +#include #include #include diff --git a/vendor/blaze/src/codegen/codegen_symbol.cc b/vendor/blaze/src/codegen/codegen_symbol.cc index ebe40ddf2..d6d4f9a53 100644 --- a/vendor/blaze/src/codegen/codegen_symbol.cc +++ b/vendor/blaze/src/codegen/codegen_symbol.cc @@ -1,4 +1,5 @@ #include +#include #include diff --git a/vendor/blaze/src/codegen/include/sourcemeta/blaze/codegen.h b/vendor/blaze/src/codegen/include/sourcemeta/blaze/codegen.h index d7319bdbb..63e4296c2 100644 --- a/vendor/blaze/src/codegen/include/sourcemeta/blaze/codegen.h +++ b/vendor/blaze/src/codegen/include/sourcemeta/blaze/codegen.h @@ -11,6 +11,7 @@ // NOLINTEND(misc-include-cleaner) #include +#include #include #include diff --git a/vendor/blaze/src/compiler/CMakeLists.txt b/vendor/blaze/src/compiler/CMakeLists.txt index d0d0da5e1..459f84564 100644 --- a/vendor/blaze/src/compiler/CMakeLists.txt +++ b/vendor/blaze/src/compiler/CMakeLists.txt @@ -26,5 +26,9 @@ target_link_libraries(sourcemeta_blaze_compiler PRIVATE sourcemeta::core::uri) target_link_libraries(sourcemeta_blaze_compiler PUBLIC sourcemeta::blaze::foundation) +target_link_libraries(sourcemeta_blaze_compiler PUBLIC + sourcemeta::blaze::frame) +target_link_libraries(sourcemeta_blaze_compiler PRIVATE + sourcemeta::blaze::bundle) target_link_libraries(sourcemeta_blaze_compiler PUBLIC sourcemeta::blaze::evaluator) diff --git a/vendor/blaze/src/compiler/compile.cc b/vendor/blaze/src/compiler/compile.cc index 857719669..425dc18ac 100644 --- a/vendor/blaze/src/compiler/compile.cc +++ b/vendor/blaze/src/compiler/compile.cc @@ -1,7 +1,8 @@ +#include #include #include - #include +#include #include // std::move, std::sort, std::unique #include // assert diff --git a/vendor/blaze/src/compiler/compile_helpers.h b/vendor/blaze/src/compiler/compile_helpers.h index d0e6df3c6..1a93740bd 100644 --- a/vendor/blaze/src/compiler/compile_helpers.h +++ b/vendor/blaze/src/compiler/compile_helpers.h @@ -2,6 +2,7 @@ #define SOURCEMETA_BLAZE_COMPILER_COMPILE_HELPERS_H_ #include +#include #include #include // std::ranges::find, std::ranges::any_of diff --git a/vendor/blaze/src/compiler/include/sourcemeta/blaze/compiler.h b/vendor/blaze/src/compiler/include/sourcemeta/blaze/compiler.h index f5dec141f..f823bc30d 100644 --- a/vendor/blaze/src/compiler/include/sourcemeta/blaze/compiler.h +++ b/vendor/blaze/src/compiler/include/sourcemeta/blaze/compiler.h @@ -11,6 +11,7 @@ #include #include +#include #include #include #include diff --git a/vendor/blaze/src/compiler/include/sourcemeta/blaze/compiler_unevaluated.h b/vendor/blaze/src/compiler/include/sourcemeta/blaze/compiler_unevaluated.h index 12b9fd65c..aa5b0e955 100644 --- a/vendor/blaze/src/compiler/include/sourcemeta/blaze/compiler_unevaluated.h +++ b/vendor/blaze/src/compiler/include/sourcemeta/blaze/compiler_unevaluated.h @@ -6,6 +6,7 @@ #endif #include +#include #include #include diff --git a/vendor/blaze/src/compiler/unevaluated.cc b/vendor/blaze/src/compiler/unevaluated.cc index 058393591..1cc8f61a2 100644 --- a/vendor/blaze/src/compiler/unevaluated.cc +++ b/vendor/blaze/src/compiler/unevaluated.cc @@ -1,4 +1,5 @@ #include +#include #include "compile_helpers.h" diff --git a/vendor/blaze/src/configuration/CMakeLists.txt b/vendor/blaze/src/configuration/CMakeLists.txt index 0b7ed316c..ab58ed041 100644 --- a/vendor/blaze/src/configuration/CMakeLists.txt +++ b/vendor/blaze/src/configuration/CMakeLists.txt @@ -11,3 +11,4 @@ target_link_libraries(sourcemeta_blaze_configuration PRIVATE sourcemeta::core::u target_link_libraries(sourcemeta_blaze_configuration PRIVATE sourcemeta::core::io) target_link_libraries(sourcemeta_blaze_configuration PRIVATE sourcemeta::core::crypto) target_link_libraries(sourcemeta_blaze_configuration PRIVATE sourcemeta::blaze::foundation) +target_link_libraries(sourcemeta_blaze_configuration PRIVATE sourcemeta::blaze::bundle) diff --git a/vendor/blaze/src/configuration/fetch.cc b/vendor/blaze/src/configuration/fetch.cc index 8de8e3c18..126c5b235 100644 --- a/vendor/blaze/src/configuration/fetch.cc +++ b/vendor/blaze/src/configuration/fetch.cc @@ -1,6 +1,7 @@ +#include #include - #include + #include #include diff --git a/vendor/blaze/src/documentation/CMakeLists.txt b/vendor/blaze/src/documentation/CMakeLists.txt index 147d3410d..189ac91c2 100644 --- a/vendor/blaze/src/documentation/CMakeLists.txt +++ b/vendor/blaze/src/documentation/CMakeLists.txt @@ -10,6 +10,8 @@ target_link_libraries(sourcemeta_blaze_documentation PUBLIC sourcemeta::core::json) target_link_libraries(sourcemeta_blaze_documentation PUBLIC sourcemeta::blaze::foundation) +target_link_libraries(sourcemeta_blaze_documentation PRIVATE + sourcemeta::blaze::frame) target_link_libraries(sourcemeta_blaze_documentation PRIVATE sourcemeta::blaze::alterschema) target_link_libraries(sourcemeta_blaze_documentation PRIVATE diff --git a/vendor/blaze/src/documentation/documentation.cc b/vendor/blaze/src/documentation/documentation.cc index 2f2b89fee..83470ef94 100644 --- a/vendor/blaze/src/documentation/documentation.cc +++ b/vendor/blaze/src/documentation/documentation.cc @@ -3,6 +3,7 @@ #include #include +#include #include #include // assert diff --git a/vendor/blaze/src/editor/CMakeLists.txt b/vendor/blaze/src/editor/CMakeLists.txt index 3c2cd375c..8dd4c489c 100644 --- a/vendor/blaze/src/editor/CMakeLists.txt +++ b/vendor/blaze/src/editor/CMakeLists.txt @@ -10,3 +10,5 @@ target_link_libraries(sourcemeta_blaze_editor PUBLIC sourcemeta::core::json) target_link_libraries(sourcemeta_blaze_editor PUBLIC sourcemeta::blaze::foundation) +target_link_libraries(sourcemeta_blaze_editor PRIVATE + sourcemeta::blaze::frame) diff --git a/vendor/blaze/src/editor/editor.cc b/vendor/blaze/src/editor/editor.cc index 638cd7279..7b3f31660 100644 --- a/vendor/blaze/src/editor/editor.cc +++ b/vendor/blaze/src/editor/editor.cc @@ -1,4 +1,5 @@ #include +#include #include // assert #include // std::map diff --git a/vendor/blaze/src/format/CMakeLists.txt b/vendor/blaze/src/format/CMakeLists.txt new file mode 100644 index 000000000..d30c6a9f7 --- /dev/null +++ b/vendor/blaze/src/format/CMakeLists.txt @@ -0,0 +1,16 @@ +sourcemeta_library(NAMESPACE sourcemeta PROJECT blaze NAME format + FOLDER "Blaze/Format" + SOURCES format.cc) + +if(BLAZE_INSTALL) + sourcemeta_library_install(NAMESPACE sourcemeta PROJECT blaze NAME format) +endif() + +target_link_libraries(sourcemeta_blaze_format PUBLIC + sourcemeta::core::json) +target_link_libraries(sourcemeta_blaze_format PUBLIC + sourcemeta::core::jsonpointer) +target_link_libraries(sourcemeta_blaze_format PUBLIC + sourcemeta::blaze::foundation) +target_link_libraries(sourcemeta_blaze_format PRIVATE + sourcemeta::blaze::frame) diff --git a/vendor/blaze/src/foundation/format.cc b/vendor/blaze/src/format/format.cc similarity index 98% rename from vendor/blaze/src/foundation/format.cc rename to vendor/blaze/src/format/format.cc index dafcff7f5..c6c659ff2 100644 --- a/vendor/blaze/src/foundation/format.cc +++ b/vendor/blaze/src/format/format.cc @@ -1,4 +1,7 @@ +#include + #include +#include #include // std::uint64_t #include // std::numeric_limits diff --git a/vendor/blaze/src/format/include/sourcemeta/blaze/format.h b/vendor/blaze/src/format/include/sourcemeta/blaze/format.h new file mode 100644 index 000000000..e81b6e9a5 --- /dev/null +++ b/vendor/blaze/src/format/include/sourcemeta/blaze/format.h @@ -0,0 +1,56 @@ +#ifndef SOURCEMETA_BLAZE_FORMAT_H_ +#define SOURCEMETA_BLAZE_FORMAT_H_ + +/// @defgroup format Format +/// @brief Format JSON Schemas by reordering keywords into a canonical order. +/// +/// This functionality is included as follows: +/// +/// ```cpp +/// #include +/// ``` + +#ifndef SOURCEMETA_BLAZE_FORMAT_EXPORT +#include +#endif + +#include + +#include + +#include // std::string_view + +namespace sourcemeta::blaze { + +/// @ingroup format +/// +/// Format a JSON Schema document by reordering all object properties throughout +/// the entire document according to an opinionated JSON Schema aware ordering. +/// This function modifies the schema in-place. For example: +/// +/// ```cpp +/// #include +/// #include +/// +/// #include +/// +/// #include +/// #include +/// +/// sourcemeta::core::JSON schema = +/// sourcemeta::core::parse_json( +/// "{ \"type\": \"string\", \"minLength\": 3 }"); +/// sourcemeta::blaze::format(schema, sourcemeta::blaze::schema_walker, +/// sourcemeta::blaze::schema_resolver); +/// std::ostringstream stream; +/// sourcemeta::core::prettify(schema, stream); +/// std::cout << stream.str() << std::endl; +/// ``` +SOURCEMETA_BLAZE_FORMAT_EXPORT +auto format(sourcemeta::core::JSON &schema, const SchemaWalker &walker, + const SchemaResolver &resolver, + std::string_view default_dialect = "") -> void; + +} // namespace sourcemeta::blaze + +#endif diff --git a/vendor/blaze/src/foundation/CMakeLists.txt b/vendor/blaze/src/foundation/CMakeLists.txt index 33b479e01..99b8d7896 100644 --- a/vendor/blaze/src/foundation/CMakeLists.txt +++ b/vendor/blaze/src/foundation/CMakeLists.txt @@ -4,10 +4,10 @@ include(./known_resolver.cmake) sourcemeta_library(NAMESPACE sourcemeta PROJECT blaze NAME foundation FOLDER "Blaze/Foundation" - PRIVATE_HEADERS bundle.h walker.h frame.h error.h + PRIVATE_HEADERS walker.h error.h types.h vocabularies.h SOURCES foundation.cc vocabularies.cc known_walker.cc - frame.cc walker.cc bundle.cc format.cc helpers.h + walker.cc helpers.h "${CMAKE_CURRENT_BINARY_DIR}/known_resolver.cc") if(BLAZE_INSTALL) diff --git a/vendor/blaze/src/foundation/foundation.cc b/vendor/blaze/src/foundation/foundation.cc index 2a3fb6ddc..e93b713e6 100644 --- a/vendor/blaze/src/foundation/foundation.cc +++ b/vendor/blaze/src/foundation/foundation.cc @@ -683,112 +683,6 @@ auto sourcemeta::blaze::schema_keyword_priority( return std::max(priority_from_dependencies, priority_from_order_dependencies); } -auto sourcemeta::blaze::wrap(const std::string_view identifier) - -> sourcemeta::core::JSON { - auto result{sourcemeta::core::JSON::make_object()}; - // JSON Schema 2020-12 is the first dialect that truly supports cross-dialect - // references In practice, others do, but we can play it safe here - result.assign_assume_new( - "$schema", - sourcemeta::core::JSON{"https://json-schema.org/draft/2020-12/schema"}); - result.assign_assume_new("$ref", sourcemeta::core::JSON{identifier}); - return result; -} - -auto sourcemeta::blaze::wrap( - const sourcemeta::core::JSON &schema, - const sourcemeta::blaze::SchemaFrame &frame, - const sourcemeta::blaze::SchemaFrame::Location &location, - const sourcemeta::blaze::SchemaResolver &resolver, - sourcemeta::core::WeakPointer &base) -> sourcemeta::core::JSON { - assert(frame.mode() == SchemaFrame::Mode::References); - assert(location.type != SchemaFrame::LocationType::Pointer); - - const auto &pointer{location.pointer}; - if (pointer.empty()) { - auto copy = schema; - if (copy.is_object()) { - copy.assign("$schema", sourcemeta::core::JSON{location.dialect}); - } - - return copy; - } - - assert(try_get(schema, pointer)); - const auto has_internal_references{ - std::any_of(frame.references().cbegin(), frame.references().cend(), - [&pointer](const auto &reference) { - return reference.first.second.starts_with(pointer); - })}; - - if (!has_internal_references) { - auto subschema{sourcemeta::core::get(schema, pointer)}; - if (subschema.is_object()) { - subschema.assign("$schema", sourcemeta::core::JSON{location.dialect}); - } - - return subschema; - } - - auto copy = schema; - copy.assign("$schema", sourcemeta::core::JSON{location.dialect}); - - auto result{sourcemeta::core::JSON::make_object()}; - // JSON Schema 2020-12 is the first dialect that truly supports - // cross-dialect references In practice, others do, but we can - // play it safe here - result.assign_assume_new( - "$schema", - sourcemeta::core::JSON{"https://json-schema.org/draft/2020-12/schema"}); - // We need to make sure the schema we are wrapping always has an identifier, - // at least an artificial one, otherwise a standalone instance of `$schema` - // outside of the root of a schema resource is not valid according to - // JSON Schema - // However, note that we use a relative URI so that references to - // other schemas whose top-level identifiers are relative URIs don't - // get affected. Otherwise, we would cause unintended base resolution. - constexpr std::string_view WRAPPER_IDENTIFIER{"__sourcemeta-core-wrap__"}; - const auto maybe_id{identify(copy, resolver, location.dialect)}; - const auto id{maybe_id.empty() ? WRAPPER_IDENTIFIER : maybe_id}; - - sourcemeta::core::URI uri{id}; - - try { - reidentify(copy, id, resolver, location.dialect); - - // Otherwise we will get an error with the `WRAPPER_IDENTIFIER`, which will - // be confusing to end users - } catch (const SchemaReferenceObjectResourceError &) { - throw SchemaError( - "Cannot process a JSON Schema Draft 7 or older with a top-level " - "`$ref` (which overrides sibling keywords) without introducing " - "undefined behavior"); - } - - result.assign_assume_new("$defs", sourcemeta::core::JSON::make_object()); - result.at("$defs").assign_assume_new("schema", std::move(copy)); - - // Add a reference to the schema - if (!uri.fragment().has_value() || uri.fragment().value().empty()) { - uri.fragment(sourcemeta::core::to_string(pointer)); - result.assign_assume_new("$ref", sourcemeta::core::JSON{uri.recompose()}); - } else { - static const sourcemeta::core::JSON::String DEFS{"$defs"}; - static const sourcemeta::core::JSON::String SCHEMA{"schema"}; - result.assign_assume_new( - "$ref", - sourcemeta::core::JSON{ - sourcemeta::core::to_uri(sourcemeta::core::WeakPointer{ - std::cref(DEFS), std::cref(SCHEMA)} - .concat(pointer)) - .recompose()}); - } - - static const sourcemeta::core::JSON::String REF{"$ref"}; - base.push_back(REF); - return result; -} - static auto parse_schema_type_string(const sourcemeta::core::JSON::String &type, sourcemeta::core::JSON::TypeSet &result) -> void { diff --git a/vendor/blaze/src/foundation/helpers.h b/vendor/blaze/src/foundation/helpers.h index 2f3b1f373..8a3d3b197 100644 --- a/vendor/blaze/src/foundation/helpers.h +++ b/vendor/blaze/src/foundation/helpers.h @@ -75,6 +75,9 @@ ref_overrides_adjacent_keywords(const SchemaBaseDialect base_dialect) -> bool { case SchemaBaseDialect::JSON_Schema_Draft_4_Hyper: case SchemaBaseDialect::JSON_Schema_Draft_3: case SchemaBaseDialect::JSON_Schema_Draft_3_Hyper: + case SchemaBaseDialect::JSON_Schema_Draft_2_Hyper: + case SchemaBaseDialect::JSON_Schema_Draft_1_Hyper: + case SchemaBaseDialect::JSON_Schema_Draft_0_Hyper: return true; default: return false; diff --git a/vendor/blaze/src/foundation/include/sourcemeta/blaze/foundation.h b/vendor/blaze/src/foundation/include/sourcemeta/blaze/foundation.h index 33d9ddabb..4f8cc5cd4 100644 --- a/vendor/blaze/src/foundation/include/sourcemeta/blaze/foundation.h +++ b/vendor/blaze/src/foundation/include/sourcemeta/blaze/foundation.h @@ -9,9 +9,7 @@ #include // NOLINTBEGIN(misc-include-cleaner) -#include #include -#include #include #include // NOLINTEND(misc-include-cleaner) @@ -418,93 +416,6 @@ auto vocabularies(const SchemaResolver &resolver, const SchemaBaseDialect base_dialect, std::string_view dialect) -> Vocabularies; -/// @ingroup foundation -/// -/// Format a JSON Schema document by reordering all object properties throughout -/// the entire document according to an opinionated JSON Schema aware ordering. -/// This function modifies the schema in-place. For example: -/// -/// ```cpp -/// #include -/// #include -/// #include -/// #include -/// -/// sourcemeta::core::JSON schema = -/// sourcemeta::core::parse_json( -/// "{ \"type\": \"string\", \"minLength\": 3 }"); -/// sourcemeta::blaze::format(schema, sourcemeta::blaze::schema_walker, -/// sourcemeta::blaze::schema_resolver); -/// std::ostringstream stream; -/// sourcemeta::core::prettify(schema, stream); -/// std::cout << stream.str() << std::endl; -/// ``` -SOURCEMETA_BLAZE_FOUNDATION_EXPORT -auto format(sourcemeta::core::JSON &schema, const SchemaWalker &walker, - const SchemaResolver &resolver, - std::string_view default_dialect = "") -> void; - -/// @ingroup foundation -/// -/// Given a schema identifier, this function creates a JSON Schema wrapper that -/// references such schema. This is useful when trying to validate an instance -/// against a specific subset of a schema, as the wrapper allows you to make use -/// of JSON Schema referencing to get there without reinventing the wheel. For -/// example: -/// -/// ```cpp -/// #include -/// #include -/// #include -/// -/// const sourcemeta::core::JSON result = -/// sourcemeta::blaze::wrap("https://www.example.com#/foo/bar"); -/// -/// sourcemeta::core::prettify(result, std::cerr); -/// std::cerr << "\n"; -/// ``` -SOURCEMETA_BLAZE_FOUNDATION_EXPORT -auto wrap(std::string_view identifier) -> sourcemeta::core::JSON; - -/// @ingroup foundation -/// -/// Wrap a schema to only access one of its subschemas. This is useful if you -/// want to perform validation on only a specific part of the schema without -/// having to reinvent the wheel. For example: -/// -/// ```cpp -/// #include -/// #include -/// #include -/// -/// const sourcemeta::core::JSON document = -/// sourcemeta::core::parse_json(R"JSON({ -/// "$schema": "https://json-schema.org/draft/2020-12/schema", -/// "items": { "type": "string" } -/// })JSON"); -/// -/// sourcemeta::blaze::SchemaFrame frame{ -/// sourcemeta::blaze::SchemaFrame::Mode::References}; -/// frame.analyse(document, sourcemeta::blaze::schema_walker, -/// sourcemeta::blaze::schema_resolver); -/// -/// const auto location{frame.traverse( -/// sourcemeta::core::WeakPointer{"items"}, -/// sourcemeta::blaze::SchemaFrame::LocationType::Subschema)}; -/// -/// sourcemeta::core::WeakPointer base; -/// const sourcemeta::core::JSON result = -/// sourcemeta::blaze::wrap(document, frame, location.value().get(), -/// sourcemeta::blaze::schema_resolver, base); -/// -/// sourcemeta::core::prettify(result, std::cerr); -/// std::cerr << "\n"; -/// ``` -SOURCEMETA_BLAZE_FOUNDATION_EXPORT -auto wrap(const sourcemeta::core::JSON &schema, const SchemaFrame &frame, - const SchemaFrame::Location &location, const SchemaResolver &resolver, - sourcemeta::core::WeakPointer &base) -> sourcemeta::core::JSON; - /// @ingroup foundation /// /// Parse the value of a JSON Schema `type` keyword (which can be a string or diff --git a/vendor/blaze/src/foundation/include/sourcemeta/blaze/foundation_error.h b/vendor/blaze/src/foundation/include/sourcemeta/blaze/foundation_error.h index 9a3283211..d9a20ad31 100644 --- a/vendor/blaze/src/foundation/include/sourcemeta/blaze/foundation_error.h +++ b/vendor/blaze/src/foundation/include/sourcemeta/blaze/foundation_error.h @@ -218,62 +218,6 @@ class SOURCEMETA_BLAZE_FOUNDATION_EXPORT SchemaKeywordError const char *message_; }; -/// @ingroup foundation -/// An error that represents a schema frame error -class SOURCEMETA_BLAZE_FOUNDATION_EXPORT SchemaFrameError - : public std::exception { -public: - SchemaFrameError(const std::string_view identifier, const char *message) - : identifier_{identifier}, message_{message} {} - - [[nodiscard]] auto what() const noexcept -> const char * override { - return this->message_; - } - - [[nodiscard]] auto identifier() const noexcept -> std::string_view { - return this->identifier_; - } - -private: - std::string identifier_; - const char *message_; -}; - -/// @ingroup foundation -/// An error that represents a schema anchor collision error -class SOURCEMETA_BLAZE_FOUNDATION_EXPORT SchemaAnchorCollisionError - : public std::exception { -public: - SchemaAnchorCollisionError(const std::string_view identifier, - sourcemeta::core::Pointer location, - sourcemeta::core::Pointer other) - : identifier_{identifier}, location_(std::move(location)), - other_(std::move(other)) {} - - [[nodiscard]] auto what() const noexcept -> const char * override { - return "Schema anchor already exists"; - } - - [[nodiscard]] auto identifier() const noexcept -> std::string_view { - return this->identifier_; - } - - [[nodiscard]] auto location() const noexcept - -> const sourcemeta::core::Pointer & { - return this->location_; - } - - [[nodiscard]] auto other() const noexcept - -> const sourcemeta::core::Pointer & { - return this->other_; - } - -private: - std::string identifier_; - sourcemeta::core::Pointer location_; - sourcemeta::core::Pointer other_; -}; - #if defined(_MSC_VER) #pragma warning(default : 4251 4275) #endif diff --git a/vendor/blaze/src/frame/CMakeLists.txt b/vendor/blaze/src/frame/CMakeLists.txt new file mode 100644 index 000000000..606d2f04d --- /dev/null +++ b/vendor/blaze/src/frame/CMakeLists.txt @@ -0,0 +1,17 @@ +sourcemeta_library(NAMESPACE sourcemeta PROJECT blaze NAME frame + FOLDER "Blaze/Frame" + PRIVATE_HEADERS error.h + SOURCES frame.cc helpers.h) + +if(BLAZE_INSTALL) + sourcemeta_library_install(NAMESPACE sourcemeta PROJECT blaze NAME frame) +endif() + +target_link_libraries(sourcemeta_blaze_frame PUBLIC + sourcemeta::core::json) +target_link_libraries(sourcemeta_blaze_frame PUBLIC + sourcemeta::core::jsonpointer) +target_link_libraries(sourcemeta_blaze_frame PUBLIC + sourcemeta::blaze::foundation) +target_link_libraries(sourcemeta_blaze_frame PRIVATE + sourcemeta::core::uri) diff --git a/vendor/blaze/src/foundation/frame.cc b/vendor/blaze/src/frame/frame.cc similarity index 99% rename from vendor/blaze/src/foundation/frame.cc rename to vendor/blaze/src/frame/frame.cc index 353488fac..a8e4a4ee7 100644 --- a/vendor/blaze/src/foundation/frame.cc +++ b/vendor/blaze/src/frame/frame.cc @@ -1,3 +1,5 @@ +#include + #include #include "helpers.h" diff --git a/vendor/blaze/src/frame/helpers.h b/vendor/blaze/src/frame/helpers.h new file mode 100644 index 000000000..75bf31b5d --- /dev/null +++ b/vendor/blaze/src/frame/helpers.h @@ -0,0 +1,89 @@ +#ifndef SOURCEMETA_BLAZE_FRAME_HELPERS_H +#define SOURCEMETA_BLAZE_FRAME_HELPERS_H + +#include + +#include // assert +#include // std::string_view + +namespace sourcemeta::blaze { + +inline auto id_keyword(const SchemaBaseDialect base_dialect) + -> std::string_view { + switch (base_dialect) { + case SchemaBaseDialect::JSON_Schema_2020_12: + case SchemaBaseDialect::JSON_Schema_2020_12_Hyper: + case SchemaBaseDialect::JSON_Schema_2019_09: + case SchemaBaseDialect::JSON_Schema_2019_09_Hyper: + case SchemaBaseDialect::JSON_Schema_Draft_7: + case SchemaBaseDialect::JSON_Schema_Draft_7_Hyper: + case SchemaBaseDialect::JSON_Schema_Draft_6: + case SchemaBaseDialect::JSON_Schema_Draft_6_Hyper: + return "$id"; + case SchemaBaseDialect::JSON_Schema_Draft_4: + case SchemaBaseDialect::JSON_Schema_Draft_4_Hyper: + case SchemaBaseDialect::JSON_Schema_Draft_3: + case SchemaBaseDialect::JSON_Schema_Draft_3_Hyper: + case SchemaBaseDialect::JSON_Schema_Draft_2_Hyper: + case SchemaBaseDialect::JSON_Schema_Draft_1_Hyper: + case SchemaBaseDialect::JSON_Schema_Draft_0_Hyper: + return "id"; + } + + assert(false); + return "$id"; +} + +inline auto definitions_keyword(const SchemaBaseDialect base_dialect) + -> std::string_view { + switch (base_dialect) { + case SchemaBaseDialect::JSON_Schema_2020_12: + case SchemaBaseDialect::JSON_Schema_2020_12_Hyper: + case SchemaBaseDialect::JSON_Schema_2019_09: + case SchemaBaseDialect::JSON_Schema_2019_09_Hyper: + return "$defs"; + case SchemaBaseDialect::JSON_Schema_Draft_7: + case SchemaBaseDialect::JSON_Schema_Draft_7_Hyper: + case SchemaBaseDialect::JSON_Schema_Draft_6: + case SchemaBaseDialect::JSON_Schema_Draft_6_Hyper: + case SchemaBaseDialect::JSON_Schema_Draft_4: + case SchemaBaseDialect::JSON_Schema_Draft_4_Hyper: + case SchemaBaseDialect::JSON_Schema_Draft_3: + case SchemaBaseDialect::JSON_Schema_Draft_3_Hyper: + return "definitions"; + case SchemaBaseDialect::JSON_Schema_Draft_2_Hyper: + case SchemaBaseDialect::JSON_Schema_Draft_1_Hyper: + case SchemaBaseDialect::JSON_Schema_Draft_0_Hyper: + return ""; + } + + assert(false); + return "$defs"; +} + +// In older drafts, the presence of `$ref` would override any sibling keywords +// See +// https://json-schema.org/draft-07/draft-handrews-json-schema-01#rfc.section.8.3 +inline auto +ref_overrides_adjacent_keywords(const SchemaBaseDialect base_dialect) -> bool { + switch (base_dialect) { + case SchemaBaseDialect::JSON_Schema_Draft_7: + case SchemaBaseDialect::JSON_Schema_Draft_7_Hyper: + case SchemaBaseDialect::JSON_Schema_Draft_6: + case SchemaBaseDialect::JSON_Schema_Draft_6_Hyper: + case SchemaBaseDialect::JSON_Schema_Draft_4: + case SchemaBaseDialect::JSON_Schema_Draft_4_Hyper: + case SchemaBaseDialect::JSON_Schema_Draft_3: + case SchemaBaseDialect::JSON_Schema_Draft_3_Hyper: + case SchemaBaseDialect::JSON_Schema_Draft_2_Hyper: + case SchemaBaseDialect::JSON_Schema_Draft_1_Hyper: + case SchemaBaseDialect::JSON_Schema_Draft_0_Hyper: + return true; + default: + return false; + } +} + +} // namespace sourcemeta::blaze + +#endif diff --git a/vendor/blaze/src/foundation/include/sourcemeta/blaze/foundation_frame.h b/vendor/blaze/src/frame/include/sourcemeta/blaze/frame.h similarity index 95% rename from vendor/blaze/src/foundation/include/sourcemeta/blaze/foundation_frame.h rename to vendor/blaze/src/frame/include/sourcemeta/blaze/frame.h index dbfdb4e5f..7481f64db 100644 --- a/vendor/blaze/src/foundation/include/sourcemeta/blaze/foundation_frame.h +++ b/vendor/blaze/src/frame/include/sourcemeta/blaze/frame.h @@ -1,16 +1,28 @@ -#ifndef SOURCEMETA_BLAZE_FOUNDATION_FRAME_H_ -#define SOURCEMETA_BLAZE_FOUNDATION_FRAME_H_ +#ifndef SOURCEMETA_BLAZE_FRAME_H_ +#define SOURCEMETA_BLAZE_FRAME_H_ -#ifndef SOURCEMETA_BLAZE_FOUNDATION_EXPORT -#include +/// @defgroup frame Frame +/// @brief A static analysis pass that maps URIs and pointers to subschemas. +/// +/// This functionality is included as follows: +/// +/// ```cpp +/// #include +/// ``` + +#ifndef SOURCEMETA_BLAZE_FRAME_EXPORT +#include #endif +// NOLINTBEGIN(misc-include-cleaner) +#include +// NOLINTEND(misc-include-cleaner) + +#include + #include #include -#include -#include - #include // std::invocable #include // std::uint8_t #include // std::reference_wrapper @@ -25,7 +37,7 @@ namespace sourcemeta::blaze { -/// @ingroup foundation +/// @ingroup frame /// /// This class performs a static analysis pass on the input schema, computing /// things such as the static identifiers and references of a schema. @@ -55,7 +67,7 @@ namespace sourcemeta::blaze { /// sourcemeta::blaze::schema_walker, /// sourcemeta::blaze::schema_resolver); /// ``` -class SOURCEMETA_BLAZE_FOUNDATION_EXPORT SchemaFrame { +class SOURCEMETA_BLAZE_FRAME_EXPORT SchemaFrame { public: /// The mode of framing. More extensive analysis can be compute and memory /// intensive @@ -100,7 +112,7 @@ class SOURCEMETA_BLAZE_FOUNDATION_EXPORT SchemaFrame { // definition), can shadow an alias defined even on a different namespace. #pragma GCC diagnostic ignored "-Wshadow" #endif - /// @ingroup foundation + /// @ingroup frame /// The type of a location frame enum class LocationType : std::uint8_t { Resource, diff --git a/vendor/blaze/src/frame/include/sourcemeta/blaze/frame_error.h b/vendor/blaze/src/frame/include/sourcemeta/blaze/frame_error.h new file mode 100644 index 000000000..0cbd333d1 --- /dev/null +++ b/vendor/blaze/src/frame/include/sourcemeta/blaze/frame_error.h @@ -0,0 +1,85 @@ +#ifndef SOURCEMETA_BLAZE_FRAME_ERROR_H +#define SOURCEMETA_BLAZE_FRAME_ERROR_H + +#ifndef SOURCEMETA_BLAZE_FRAME_EXPORT +#include +#endif + +#include + +#include // std::exception +#include // std::string +#include // std::string_view +#include // std::move + +namespace sourcemeta::blaze { + +// Exporting symbols that depends on the standard C++ library is considered +// safe. +// https://learn.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-level-2-c4275?view=msvc-170&redirectedfrom=MSDN +#if defined(_MSC_VER) +#pragma warning(disable : 4251 4275) +#endif + +/// @ingroup frame +/// An error that represents a schema frame error +class SOURCEMETA_BLAZE_FRAME_EXPORT SchemaFrameError : public std::exception { +public: + SchemaFrameError(const std::string_view identifier, const char *message) + : identifier_{identifier}, message_{message} {} + + [[nodiscard]] auto what() const noexcept -> const char * override { + return this->message_; + } + + [[nodiscard]] auto identifier() const noexcept -> std::string_view { + return this->identifier_; + } + +private: + std::string identifier_; + const char *message_; +}; + +/// @ingroup frame +/// An error that represents a schema anchor collision error +class SOURCEMETA_BLAZE_FRAME_EXPORT SchemaAnchorCollisionError + : public std::exception { +public: + SchemaAnchorCollisionError(const std::string_view identifier, + sourcemeta::core::Pointer location, + sourcemeta::core::Pointer other) + : identifier_{identifier}, location_(std::move(location)), + other_(std::move(other)) {} + + [[nodiscard]] auto what() const noexcept -> const char * override { + return "Schema anchor already exists"; + } + + [[nodiscard]] auto identifier() const noexcept -> std::string_view { + return this->identifier_; + } + + [[nodiscard]] auto location() const noexcept + -> const sourcemeta::core::Pointer & { + return this->location_; + } + + [[nodiscard]] auto other() const noexcept + -> const sourcemeta::core::Pointer & { + return this->other_; + } + +private: + std::string identifier_; + sourcemeta::core::Pointer location_; + sourcemeta::core::Pointer other_; +}; + +#if defined(_MSC_VER) +#pragma warning(default : 4251 4275) +#endif + +} // namespace sourcemeta::blaze + +#endif diff --git a/vendor/blaze/src/output/CMakeLists.txt b/vendor/blaze/src/output/CMakeLists.txt index dc5a79336..486d47cf9 100644 --- a/vendor/blaze/src/output/CMakeLists.txt +++ b/vendor/blaze/src/output/CMakeLists.txt @@ -16,6 +16,8 @@ target_link_libraries(sourcemeta_blaze_output PUBLIC sourcemeta::core::jsonpointer) target_link_libraries(sourcemeta_blaze_output PUBLIC sourcemeta::blaze::foundation) +target_link_libraries(sourcemeta_blaze_output PUBLIC + sourcemeta::blaze::frame) target_link_libraries(sourcemeta_blaze_output PUBLIC sourcemeta::blaze::evaluator) diff --git a/vendor/blaze/src/output/include/sourcemeta/blaze/output_trace.h b/vendor/blaze/src/output/include/sourcemeta/blaze/output_trace.h index 75aca202f..8e8c05231 100644 --- a/vendor/blaze/src/output/include/sourcemeta/blaze/output_trace.h +++ b/vendor/blaze/src/output/include/sourcemeta/blaze/output_trace.h @@ -5,12 +5,13 @@ #include #endif +#include #include +#include + #include #include -#include - #include // std::uint8_t #include // std::function, std::reference_wrapper #include // std::optional, std::nullopt diff --git a/vendor/blaze/src/output/output_trace.cc b/vendor/blaze/src/output/output_trace.cc index eb9fd0bfb..6f8e23e89 100644 --- a/vendor/blaze/src/output/output_trace.cc +++ b/vendor/blaze/src/output/output_trace.cc @@ -1,3 +1,4 @@ +#include #include #include diff --git a/vendor/blaze/src/test/test_parser.cc b/vendor/blaze/src/test/test_parser.cc index 6f0c25774..465c5a717 100644 --- a/vendor/blaze/src/test/test_parser.cc +++ b/vendor/blaze/src/test/test_parser.cc @@ -10,6 +10,18 @@ #include // std::move namespace { +inline auto wrap_identifier(const std::string_view identifier) + -> sourcemeta::core::JSON { + auto result{sourcemeta::core::JSON::make_object()}; + // JSON Schema 2020-12 is the first dialect that truly supports cross-dialect + // references In practice, others do, but we can play it safe here + result.assign_assume_new( + "$schema", + sourcemeta::core::JSON{"https://json-schema.org/draft/2020-12/schema"}); + result.assign_assume_new("$ref", sourcemeta::core::JSON{identifier}); + return result; +} + inline auto TEST_ERROR_IF( bool condition, const sourcemeta::core::PointerPositionTracker &tracker, const sourcemeta::core::Pointer &pointer, const char *message) -> void { @@ -158,7 +170,7 @@ auto TestSuite::parse(const sourcemeta::core::JSON &document, test_suite.schemas_exhaustive.reserve(test_suite.targets.size()); for (const auto &target : test_suite.targets) { - const auto target_schema{sourcemeta::blaze::wrap(target)}; + const auto target_schema{wrap_identifier(target)}; try { test_suite.schemas_fast.push_back(compile( diff --git a/vendor/core/cmake/common/compiler/options.cmake b/vendor/core/cmake/common/compiler/options.cmake index 37754283d..d43bb2d91 100644 --- a/vendor/core/cmake/common/compiler/options.cmake +++ b/vendor/core/cmake/common/compiler/options.cmake @@ -1,3 +1,13 @@ +# Applies the shared compiler defaults to at . +# +# Flag categories handled here: +# - Diagnostics (-W...): always on, every config +# - Language semantics (-fwrapv, -fstrict-aliasing, -fno-rtti on GCC): always on +# - Optimization-related (loop unrolling, vectorization, fast-math relaxations): +# gated to non-Debug configs because they have no effect at -O0 but still +# cost Clang/GCC pipeline time +# +# Do not add optimization-only flags here without a generator-expression gate function(sourcemeta_add_default_options visibility target) if(SOURCEMETA_COMPILER_MSVC) # See https://learn.microsoft.com/en-us/cpp/build/reference/compiler-options-listed-by-category @@ -40,22 +50,28 @@ function(sourcemeta_add_default_options visibility target) $<$,$>:-Wnon-virtual-dtor> $<$,$>:-Woverloaded-virtual> $<$,$>:-Winvalid-offsetof> - -funroll-loops + # Semantics, not optimization: keep on for every config -fstrict-aliasing - -ftree-vectorize - - # To improve how much GCC/Clang will vectorize + # Assume that signed arithmetic overflow of addition, subtraction and + # multiplication wraps around using twos-complement representation + # See https://users.cs.utah.edu/~regehr/papers/overflow12.pdf + # See https://www.postgresql.org/message-id/1689.1134422394@sss.pgh.pa.us + -fwrapv + # Fast-math relaxations relax IEEE conformance (errno after math.h, + # signed-zero handling, reassociation), so they affect observable + # behavior and must apply to every config to keep Debug and Release + # semantics aligned -fno-math-errno -fno-trapping-math -fno-signed-zeros -freciprocal-math -fassociative-math - # Assume that signed arithmetic overflow of addition, subtraction and - # multiplication wraps around using twos-complement representation - # See https://users.cs.utah.edu/~regehr/papers/overflow12.pdf - # See https://www.postgresql.org/message-id/1689.1134422394@sss.pgh.pa.us - -fwrapv) + # Optimization-only: emitted only when not building Debug. At -O0 these + # run analyses that never reach codegen, costing build time for no + # behavioral effect + $<$>:-funroll-loops> + $<$>:-ftree-vectorize>) endif() if(SOURCEMETA_COMPILER_LLVM) @@ -81,9 +97,9 @@ function(sourcemeta_add_default_options visibility target) -Wrange-loop-analysis # Enable loop vectorization for performance reasons - -fvectorize + $<$>:-fvectorize> # Enable vectorization of straight-line code for performance - -fslp-vectorize) + $<$>:-fslp-vectorize>) elseif(SOURCEMETA_COMPILER_GCC) target_compile_options("${target}" ${visibility} # Newer versions of GCC (i.e. 14) seem to print a lot of false-positives here diff --git a/vendor/core/cmake/common/targets/googletest.cmake b/vendor/core/cmake/common/targets/googletest.cmake index 176d2872b..39cb9f22f 100644 --- a/vendor/core/cmake/common/targets/googletest.cmake +++ b/vendor/core/cmake/common/targets/googletest.cmake @@ -18,6 +18,14 @@ function(sourcemeta_googletest) target_link_libraries("${TARGET_NAME}" PRIVATE GTest::gtest GTest::gmock GTest::gtest_main) + + # Test executables are not shipped, so LTO buys nothing and significantly + # slows the link step (GCC's LTRANS phase serializes per executable) + if(SOURCEMETA_COMPILER_LLVM OR SOURCEMETA_COMPILER_GCC) + target_compile_options("${TARGET_NAME}" PRIVATE -fno-lto) + target_link_options("${TARGET_NAME}" PRIVATE -fno-lto) + endif() + add_test(NAME "${SOURCEMETA_GOOGLETEST_PROJECT}.${SOURCEMETA_GOOGLETEST_NAME}" COMMAND "${TARGET_NAME}" --gtest_brief=1) endfunction()