[WIP] JSON schema generation#723
Open
ad-cqc wants to merge 18 commits into
Open
Conversation
Merged
Replaces the hand-written JSON schema files under scripts/schema/ with a build-time pipeline that emits a single config-schema.json from reflect-cpp-annotated C++ types under palace/schema/types/. Descriptions are transcribed verbatim from PR #716 so generated docs stay in sync with the configuration structs by construction. - palace/schema/utils/: schema-generator utilities (reflect-cpp post-emit passes for defaults, required pruning, additionalProperties, TaggedUnion tag descriptions, anyOf to oneOf rewrite). - palace/schema/types/: annotated mirrors of Palace's config structs. - palace/schema/emit_schema.cpp: standalone helper invoked by CMake to produce build/generated/schema/config-schema.json, which is then embedded via the existing embed_schema.cmake helper. - cmake/EmbedSchema.cmake: rewritten to drive palace_emit_schema. - palace/utils/jsonschema.cpp: drop the external-$ref branch; the single-file schema resolves refs in-document via $defs. - scripts/schema/: hand-written per-section JSONs removed. Bumps CMAKE_CXX_STANDARD to 20 (reflect-cpp floor). Runtime config parsing is untouched in this phase.
Closes the three gaps between Palace's generated schema and PR #716's hand-written version: - Per-enum-value descriptions: `PALACE_SCHEMA_ENUM(E, (V, "desc"), ...)` declares an `enum class` and the matching `enum_descriptions<E>` specialization in one place; the post-emit `inject_enum_descriptions` pass rewrites `{"type":"string","enum":[...]}` to `{"oneOf":[{"const":V,"description":"..."},...]}`. Empty descriptions opt an enumerator out of the prose (bare `{"const": V}`). - x-palace-advanced / x-palace-deprecated: new `PALACE_SCHEMA_DESC_ADVANCED` / `_DEPRECATED` macros attach the flag at the field declaration via a `FieldFlagTag<"FieldName">`-keyed hidden friend; the walker finds it via ADL on the enclosing struct and the `inject_custom_keywords` pass stamps `x-palace-<flag>: true` onto the matching property. 53 advanced + 4 deprecated fields now land correctly. - Root-level conditionals: emit_schema.cpp parses a verbatim copy of PR 716's `allOf`/`if`/`then` block (driven/eigenmode/transient implying the matching `Solver.<Mode>` requirement) and splices it into the root. The block isn't expressible from the C++ types alone.
Introduce `version.hpp` with a `schema_version` constant following the SchemaVer format (MODEL-REVISION-ADDITION), decoupling schema compatibility tracking from `PROJECT_VERSION`. Remove the `PALACE_VERSION` compile definition from the emit_schema target and use the new constant directly instead.
…om $defs - cmake/EmbedSchema.cmake: after palace_emit_schema runs, copy the generated config-schema.json into scripts/schema/ via copy_if_different. The copy is part of the default build (palace_schema_file ALL target), so the committed schema stays in sync with the annotated types. Downstream tooling (docs, Julia validator) consumes the stable scripts/schema/ path; embedding still reads the build-tree copy. The existing check-config GitHub Actions job validates example configs against this file unchanged. - SchemaOptions gains `defs_prefix`. When non-empty, a final `strip_defs_prefix` pass renames each `$defs` entry and rewrites every matching `$ref` string to drop the prefix. emit_schema.cpp sets it to `"palace__schema__"`, so the published schema carries short names (`ProblemData`, `LinearSolverData`, ...) instead of reflect-cpp's fully-qualified `palace__schema__ProblemData`. - scripts/schema/config-schema.json is the baseline generated output — committed alongside the types so `./scripts/validate-config` and ValidateConfig.jl work out of the box.
Replace the previous "prune unneeded from required" heuristic with an
explicit allow-list: fields marked PALACE_SCHEMA_DESC_REQUIRED (and
PALACE_SCHEMA_TAG for tagged-union discriminators) are the only entries
in each `$defs` required array. The marker is expressed as a subtype of
rfl::Description — DescRequired / DescAdvanced / DescDeprecated — so
detection is a pure compile-time type trait, with no hidden-friend or
ADL tricks and no PALACE_SCHEMA_SELF boilerplate on the struct.
Also:
- Drop `default` emission on $ref-bearing properties; the referenced
$defs entry already carries defaults on its own fields.
- Flatten rfl::Validator-induced `allOf: [{minimum,type},{maximum,type}]`
into a single `{minimum, maximum, type}` body.
- Introduce schema_oneof_required<T> trait and inject_oneof_required
pass; specialize for LumpedPort / SurfaceCurrent to match PR 716's
"exactly one of Attributes or Elements" item constraint.
- Remove std::optional usage from annotated types; every field has a
concrete default, optional-ness is expressed only through the
absence of PALACE_SCHEMA_DESC_REQUIRED.
- Strip the `Data` suffix from all struct names (Boundary, Model,
LumpedPort, ...); three collisions with existing enums
(SurfaceFlux, InterfaceDielectric, LinearSolver) are renamed to
SurfaceFluxProbe, DielectricInterface, LinearSolverConfig.
The generated config-schema.json reflects all of the above: required
arrays now match PR 716 per-type, every validator property is flat,
LumpedPort / SurfaceCurrent carry the oneOf mutex, and no $ref
property carries a default.
Simplify and standardize struct names across the schema type definitions. Remove redundant suffixes (e.g. "Boundary", "Probe", "Interface") where context is already clear, and expand abbreviated names for explicitness (e.g. PalaceConfig → PalaceConfiguration). Rename enum SurfaceFlux → SurfaceFluxType to avoid collision with the similarly-named struct.
Introduce a shared `Direction` type alias
(`rfl::Variant<DirectionLabel, std::array<double, 3>>`) in
`schema/types/common.hpp`, replacing the four bare `std::string` Direction
fields across `Element`, `LumpedPort`, `SurfaceCurrent`, and `CurrentDipole`
so the emitted schema matches PR 716's `anyOf: [pattern-string, 3-array]`
shape. `DirectionLabel` is a `rfl::Pattern<"^[+\\-]?[XYZRxyzr]$", ...>`
giving the pattern-validated string arm; fields default to
`DirectionLabel("+X")`.
Teach the type-graph walker about `rfl::Variant` so it recurses into
alternatives without treating the variant itself as a reflectable
aggregate (which would default-construct the validated-string arm against
an empty value and throw).
Add a marker-driven alias-hoisting pass: specialize
`schema_alias_name<T>` with a non-empty `value` and the post-emit
`inject_field_aliases` pass promotes every matching field's inline body
into a shared `$defs[alias]` entry, leaving
`{"$ref": "#/$defs/<alias>", ...}` at the field site with
`description`/`default` preserved as siblings. Applied to `Direction` so
all four use sites share a single `$defs/Direction` body instead of
duplicating the `anyOf` inline. Adding another alias is a two-line change:
the `using` plus a trait specialization.
Sync `scripts/schema/config-schema.json` with the new emission.
Switch DirectionLabel from rfl::Pattern (regex) to rfl::Literal
enumerating all 24 valid axis keywords. This produces a clearer
JSON schema with an explicit "enum" array instead of a regex pattern.
Default-initialize Direction fields with {} rather than an explicit
DirectionLabel("+X"), as the Literal's first alternative ("R") now
serves as the natural default.
- Add Makefile `build` target with Homebrew LLVM/OpenBLAS config and parallel build support via NPROCS - Fix LIBXSMM macOS build by passing -isysroot via CFLAGS when CMAKE_OSX_SYSROOT is set (fixes missing headers with non-Xcode compilers) - Disable MFEM test step to speed up builds - Rename Direction schema type to PortDirection to disambiguate from general direction usage - Add variant arm alias support in schema visitor for improved JSON schema codegen output
…enums
Replace the custom `x-schema-version` root key with the spec-blessed
`$id: urn:palace:schema:<version>`, placed right after `$schema`.
`PALACE_SCHEMA_ENUM` now auto-emits a `schema_alias_name<EnumName>`
specialization so every described enum hoists to its own `$defs` entry
without per-enum boilerplate.
Other changes folded into the regen:
* Rename `Domain` -> `Domains`, `LinearSolverConfig` -> `Linear` for
consistency with their schema slot names.
* `PalaceConfiguration::SchemaVersion` is now required.
* Tagged-union discriminator arms emit as `{const, description}` instead
of the verbose `{type, enum, default}` reflect-cpp default.
* New `inject_field_composition` pass handles `anyOf` -> `oneOf`
rewrites on nested variant fields (the root-level pass only touches
the top object).
* `container_inner` helper lets the variant-composition walk descend
into `vector` / `array` element types without force-instantiating
`value_type` on non-containers.
…-array items
Add `MaterialAxes` to `Material` as a fixed 3x3 array of doubles, default
identity matrix, matching PR 716's `$defs/Vector3`-backed inner-row
shape.
Make this layout possible by:
- Promoting `std::array<double, 3>` to a named
`palace::schema::Vector3` alias and switching every Cartesian field
site (Center, Translation, FloquetWaveVector, BoundingBox*,
Direction's numeric arm, ...) to use it.
- Adding `inject_nested_items_aliases`, a small post-emit pass that
rewrites `properties[field]/items` to `{"$ref": "#/$defs/<alias>"}`
when the field's container element type specializes
`schema_alias_name`. The existing `inject_field_aliases` pass only
fires at the top-level field body, so nested 3-arrays (the inner
row of `MaterialAxes`) need this targeted rewrite to share the
canonical `$defs/Vector3` entry. Reusable for any future
`vector<Vector3>` / `array<Vector3, N>` field.
3ee4ed8 to
dff9037
Compare
The enum types `AdaptiveCircuitSynthesisDomainOrthogonalization` and `Excitation` are now fully qualified with `::palace::schema::` to avoid potential name resolution conflicts with struct member names in the PALACE_SCHEMA_DESC macros.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Collapses Palace's three disconnected schema sources: the C++ structs, the hand-written JSON schemas under scripts/schema/, and PR #716's annotated schema into one C++ source of truth. The new
palace_emit_schemabinary generatesconfig-schema.jsonat build time fromreflect-cpp-annotatedmirrors of the config structs; the embed pipeline picks up the generated file and bakes it intolibpalacevia the existingembed_schema.cmakehelper. The hand-written schema files are deleted.Descriptions in the annotated types are transcribed verbatim from awslabs/palace#716, so the doc-generator output matches PR #716 by construction. CMAKE_CXX_STANDARD bumps to C++20 (
reflect-cppfloor).Test plan
libpalace+palacebuild clean.palace_emit_schemaproduces a 2655-line schema; embedded header regenerates via the CMake custom command.test/unit/test-schema.cpp(sample configs validate against the embedded schema).scripts/schema/ValidateConfig.jlagainst the build-generated path.docs/generate_config_docs.jlagainst the generated schema. The expected output should match PR 716's committed reference.md modulo trivial ordering.