aica-package.toml
configuration reference
The aica-package.toml
file is used to configure the build process for a component package.
TOML
syntax
The configuration uses the simple TOML file format which is easy to read and write. Here are a few examples:
# add a category
[category]
# then you can add keys and values
key-string = "value string"
key-bool = true
key-int = 42
key-float = 3.14
# you can nest categories
[category.sub-category]
key = "value"
Note that each string
value needs to be quoted, but keys and values of other types do not.
aica-package.toml
file structure
This example contains every possible key and value that can be used in an aica-package.toml
file and explains each of
them.
#syntax
Each aica-package.toml
should start with #syntax=ghcr.io/aica-technology/package-builder
. This is used to specify
the Docker syntax and version used to build the package.
#syntax=ghcr.io/aica-technology/package-builder:v1
You can find the available versions here.
The upcoming sections describe the syntax for the package-builder
version 1.0.0
and later. If you are migrating from an older version, please refer to the corresponding guide:
[build]
[build.type]
Required. The only [build.type]
currently supported is ros
.
[build]
type = "ros"
[build.ssh]
Optional. Default is false
. If set to true
, any call to CMake
, pip
or custom stage will have access to the SSH credentials given to Docker with --ssh default
. This is useful if you need to clone a private repository in CMakeLists.txt or install a private Python package from git.
[build]
ssh = false
[build.image]
Required. [build.image]
is the tag of the AICA ghcr.io/aica-technology/ros2-ws
image that will be used to build the components. Those images are tagged after the versions of the ROS 2 distributions and are available here.
[build]
image = "v1.0.0-iron"
[build.cmake-args]
Optional. In this category, you can specify arguments which will be passed to CMake through -DK1=V1 -DK2=V2 ...
[build.cmake-args]
# here are a few examples
PACKAGE_NAME = "my_package"
USE_FEATURE_X = "ON"
[build.apt-repos]
Optional. This category allows you to add extra APT repositories to the image. This is useful if you need to install some packages that are not available in the default repositories, which is common for third-party packages.
Multiple syntaxes are supported:
Using a deb
file
[build.apt-repos]
cuda = { deb-uri = 'https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2204/{{ if eq .Arch "amd64" }}x86_64{{else}}sbsa{{end}}/cuda-keyring_1.0-1_all.deb' }
Note that {{ if eq .Arch "amd64" }}x86_64{{else}}sbsa{{end}}
is a template that will be replaced by x86_64
if the architecture is amd64
and sbsa
otherwise. See here for more information on the templating syntax.
Using a repository (with an optional keyring)
[build.apt-repos.librealsense]
type = "deb" # optional, defaults to "deb", can also be "deb-src"
uri = "https://librealsense.intel.com/Debian/apt-repo"
distribution = "jammy" # optional, will default to the current distribution
components = ["main"]
keyring = "https://librealsense.intel.com/Debian/librealsense.pgp" # optional
Note that in this case we use a subcategory to specify the property of this repository, but it can also be expressed using the {}
syntax like the previous example:
[build.apt-repos]
librealsense = { type = "deb", uri = "https://librealsense.intel.com/Debian/apt-repo", distribution = "jammy", components = ["main"], keyring = "https://librealsense.intel.com/Debian/librealsense.pgp" }
[build.dependencies]
Optional. [build.dependencies]
is used to specify the AICA libraries and ROS 2 packages that will be installed in the
image.
Those libraries and packages will not be available at runtime, only while building. If you need them for building and running, add them to the [build.packages.XYZ.dependencies]
section below instead.
Libraries that are only required at runtime, and not at build time, can be declared with [build.run-dependencies]
instead.
Those libraries and packages are built for specific versions of ROS 2, so make sure that the version you are using is compatible with your [build.image]
.
Components usually require the control-libraries
library. You can find the available
versions here. If you are
building a hardware interface, you might want to use the network-interfaces
library; you can find the available
versions here.
Components usually require the modulo
package. You can find the available
versions here.
Libraries are built in a specific way to be compatible with the AICA packaging system. Custom libraries are not available yet.
Starting with version 1.0.0
of the package-builder
, all libraries and packages need to have special metadata associated in their image. This is done automatically when building with newer versions of package-builder
. This means you won't be able to use older versions of certain libraries and packages with newer versions of package-builder
.
[build.dependencies]
"@aica/foss/control-libraries" = "v7.5.0"
"@aica/foss/network-interfaces" = "v3.0.0"
"@aica/foss/modulo" = "v4.2.0"
"@myorg/mypackage" = "docker-image://ghcr.io/myorg/mypackage:v1.0.0"
Adding version constraints
Because package-builder
will use your dependencies in the metadata of the component, the version of the dependencies will be expected when the component is used. You can tweak which exact version is expected by using version constraints.
[build.dependencies]
"@aica/foss/control-libraries" = {version = "v7.5.0", constraints = ">= 7.5.0"}
By default, the version constraint is derived from the given version. It will be ~> X.Y
where X
and Y
are the major and minor versions of the given version. This means that any version with the same major version and a minor version greater than or equal to the given minor version will be accepted.
You can find more information about version constraints and their syntax here.
Adding build-time dependencies
Sometimes you might want to add a library but not require it in the metadata of your component. We call those dependencies build-only dependencies. This might be useful when using a static library that is built into your component and thus is not needed at runtime.
[build.dependencies]
"@myorg/mylib" = {version = "v7.5.0", build-only = true}
You can also add version constraints to build-only dependencies.
[build.run-dependencies]
Optional. This field is similar to [build.dependencies]
but its content is not downloaded and added to the image during the build. Instead, the information contained there is used in the metadata of your component. When someone uses your component, they will be notified that they need to use a specific version of the added dependencies matching the one specified in this section.
[build.run-dependencies]
"@myorg/mypackage" = ">= 1.0.0"
The value expected for each entry is not a version but a version constraint. You can find more information about this concept and its syntax in the corresponding section here.
[build.packages]
Required. This category describes the different components that will be built.
The name of the component doesn't need to match the one set in package.xml
; it is only used during the build.
[build.packages.XYZ.source]
is required and defines the path to the component source code relative to the Docker
context (usually the root of the repository).
[build.packages.my_component]
source = "my_component"
You can add as many components as you want, e.g.
[build.packages.another]
source = "another"
[build.packages.evenmore]
source = "different-folder/event-more"
[build.packages.XYZ.dependencies.apt]
Optional. This category allows you to specify the APT packages that will be installed in the image. This is useful if you need to install some system dependencies.
By default, available packages are based on the Ubuntu version used by the specified version of ROS2. You can find the list of packages here and the Ubuntu version used by the ROS2 distribution here. See this section to learn how to add extra APT repositories.
You can either provide a version to be installed or *
to install the latest available version.
Packages specified in <*depend>
in package.xml
will be installed through rosdep
automatically.
[build.packages.my_component.dependencies.apt]
libopencv-dev = "*"
libyaml-cpp-dev = "2.55.1"
[build.packages.XYZ.dependencies.pip]
Optional. This category allows you to specify the Python packages that will be installed in the image.
By default, aica-package.toml
will check if your [build.packages.XYZ.source]
folder contains a requirements.txt
file and install the packages specified inside.
You can override the name of the file by using the [build.packages.XYZ.dependencies.pip.file]
key.
The [build.packages.XYZ.dependencies.pip.file]
key and the [build.packages.XYZ.dependencies.pip.packages]
list are
mutually exclusive; you can only use one of them.
[build.packages.XYZ.dependencies.pip.packages]
roughly follows the format
of pip
: https://pip.pypa.io/en/stable/reference/requirements-file-format/
[build.packages.my_component.dependencies.pip]
# e.g. if your requirements file is called `aica-requirements.txt`
file = "aica-requirements.txt"
# OR
[build.packages.my_component.dependencies.pip.packages]
numpy = "2.0.0" # equivalent to `numpy==2.0.0`
opencv-python = "\*" # equivalent to `opencv-python`
scipy = ">=1.0.0" # equivalent to `scipy>=1.0.0`
[build.stages]
Optional. This category allows you to define custom stages that can be used with docker build --target
. These are
useful if you want to run some extra commands during or at the end of the build process or if you need a custom output.
Here is an example that will call some Python code during the build process. The name of the stage, list
, will be used
with docker build --target list
to run this stage.
[build.stages.XYZ.from]
is required; it is the stage of the build process on top of which this stage will run. It can
be any of:
base
: only the environment has been appliedcode
: based onbase
, the code has been copied)development
: based oncode
, runnable image setup for debugging with dependencies installedbuild
: based oncode
, the component has been built and the dependencies installedtest
: based onbuild
, the component has been testedproduction
: scratch image containing just to built component
[build.stages.XYZ.run]
is required. It is the command that will be run in the stage. The command will be run as a
non-root user with the colcon
workspace setup and the current working directory will be the colcon
workspace. The
following environment variables should be available:
WORKSPACE
: the path to thecolcon
workspace, which is also the current working directoryUSER
: the name of the non-root userHOME
: the path to the home directory of the non-root userROS_DISTRO
: the ROS2 distribution used to build the componentROS2_WORKSPACE
: the path to the workspace where the standard ROS2 packages are installed as well as the[build.dependencies]
packages- any other environment variables set by
colcon
's${WORKSPACE}/install/setup.bash
[build.stages.list]
from = "development"
run = """
python3 ${WORKSPACE}/src/my_component/script.py
"""
[metadata]
Required.
[metadata.version]
Required. Must be a semver-compliant version string.
[metadata]
version = "1.0.0"
[metadata.description]
Optional. A short description of the component.
[metadata]
description = "My awesome component"
[metadata.collection]
Required when using multiple [build.packages]
, otherwise ignored. This must contain the names of the collection being built (the set of multiple components).
[metadata.collection.name]
is required. It is the name of the collection.
[metadata.collection.ros-name]
is optional. This must be specified if [metadata.collection.name]
is not a valid ROS package name.
[metadata.collection]
name = "my-collection"
ros-name = "my_collection" # required because `my-collection` is not a valid ROS package name
Advanced usage
docker-image://
As documented above, you can use docker-image://
to specify your own [build.dependencies]
packages. However,
you can also use it anywhere you are giving the tag to a Docker image. This is useful if you want to use a custom base
image for example.
Some examples:
[build]
image = "docker-image://ghcr.io/myorg/myimage:v1.0.0"
# and/or
[build.dependencies]
"@aica/foss/control-libraries" = "docker-image://ghcr.io/myorg/myimage:v1.0.0"
build-context://
Similarly to docker-image://
, you can use build-context://
to specify a context given to docker build
with --build-context
(see here).
This can also be used in [build.packages.XYZ.source]
to build a component from a folder outside your root context or
from another Docker image.
Some examples:
[build.dependencies]
"@aica/foss/control-libraries" = "build-context://cl"
# and/or
[build.packages.component]
source = "build-context://my_source"
Version constraints
Version constraints follow the syntax of the Terraform version constraints which is similar to syntax used by NPM, yarn or pip. Here is a quick summary:
- Versions constraints are composed of one or more conditions separated by commas, e.g.
>= 1.0.0, < 2.0.0
. - Each version specified must be a valid semver version, e.g.
1.0.0
. - The following operators are supported:
=
or no operator: allow only the exact version, cannot be combined with other conditions.!=
: exclude a specific version.>
,>=
,<
,<=
: comparison against a specific version, allowing any version matching the operator.>
allows newer version and<
allows older version.~>
: allows only the right most number of the version to increase. This is useful to allow only patch or minor versions to increase, e.g.~> 1.0
allows1.1
,1.2
, etc. but not2.0
and~> 1.0.0
allows1.0.1
,1.0.2
, etc. but not1.1.0
.
Examples
Basic setup
Run with:
docker build -f aica-package.toml -t my_component .
#syntax=ghcr.io/aica-technology/package-builder:v1
[build]
type = "ros"
image = "v1.0.0-iron"
[build.dependencies]
"@aica/foss/control-libraries" = "v7.3.0"
"@aica/foss/modulo" = "v4.0.0"
[build.packages.component]
source = "./custom_component_package"
With dependencies
Run with:
docker build -f aica-package.toml -t my_component .
#syntax=ghcr.io/aica-technology/package-builder:v1
[build]
type = "ros"
image = "v1.0.0-iron"
[build.dependencies]
"@aica/foss/control-libraries" = "v7.3.0"
"@aica/foss/modulo" = "v4.0.0"
[build.packages.component]
source = "./custom_component_package"
# `requirements.txt` will be used by default
[build.packages.component.dependencies.apt]
libopencv-dev = "*"
Advanced usage
Run with:
docker build -f aica-package.toml \
--build-context my_source=../my_folder \
--build-context base=ghcr.io/myorg/myimage:v1.0.0 \
--build-context cl=ghcr.io/myorg/myimage:v2.0.0 \
-t my_component \
.
#syntax=ghcr.io/aica-technology/package-builder:v1
[build]
type = "ros"
image = "docker-image://base"
[build.dependencies]
"@aica/foss/control-libraries" = "build-context://cl"
"@aica/foss/modulo" = "v4.0.0"
[build.packages.component]
source = "build-context://my_source"
Custom stage
docker build -f aica-package.toml -t my_component --target list .
#syntax=ghcr.io/aica-technology/package-builder:v1
[build]
type = "ros"
image = "v1.0.0-iron"
[build.packages.component]
[build.stages.list]
from = "development"
run = """
python3 ${WORKSPACE}/src/my_component/script.py
"""
docker build
usage
We use a custom Docker syntax, which allows us to define the configuration through aica-package.toml
instead of
a Dockerfile
. However, we are still using docker
directly to build. This means we have access to all the
options docker build
provides but also that you don't need to install or learn anything new to build your component.
Some useful options:
-t
or--tag
: tag the image with a name and an optional tag, e.g.-t my_component
or-t my_component:latest
--platform
: build for a specific platform, e.g.--platform linux/amd64
or--platform linux/arm64
--build-arg
: this can be used to override any key inaica-package.toml
, e.g.--build-arg config.build.image=jazzy
--ssh
: this can be used to pass SSH credentials to Docker, e.g.--ssh default
. You will also need to set[build.ssh]
totrue
inaica-package.toml
--build-context
: this can be used to pass a context to Docker, e.g.--build-context my_source=../my_folder
. You can then usebuild-context://my_source
inaica-package.toml
to build a component from a folder outside your root context or from another Docker image--target
: this can be used to run a custom stage, e.g.--target list
. You can then use[build.stages.list]
inaica-package.toml
to define the stage--no-cache
: this can be used to force Docker to rebuild the image from scratch--progress
: this can be used to change the progress output, e.g.--progress plain
or--progress auto
--label
: this can be used to add metadata to the image, e.g.--label my_label=my_value