Skip to content

feat(overrides): GIR override system — replace hardcoded exclusion lists with sexp files#89

Merged
chris-armstrong merged 25 commits intomainfrom
manual-overrides
Apr 5, 2026
Merged

feat(overrides): GIR override system — replace hardcoded exclusion lists with sexp files#89
chris-armstrong merged 25 commits intomainfrom
manual-overrides

Conversation

@chris-armstrong
Copy link
Copy Markdown
Owner

@chris-armstrong chris-armstrong commented Apr 4, 2026

Summary

Implements a declarative s-expression override system for the GIR code generator, replacing all hardcoded exclusion lists scattered across exclude_list.ml, filtering.ml, and gir_parser.ml with per-namespace configuration files in ocgtk/overrides/.

  • Phase 1: Override types (override_types.ml) and s-expression parser (override_parser.ml) with full unit test coverage
  • Phase 2: Override application (override_apply.ml) integrated into the gir_gen generate and gir_gen references pipelines; version overrides feed directly into C #if version guards
  • Phase 3: New gir_gen overrides subcommand extracts "Since X.Y" version annotations from GIR <doc> text; generate-bindings.sh updated to wire -o override files into all 9 namespace generations; initial ocgtk/overrides/ files created; GIR parser extended to read version XML attributes on <member>/<field> elements natively
  • Phase 4: All hardcoded lists removed from source; ignored types moved to the correct namespace override file (SettingsBackendgio.sexp, BroadwayRenderer/NglRenderergsk.sexp, PixbufNonAnim/PixbufModule/PixbufModulePatterngdkpixbuf.sexp, print-related GTK types stay in gtk.sexp)
  • Phase 5: [@@deriving sexp] added to override types; 9 round-trip sexp tests; end-to-end smoke test parsing synthetic GIR XML, applying overrides, and asserting on results
  • Phase 6: Per-member #if/#else version guards in enum/bitfield C converters and _decls.h declaration headers; 9 unit tests
  • Phase 7: Documentation — README_GIR_GEN.md documents the overrides command, -o flag, and override file format; scripts/README.md updated for all 9 namespaces and override workflow; README.md and CLAUDE.md updated with override file references; architecture docs in ocgtk/architecture/ updated with pipeline diagram and override system design
  • Bug fix: render_version_component was emitting (NAME (version "X.Y")) but the parser's parse_components_of_kind requires (member NAME ...) / (field NAME ...) as the first atom — all member version entries were silently skipped, producing no version guards. Fixed by adding a ~kind parameter to the render function.
  • Override file fix: 250+ bare-name member entries in gio.sexp, pango.sexp, graphene.sexp, gdkpixbuf.sexp corrected to (member NAME ...) / (field NAME ...) form
  • Merge safety: gir_gen overrides now reads any existing override file before writing, preserving all (ignore) entries and only replacing version annotations with fresh GIR data — re-running the command no longer loses manual edits
  • Regenerated: ml_gio_enums_gen.c, ml_pango_enums_gen.c, ml_graphene_enums_gen.c, ml_gdkpixbuf_enums_gen.c updated with correct per-member version guards

Test plan

  • cd ocgtk && dune build — clean build
  • cd ocgtk && xvfb-run dune runtest — all tests pass
  • bash scripts/generate-bindings.sh — no warnings, all 9 namespaces generate cleanly
  • Confirmed deleted types (SettingsBackend, BroadwayRenderer, PixbufModule, etc.) no longer appear in generated output
  • Confirmed version guards appear in ml_gsk_enums_gen.c and gsk_decls.h (e.g. #if GTK_CHECK_VERSION(4,14,0) around GskFillRule)
  • Confirmed version guards appear in ml_gio_enums_gen.c (168 guards), ml_pango_enums_gen.c, ml_graphene_enums_gen.c, ml_gdkpixbuf_enums_gen.c

🤖 Generated with Claude Code

chris-armstrong and others added 25 commits April 4, 2026 16:12
- override_types.ml/mli: variant types (override_action, component_override,
  class_override, interface_override, record_override, enum_override,
  bitfield_override, library_overrides) with deriving eq
- override_parser.ml/mli: s-expression parser with duplicate detection,
  version validation, context-disambiguated (function ...) handling
- types.ml: member_version, flag_version, field_version fields on
  gir_enum_member, gir_bitfield_member, gir_record_field
- gir_parser.ml: initialize new version fields to None
- test_override_types.ml: 12 tests for type construction and equality
- test_override_parser.ml: 23 tests covering all entity types, error cases,
  duplicate detection, ignore/version overrides
- Fixed ppxlib 0.35.0 API changes (Ptyp_alias, Pexp_function)
- override_apply module: applies parsed overrides to GIR data structures
  (entity/component ignore, version overrides, unknown-name warnings)
- 24 unit tests covering all entity types, edge cases, warnings
- gir_gen generate: -o flag to load override files before generation
- gir_gen references: -o flag to filter ignored entities from references
- Overrides applied after parsing, before type-mapping context is built
- override_apply: extract apply_components_by_name generic helper
  (single-pass filter_map replacing separate filter+map passes)
- override_apply: extract check_unknown_entity_names generic helper
  replacing 5 near-identical check_unknown_*_names functions
- override_apply: extract apply_*_components helpers per entity type
- override_apply: fix entity-level Set_version from no-op to actually
  setting the entity version field before applying component overrides
- override_apply: fix = Some Ignore structural equality → exhaustive
  pattern matching on entity action in all 5 apply_*_overrides
- gir_gen: fix List.length > 0 → <> [] (O(1) check)
- gir_gen: add comment explaining ~functions:[] is correct
- overrides.md: document entity-level version behaviour (Design Decision #2),
  mark Phase 2 tasks complete with implementation notes

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tial override files

- Add `gir_gen overrides` subcommand to extract Since version annotations
  from GIR doc text into override sexp files; also reads version XML
  attribute directly from <member>/<flag>/<field> elements (fixing GDK
  and other namespaces that use XML attrs rather than doc text)
- Add override_extractor.ml with extract_since_version (Re regex) for
  testability; used by generate_overrides in gir_gen.ml
- Fix gir_parser.ml: member_version, flag_version, field_version were
  hardcoded None — now read from the element's version XML attribute
- Fix override_apply.ml: warn_unknown_components checked the filtered
  (post-ignore) entity list causing false-positive warnings for
  successfully-ignored items; now checks the original entity list
- Add parse_doc_text helper in gir_parser.ml that skips nested XML
  elements instead of failing, enabling robust doc text extraction
- Add test_override_extractor.ml with 10 tests (unit + integration)
- Update generate-bindings.sh to pass -o override file to all 9
  references and generate commands
- Add overrides/ directory with initial sexp files for all 9 namespaces:
  gtk.sexp migrated from hardcoded exclude lists; gdkpixbuf/graphene/
  gio/pango populated with Since version entries

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…or doc-text-only

The GIR parser now reads the `version` XML attribute from <member>, <flag>,
and <field> elements into member_version/flag_version/field_version fields,
so code generation can use them natively without override files.

The `gir_gen overrides` extractor remains doc-text-only (Since X.Y patterns),
since XML version attributes are already captured by the parser and don't need
to be duplicated into override sexp files.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…mplementation

- Task 1.1b: correct the claim that GIR XML never includes version attrs on
  member/field elements — some GDK4/GTK4 GIR files do use version="4.x"
- Note that gir_parser.ml now reads version XML attrs into member_version/
  flag_version/field_version natively
- Phase 3 extractor description: clarify it reads only <doc> text, not XML
  version attrs (which the parser already handles)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- override_extractor.ml: fix docstring — regex matches literal 'Since' (capital S,
  no prefix), not 'Available since' or lowercase variants; clarify this explicitly
- test_override_extractor.ml: add record_field_doc integration test covering the
  parse_doc_text call site in parse_record_contents (was not previously tested)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ride files

- exclude_list.ml: remove variadic_function_exclude_list, is_variadic_function,
  platform_specific_type_exclude_list, is_platform_specific_type,
  type_name_exclude_list, is_excluded_type_name, function_exclude_list,
  is_excluded_function, should_skip_class — all migrated to override sexp files
- gir_parser.ml: remove is_platform_specific_type guards from parse_enumeration
  and parse_bitfield — previously-skipped enums/bitfields now parsed and filtered
  by the override system
- filtering.ml: remove is_excluded_type_name check from has_simple_type;
  remove property_exclude_list and its check from should_generate_property;
  remove method_has_excluded_type; remove is_excluded_function and
  is_variadic_function (from Exclude_list) from should_skip_method_binding;
  remove banned_records from should_skip_private_record; simplify
  should_generate_class and should_generate_interface
- library_module.ml: remove should_skip_class filter (overrides handle this)
- layer1_property.ml: remove is_excluded_type_name check from
  has_property_type_mapping
- Override files updated: gdkpixbuf.sexp (PixbufNonAnim, PixbufModule,
  PixbufModulePattern), gio.sexp (SettingsBackend), gsk.sexp (BroadwayRenderer,
  NglRenderer) — each ignored in their correct namespace
- Deleted stale generated files for now-ignored types

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Types that existed on main and compiled fine were incorrectly excluded.
The exclusions were premature — the old hardcoded lists weren't fully
preventing generation of these types (stale files remained in the repo).

Un-ignored (now generated):
- GTK: PageSetup, PrintContext, PrintOperation, PrintSettings,
  PrintDialog, PrintOperationPreview (interface)

Kept ignored (genuinely not in public headers / require special setup):
- GTK: PrintBackend (GtkPrintBackend not in public headers),
  PrintJob, PrintUnixDialog, PageSetupUnixDialog, Printer,
  PrintCapabilities (bitfield), License (enum)
- GIO: SettingsBackend (requires G_SETTINGS_ENABLE_BACKEND define)
- GSK: BroadwayRenderer, NglRenderer (not in standard public headers)
- GdkPixbuf: PixbufNonAnim, PixbufModule, PixbufModulePattern
  (GdkPixbufModule/Pattern not in public gdk-pixbuf.h)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Files created during intermediate regeneration passes (before ignore
entries were finalised) left unreferenced stale files. Remove them:
- gdkpixbuf: gPixbuf_module/pattern/non_anim, pixbuf_module/pattern/non_anim,
  ml_pixbuf_module/pattern/non_anim_gen.c
- gio: gSettings_backend, settings_backend, ml_settings_backend_gen.c
- gsk: gBroadway_renderer, broadway_renderer, gNgl_renderer, ngl_renderer,
  ml_broadway_renderer/ngl_renderer_gen.c

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Generalise the five per-entity apply functions into a single
apply_entity_overrides helper, eliminating ~100 lines of repetitive code.

Also replace the temp-file round-trip in parse_overrides_from_string with
a direct Sexp.of_string call.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add [@@deriving sexp] to override_types.ml/.mli (previously only eq).
Add 9 round-trip tests: serialise library_overrides with sexplib,
deserialise back, verify structural equality. Covers all entity types.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add test_override_e2e.ml: parses a synthetic GIR XML, applies programmatic
overrides (ignore Button class, ignore Widget.create method), and asserts
on the apply_overrides result. Verifies ignored_entities list, method
filtering, and absence of spurious warnings.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…notes

Document the apply_entity_overrides generic helper refactor and the
parse_overrides_from_string cleanup. Add implementation note to Task 5.1
about [@@deriving sexp] being deferred from Phase 1 and the round-trip
using sexplib serialisation (not the human-format parser).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tfield converters

Add emit_member_branch helper to enum_code.ml. Update
generate_c_enum_converters and generate_c_bitfield_converters to accept
~class_version and emit #if/#else caml_failwith/#endif guards around
individual members/flags when member_version is set and newer than the
class version. Update generate_enum_files in gir_gen.ml to pass
~class_version to both converter functions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… in enum converters

Add test_enum_member_version.ml: 9 tests covering no-guard baseline,
member-version-only guard (with #else caml_failwith), class-guard
suppression (member_version <= class_version), member-guard emission
(member_version > class_version), and bitfield equivalents.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…, scripts/README, README, CLAUDE

- README_GIR_GEN.md: add `overrides` as a third command, document -o/--overrides flag for generate and references commands, add Override System section with format reference, workflow, and examples
- scripts/README.md: rewrite to cover all 9 namespaces, describe two-phase generation with -o flag, add Override files section
- README.md: add link to overrides/ directory in Key Documents
- CLAUDE.md: update single-library regeneration example to include -o flag, add note about override files

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- docs/plans/overrides.md: mark all phases complete (1-7); replace Phase 7 task stubs with what was actually implemented
- architecture/README.md: update pipeline diagram to show override_parser/apply stage; add override modules to Core Modules table; add Override System section with design summary; fix cross-namespace reference file paths
- architecture/gir_gen/overrides.md: new doc covering pipeline position, type model, parser decisions, apply logic, version guard behaviour, and update workflow

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ge safety

- render_version_component: add ~kind parameter, emit (member/field NAME (version X.Y))
  instead of bare (NAME (version X.Y)) — the parser requires the kind keyword as
  the first sexp atom to match parse_components_of_kind
- Fix gio.sexp, pango.sexp, graphene.sexp, gdkpixbuf.sexp: convert 250+ bare-name
  member entries to (member NAME ...) / (field NAME ...) form
- generate_overrides: merge with existing file instead of overwriting — preserves
  all (ignore) entries, replaces only version annotations with fresh GIR data
- Regenerate ml_gio_enums_gen.c, ml_pango_enums_gen.c, ml_graphene_enums_gen.c,
  ml_gdkpixbuf_enums_gen.c with correct per-member version guards

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ries)

Previously parse_components_of_kind silently skipped any sexp whose first
atom did not match the expected kind — causing bare-name entries like
(foo (version "X.Y")) to be silently discarded instead of reported.

Add validate_body_elements which runs once per entity body and fails with
Unknown_component_kind on any list-form element whose leading atom is not
a valid kind for that entity type (or the action markers ignore/version).

Valid kinds per entity:
- class:       constructor, method, property, signal
- interface:   method, property, signal
- record:      field, constructor, method, function
- enumeration: member, function
- bitfield:    member

Add Unknown_component_kind constructor to parse_error (ml + mli) and four
new parser tests covering: bare-name member in enum, bare-name member in
bitfield, wrong kind (field) in class, wrong kind (signal) in record.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@chris-armstrong chris-armstrong merged commit 31ecce7 into main Apr 5, 2026
2 checks passed
@chris-armstrong chris-armstrong deleted the manual-overrides branch April 5, 2026 06:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant