Add "includes" to Nodl schema - #93
Conversation
5b11b7a to
f1d06a2
Compare
emersonknapp
left a comment
There was a problem hiding this comment.
Is this approximately equivalent to #92 ? I was making progress on it, if slowly 😅
Is this actually a prerequisite for code module generation? I don't really think so... You just generate the part that is specified. Includes are necessary for testing and doc generation, from our discussion I don't think generators have to know about includes, at least not at first.
|
I came to the conclusion that the best way to model the base class was via an includes pattern rather than a separate explict "base" key. Either way we need some sort of key to signify "use this node as the base node". I think its better to do it as an includes because for the purpose of "description" (everything other than codegen) - the node base isnt special, its just another thing that gets included in the node interface. Then for code generation we can have a separate way to determine what "type" of include it is - i was thinking a sidecar file with generation specific things in it. This way we arent tying any of our schema to a specific code generation constraint, and different code generators can ask for different things from their sidecars. This is somewhat described here: https://github.com/ros-tooling/nodl/pull/93/changes#diff-3cc7cfeabd380765b91f376f5dd549227d856362b62ec5743eb50f838368c251 |
Replace the single ament_nodl_register_node macro with three layered macros per the ament-nodl-macros design doc: - ament_nodl_install: validates and installs NoDL files, registers them in the nodl_interfaces ament index resource. Idempotent via a CMake global property guard. - ament_nodl_register_executable: maps an executable name to a NoDL spec in the nodl_executables index. - ament_nodl_register_component: maps a component class to a NoDL spec in the nodl_components index. Both higher-level macros call ament_nodl_install internally and use ament_index_register_resource for index registration. Signed-off-by: Alistair English <hello@alistairenglish.com>
- Add test_ws/ with minimal ament_cmake packages (nodl_fragment_a, nodl_fragment_b) that install .nodl.yaml files via ament_nodl_install() - Add conftest.py with session-scoped fixture that builds the workspace once, sources the install, and strips outer overlays - Add smoke tests verifying packages appear on AMENT_PREFIX_PATH and NoDL files are installed - Fix ament_nodl_install.cmake: accumulate filenames and register the ament index resource once instead of per-file (avoids CMake duplicate output file error when multiple FILES are passed) - Add test README documenting the setup Signed-off-by: Alistair English <hello@alistairenglish.com>
Covers the includes mechanism, schema change, API layering, ament index registration (all three resource types), and codegen sidecars. All open questions are resolved inline. Signed-off-by: Alistair English <hello@alistairenglish.com>
Replace the hand-rolled ~50-line session fixture with two small fixtures consumed by the pytest-colcon-ws plugin: - test_ws_path: points the plugin at the embedded test workspace - test_ws_underlays: sources the outer workspace so ament_nodl is available Benefits: - Clean-shell isolation (no inherited env leakage) - Better build failure reporting via pytest.fail() - Less code to maintain (~15 lines vs ~50) The test_ws_env fixture contract (dict[str, str]) is unchanged, so test_integration.py requires no modifications. Requires pytest>=8 and pytest-colcon-ws (workspace-level pixi.toml). Signed-off-by: Alistair English <hello@alistairenglish.com>
Replace the standalone test_ament_nodl package with pytest-colcon-ws based integration tests inside ament_nodl/test/. A jig package (test_ament_nodl) in an embedded sub-workspace exercises all three CMake macros: ament_nodl_install, ament_nodl_register_executable, and ament_nodl_register_component. Changes: - Add ament_nodl/test/ with conftest, four test files, and a test_ws containing the test_ament_nodl jig package - Add ament_cmake_pytest, python3-pytest, and pytest-colcon-ws as test dependencies in ament_nodl/package.xml - Add ament_add_pytest_test calls in ament_nodl/CMakeLists.txt - Update nodl_schema/test/conftest.py to use environment-based underlay discovery via AMENT_PREFIX_PATH instead of a hardcoded relative path - Add pytest-colcon-ws test_depend to nodl_schema/package.xml - Delete the standalone test_ament_nodl package Signed-off-by: Alistair English <hello@alistairenglish.com>
ament_nodl_install claimed to be idempotent but calling it more than once per package (e.g. directly and via ament_nodl_register_executable) caused CMake to fail with 'Files to be generated by multiple different commands' because each invocation independently called ament_index_register_resource(nodl_interfaces ...). Root cause: the ament_register_extension() call was inside ament_nodl_install(), a function(). Since ament_register_extension is a macro() that does list(APPEND ...), the append targeted a local variable copy that was discarded when the function returned. The ament_package() hook was never actually registered, and each ament_nodl_install call fell through to its own ament_index_register_resource. Fix: defer the nodl_interfaces registration to ament_package() time via a hook registered in ament_nodl-extras.cmake.in (which runs at find_package scope). ament_nodl_install now only accumulates filenames into a global property; the new ament_nodl_package_hook.cmake collects them into a single ament_index_register_resource call. Signed-off-by: Alistair English <hello@alistairenglish.com>
Package was deleted as a top-level package and now only exists as a nested test fixture at ament_nodl/test/test_ws/src/test_ament_nodl/, causing all CI jobs to fail with 'Package test_ament_nodl specified with --packages-up-to was not found'. Signed-off-by: Alistair English <hello@alistairenglish.com>
I know its a bit of a hack, eventually ill get the package released Signed-off-by: Alistair English <hello@alistairenglish.com>
- Install pluggy with --ignore-installed to bypass the Debian-managed pluggy 1.4 on noble (jazzy/kilted) which pip cannot uninstall. - Pin pytest>=8,<9 for launch_testing compatibility on Humble, Jazzy, and Kilted (pytest 9 removed the deprecated 'path' arg used by launch_testing's pytest_pycollect_makemodule hook). - Add <test_depend>ament_nodl</test_depend> to nodl_schema so colcon sources ament_nodl's install prefix during tests, making it available on AMENT_PREFIX_PATH for the integration test conftest. Signed-off-by: Alistair English <hello@alistairenglish.com>
Eliminate the circular dependency between ament_nodl (exec_depend on nodl_schema) and nodl_schema (test_depend on ament_nodl) by merging them into a single ament_cmake_python package. ament_nodl contained no Python code — just four CMake files whose sole runtime dependency was nodl_schema. They are two halves of the same thing, split prematurely. The merged nodl_schema package: - Uses ament_cmake_python to install both the Python package and the CMake macros - Keeps all ament_nodl_*() macro names unchanged - Ships nodl_schema-extras.cmake.in so find_package(nodl_schema) brings the macros into scope - Consolidates both test suites and test workspaces Downstream migration: - find_package(ament_nodl) -> find_package(nodl_schema) - <buildtool_depend>ament_nodl -> <buildtool_depend>nodl_schema - Macro calls (ament_nodl_install, etc.) are unchanged - Python API and CLI are unchanged See docs/design/merge-nodl-schema-ament-nodl.md for rationale. Signed-off-by: Alistair English <hello@alistairenglish.com>
Jammy's pip 22 has a build-isolation bug that causes pyproject.toml [project] metadata to be ignored, producing an empty UNKNOWN-0.0.0 wheel. Upgrading pip first avoids this. The upgrade is allowed to fail (|| true) because Noble's Debian-installed pip has no RECORD file. Also consolidate the pip install into a single --ignore-installed line so both pluggy and pytest (which also lack RECORD files on Noble) are handled together. Signed-off-by: Alistair English <hello@alistairenglish.com>
Add 'includes' property to nodl.schema.yaml: a list of objects each with a required 'ref' string constrained to either a nodl:// package URI or a relative file path (./ or ../) ending in .nodl.yaml. Add IncludeRef model and includes field to NodlDocument in models.py. Phase 1 of the composition plan — structural validation only, no resolution or merging. Signed-off-by: Alistair English <hello@alistairenglish.com>
- load_nodl() now takes a Path, reads the file, validates against the schema, recursively resolves relative includes, merges interfaces, and returns a NodlDocument with includes stripped. - Merge semantics: local declarations win over includes, identical duplicates across includes are silently deduplicated, conflicting duplicates raise ValueError. - Diamond and circular include graphs handled via canonical-path dedup. - CLI entry points (python -m nodl_schema, ros2 nodl validate) use validate() consistently for schema-only checking. - Removed str/bytes/IO overloads from load_nodl() — Path is the only supported input type. Signed-off-by: Alistair English <hello@alistairenglish.com>
Add _resolve_package_uri() to resolve nodl://package/name refs to <prefix>/share/<package>/nodl/<name>.nodl.yaml using ament_index_python.get_package_prefix(). Enrich test workspace fragment NoDL files with actual interface content (publishers, parameters) so integration tests can assert on merged results. Add 4 integration tests exercising single-package resolution, cross-package merging, and error paths for missing packages/files. Signed-off-by: Alistair English <hello@alistairenglish.com>
Confirm the end-to-end path: python -m nodl_schema accepts well-formed
includes entries and rejects malformed ones (bare strings instead of
{ref: ...} objects). No new implementation needed — the CLI already
calls validate() which uses the schema updated in Phase 1.
Signed-off-by: Alistair English <hello@alistairenglish.com>
_merge_parameters() was reused for the include-accumulation step in _load_and_resolve(), passing the accumulator as the 'local' argument. The 'local wins' branch silently skipped conflicting parameters from subsequent includes instead of raising an error. Add _accumulate_parameters() — a strict merge helper with no local-wins semantics — and use it for the accumulation step. Identical duplicates are still deduplicated; differing definitions now correctly raise ValueError. Add 16 tests covering merge-if-identical and merge-conflict for all section types (subscriptions, service_servers, service_clients, action_servers, action_clients, parameters), QoS-difference conflicts, and local-wins for parameters. Signed-off-by: Alistair English <hello@alistairenglish.com>
Signed-off-by: Alistair English <hello@alistairenglish.com>
Relative file includes (./ ../) break when an installed nodl:// reference contains its own relative includes — the paths resolve against the installed location, not the original source tree. Remove relative path support entirely. Packages that need to reference their own schemas can self-reference via nodl://. Changes: - Schema regex now only accepts nodl://package/name - Collapse _resolve_package_uri into _resolve_ref (single code path) - load_nodl() accepts Path | dict since source path is no longer needed for include resolution - Drop base_dir threading and source path seeding of seen set - Move merge-semantics tests from unit tests to integration tests using installed test workspace fragments - Add 21 test fragment files across nodl_fragment_a and nodl_fragment_b to cover merge, dedup, conflict, diamond, nested, and circular cases Signed-off-by: Alistair English <hello@alistairenglish.com>
1758739 to
8d7567e
Compare
The first step in code generation is the includes field - so that we can represent "externally defined" things like node base types or interfaces we want to inherit.
My thinking about this is documented in docs/design/nodl-composition.md.
Includes field
Added a new optional
includesfield to the NoDL schema, enabling interface composition:load_nodl()recursively resolves relative paths andnodl://package URIs (via ament index), merges interfaces, and returns a single resolved documentCmake macros (see docs/design/ament-nodl-macros.md)
Replace the single
ament_nodl_register_nodemacro with three layered macros:ament_nodl_install- validates and installs.nodl.yamlfiles, registers them in thenodl_interfacesament indexament_nodl_register_executable- maps an executable to a NoDL spec (nodl_executablesindex)ament_nodl_register_component- maps a component class to a NoDL spec (nodl_componentsindex)Testing / package merge
To support testing nodl_schema:
pytest-colcon-wswith embedded sub-workspacesament_nodlintonodl_schemaKey files to look at: