feat: make test-ui checks the UI files with blueprint-compiler

The interface is hand-written GtkBuilder XML and nothing in the build can see a mistake in it: a
property a class does not have, or a child a container does not accept, is a Gtk-ERROR when the
window is built -- and Gtk-ERROR aborts the process, so a typo in a .ui takes the application down
rather than failing to open a dialog. xml-stripblanks catches malformed XML and nothing else, and
the probes catch the rest only for the windows they open. Three of those aborts happened during
this refactor.

tools/check_ui.py round-trips each file through blueprint-compiler: decompile to blueprint, then
compile that, which type-checks every property and child type against the GIR. The XML stays the
source of truth and stays hand-written -- this is a check, not a compiler, and nothing here is
needed to build the application. Which is why the target is opt-in (no ALL) and the tool is looked
for at configure time rather than required: without it the target is absent, not silently passing.

    $ make -C build test-ui
    check_ui: 5 files in src/ui
      ok    help.ui ... source-form.ui
    check_ui: 5 ok, 0 failed

Injected the exact mistake that cost a run earlier (an AdwEntryRow subtitle) and the check caught
it, with the caret and the "did you mean title?" hint, exiting non-zero. --lint prints the linter's
advice separately: it is advice, and the linter has opinions this project does not share.
This commit is contained in:
Martino Ferrari committed 2026-10-12 10:41:25 +02:00
1 parent 8073e7b3fd
commit 7a982da6c4
4 files changed
+190 -17

No files matched your search

+25
View File
@@ -102,6 +102,31 @@ add_custom_command(
set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/udpscope_ui_resources.c
PROPERTIES COMPILE_OPTIONS "-Wno-overlength-strings")
# --- the UI files: an opt-in check, never part of `cmake --build` ---------------
# The interface is hand-written GtkBuilder XML, and nothing in the build sees a mistake in it: a
# property a class does not have is a `Gtk-ERROR` when the window is built, and `Gtk-ERROR` *aborts
# the process* -- so a typo takes the application down rather than failing to open a dialog.
# `xml-stripblanks` catches malformed XML and nothing else, and the probes catch the rest only for the
# windows they open.
#
# blueprint-compiler type-checks every property and child type against the GIR, and it reads XML as
# well as blueprint (`decompile` turns one into the other), so `tools/check_ui.py` round-trips each
# file through it. The XML stays the source of truth and stays hand-written; nothing here is needed to
# build the application, which is the whole point of not converting the files.
#
# No ALL: this runs only when it is named, so `cmake --build build` is unaffected and
# blueprint-compiler is not a build dependency.
find_program(UDPSCOPE_PYTHON NAMES python3)
if(UDPSCOPE_PYTHON)
add_custom_target(test-ui
COMMAND ${UDPSCOPE_PYTHON} ${CMAKE_CURRENT_SOURCE_DIR}/tools/check_ui.py ${UDPSCOPE_UI_DIR}
DEPENDS ${UDPSCOPE_UI_FILES} ${CMAKE_CURRENT_SOURCE_DIR}/tools/check_ui.py
COMMENT "Checking the UI files with blueprint-compiler"
VERBATIM)
else()
message(WARNING "python3 was not found: the test-ui target is not available")
endif()
# --- build info: version, commit and timestamp compiled into the binary -------
# Written at configure time so add_executable can name the file, and again on every
# build so the commit and the timestamp belong to the binary that is running rather