From a51653209a6e64cb9b926f4bc1a30acd40f139ac Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Mon, 11 Jan 2021 17:41:53 -0300 Subject: [PATCH 1/7] Add generalized interface generation architecture draft proposal Signed-off-by: Michel Hidalgo --- articles/generalized_interface_generation.md | 216 +++++++++++++++++++ 1 file changed, 216 insertions(+) create mode 100644 articles/generalized_interface_generation.md diff --git a/articles/generalized_interface_generation.md b/articles/generalized_interface_generation.md new file mode 100644 index 000000000..98698f0ea --- /dev/null +++ b/articles/generalized_interface_generation.md @@ -0,0 +1,216 @@ +--- +layout: default +title: Generalized interface generation pipeline +permalink: articles/generalized_interface_generation.html +abstract: Review of the current interface generation architecture and its drawbacks. Design proposal towards a multi-language and multi-build system architecture. +published: false +author: '[Michel Hidalgo](https://github.com/hidmic)' +--- + +{:toc} + +# {{ page.title }} + +
+{{ page.abstract }} +
+ +Original Author: {{ page.author }} + +## Scope + +This article discusses a generalization of the `rosidl` interface generation pipeline to work with build system generators other than CMake and set the scene for better multi-language support in ROS. + +## Review + +### Overview + +For a system architecture-level overview and rationale for the `rosidl` interface generation pipeline, see [here](https://github.com/ros2/ros_core_documentation/blob/e0b7e2bbd026aa466b23cfc5e070144c9114fe29/source/developer_overview.rst#type-specific-interfaces). + +From a structural point-of-view, the `rosidl` interface generation pipeline is comprised of: + +- **Generator packages**. These packages provide tooling to generate language-specific, in-memory representations of ROS interfaces. Common runtime (i.e. interface agnostic) functionality is conventionally but not necessarily split into a separate package (e.g. [`rosidl_runtime_c`](https://github.com/ros2/rosidl/tree/master/rosidl_runtime_c), [`rosidl_runtime_cpp`](https://github.com/ros2/rosidl/tree/master/rosidl_runtime_cpp)). It is not unusual for a generator to use another generator's output via language-specific binding machinery (e.g. [`rosidl_generator_py`](https://github.com/ros2/rosidl_python/blob/master/rosidl_generator_py/), which uses Python C bindings to wrap [`rosidl_generator_c`](https://github.com/ros2/rosidl/tree/master/rosidl_generator_c) representations). +- **Type support packages**. These packages provide tooling to generate language-specific and often middleware-specific support code for middleware implementations to interact (e.g. on publish, on take) with in-memory representations of ROS interfaces. Type support functionality is encapsulated in `rosidl_message_type_support_t`, `rosidl_service_type_support_t`, and `rosidl_action_type_support_t` C structs in read-only global storage, one for each interface type. To support middleware runtime selection, it is not unusual to find language-specific type support packages that do not provide any functionality of their own but defer to middleware-specific type supports accordingly (e.g. [`rosidl_typesupport_c`](https://github.com/ros2/rosidl_typesupport/tree/master/rosidl_typesupport_c), [`rosidl_typesupport_cpp`](https://github.com/ros2/rosidl_typesupport/tree/master/rosidl_typesupport_c)). + +Packages for interface definition translation (e.g. [`rosidl_adapter`](https://github.com/ros2/rosidl/tree/master/rosidl_adapter), [`rosidl_generator_dds_idl`](https://github.com/ros2/rosidl_dds/tree/master/rosidl_generator_dds_idl)), interface definition parsing (e.g. [`rosidl_parser`](https://github.com/ros2/rosidl/tree/master/rosidl_parser)), and `ament_cmake` integration (e.g. [`rosidl_cmake`](https://github.com/ros2/rosidl/tree/master/rosidl_cmake/rosidl_cmake)) complement and support generator and type support packages implementation. + +Alongside generated source code, generator and type support packages are expected to provide the means to build it by adding to the `rosidl_generate_idl_interfaces`' [`ament` extension point](https://index.ros.org/doc/ros2/Tutorials/Ament-CMake-Documentation/#adding-to-extension-points). By tapping into this extension, downstream `ament_cmake` packages can delegate interface generation to all available packages in the workspace (or any of its underlays). + +### Drawbacks + +As it stands, the `rosidl` interface generation pipeline: + +- **Is strongly coupled with CMake**, and in particular with `ament_cmake` machinery. There's no way for packages using different build systems or build system generators to generate their own interfaces (e.g. pure Python packages cannot generate interfaces nor extend the pipeline). There's no way for external projects using different build systems or build system generators to provide their own generator or type support packages. + +- **Favors monolithic builds**. Since interface generation output cannot be controlled at the package level (i.e. all available extensions will be invoked), one and only one package can build and install all generated artifacts. It also implies that the set of artifacts that the package provides depends on the set of generator and type support packages present in the workspace at build-time. This results in package rebuilds to extend or restrict that support (e.g. adding support for a non-core language requires a rebuild of core interface packages), and loose package versioning (e.g. a change in a generator package can induce an API/ABI break in an interface package). + +## Proposal + +### Motivation + +This proposal aims to mitigate or provide the means to mitigate all drawbacks in the current pipeline architecture. +As such, it does not fundamentally change the concepts that underpin it nor its outcome (e.g. public APIs). + +### Goals + +- Decouple tooling from all build systems and build system generators +- Standardize tooling to simplify user and developer experience +- Support tooling extensibility to encourage code share and reuse +- Enforce tooling versioning to ensure API/ABI stability and simplify deprecation cycles +- Allow tooling configuration to enable scoped builds + +### Tooling updates + +#### Source code generation + +##### Description + +Migrate generation logic to extensible, standardized tools that can compile interface definitions into language-specific type representations and middleware-specific type support code. +Support for new languages and middlewares can be added by external packages via a plugin system. + +##### Specification + +Interface type representation and type support generation CLIs are roughly equivalent but conceptually different, and thus kept separate. +This allows them to deviate from each other in the future if need be. + +*For interface type representations* +```sh +rosidl_generate ((-d|--output-directory) PATH)? ((-l|--language) IDENTIFIER(==VERSION)?)* ((-I|--include-path) PATH)* PATH+ +``` + +*For interface type supports* +```sh +rosidl_typesupport_generate ((-d|--output-directory) PATH)? ((-t|--type-support) IDENTIFIER(==VERSION)?)* ((-I|--include-path) PATH)* PATH+ +``` + +All positional arguments are paths to interface definition files. +If no output directory is specified, the current working directory is used. +If no type representation / type support generators are specified, all available generators available are used. +If a type representation / type support generator is specified but not found, the tool fails. +If a specific version of a type representation / type support generator is specified but not found, the tool fails. + +##### Implementation considerations + +Using Python allows for significant code reuse in the vast majority of existing generator and type support packages. +Additionally, [`setuptools` entrypoints](https://setuptools.readthedocs.io/en/latest/userguide/entry_point.html#advertising-behavior) can be leveraged as a plugin system. + +#### Build system configuration + +##### Description + +Migrate build logic to extensible, standardized tools that can generate [common build specifications](#a-common-build-specification) for language-specific type representations and middleware-specific type support code. +Support for new languages and middlewares can be added by external packages via a plugin system. + +##### Rationale + +Source code generators output may vary significantly. +It may be one source file or many source files in a hierarchy of directories. +To build these source files, a command or a script specific to the code generator may have to be executed. +Thus, in the most general case, build system integration is necessary. + +However, integration with any given build system or build system generator couples a large portion of the interface pipeline to that set of tools, which then has to be ported over for projects that use other build systems or build system generators. +Programs that can bridge across build systems and/or build system generators do exist, but these are rare and often limited to a subset of the functionality that is shared. + +By generating a build specification in a format designed to be simple yet general, an appropriate build system can be generated on consumption. + +##### Specification + +Interface type representation and type support build configuration CLIs are roughly equivalent but conceptually different, and thus kept separate. +This allows them to deviate from each other in the future if need be. + +*For interface type representations* +```sh +rosidl_build_configure ((-o|--output-file) PATH)? ((-l|--language) IDENTIFIER(==VERSION)?)* ((-I|--include-path) PATH)* PATH+ +``` + +*For interface type supports* +```sh +rosidl_typesupport_build_configure ((-o|--output-file) PATH)? ((-t|--type-support) IDENTIFIER(==VERSION)?)* ((-I|--include-path) PATH)* PATH+ +``` + +All positional arguments are paths to interface definition files. +If no language / type support is specified, all type / type support generators available are used. +If no type / type support generator is found, the command fails. +If no output file path is provided, the generated build specification is sent to standard output. + +##### Implementation considerations + +All build logic in existing generator and type support packages is CMake code, and thus a port is necessary. +Using Python would ensure tooling interoperability and their supporting libraries. +Additionally, [`setuptools` entrypoints](https://setuptools.readthedocs.io/en/latest/userguide/entry_point.html#advertising-behavior) can be leveraged as plugin system. + +Since common build specifications are eminently declarative, most configuration must be resolved prior to generation e.g. for which OS to build. +This includes dependencies between generated outputs e.g. type support code may depend on type representation code to build, and thus a build specification for the former must ensure the presence of the latter by conditionally generating it if not present. + +#### Meta build tool + +##### Description + +Develop an extensible, standardized tool that can translate [common build specifications](#a-common-build-specification) into an actual build system or build system generator code. +Support for new build systems and build system generators can be added by external packages via a plugin system. + +##### Specification + +```sh +ament_meta_build ((-o|--output-file) PATH)? ((-b|--build-system) IDENTIFIER)? PATH? +``` + +Its only positional argument is a path to a common build specification file. +If no common build specification file is given, it is read from standard input. +If no output file path is provided, generated build system sources are written to standard output. +If no build system is specified nor it can be deduced from output file extension, the tool fails. +If the specific build system is not supported, the tool fails. + +### Build system integration + +#### `ament_cmake` integration + +It can be achieved by a thin layer that leverages [build system configuration](#build-system-configuration) and [meta build](#meta-build-tool) tooling to bootstrap an `ament_cmake` package i.e. to generate and include CMake code during the configuration stage. +This layer must bring in package dependencies as there is no build system-agnostic mechanism to do so. + +## Appendix + +### A. Common Build Specification + +This specification is based on the [Common Package Specification](https://mwoehlke.github.io/cps/) format, a simple and relatively standard format that describes packages for downstream consumption in a build system and build tool agnostic way. + +This [patch](TODO(hidmic): draft patch) extends the aforementioned format schema to describe component builds. + +As an example, the following snippet describes a Python package `bar` that depends on a shared library `foo`, +whose sources are generated by a `foo-code` command. + +```json +{ + "Components": { + "foo-code": { + "Type": "gen", + "Command": ["/bin/touch", "foo.c", "foo.h"], + "Output": ["foo.c", "foo.h"] + }, + "foo": { + "Type": "module", + "Requires": [":foo-code"], + "Sources": ["foo.c"], + "Includes": ["foo.h"], + "Link-Libraries": ["libpython-dev.so"] + }, + "bar": { + "Type": "pymodule", + "Requires": [":foo"], + "Sources": ["bar.py"] + } + } +} +``` + +Note that the format is declarative in nature. + +### B. CLI Specification Syntax + +The syntax used to specify command line interfaces in this article takes after [Python 3 regular expression syntax](https://docs.python.org/3/library/re.html#re-syntax). + +Upper case tokens are expressions aliases, namely: + +- `IDENTIFIER` is a string of non-whitespace characters +- `VERSION` is a [semantic](https://semver.org/) version +- `PATH` is a host file system path From 512a22dd06f9161de2ab8adda06b2a0dc41b0f9b Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Mon, 18 Jan 2021 18:53:13 -0300 Subject: [PATCH 2/7] Address questions and comments. Signed-off-by: Michel Hidalgo --- articles/generalized_interface_generation.md | 72 ++++++++++++++------ 1 file changed, 51 insertions(+), 21 deletions(-) diff --git a/articles/generalized_interface_generation.md b/articles/generalized_interface_generation.md index 98698f0ea..f9390b52a 100644 --- a/articles/generalized_interface_generation.md +++ b/articles/generalized_interface_generation.md @@ -25,16 +25,16 @@ This article discusses a generalization of the `rosidl` interface generation pip ### Overview -For a system architecture-level overview and rationale for the `rosidl` interface generation pipeline, see [here](https://github.com/ros2/ros_core_documentation/blob/e0b7e2bbd026aa466b23cfc5e070144c9114fe29/source/developer_overview.rst#type-specific-interfaces). +For a system architecture-level overview and rationale for the `rosidl` interface generation pipeline, see [here](https://github.com/ros2/ros_core_documentation/blob/e0b7e2bbd026aa466b23cfc5e070144c9114fe29/source/developer_overview.rst#type-specific-interfaces) (though bear in mind this documentation has not yet been updated to reflect the fact that `rosidl` generators consume `.idl` files nowadays, see [ros2/rosidl#298](https://github.com/ros2/rosidl/pull/298)). From a structural point-of-view, the `rosidl` interface generation pipeline is comprised of: -- **Generator packages**. These packages provide tooling to generate language-specific, in-memory representations of ROS interfaces. Common runtime (i.e. interface agnostic) functionality is conventionally but not necessarily split into a separate package (e.g. [`rosidl_runtime_c`](https://github.com/ros2/rosidl/tree/master/rosidl_runtime_c), [`rosidl_runtime_cpp`](https://github.com/ros2/rosidl/tree/master/rosidl_runtime_cpp)). It is not unusual for a generator to use another generator's output via language-specific binding machinery (e.g. [`rosidl_generator_py`](https://github.com/ros2/rosidl_python/blob/master/rosidl_generator_py/), which uses Python C bindings to wrap [`rosidl_generator_c`](https://github.com/ros2/rosidl/tree/master/rosidl_generator_c) representations). +- **Generator packages**. These packages provide tooling to generate language-specific, in-memory representations of ROS interfaces. Common runtime (i.e. interface agnostic) functionality is conventionally but not necessarily split into a separate package (e.g. [`rosidl_runtime_c`](https://github.com/ros2/rosidl/tree/master/rosidl_runtime_c), [`rosidl_runtime_cpp`](https://github.com/ros2/rosidl/tree/master/rosidl_runtime_cpp)). It is not unusual for a generator to use another generator's output via language-specific binding machinery (e.g. [`rosidl_generator_py`](https://github.com/ros2/rosidl_python/blob/master/rosidl_generator_py/), which uses Python C bindings to wrap [`rosidl_generator_c`](https://github.com/ros2/rosidl/tree/master/rosidl_generator_c) representations), although this dependency is not explicit: generator packages do depend on each other but it is assumed that their generated code will all be built within the same package so that build system dependencies can be established. - **Type support packages**. These packages provide tooling to generate language-specific and often middleware-specific support code for middleware implementations to interact (e.g. on publish, on take) with in-memory representations of ROS interfaces. Type support functionality is encapsulated in `rosidl_message_type_support_t`, `rosidl_service_type_support_t`, and `rosidl_action_type_support_t` C structs in read-only global storage, one for each interface type. To support middleware runtime selection, it is not unusual to find language-specific type support packages that do not provide any functionality of their own but defer to middleware-specific type supports accordingly (e.g. [`rosidl_typesupport_c`](https://github.com/ros2/rosidl_typesupport/tree/master/rosidl_typesupport_c), [`rosidl_typesupport_cpp`](https://github.com/ros2/rosidl_typesupport/tree/master/rosidl_typesupport_c)). Packages for interface definition translation (e.g. [`rosidl_adapter`](https://github.com/ros2/rosidl/tree/master/rosidl_adapter), [`rosidl_generator_dds_idl`](https://github.com/ros2/rosidl_dds/tree/master/rosidl_generator_dds_idl)), interface definition parsing (e.g. [`rosidl_parser`](https://github.com/ros2/rosidl/tree/master/rosidl_parser)), and `ament_cmake` integration (e.g. [`rosidl_cmake`](https://github.com/ros2/rosidl/tree/master/rosidl_cmake/rosidl_cmake)) complement and support generator and type support packages implementation. -Alongside generated source code, generator and type support packages are expected to provide the means to build it by adding to the `rosidl_generate_idl_interfaces`' [`ament` extension point](https://index.ros.org/doc/ros2/Tutorials/Ament-CMake-Documentation/#adding-to-extension-points). By tapping into this extension, downstream `ament_cmake` packages can delegate interface generation to all available packages in the workspace (or any of its underlays). +Alongside generated source code, generator and type support packages are expected to provide the means to build it by adding to the `rosidl_generate_idl_interfaces`' [`ament` extension point](https://index.ros.org/doc/ros2/Tutorials/Ament-CMake-Documentation/#adding-to-extension-points). By tapping into this extension, downstream `ament_cmake` packages can delegate interface generation to all available packages in the workspace (or any of its underlays). Note that the order in which each generator runs is the order in which each generator is impored into CMake, by virtue of how `ament` extensions work. This order is further obfuscated by the discovery process that the [`rosidl_default_generators`](https://github.com/ros2/rosidl_defaults/tree/master/rosidl_default_generators) package performs. Thus, for all practical purposes this order cannot be relied on, and build system dependencies must be leveraged to establish dependencies across generators' output. ### Drawbacks @@ -42,7 +42,7 @@ As it stands, the `rosidl` interface generation pipeline: - **Is strongly coupled with CMake**, and in particular with `ament_cmake` machinery. There's no way for packages using different build systems or build system generators to generate their own interfaces (e.g. pure Python packages cannot generate interfaces nor extend the pipeline). There's no way for external projects using different build systems or build system generators to provide their own generator or type support packages. -- **Favors monolithic builds**. Since interface generation output cannot be controlled at the package level (i.e. all available extensions will be invoked), one and only one package can build and install all generated artifacts. It also implies that the set of artifacts that the package provides depends on the set of generator and type support packages present in the workspace at build-time. This results in package rebuilds to extend or restrict that support (e.g. adding support for a non-core language requires a rebuild of core interface packages), and loose package versioning (e.g. a change in a generator package can induce an API/ABI break in an interface package). +- **Favors monolithic builds**. Since interface generation output cannot be controlled at the package level (i.e. all available extensions must and will be invoked), one and only one package can build and install all generated artifacts. It also implies that the set of artifacts that the package provides depends on the set of generator and type support packages present in the workspace at build-time. This results in package rebuilds to extend or restrict that support (e.g. adding support for a non-core language requires a rebuild of core interface packages), and loose package versioning (e.g. a change in a generator package can induce an API/ABI break in an interface package). ## Proposal @@ -65,12 +65,16 @@ As such, it does not fundamentally change the concepts that underpin it nor its ##### Description -Migrate generation logic to extensible, standardized tools that can compile interface definitions into language-specific type representations and middleware-specific type support code. +Migrate generation logic to extensible, standardized tools that can compile interface definitions into language-specific type representation and middleware-specific type support code. +These tools take a set of interface definition files and generate type representation or type support source code. Support for new languages and middlewares can be added by external packages via a plugin system. +Each plugin version is expected to generate the same output for the same input over time. +Users can (but are not forced to) target specific plugin versions to prevent workspace (or underlay) updates from affecting tool output. ##### Specification -Interface type representation and type support generation CLIs are roughly equivalent but conceptually different, and thus kept separate. +Interface type representation and interface type support are conceptually different pieces of functionality. +Therefore, their generation CLIs, even if roughly equivalent, are kept separate. This allows them to deviate from each other in the future if need be. *For interface type representations* @@ -98,7 +102,8 @@ Additionally, [`setuptools` entrypoints](https://setuptools.readthedocs.io/en/la ##### Description -Migrate build logic to extensible, standardized tools that can generate [common build specifications](#a-common-build-specification) for language-specific type representations and middleware-specific type support code. +Migrate build logic to extensible, standardized tools that can generate [common build specifications](#a-common-build-specification) for language-specific type representation and middleware-specific type support code. +These tools take a set of interface definition files and generate a build tool-agnostic description (aka a [common build specification](#a-common-build-specification)) that outlines how type representation and type support code must be generated (e.g. by invoking a source code generator) and built (e.g. by building a shared library). Support for new languages and middlewares can be added by external packages via a plugin system. ##### Rationale @@ -108,14 +113,17 @@ It may be one source file or many source files in a hierarchy of directories. To build these source files, a command or a script specific to the code generator may have to be executed. Thus, in the most general case, build system integration is necessary. -However, integration with any given build system or build system generator couples a large portion of the interface pipeline to that set of tools, which then has to be ported over for projects that use other build systems or build system generators. -Programs that can bridge across build systems and/or build system generators do exist, but these are rare and often limited to a subset of the functionality that is shared. +However, integration with any given build-system (generator) A couples a large portion of the interface pipeline to it. +If a different build-system (generator) B is to be used, users are forced to either: +* **Delegate from build-system (generator) B to build-system (generator) A**. This is challenging. Build-system (generator) A must be properly embedded in build-system (generator) B, pushing and pulling information across the gap for builds and installs to succeed, adapting invocations of build-system (generator) B to any constraints that build-system (generator) A may impose (e.g. Bazel performs sandboxed builds). There is a performance penalty in doing this. +* **Port from build-system (generator) A to build-system (generator) B**. This may be (and currently is) a non-trivial effort. Programs that can convert from one build-system (generator) to another do exist, but these are rare and often limited to a subset of the functionality that is shared between source and target. By generating a build specification in a format designed to be simple yet general, an appropriate build system can be generated on consumption. ##### Specification -Interface type representation and type support build configuration CLIs are roughly equivalent but conceptually different, and thus kept separate. +Interface type representation and interface type support are conceptually different pieces of functionality. +Therefore, their build configuration CLIs, even if roughly equivalent, are kept separate. This allows them to deviate from each other in the future if need be. *For interface type representations* @@ -140,41 +148,63 @@ Using Python would ensure tooling interoperability and their supporting librarie Additionally, [`setuptools` entrypoints](https://setuptools.readthedocs.io/en/latest/userguide/entry_point.html#advertising-behavior) can be leveraged as plugin system. Since common build specifications are eminently declarative, most configuration must be resolved prior to generation e.g. for which OS to build. -This includes dependencies between generated outputs e.g. type support code may depend on type representation code to build, and thus a build specification for the former must ensure the presence of the latter by conditionally generating it if not present. +This includes dependencies between generated outputs e.g. type support code may depend on type representation code to build. +Build specification must ensure the presence of all dependencies by conditionally generating them if not present. + +There are no requirements nor constraints as to how a build is specified. +A build may specify some generic components, such as shared libraries, and it may specify an arbitrary command for execution (which could easily be a build tool, such as `cmake`). +The more generic a build specification is, the better will build-system integration be -- a build-system adapter plugin in `ament_meta_build` cannot workaround a build specification that forces `cmake` to be called. #### Meta build tool ##### Description -Develop an extensible, standardized tool that can translate [common build specifications](#a-common-build-specification) into an actual build system or build system generator code. -Support for new build systems and build system generators can be added by external packages via a plugin system. +Develop an extensible, standardized tool that can parse [common build specifications](#a-common-build-specification) and generate an actual build system or build system generator code. +Support for new build systems and build system generators can be added by external packages via a system of __build-system adapter_ plugins. ##### Specification ```sh -ament_meta_build ((-o|--output-file) PATH)? ((-b|--build-system) IDENTIFIER)? PATH? +ament_meta_build ((-o|--output-file) PATH)? (--build-prefix PATH)? (--install-prefix PATH)? ((-b|--build-system) IDENTIFIER)? PATH? ``` Its only positional argument is a path to a common build specification file. If no common build specification file is given, it is read from standard input. If no output file path is provided, generated build system sources are written to standard output. -If no build system is specified nor it can be deduced from output file extension, the tool fails. +If no build-system adapter plugin is specified nor it can be deduced from output file extension, the tool fails. +If a build prefix is specified, it is used If the specific build system is not supported, the tool fails. +##### Implementation considerations + +There are no requirements nor constraints as to whether and how any given build-system adapter plugin supports the build specification schema. +A plugin may generate native build system generator code to build some components, delegate on other plugins (and other build systems) to build some components, and not support some components at all. +The broader native support is, the lower will build overhead (usually) be and the more useful will an (open source) plugin be to the community. + ### Build system integration -#### `ament_cmake` integration +It can be achieved by adding thin layers that leverage [build system configuration](#build-system-configuration) and [meta build](#meta-build-tool) tooling to bootstrap an package builds. -It can be achieved by a thin layer that leverages [build system configuration](#build-system-configuration) and [meta build](#meta-build-tool) tooling to bootstrap an `ament_cmake` package i.e. to generate and include CMake code during the configuration stage. -This layer must bring in package dependencies as there is no build system-agnostic mechanism to do so. +For instance, an `ament_cmake` integration would generate and include CMake code during the configuration stage: + +```cmake +# Generate C++ type representation build specification +execute_process(COMMAND rosidl_build_configure --language cpp -I path/to/interface/dependencies/ interface0.idl interface1.idl OUTPUT_FILE build.spec) +# Translate build specification into CMake code +execute_process(COMMAND ament_meta_build -b cmake INPUT_FILE build.spec OUTPUT_FILE build.cmake) +# Use generate CMake code +include(build.cmake) +``` + +It is worth noting that these integration layers must bring in package dependencies on their own, as there is no build system-agnostic mechanism to do so. ## Appendix ### A. Common Build Specification -This specification is based on the [Common Package Specification](https://mwoehlke.github.io/cps/) format, a simple and relatively standard format that describes packages for downstream consumption in a build system and build tool agnostic way. - -This [patch](TODO(hidmic): draft patch) extends the aforementioned format schema to describe component builds. +This specification is based on the [Common Package Specification](https://mwoehlke.github.io/cps/) format, a simple and relatively standard format that describes packages for downstream consumption in a build tool-agnostic way. +This format was not designed to describe builds, and thus why the need to extend its schema. +This [patch](TODO(hidmic): draft patch) introduces support to describe component builds. As an example, the following snippet describes a Python package `bar` that depends on a shared library `foo`, whose sources are generated by a `foo-code` command. From 78cd763d9c1be51179ed8b30ebad04b91c95d570 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Tue, 19 Jan 2021 13:55:37 -0300 Subject: [PATCH 3/7] Add interface generation pipeline diagrams. Signed-off-by: Michel Hidalgo --- articles/generalized_interface_generation.md | 50 +++++++++++++----- .../rosidl_3_stage_pipeline.png | Bin 0 -> 17010 bytes .../rosidl_4_stage_pipeline.png | Bin 0 -> 21502 bytes 3 files changed, 36 insertions(+), 14 deletions(-) create mode 100644 img/generalized_interface_generation/rosidl_3_stage_pipeline.png create mode 100644 img/generalized_interface_generation/rosidl_4_stage_pipeline.png diff --git a/articles/generalized_interface_generation.md b/articles/generalized_interface_generation.md index f9390b52a..b09a62154 100644 --- a/articles/generalized_interface_generation.md +++ b/articles/generalized_interface_generation.md @@ -34,7 +34,11 @@ From a structural point-of-view, the `rosidl` interface generation pipeline is c Packages for interface definition translation (e.g. [`rosidl_adapter`](https://github.com/ros2/rosidl/tree/master/rosidl_adapter), [`rosidl_generator_dds_idl`](https://github.com/ros2/rosidl_dds/tree/master/rosidl_generator_dds_idl)), interface definition parsing (e.g. [`rosidl_parser`](https://github.com/ros2/rosidl/tree/master/rosidl_parser)), and `ament_cmake` integration (e.g. [`rosidl_cmake`](https://github.com/ros2/rosidl/tree/master/rosidl_cmake/rosidl_cmake)) complement and support generator and type support packages implementation. -Alongside generated source code, generator and type support packages are expected to provide the means to build it by adding to the `rosidl_generate_idl_interfaces`' [`ament` extension point](https://index.ros.org/doc/ros2/Tutorials/Ament-CMake-Documentation/#adding-to-extension-points). By tapping into this extension, downstream `ament_cmake` packages can delegate interface generation to all available packages in the workspace (or any of its underlays). Note that the order in which each generator runs is the order in which each generator is impored into CMake, by virtue of how `ament` extensions work. This order is further obfuscated by the discovery process that the [`rosidl_default_generators`](https://github.com/ros2/rosidl_defaults/tree/master/rosidl_default_generators) package performs. Thus, for all practical purposes this order cannot be relied on, and build system dependencies must be leveraged to establish dependencies across generators' output. +Alongside generated source code, generator and type support packages are expected to provide the means to build it by adding to the `rosidl_generate_idl_interfaces`' [`ament` extension point](https://index.ros.org/doc/ros2/Tutorials/Ament-CMake-Documentation/#adding-to-extension-points). By tapping into this extension, downstream `ament_cmake` packages can delegate build-system configuration to all available generator and type support packages in the workspace (or any of its underlays) during the configuration phase, so as to generate and then build source code during the build phase. This is what constitutes a (roughly) 3-stage pipeline, from interface definition files to source code to artifacts: + +![ROSIDL 3-stage pipeline](/img/generalized_interface_generation/rosidl_3_stage_pipeline.png) + +Note that the order in which each `ament` extension runs is the order in which each package was imported into CMake, by virtue of how these work. This order is further obfuscated by the discovery process that the [`rosidl_default_generators`](https://github.com/ros2/rosidl_defaults/tree/master/rosidl_default_generators) package performs. Thus, for all practical purposes this order cannot be relied on, and build-system dependencies must be leveraged to establish dependencies across generators' output. ### Drawbacks @@ -53,15 +57,25 @@ As such, it does not fundamentally change the concepts that underpin it nor its ### Goals -- Decouple tooling from all build systems and build system generators +- Decouple tooling from all build systems (generators) - Standardize tooling to simplify user and developer experience - Support tooling extensibility to encourage code share and reuse - Enforce tooling versioning to ensure API/ABI stability and simplify deprecation cycles - Allow tooling configuration to enable scoped builds +### Overview + +The first stage in the current pipeline is split in two, configuring a 4-stage pipeline. + +![ROSIDL 4-stage pipeline](/img/generalized_interface_generation/rosidl_4_stage_pipeline.png) + +In the first stage, a [common build specification](#a-common-build-specification) that outlines how type representation and type support code must be generated (e.g. by invoking a source code generator) and built (e.g. by building a shared library) is generated. +In the second stage, this specification is translated into build-system files. +Both first and second stages would occur during the configuration phase once integrated into a build-system. + ### Tooling updates -#### Source code generation +#### Source code generators ##### Description @@ -74,7 +88,7 @@ Users can (but are not forced to) target specific plugin versions to prevent wor ##### Specification Interface type representation and interface type support are conceptually different pieces of functionality. -Therefore, their generation CLIs, even if roughly equivalent, are kept separate. +Therefore, their generation CLIs, even if roughly equivalent, are kept separate. This allows them to deviate from each other in the future if need be. *For interface type representations* @@ -98,17 +112,17 @@ If a specific version of a type representation / type support generator is speci Using Python allows for significant code reuse in the vast majority of existing generator and type support packages. Additionally, [`setuptools` entrypoints](https://setuptools.readthedocs.io/en/latest/userguide/entry_point.html#advertising-behavior) can be leveraged as a plugin system. -#### Build system configuration +#### Build specification generators ##### Description Migrate build logic to extensible, standardized tools that can generate [common build specifications](#a-common-build-specification) for language-specific type representation and middleware-specific type support code. -These tools take a set of interface definition files and generate a build tool-agnostic description (aka a [common build specification](#a-common-build-specification)) that outlines how type representation and type support code must be generated (e.g. by invoking a source code generator) and built (e.g. by building a shared library). +These tools take a set of interface definition files and generate a [common build specification](#a-common-build-specification). Support for new languages and middlewares can be added by external packages via a plugin system. ##### Rationale -Source code generators output may vary significantly. +Source code generators output may vary significantly. It may be one source file or many source files in a hierarchy of directories. To build these source files, a command or a script specific to the code generator may have to be executed. Thus, in the most general case, build system integration is necessary. @@ -123,7 +137,7 @@ By generating a build specification in a format designed to be simple yet genera ##### Specification Interface type representation and interface type support are conceptually different pieces of functionality. -Therefore, their build configuration CLIs, even if roughly equivalent, are kept separate. +Therefore, their build configuration CLIs, even if roughly equivalent, are kept separate. This allows them to deviate from each other in the future if need be. *For interface type representations* @@ -143,7 +157,7 @@ If no output file path is provided, the generated build specification is sent to ##### Implementation considerations -All build logic in existing generator and type support packages is CMake code, and thus a port is necessary. +All build logic in existing generator and type support packages is CMake code, and thus a port is necessary. Using Python would ensure tooling interoperability and their supporting libraries. Additionally, [`setuptools` entrypoints](https://setuptools.readthedocs.io/en/latest/userguide/entry_point.html#advertising-behavior) can be leveraged as plugin system. @@ -185,13 +199,21 @@ The broader native support is, the lower will build overhead (usually) be and th It can be achieved by adding thin layers that leverage [build system configuration](#build-system-configuration) and [meta build](#meta-build-tool) tooling to bootstrap an package builds. -For instance, an `ament_cmake` integration would generate and include CMake code during the configuration stage: +For instance, an `ament_cmake` integration would generate and include CMake code during the configuration stage e.g.: ```cmake # Generate C++ type representation build specification -execute_process(COMMAND rosidl_build_configure --language cpp -I path/to/interface/dependencies/ interface0.idl interface1.idl OUTPUT_FILE build.spec) +execute_process( + COMMAND rosidl_build_configure + --language cpp + -I path/to/interface/dependencies/ + interface0.idl interface1.idl + OUTPUT_FILE build.spec) # Translate build specification into CMake code -execute_process(COMMAND ament_meta_build -b cmake INPUT_FILE build.spec OUTPUT_FILE build.cmake) +execute_process( + COMMAND ament_meta_build -b cmake + INPUT_FILE build.spec + OUTPUT_FILE build.cmake) # Use generate CMake code include(build.cmake) ``` @@ -237,10 +259,10 @@ Note that the format is declarative in nature. ### B. CLI Specification Syntax -The syntax used to specify command line interfaces in this article takes after [Python 3 regular expression syntax](https://docs.python.org/3/library/re.html#re-syntax). +The syntax used to specify command line interfaces in this article takes after [Python 3 regular expression syntax](https://docs.python.org/3/library/re.html#re-syntax). Upper case tokens are expressions aliases, namely: - `IDENTIFIER` is a string of non-whitespace characters -- `VERSION` is a [semantic](https://semver.org/) version +- `VERSION` is a [semantic](https://semver.org/) version - `PATH` is a host file system path diff --git a/img/generalized_interface_generation/rosidl_3_stage_pipeline.png b/img/generalized_interface_generation/rosidl_3_stage_pipeline.png new file mode 100644 index 0000000000000000000000000000000000000000..2221e02cc368789fbade269da4e326d15b89e0de GIT binary patch literal 17010 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⩔Vq4RCairDX`Jg)2Fl{7W>O}}HS zvR?b>kTKGzRwMEdI1Z@HrAa=lAdU)>91WzVbzLlm5<(O^6_V;&J$2 z5FSx<#Mg|nv_3yTSk<;#l8qFF2ln0%x7O;RZ$fcV5UL#LYmpkCJaE%&^=u8WYqB0? zxHO5T?cXDO8u5AkB_vPDNekJUji8jT>88|A9uSa*#t5}dGV3Y?svAQ0>DxC3K%UbF zkw(>j@Ar2v-!zgb?jF!oP~RcHS^X=vra3l^1mQXz4~-?2~+7!{bO~2}y9s1vco_Fth`4d{WBJT)}=PIrwT~Ra1NJU~j zkE_TWq{2`+T?AE;qr(PLLVxu=NY>=@l+P-!>Vxq7yfeqv;!K=H1NtF7?x1YkEe}eq zs)9%`$j)FSsBXf<&O~!K@HA!-ZO}ep!lL3kDFCv1kF*ck<>GR>g4y@PL>9W0?(3HyrMz-F&&KOLnjXCK2af`!Tm}kZich3wBr-z~iWt6wTev z=aGwER*2;O;^9B7$YA)vPX5yW)jiAY1?oBJ`(xaIT@pk$M&)CUa%~G)*hpG9gX!pa z&(n(JM!6^t;RR)@KKEaTeVIR0J^xDT`pL0f;WLNjb{`45@W~#iMF>I;k6NgTQq=Fh z`5KPE`2$mr!byU-UnsWeQSGXsIp54KP8a7#&{PUO!*`yyF>VHE5BV^lIZkWfeV!Y7 z4-z0v@+YR;Op5@Own#*NyLX2^=AbW0!5YoB>Z$e9kkI*a93TnmIc@VuQvGpWYb-dd z&)ZFMkQtV*50`6pWS^kjPz}lCxhAv$3Sy=q$&&OGCL48R7jf54)XoIZ!@UPCNikot zM~^i~znyr7SSPoWVLNNMWWu@`-AL>8^N}*KJX;#46fh>K3s5Fo_QLF)pS!FY{jv9I zdy{*!7!j*9J24{>^1;2v|L-p^=2ez=Szpa$w)bXDs+CUu|2^~X`i%r;azQmOSlJ~k zP@!qHHD=c#67(C0+ckdXqW}A+wGe?kTF=x?YP~rEIGsj(y{jDQ>JM2iVmoiWmN-)H zXw=z>W9=z(M4izme}__!0xn!yXduB=?LO3ha!YcZ7OW1M7|ZZ>B(`UXyo!r11JyDt z@EdrPp_vmG_Alh_rpY8B+CW?XyuEq-TwJV0p;~q8eKEf(lp8+#lnJeYdd?PKTli(3 zi>)NJ<=dK#yXHdm_4guQu-~w=4wIgCU$jlk&4r3f!yPQKsZ+r)c?2B56zA4~1#zSE z423F8HW#*sxo+b4?$9=Qw@>9idfP;<&P~%y)uvTWv}8AK%#_w4JgERIV&#Wp?KidK ztl+Gch&g3_2?;vHJV!V}2P#@k8xV{LT9PAVt?5O1ph57)HaNT>ZsniT;OrO|X_0SE zbV3j?2b8U~-cWpVCmNQ&*%ZXqA+m|6iz}39-8<}^jN25l_pT1Vvu9h0)_l-7Z;`R2 zCOc-x3gqaIP{PLI-^1{crwem}@Ef|io_k^XBfBAcu}n6J{IDC=$6BRNb?GXMG}zGD zGHnhOaG{?zzg;Hbb!=;6Wx0e>aE0S0(J>}7enK`lHkGpF`fZDH}pmZd1pr@B~bR`p|cjenn_^K?Sfqbw6p zC8B!l=kW-TD(r1S^bwpFNm&18%{Sb$nr_6Q4_VTR)g{QdV>_F*SpMW4ccJ4pWdeFO z!6IMn7Tgq~YZw1Uuzrm#wlK5FYs85zCBpGwzS4KbdCaJ#Uva80pS@%3qB>2~3m-Q0 zEo_4_4>>4}0`&f~&JnIIn7TsIw%_*Q!$#K4jyUfGVH{$P>W_SFS+N^{ZX>Ly;g)etP_S^tYGzqz;-pXL0b6VuuvoBkfwyp~)|j VoyNnQu^sR^dEEY31^L4De*nMMB3J+b literal 0 HcmV?d00001 From 155dea287cd1e39999f92b0ad34596f815987f22 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Mon, 25 Jan 2021 08:59:42 -0300 Subject: [PATCH 4/7] Update CBS appendix. Signed-off-by: Michel Hidalgo --- articles/generalized_interface_generation.md | 34 ++------------------ 1 file changed, 3 insertions(+), 31 deletions(-) diff --git a/articles/generalized_interface_generation.md b/articles/generalized_interface_generation.md index b09a62154..398257052 100644 --- a/articles/generalized_interface_generation.md +++ b/articles/generalized_interface_generation.md @@ -224,38 +224,10 @@ It is worth noting that these integration layers must bring in package dependenc ### A. Common Build Specification -This specification is based on the [Common Package Specification](https://mwoehlke.github.io/cps/) format, a simple and relatively standard format that describes packages for downstream consumption in a build tool-agnostic way. -This format was not designed to describe builds, and thus why the need to extend its schema. -This [patch](TODO(hidmic): draft patch) introduces support to describe component builds. - -As an example, the following snippet describes a Python package `bar` that depends on a shared library `foo`, -whose sources are generated by a `foo-code` command. - -```json -{ - "Components": { - "foo-code": { - "Type": "gen", - "Command": ["/bin/touch", "foo.c", "foo.h"], - "Output": ["foo.c", "foo.h"] - }, - "foo": { - "Type": "module", - "Requires": [":foo-code"], - "Sources": ["foo.c"], - "Includes": ["foo.h"], - "Link-Libraries": ["libpython-dev.so"] - }, - "bar": { - "Type": "pymodule", - "Requires": [":foo"], - "Sources": ["bar.py"] - } - } -} -``` +The Common Build Specification is a mechanism for describing how to build and distribute a software package, such that any build tool can carry out the process. The draft specification can be found [here](https://github.com/hidmic/cps). -Note that the format is declarative in nature. +This specification is based on the [Common Package Specification](https://mwoehlke.github.io/cps/) format, a simple and relatively standard format that describes packages for downstream consumption in a build tool-agnostic way. +The CPS format was not designed to describe builds, and thus why the need to modify it. ### B. CLI Specification Syntax From 5e7e8e7d773b39345c7de1a7904727b1467d1e6e Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Mon, 25 Jan 2021 10:20:35 -0300 Subject: [PATCH 5/7] Update draft proposal Signed-off-by: Michel Hidalgo --- articles/generalized_interface_generation.md | 538 +++++++++++++++++-- 1 file changed, 480 insertions(+), 58 deletions(-) diff --git a/articles/generalized_interface_generation.md b/articles/generalized_interface_generation.md index 398257052..97ec271ae 100644 --- a/articles/generalized_interface_generation.md +++ b/articles/generalized_interface_generation.md @@ -29,24 +29,36 @@ For a system architecture-level overview and rationale for the `rosidl` interfac From a structural point-of-view, the `rosidl` interface generation pipeline is comprised of: -- **Generator packages**. These packages provide tooling to generate language-specific, in-memory representations of ROS interfaces. Common runtime (i.e. interface agnostic) functionality is conventionally but not necessarily split into a separate package (e.g. [`rosidl_runtime_c`](https://github.com/ros2/rosidl/tree/master/rosidl_runtime_c), [`rosidl_runtime_cpp`](https://github.com/ros2/rosidl/tree/master/rosidl_runtime_cpp)). It is not unusual for a generator to use another generator's output via language-specific binding machinery (e.g. [`rosidl_generator_py`](https://github.com/ros2/rosidl_python/blob/master/rosidl_generator_py/), which uses Python C bindings to wrap [`rosidl_generator_c`](https://github.com/ros2/rosidl/tree/master/rosidl_generator_c) representations), although this dependency is not explicit: generator packages do depend on each other but it is assumed that their generated code will all be built within the same package so that build system dependencies can be established. -- **Type support packages**. These packages provide tooling to generate language-specific and often middleware-specific support code for middleware implementations to interact (e.g. on publish, on take) with in-memory representations of ROS interfaces. Type support functionality is encapsulated in `rosidl_message_type_support_t`, `rosidl_service_type_support_t`, and `rosidl_action_type_support_t` C structs in read-only global storage, one for each interface type. To support middleware runtime selection, it is not unusual to find language-specific type support packages that do not provide any functionality of their own but defer to middleware-specific type supports accordingly (e.g. [`rosidl_typesupport_c`](https://github.com/ros2/rosidl_typesupport/tree/master/rosidl_typesupport_c), [`rosidl_typesupport_cpp`](https://github.com/ros2/rosidl_typesupport/tree/master/rosidl_typesupport_c)). +- **Generator packages**. These packages provide tooling to generate language-specific, in-memory representations of ROS interfaces. + Common runtime (i.e. interface agnostic) functionality is conventionally but not necessarily split into a separate package (e.g. [`rosidl_runtime_c`](https://github.com/ros2/rosidl/tree/master/rosidl_runtime_c), [`rosidl_runtime_cpp`](https://github.com/ros2/rosidl/tree/master/rosidl_runtime_cpp)). It is not unusual for a generator to use another generator's output via language-specific binding machinery (e.g. [`rosidl_generator_py`](https://github.com/ros2/rosidl_python/blob/master/rosidl_generator_py/), which uses Python C bindings to wrap [`rosidl_generator_c`](https://github.com/ros2/rosidl/tree/master/rosidl_generator_c) representations), although this dependency is not explicit: generator packages do depend on each other but it is assumed that their generated code will all be built within the same package so that build system dependencies can be established. +- **Type support packages**. These packages provide tooling to generate language-specific and often middleware-specific support code for middleware implementations to interact (e.g. on publish, on take) with in-memory representations of ROS interfaces. + Type support functionality is encapsulated in `rosidl_message_type_support_t`, `rosidl_service_type_support_t`, and `rosidl_action_type_support_t` C structs in read-only global storage, one for each interface type. + To support middleware runtime selection, it is not unusual to find language-specific type support packages that do not provide any functionality of their own but defer to middleware-specific type supports accordingly (e.g. [`rosidl_typesupport_c`](https://github.com/ros2/rosidl_typesupport/tree/master/rosidl_typesupport_c), [`rosidl_typesupport_cpp`](https://github.com/ros2/rosidl_typesupport/tree/master/rosidl_typesupport_c)). Packages for interface definition translation (e.g. [`rosidl_adapter`](https://github.com/ros2/rosidl/tree/master/rosidl_adapter), [`rosidl_generator_dds_idl`](https://github.com/ros2/rosidl_dds/tree/master/rosidl_generator_dds_idl)), interface definition parsing (e.g. [`rosidl_parser`](https://github.com/ros2/rosidl/tree/master/rosidl_parser)), and `ament_cmake` integration (e.g. [`rosidl_cmake`](https://github.com/ros2/rosidl/tree/master/rosidl_cmake/rosidl_cmake)) complement and support generator and type support packages implementation. -Alongside generated source code, generator and type support packages are expected to provide the means to build it by adding to the `rosidl_generate_idl_interfaces`' [`ament` extension point](https://index.ros.org/doc/ros2/Tutorials/Ament-CMake-Documentation/#adding-to-extension-points). By tapping into this extension, downstream `ament_cmake` packages can delegate build-system configuration to all available generator and type support packages in the workspace (or any of its underlays) during the configuration phase, so as to generate and then build source code during the build phase. This is what constitutes a (roughly) 3-stage pipeline, from interface definition files to source code to artifacts: +Alongside generated source code, generator and type support packages are expected to provide the means to build it by adding to the `rosidl_generate_idl_interfaces`' [`ament` extension point](https://index.ros.org/doc/ros2/Tutorials/Ament-CMake-Documentation/#adding-to-extension-points). +By tapping into this extension, downstream `ament_cmake` packages can delegate build-system configuration to all available generator and type support packages in the workspace (or any of its underlays) during the configuration phase, so as to generate and then build source code during the build phase. +This is what constitutes a (roughly) 3-stage pipeline, from interface definition files to source code to artifacts: ![ROSIDL 3-stage pipeline](/img/generalized_interface_generation/rosidl_3_stage_pipeline.png) -Note that the order in which each `ament` extension runs is the order in which each package was imported into CMake, by virtue of how these work. This order is further obfuscated by the discovery process that the [`rosidl_default_generators`](https://github.com/ros2/rosidl_defaults/tree/master/rosidl_default_generators) package performs. Thus, for all practical purposes this order cannot be relied on, and build-system dependencies must be leveraged to establish dependencies across generators' output. +Note that the order in which each `ament` extension runs is the order in which each package was imported into CMake, by virtue of how these work. +This order is further obfuscated by the discovery process that the [`rosidl_default_generators`](https://github.com/ros2/rosidl_defaults/tree/master/rosidl_default_generators) package performs. +Thus, for all practical purposes this order cannot be relied on, and build-system dependencies must be leveraged to establish dependencies across generators' output. ### Drawbacks As it stands, the `rosidl` interface generation pipeline: -- **Is strongly coupled with CMake**, and in particular with `ament_cmake` machinery. There's no way for packages using different build systems or build system generators to generate their own interfaces (e.g. pure Python packages cannot generate interfaces nor extend the pipeline). There's no way for external projects using different build systems or build system generators to provide their own generator or type support packages. +- **Is strongly coupled with CMake**, and in particular to `ament_cmake` machinery. + There's no way for packages using different build systems or build system generators to generate their own interfaces (e.g. pure Python packages cannot generate interfaces nor extend the pipeline). + There's no way for external projects using different build systems or build system generators to provide their own generator or type support packages. -- **Favors monolithic builds**. Since interface generation output cannot be controlled at the package level (i.e. all available extensions must and will be invoked), one and only one package can build and install all generated artifacts. It also implies that the set of artifacts that the package provides depends on the set of generator and type support packages present in the workspace at build-time. This results in package rebuilds to extend or restrict that support (e.g. adding support for a non-core language requires a rebuild of core interface packages), and loose package versioning (e.g. a change in a generator package can induce an API/ABI break in an interface package). +- **Favors monolithic builds**. + Since interface generation output cannot be controlled at the package level (i.e. all available extensions must and will be invoked), one and only one package can build and install all generated artifacts. + It also implies that the set of artifacts that the package provides depends on the set of generator and type support packages present in the workspace at build-time. + This results in package rebuilds to extend or restrict that support (e.g. adding support for a non-core language requires a rebuild of core interface packages), and loose package versioning (e.g. a change in a generator package can induce an API/ABI break in an interface package). ## Proposal @@ -69,9 +81,12 @@ The first stage in the current pipeline is split in two, configuring a 4-stage p ![ROSIDL 4-stage pipeline](/img/generalized_interface_generation/rosidl_4_stage_pipeline.png) -In the first stage, a [common build specification](#a-common-build-specification) that outlines how type representation and type support code must be generated (e.g. by invoking a source code generator) and built (e.g. by building a shared library) is generated. -In the second stage, this specification is translated into build-system files. -Both first and second stages would occur during the configuration phase once integrated into a build-system. +In the first stage, a [common build specification](#a-common-build-specification) that outlines how type representation and type support code must be generated, built, and installed is generated by `rosidl_build_configure` (see below). +In the second stage, the build specification is translated into build-system's files by `ament_meta_build` (see below). +Both the first and second stages would normally occur during the build-system's __configuration_ phase. +In the third stage, code is actually generated by `rosidl_generate` (see below). +In the fourth stage, the build-system builds and install all artifacts. +Both the third and fourth stages would normally occur during the build-system's __build_ phase. ### Tooling updates @@ -80,32 +95,25 @@ Both first and second stages would occur during the configuration phase once int ##### Description Migrate generation logic to extensible, standardized tools that can compile interface definitions into language-specific type representation and middleware-specific type support code. + These tools take a set of interface definition files and generate type representation or type support source code. -Support for new languages and middlewares can be added by external packages via a plugin system. +Customarily, interface definition files are [IDL](https://www.omg.org/spec/IDL/About-IDL/) files. + +Support for new languages and middlewares can be added by external packages via a system of _code generator_ plugins. Each plugin version is expected to generate the same output for the same input over time. Users can (but are not forced to) target specific plugin versions to prevent workspace (or underlay) updates from affecting tool output. ##### Specification -Interface type representation and interface type support are conceptually different pieces of functionality. -Therefore, their generation CLIs, even if roughly equivalent, are kept separate. -This allows them to deviate from each other in the future if need be. - -*For interface type representations* ```sh -rosidl_generate ((-d|--output-directory) PATH)? ((-l|--language) IDENTIFIER(==VERSION)?)* ((-I|--include-path) PATH)* PATH+ -``` - -*For interface type supports* -```sh -rosidl_typesupport_generate ((-d|--output-directory) PATH)? ((-t|--type-support) IDENTIFIER(==VERSION)?)* ((-I|--include-path) PATH)* PATH+ +rosidl_generate ((-d|--output-directory) PATH)? ((-l|--language) IDENTIFIER(==VERSION)?)* ((-t|--type-support) IDENTIFIER(==VERSION)?)* ((-I|--include-path) PATH)* PATH+ ``` All positional arguments are paths to interface definition files. If no output directory is specified, the current working directory is used. -If no type representation / type support generators are specified, all available generators available are used. -If a type representation / type support generator is specified but not found, the tool fails. -If a specific version of a type representation / type support generator is specified but not found, the tool fails. +If no language nor type support generator is specified, all available generators are used. +If a language or a type support generator is specified but not found, the tool fails. +If a specific version of a language or type support generator is specified but not found, the tool fails. ##### Implementation considerations @@ -116,9 +124,15 @@ Additionally, [`setuptools` entrypoints](https://setuptools.readthedocs.io/en/la ##### Description -Migrate build logic to extensible, standardized tools that can generate [common build specifications](#a-common-build-specification) for language-specific type representation and middleware-specific type support code. -These tools take a set of interface definition files and generate a [common build specification](#a-common-build-specification). -Support for new languages and middlewares can be added by external packages via a plugin system. +Migrate build logic to extensible, standardized tools that can generate [common build specification](#a-common-build-specification) files for language-specific type representation and middleware-specific type support code. + +These tools take a set of interface definition files and generate a [common build specification](#a-common-build-specification) files. +Customarily, interface definition files are [IDL](https://www.omg.org/spec/IDL/About-IDL/) or [ROS interface](https://index.ros.org/doc/ros2/Concepts/About-ROS-Interfaces/) files. +A build specification must take care of any and all necessary interface definition conversions for the associated source code generator to work. + +Support for new languages and middlewares can be added by external packages via a system of _build generator_ plugins. +Each plugin version is expected to generate the same output for the same input over time. +Users can (but are not forced to) target specific plugin versions to prevent workspace (or underlay) updates from affecting tool output. ##### Rationale @@ -129,31 +143,25 @@ Thus, in the most general case, build system integration is necessary. However, integration with any given build-system (generator) A couples a large portion of the interface pipeline to it. If a different build-system (generator) B is to be used, users are forced to either: -* **Delegate from build-system (generator) B to build-system (generator) A**. This is challenging. Build-system (generator) A must be properly embedded in build-system (generator) B, pushing and pulling information across the gap for builds and installs to succeed, adapting invocations of build-system (generator) B to any constraints that build-system (generator) A may impose (e.g. Bazel performs sandboxed builds). There is a performance penalty in doing this. -* **Port from build-system (generator) A to build-system (generator) B**. This may be (and currently is) a non-trivial effort. Programs that can convert from one build-system (generator) to another do exist, but these are rare and often limited to a subset of the functionality that is shared between source and target. +* **Delegate from build-system (generator) B to build-system (generator) A**. + This is challenging. Build-system (generator) A must be properly embedded in build-system (generator) B, pushing and pulling information across the gap for builds and installs to succeed, adapting invocations of build-system (generator) B to any constraints that build-system (generator) A may impose (e.g. Bazel performs sandboxed builds). + There is a performance penalty in doing this. +* **Port from build-system (generator) A to build-system (generator) B**. + This may be (and currently is) a non-trivial effort. + Programs that can convert from one build-system (generator) to another do exist, but these are rare and often limited to a subset of the functionality that is shared between source and target. -By generating a build specification in a format designed to be simple yet general, an appropriate build system can be generated on consumption. +By generating a build specification in a format designed to be simple yet general, an appropriate build system can be generated when needed. ##### Specification -Interface type representation and interface type support are conceptually different pieces of functionality. -Therefore, their build configuration CLIs, even if roughly equivalent, are kept separate. -This allows them to deviate from each other in the future if need be. - -*For interface type representations* ```sh -rosidl_build_configure ((-o|--output-file) PATH)? ((-l|--language) IDENTIFIER(==VERSION)?)* ((-I|--include-path) PATH)* PATH+ -``` - -*For interface type supports* -```sh -rosidl_typesupport_build_configure ((-o|--output-file) PATH)? ((-t|--type-support) IDENTIFIER(==VERSION)?)* ((-I|--include-path) PATH)* PATH+ +rosidl_build_configure ((-o|--output-file) PATH)? ((-l|--language) IDENTIFIER(==VERSION)?)* ((-t|--type-support) IDENTIFIER(==VERSION)?)* ((-I|--include-path) PATH)* PATH+ ``` All positional arguments are paths to interface definition files. -If no language / type support is specified, all type / type support generators available are used. -If no type / type support generator is found, the command fails. -If no output file path is provided, the generated build specification is sent to standard output. +If no language nor type support generator is specified, all available generators are used. +If no language nor type support generator is found, the command fails. +If no output file path is provided, the common build specification is sent to standard output. ##### Implementation considerations @@ -161,39 +169,45 @@ All build logic in existing generator and type support packages is CMake code, a Using Python would ensure tooling interoperability and their supporting libraries. Additionally, [`setuptools` entrypoints](https://setuptools.readthedocs.io/en/latest/userguide/entry_point.html#advertising-behavior) can be leveraged as plugin system. -Since common build specifications are eminently declarative, most configuration must be resolved prior to generation e.g. for which OS to build. -This includes dependencies between generated outputs e.g. type support code may depend on type representation code to build. -Build specification must ensure the presence of all dependencies by conditionally generating them if not present. +Common build specifications can only describe platform- and/or configuration-specific variations in how a component is built and distributed. +Any other conditionals must be resolved prior to generating an specification. +There are otherwise no requirements nor constraints as to whether and how a build is specified e.g. it may specify shared libraries as well as generic components on an arbitrary command (which could be a build tool). +Build specifications that use well-known component types are, however, more portable and yield better build-system integration. +A build-system adapter plugin in `ament_meta_build` cannot workaround a build specification that forces `cmake` to be called. -There are no requirements nor constraints as to how a build is specified. -A build may specify some generic components, such as shared libraries, and it may specify an arbitrary command for execution (which could easily be a build tool, such as `cmake`). -The more generic a build specification is, the better will build-system integration be -- a build-system adapter plugin in `ament_meta_build` cannot workaround a build specification that forces `cmake` to be called. +Common build specifications describe full packages. +In order to support running multiple generators at once, the tooling must merge build specifications and resolve all dependencies between these prior to generating an specification. +It is also recommended that equivalent components (e.g. `.idl` files generated from `.msg` files) be de-duplicated. #### Meta build tool ##### Description -Develop an extensible, standardized tool that can parse [common build specifications](#a-common-build-specification) and generate an actual build system or build system generator code. +Develop an extensible, standardized tool that can parse [common build specifications](#a-common-build-specification) and generate actual build system (generator) code. + Support for new build systems and build system generators can be added by external packages via a system of __build-system adapter_ plugins. +Each plugin version is expected to generate the same output for the same input over time. +Users can (but are not forced to) target specific plugin versions to prevent workspace (or underlay) updates from affecting tool output. ##### Specification ```sh -ament_meta_build ((-o|--output-file) PATH)? (--build-prefix PATH)? (--install-prefix PATH)? ((-b|--build-system) IDENTIFIER)? PATH? +ament_meta_build ((-o|--output-file) PATH)? (--build-prefix PATH)? (--install-prefix PATH)? ((-b|--build-system) IDENTIFIER(==VERSION)?)? PATH? ``` Its only positional argument is a path to a common build specification file. If no common build specification file is given, it is read from standard input. If no output file path is provided, generated build system sources are written to standard output. -If no build-system adapter plugin is specified nor it can be deduced from output file extension, the tool fails. -If a build prefix is specified, it is used +If no build prefix is specified, the default build prefix (if any) for the chosen build system is used. +If no install prefix is specified, the default install prefix (if any) for the chosen build system is used. +If no build system is specified nor it can be deduced from output file extension, the tool fails. If the specific build system is not supported, the tool fails. ##### Implementation considerations -There are no requirements nor constraints as to whether and how any given build-system adapter plugin supports the build specification schema. -A plugin may generate native build system generator code to build some components, delegate on other plugins (and other build systems) to build some components, and not support some components at all. -The broader native support is, the lower will build overhead (usually) be and the more useful will an (open source) plugin be to the community. +There are no requirements nor constraints as to whether and how any given build-system adapter plugin supports the common build specification schema. +A plugin may generate native build-system generator code to build some component types, delegate on other plugins (and thus to other build systems) to build other component types, and not support some component types at all. +The broader native support is, the lower will the build overhead (usually) be, and the more useful will an (open source) plugin be to the community. ### Build system integration @@ -220,6 +234,14 @@ include(build.cmake) It is worth noting that these integration layers must bring in package dependencies on their own, as there is no build system-agnostic mechanism to do so. +### Migration + +Migrating the current architecture is fairly straightforward. + +- Each tightly coupled pair of plugins for `rosidl_generate` and `rosidl_build_configure` can be built on top of each existing generator or type support package. +- Generated build specifications can delegate to CMake while the `ament_meta_build` tooling is independently developed. +- New `ament_cmake` macros can be added to tap into the pipeline, while keeping existing ones (which may be deprecated over time, or re-implemented on top of the former). + ## Appendix ### A. Common Build Specification @@ -227,7 +249,407 @@ It is worth noting that these integration layers must bring in package dependenc The Common Build Specification is a mechanism for describing how to build and distribute a software package, such that any build tool can carry out the process. The draft specification can be found [here](https://github.com/hidmic/cps). This specification is based on the [Common Package Specification](https://mwoehlke.github.io/cps/) format, a simple and relatively standard format that describes packages for downstream consumption in a build tool-agnostic way. -The CPS format was not designed to describe builds, and thus why the need to modify it. +The CPS format was not designed to describe builds, and thus why a modification is needed. + +As examples, see below the `.cbs` files that would result from running `rosidl_generator_c` and `rosidl_generator_py` on a `foo_msgs` package. + + +*foo_msgs_c.cbs* +```json +{ + "Name": "foo_msgs_c", + "Description": "Build specification for ROS2 C foo_msgs interfaces", + "License": "Apache License 2", + "Version": "0.1.0", + "Compat-Version": "0.8.0", + "Platforms": { + "GNU" : { + "Cpp-Compiler": "gnu", + }, + "Clang" : { + "Cpp-Compiler": "clang", + }, + "Windows": { + "Kernel-Version": "windows" + } + }, + "Requires": { + "rosidl_runtime_c": {}, + "rosidl_typesupport_interface": {}, + "action_msgs": {} + }, + "Configurations": [ "Optimized", "Debug" ], + "Default-Components": [ "c" ], + "Components": { + "idls": { + "Type": "generic", + "Assembly": { + "Command": "rosidl_adapt -p foo_msgs $(sources)", + "Sources": [ + "msg/foo.msg", + "srv/bar.srv", + "action/baz.action" + ], + "Artifacts": [ + "msg/foo.idl", + "srv/bar.idl", + "action/baz.idl" + ] + }, + "Distribution": { + "Location": "@prefix@/share/foo_msgs" + } + }, + "c_code": { + "Type": "generic", + "Assembly": { + "Requires": [ "action_msgs:idls", ":idls" ], + "Command": "rosidl_generate --language c -p foo_msgs -I $(location action_msgs:idls) $(sources)", + "Sources": [ + "msg/foo.idl", + "srv/bar.idl", + "action/baz.idl" + ], + "Artifacts": [ + "msg/foo.h", + "msg/detail/foo__functions.h", + "msg/detail/foo__struct.h", + "msg/detail/foo__type_support.h", + "msg/detail/foo__functions.c", + "srv/bar.h", + "srv/detail/bar__functions.h", + "srv/detail/bar__struct.h", + "srv/detail/bar__type_support.h", + "srv/detail/bar__functions.c", + "action/baz.h", + "action/detail/baz__functions.h", + "action/detail/baz__struct.h", + "action/detail/baz__type_support.h", + "action/detail/baz__functions.c", + "rosidl_generator_c_visibility_control.h" + ] + } + }, + "c": { + "Type": "dylib", + "Assembly": { + "Requires": [ + ":c_code", + "rosidl_runtime_c", + "rosidl_typesupport_interface", + "action_msgs" + ], + "Sources": [ + "msg/foo.h", + "msg/detail/foo__functions.h", + "msg/detail/foo__functions.c", + "msg/detail/foo__struct.h", + "msg/detail/foo__type_support.h", + "srv/bar.h", + "srv/detail/bar__functions.h", + "srv/detail/bar__functions.c", + "srv/detail/bar__struct.h", + "srv/detail/bar__type_support.h", + "action/baz.h", + "action/detail/baz__functions.h", + "action/detail/baz__functions.c" + "action/detail/baz__struct.h", + "action/detail/baz__type_support.h", + "rosidl_generator_c_visibility_control.h" + ], + "Configurations": { + "GNU": { + "Compiler-Features": [ "c11" ], + "Compiler-Flags": [ "-Wall", "-Wextra", "-Wpedantic" ], + }, + "Clang": { + "Compiler-Features": [ "c11" ], + "Compiler-Flags": [ "-Wall", "-Wextra", "-Wpedantic" ], + }, + "Windows": { + "Definitions": [ "ROSIDL_GENERATOR_C_BUILDING_DLL_foo_msgs" ], + } + } + }, + "Distribution": { + "Name": "foo_msgs_c", + "Requires": [ + "rosidl_runtime_c", + "rosidl_typesupport_interface", + "action_msgs" + ], + "Include-Location": { + "@prefix@/include/foo_msgs": [ + "msg/foo.h", + "msg/detail/foo__functions.h", + "msg/detail/foo__struct.h", + "msg/detail/foo__type_support.h", + "srv/bar.h", + "srv/detail/bar__functions.h", + "srv/detail/bar__struct.h", + "srv/detail/bar__type_support.h", + "action/baz.h", + "action/detail/baz__functions.h", + "action/detail/baz__struct.h", + "action/detail/baz__type_support.h", + "rosidl_generator_c_visibility_control.h" + ] + }, + "Includes": [ "@prefix@/include" ] + } + } + } +} +``` + +*foo_msgs_py.cbs* +```json +{ + "Name": "foo_msgs_py", + "Description": "Build specification for ROS2 Python foo_msgs interfaces", + "License": "Apache License 2", + "Version": "0.1.0", + "Compat-Version": "0.8.0", + "Platforms": { + "GNU" : { + "Cpp-Compiler": "gnu", + }, + "Clang" : { + "Cpp-Compiler": "clang", + }, + "Windows": { + "Kernel-Version": "windows" + } + }, + "Requires": { + // Base common dependencies + "rmw": {}, + "rosidl_runtime_c": {}, + "rosidl_typesupport_c": {}, + "rosidl_typesupport_interface": {}, + "rosidl_typesupport_c": {}, + "rosidl_typesupport_fastrtps_c": {}, + "rosidl_typesupport_introspection_c": {}, + // CPython development dependencies + "python-dev": { + "Version": "3.5" + }, + // A dependency on the package described above + "foo_msgs_c": {}, + // Other foo_msgs generated packages + "foo_msgs_typesupport_c": {}, + "foo_msgs_typesupport_fastrtps_c": {}, + "foo_msgs_typesupport_introspection_c": {}, + // Common action dependencies + "action_msgs": {} + }, + "Configurations": [ "Optimized", "Debug" ], + "Default-Components": [ "py" ], + "Components": { + "idls": { + // Generated IDL files from ROS legacy definitions + "Type": "generic", + "Assembly": { + "Command": "rosidl_adapt -p foo_msgs $(sources)", + "Sources": [ + "msg/foo.msg", + "srv/bar.srv", + "action/baz.action" + ], + "Artifacts": [ + "msg/foo.idl", + "srv/bar.idl", + "action/baz.idl" + ] + }, + "Distribution": { + "Location": "@prefix@/share/foo_msgs" + } + }, + "py_code": { + // Generated Python and C extension code + "Type": "generic", + "Assembly": { + "Requires": [ "action_msgs:idls", ":idls" ], + "Command": "rosidl_generate -l python -p foo_msgs -I $(location action_msgs:idls) $(sources)", + "Sources": [ + "msg/foo.idl", + "srv/bar.idl", + "action/baz.idl" + ], + "Artifacts": [ + "__init__.py", + "msg/__init__.py", + "msg/_foo.py", + "msg/_foo_s.c", + "srv/__init__.py", + "srv/_bar.py", + "srv/_bar_s.c", + "action/__init__.py", + "action/_baz.py", + "action/_baz_s.c", + "_foo_msgs_s.ep.rosidl_typesupport_c.c", + "_foo_msgs_s.ep.rosidl_typesupport_fastrtps_c.c", + "_foo_msgs_s.ep.rosidl_typesupport_introspection_c.c", + ] + }, + }, + "py": { + // Python module + "Type": "pymodule", + "Assembly": { + "Requires": [ ":py_code" ], + "Sources": [ + "__init__.py", + "msg/__init__.py", + "msg/_foo.py", + "srv/__init__.py", + "srv/_bar.py", + "action/__init__.py", + "action/_baz.py", + ] + }, + "Distribution": { + "Name": "foo_msgs" + } + }, + "cpython": { + // Common CPython shared library to support extensions + "Type": "dylib", + "Assembly": { + "Requires": [ + ":py_code", + "foo_msgs_c", + "action_msgs:cpython", + "python-dev", + ], + "Sources": [ + "msg/_foo_s.c", + "srv/_bar_s.c" + "action/_baz_s.c" + ], + // Use include directories from default component of foo_msgs_typesupport_c. + "Include-Requires": [ "foo_msgs_typesupport_c" ], + "Configurations": { + "Optimized": { + // Use Python interpreter to resolve numpy include path. + "System-Includes": [ "$(shell python -c \"import numpy; print(numpy.get_include())\")" ] + }, + "Debug": { + // Use debug Python interpreter to resolve numpy include path. + "System-Includes": [ "$(shell pythond -c \"import numpy; print(numpy.get_include())\")" ] + } + } + }, + "Distribution": { + // Install to default library location + "Name": "foo_msgs_cpython" + } + }, + "pyext_typesupport_c": { + // CPython extension to access C typesupport + "Type": "cpyext", + "Assembly": { + "Requires": [ + ":py_code", + ":cpython", + "foo_msgs_typesupport_c", + "rosidl_runtime_c", + "rosidl_typesupport_c", + "rosidl_typesupport_interface", + "action_msgs", + ], + "Configurations": { + "GNU": { + "Compiler-Flags": [ "-Wall", "-Wextra" ], + } + "Clang": { + "Compiler-Flags": [ "-Wall", "-Wextra" ], + } + }, + "Sources": [ "_foo_msgs_s.ep.typesupport_c.c" ], + // Use include directories from default component of foo_msgs_typesupport_c. + "Include-Requires": [ "foo_msgs_typesupport_c" ] + }, + "Distribution": { + // Prefix should be set to the proper site-packages subdirectory + "Location": "@python-prefix@/foo_msgs", + // A proper Python extension suffix will be added for pyext components. + "Name": "foo_msgs_s__typesupport_c" + } + }, + "pyext_typesupport_fastrtps_c": { + // CPython extension to access Fast-RTPS C typesupport + "Type": "cpyext", + "Assembly": { + "Requires": [ + ":py", + ":cpython", + "foo_msgs_typesupport_fastrtps_c", + "rosidl_runtime_c", + "rosidl_typesupport_c", + "rosidl_typesupport_interface", + "action_msgs" + ], + "Configurations": { + "GNU": { + "Compiler-Flags": [ "-Wall", "-Wextra" ], + } + "Clang": { + "Compiler-Flags": [ "-Wall", "-Wextra" ], + } + }, + "Sources": [ "_foo_msgs_s.ep.typesupport_fastrtps_c.c" ], + "Include-Requires": [ "foo_msgs_typesupport_c" ] + }, + "Distribution": { + // Prefix should be set to the proper site-packages subdirectory + "Location": "@python-prefix@/foo_msgs", + // A proper Python extension suffix will be added for pyext components. + "Name": "foo_msgs_s__typesupport_fastrtps_c" + } + }, + "pyext_typesupport_introspection_c": { + // CPython extension to access introspection C typesupport + "Type": "cpyext", + "Assembly": { + "Requires": [ + ":py", + ":cpython", + "foo_msgs_typesupport_introspection_c" + "rosidl_runtime_c", + "rosidl_typesupport_c", + "rosidl_typesupport_interface", + "action_msgs", + ], + "Configurations": { + "GNU": { + "Compiler-Flags": [ "-Wall", "-Wextra" ], + } + "Clang": { + "Compiler-Flags": [ "-Wall", "-Wextra" ], + } + }, + "Sources": [ "_foo_msgs_s.ep.typesupport_introspection_c.c" ], + "Include-Requires": [ "foo_msgs_typesupport_c" ] + }, + "Distribution": { + // Prefix should be set to the proper site-packages subdirectory + "Location": "@python-prefix@/foo_msgs", + // A proper Python extension suffix will be added for pyext components + "Name": "foo_msgs_s__typesupport_introspection_c" + } + } + } +} +``` + +A few details worth noting: + +- The explicit dependency on other `foo_msgs_*` generated packages, which the tooling should resolve if all are to be present on the same specification. +- The requirement on the `python-dev` package, which is a hint for the build system to build against CPython development libraries. +- The duplication of the `":idls"` component, which the tooling can (and should) de-duplicate. +- The overlap between build and source trees, which is a characteristic of CBS (for portability's sake) and that has to be dealt with by the build system (e.g. using precedence rules). ### B. CLI Specification Syntax From 3c8c1d3e4e206f18a35ee6a37c496fcc646bf8a4 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Tue, 26 Jan 2021 12:17:27 -0300 Subject: [PATCH 6/7] Adjust CLI specifications Signed-off-by: Michel Hidalgo --- articles/generalized_interface_generation.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/articles/generalized_interface_generation.md b/articles/generalized_interface_generation.md index 97ec271ae..005f15ef9 100644 --- a/articles/generalized_interface_generation.md +++ b/articles/generalized_interface_generation.md @@ -106,10 +106,12 @@ Users can (but are not forced to) target specific plugin versions to prevent wor ##### Specification ```sh -rosidl_generate ((-d|--output-directory) PATH)? ((-l|--language) IDENTIFIER(==VERSION)?)* ((-t|--type-support) IDENTIFIER(==VERSION)?)* ((-I|--include-path) PATH)* PATH+ +rosidl generate ((-b|--base-configuration) PATH)? ((-d|--output-directory) PATH)? ((-l|--language) IDENTIFIER(==VERSION)?)* ((-t|--type-support) IDENTIFIER(==VERSION)?)* ((-I|--include-path) PATH)* ((ABSPATH:)?RELPATH)+ ``` All positional arguments are paths to interface definition files. +To derive interface namespaces unambiguously when applicable, absolute paths are expressed as an absolute path prefix, followed by a colon ':', followed by a relative path. +If a base configuration file is specified, argument defaults will be read from it. If no output directory is specified, the current working directory is used. If no language nor type support generator is specified, all available generators are used. If a language or a type support generator is specified but not found, the tool fails. @@ -155,10 +157,12 @@ By generating a build specification in a format designed to be simple yet genera ##### Specification ```sh -rosidl_build_configure ((-o|--output-file) PATH)? ((-l|--language) IDENTIFIER(==VERSION)?)* ((-t|--type-support) IDENTIFIER(==VERSION)?)* ((-I|--include-path) PATH)* PATH+ +rosidl build-configure ((-b|--base-configuration) PATH)? ((-o|--output-file) PATH)? ((-l|--language) IDENTIFIER(==VERSION)?)* ((-t|--type-support) IDENTIFIER(==VERSION)?)* ((-I|--include-path) PATH)* ((ABSPATH:)?RELPATH)+ ``` All positional arguments are paths to interface definition files. +To derive interface namespaces unambiguously when applicable, absolute paths are expressed as an absolute path prefix, followed by a colon ':', followed by a relative path. +If a base configuration file is specified, argument defaults will be read from it. If no language nor type support generator is specified, all available generators are used. If no language nor type support generator is found, the command fails. If no output file path is provided, the common build specification is sent to standard output. @@ -660,3 +664,5 @@ Upper case tokens are expressions aliases, namely: - `IDENTIFIER` is a string of non-whitespace characters - `VERSION` is a [semantic](https://semver.org/) version - `PATH` is a host file system path +- `ABSPATH` is a host file system absolute path +- `RELPATH` is a host file system relative path From 80d3069b17b032a1a32d174f7a84dab25ca7147b Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Thu, 11 Mar 2021 18:38:26 -0300 Subject: [PATCH 7/7] Update draft proposal - Add section on interface definition translators and rosidl translate - Update CLI specifications for all verbs - Update CLI examples Signed-off-by: Michel Hidalgo --- articles/generalized_interface_generation.md | 90 +++++++++++++------- 1 file changed, 61 insertions(+), 29 deletions(-) diff --git a/articles/generalized_interface_generation.md b/articles/generalized_interface_generation.md index 005f15ef9..8c04e6b0a 100644 --- a/articles/generalized_interface_generation.md +++ b/articles/generalized_interface_generation.md @@ -81,10 +81,10 @@ The first stage in the current pipeline is split in two, configuring a 4-stage p ![ROSIDL 4-stage pipeline](/img/generalized_interface_generation/rosidl_4_stage_pipeline.png) -In the first stage, a [common build specification](#a-common-build-specification) that outlines how type representation and type support code must be generated, built, and installed is generated by `rosidl_build_configure` (see below). +In the first stage, a [common build specification](#a-common-build-specification) that outlines how type representation and type support code must be generated, built, and installed is generated by `rosidl build-configure` (see below). In the second stage, the build specification is translated into build-system's files by `ament_meta_build` (see below). Both the first and second stages would normally occur during the build-system's __configuration_ phase. -In the third stage, code is actually generated by `rosidl_generate` (see below). +In the third stage, code is actually generated by `rosidl generate` (see below). In the fourth stage, the build-system builds and install all artifacts. Both the third and fourth stages would normally occur during the build-system's __build_ phase. @@ -97,30 +97,64 @@ Both the third and fourth stages would normally occur during the build-system's Migrate generation logic to extensible, standardized tools that can compile interface definitions into language-specific type representation and middleware-specific type support code. These tools take a set of interface definition files and generate type representation or type support source code. -Customarily, interface definition files are [IDL](https://www.omg.org/spec/IDL/About-IDL/) files. +Typically, interface definition files are [IDL](https://www.omg.org/spec/IDL/About-IDL/) files. Support for new languages and middlewares can be added by external packages via a system of _code generator_ plugins. -Each plugin version is expected to generate the same output for the same input over time. -Users can (but are not forced to) target specific plugin versions to prevent workspace (or underlay) updates from affecting tool output. ##### Specification ```sh -rosidl generate ((-b|--base-configuration) PATH)? ((-d|--output-directory) PATH)? ((-l|--language) IDENTIFIER(==VERSION)?)* ((-t|--type-support) IDENTIFIER(==VERSION)?)* ((-I|--include-path) PATH)* ((ABSPATH:)?RELPATH)+ +rosidl generate ((-o|--output-path) PATH)? ((-t|--type) IDENTIFIER)* ((-ts|--type-support) IDENTIFIER)* ((-I|--include-path) PATH)* IDENTIFIER ((ABSPATH:)?RELPATH)+ ``` -All positional arguments are paths to interface definition files. -To derive interface namespaces unambiguously when applicable, absolute paths are expressed as an absolute path prefix, followed by a colon ':', followed by a relative path. -If a base configuration file is specified, argument defaults will be read from it. -If no output directory is specified, the current working directory is used. -If no language nor type support generator is specified, all available generators are used. -If a language or a type support generator is specified but not found, the tool fails. -If a specific version of a language or type support generator is specified but not found, the tool fails. +The first positional argument is the package name. +All other positional arguments are paths to interface definition files. +To derive interface namespaces unambiguously when applicable, interface paths are expressed as an absolute path prefix, followed by a colon ':', followed by a relative path. +If no output path is specified, the current working directory is used. +If no type representation nor type support generator is specified, all available generators are used. +If a type representation or a type support generator is specified but not found, the tool fails. ##### Implementation considerations Using Python allows for significant code reuse in the vast majority of existing generator and type support packages. -Additionally, [`setuptools` entrypoints](https://setuptools.readthedocs.io/en/latest/userguide/entry_point.html#advertising-behavior) can be leveraged as a plugin system. +Additionally, [`setuptools` entry points](https://setuptools.readthedocs.io/en/latest/userguide/entry_point.html#advertising-behavior) can be leveraged as a plugin system. + +#### Interface definition translators + +##### Description + +Migrate interface definition adapters and generation logic to extensible, standardized tools that can trnaslate interface definitions from one format to another. +An API must also be provided for source code generation tools to leverage these tools. + +Support for new formats can be added by external packages via a system of _translator_ plugins. + +##### Rationale + +A source code generator typically deals with one interface definition file format -- [IDL](https://www.omg.org/spec/IDL/About-IDL/) being the most common. +However, the pipeline has to deal with multiple such formats, as different source code generators may be bound to different file formats and users may be describe their interfaces in different file formats (e.g. to ease integration with other systems). +In ROS, the .idl file format already coexists with .msg, .srv, and .action file formats. + +To restrict the set of valid interface definition file formats (by convention or in code) prevents pipeline extension and reuse, particularly when integrating ROS with other systems. +To extend source code generators for every interface definition file format, present or future, is expensive and likely to result in code duplication. + +However, if interface definition file formats can be translated from one to the other, and the translation logic is decoupled from source code generation, then every source code generator in the pipeline can afford handling any file format, present and future. + +##### Specification + +```sh +rosidl translate ((-o|--output-path) PATH)? ((--use|--translator) IDENTIFIER)* (--to|--output-format) IDENTIFIER ((--from|--input-format) IDENTIFIER)? ((-I|--include-path) PATH)* IDENTIFIER ((ABSPATH:)?RELPATH)+ +``` + +The first positional argument is the package name. +All other positional arguments are paths to interface definition files. +To derive interface namespaces unambiguously when applicable, interface paths are expressed as an absolute path prefix, followed by a colon ':', followed by a relative path. +It is required that an output format is specified. +If an input format is specified, all interface definition files are assumed to be in that format. +If no input format is specified, the format of each interface definition file is deduced. +If no translator is specified, all available translators will be looked up. +If an specified translator cannot be found, the tool fails. +If no translation from input format to output format is possible, the tool fails. +If no output path is specified, the current working directory is used. #### Build specification generators @@ -133,8 +167,6 @@ Customarily, interface definition files are [IDL](https://www.omg.org/spec/IDL/A A build specification must take care of any and all necessary interface definition conversions for the associated source code generator to work. Support for new languages and middlewares can be added by external packages via a system of _build generator_ plugins. -Each plugin version is expected to generate the same output for the same input over time. -Users can (but are not forced to) target specific plugin versions to prevent workspace (or underlay) updates from affecting tool output. ##### Rationale @@ -157,12 +189,12 @@ By generating a build specification in a format designed to be simple yet genera ##### Specification ```sh -rosidl build-configure ((-b|--base-configuration) PATH)? ((-o|--output-file) PATH)? ((-l|--language) IDENTIFIER(==VERSION)?)* ((-t|--type-support) IDENTIFIER(==VERSION)?)* ((-I|--include-path) PATH)* ((ABSPATH:)?RELPATH)+ +rosidl build-configure ((-o|--output-file) PATH)? ((-t|--type) IDENTIFIER)* ((-t|--type-support) IDENTIFIER)* ((-I|--include-path) PATH)* IDENTIFIER ((ABSPATH:)?RELPATH)+ ``` -All positional arguments are paths to interface definition files. -To derive interface namespaces unambiguously when applicable, absolute paths are expressed as an absolute path prefix, followed by a colon ':', followed by a relative path. -If a base configuration file is specified, argument defaults will be read from it. +The first positional argument is the package name. +All other positional arguments are paths to interface definition files. +To derive interface namespaces unambiguously when applicable, interface paths are expressed as an absolute path prefix, followed by a colon ':', followed by a relative path. If no language nor type support generator is specified, all available generators are used. If no language nor type support generator is found, the command fails. If no output file path is provided, the common build specification is sent to standard output. @@ -171,7 +203,7 @@ If no output file path is provided, the common build specification is sent to st All build logic in existing generator and type support packages is CMake code, and thus a port is necessary. Using Python would ensure tooling interoperability and their supporting libraries. -Additionally, [`setuptools` entrypoints](https://setuptools.readthedocs.io/en/latest/userguide/entry_point.html#advertising-behavior) can be leveraged as plugin system. +Additionally, [`setuptools` entry points](https://setuptools.readthedocs.io/en/latest/userguide/entry_point.html#advertising-behavior) can be leveraged as plugin system. Common build specifications can only describe platform- and/or configuration-specific variations in how a component is built and distributed. Any other conditionals must be resolved prior to generating an specification. @@ -196,7 +228,7 @@ Users can (but are not forced to) target specific plugin versions to prevent wor ##### Specification ```sh -ament_meta_build ((-o|--output-file) PATH)? (--build-prefix PATH)? (--install-prefix PATH)? ((-b|--build-system) IDENTIFIER(==VERSION)?)? PATH? +ament_meta_build ((-o|--output-file) PATH)? (--build-prefix PATH)? (--install-prefix PATH)? ((-b|--build-system) IDENTIFIER)? PATH? ``` Its only positional argument is a path to a common build specification file. @@ -223,9 +255,9 @@ For instance, an `ament_cmake` integration would generate and include CMake code # Generate C++ type representation build specification execute_process( COMMAND rosidl_build_configure - --language cpp + --type cpp -I path/to/interface/dependencies/ - interface0.idl interface1.idl + ${PROJECT_NAME} interface0.idl interface1.idl OUTPUT_FILE build.spec) # Translate build specification into CMake code execute_process( @@ -242,7 +274,7 @@ It is worth noting that these integration layers must bring in package dependenc Migrating the current architecture is fairly straightforward. -- Each tightly coupled pair of plugins for `rosidl_generate` and `rosidl_build_configure` can be built on top of each existing generator or type support package. +- Each tightly coupled pair of plugins for `rosidl generate` and `rosidl build-configure` can be built on top of each existing generator or type support package. - Generated build specifications can delegate to CMake while the `ament_meta_build` tooling is independently developed. - New `ament_cmake` macros can be added to tap into the pipeline, while keeping existing ones (which may be deprecated over time, or re-implemented on top of the former). @@ -288,7 +320,7 @@ As examples, see below the `.cbs` files that would result from running `rosidl_g "idls": { "Type": "generic", "Assembly": { - "Command": "rosidl_adapt -p foo_msgs $(sources)", + "Command": "rosidl translate -o idl foo_msgs $(sources)", "Sources": [ "msg/foo.msg", "srv/bar.srv", @@ -308,7 +340,7 @@ As examples, see below the `.cbs` files that would result from running `rosidl_g "Type": "generic", "Assembly": { "Requires": [ "action_msgs:idls", ":idls" ], - "Command": "rosidl_generate --language c -p foo_msgs -I $(location action_msgs:idls) $(sources)", + "Command": "rosidl generate --type c -I $(location action_msgs:idls) foo_msgs $(sources)", "Sources": [ "msg/foo.idl", "srv/bar.idl", @@ -454,7 +486,7 @@ As examples, see below the `.cbs` files that would result from running `rosidl_g // Generated IDL files from ROS legacy definitions "Type": "generic", "Assembly": { - "Command": "rosidl_adapt -p foo_msgs $(sources)", + "Command": "rosidl translate -o idl foo_msgs $(sources)", "Sources": [ "msg/foo.msg", "srv/bar.srv", @@ -475,7 +507,7 @@ As examples, see below the `.cbs` files that would result from running `rosidl_g "Type": "generic", "Assembly": { "Requires": [ "action_msgs:idls", ":idls" ], - "Command": "rosidl_generate -l python -p foo_msgs -I $(location action_msgs:idls) $(sources)", + "Command": "rosidl generate -t python -I $(location action_msgs:idls) foo_msgs $(sources)", "Sources": [ "msg/foo.idl", "srv/bar.idl",