Skip to content

pixi-build-ros#

The pixi-build-ros backend is designed for building ROS (Robot Operating System) packages using the native ROS build systems. No more requirement to use colcon or catkin_tools to build your ROS packages. It provides seamless integration with Pixi's package management workflow while supporting ROS1 and ROS2 packages with automatic dependency resolution.

Warning

pixi-build is a preview feature, and will change until it is stabilized. This is why we require users to opt in to that feature by adding "pixi-build" to workspace.preview.

[workspace]
preview = ["pixi-build"]

Overview#

This backend automatically generates conda packages from ROS projects by:

  • package.xml Integration: Automatically reads package metadata (name, version, description, maintainers, dependencies) from your ROS package.xml file
  • Multi-build system support: Supports ament_cmake, ament_python, catkin, and cmake build types
  • ROS Distribution Support: Works with both ROS1 and ROS2 distributions (noetic, humble, jazzy, etc.)
  • Cross-platform support: Supports Linux, macOS and Windows
  • Automatic dependency mapping: Maps ROS dependencies to conda packages using RoboStack mappings

Basic Usage#

To use the ROS backend in your pixi.toml, add it to your package's build configuration:

[workspace]
preview = ["pixi-build"]
channels = [
    "https://prefix.dev/pixi-build-backends",
    "https://prefix.dev/robostack-jazzy",  # or robostack-humble, robostack-noetic, etc.
    "https://prefix.dev/conda-forge"
]
platforms = ["linux-64", "osx-arm64"]

[package.build]
backend = { name = "pixi-build-ros", version = "*" }

[package.build.config]
distro = "jazzy"  # or "humble", "noetic", etc.
Workspace Configuration

The workspace can be defined in the pixi.toml of the package or in a separate pixi.toml at the workspace root. For example, with a workspace structure like this:

tree -L 2
.
├── pixi.toml  # Workspace configuration
└── src
    └── my_ros_package
        ├── package.xml  # ROS package manifest
        └── pixi.toml  # Package configuration

Then you can run pixi build to create conda packages for your ROS packages.

pixi build

When you want to install it into your environment, you can do so by adding the following to your workspace pixi.toml:

[dependencies]
ros-jazzy-my-ros-package = { path = "." }
# or if the package is in a separate pixi.toml
# ros-jazzy-my-ros-package = { path = "src/my_ros_package" }
Note that you need to specify the ros-jazzy- prefix when you use a distro configuration.

Automatic Metadata Detection#

The backend will automatically read metadata from your package.xml file to populate package information that is not explicitly defined in your pixi.toml. This includes:

  • Package name and version: Automatically used if not specified in pixi.toml
  • Description: Uses the description from package.xml
  • Maintainers: Extracted from maintainer fields in package.xml
  • Homepage: From URL fields with type "website" in package.xml
  • Repository: From URL fields with type "repository" in package.xml

For example, if your package.xml contains:

<package format="3">
  <name>my_ros_package</name>
  <version>1.0.0</version>
  <description>A useful ROS package for navigation</description>
  <maintainer email="developer@example.com">John Doe</maintainer>
  <url type="website">https://github.com/user/my_ros_package</url>
  <url type="repository">https://github.com/user/my_ros_package</url>
</package>

It would be equivalent to the following pixi.toml:

[package]
name = "my_ros_package"
version = "1.0.0"
description = "A useful ROS package for navigation"
maintainers = ["John Doe <developer@example.com"]
homepage = "https://github.com/user/my_ros_package"
repository = "https://github.com/user/my_ros_package"

The backend will automatically use the metadata from package.xml to generate a complete conda package named ros-jazzy-my-ros-package. The fields in the pixi.toml will override the values from package.xml if they are explicitly set.

Automatic Dependency Resolution#

Because the definition of a dependency in a package.xml file is not similar to a conda package name, the backend needs to map ROS dependencies to conda packages.

The <distro> part of the package name is automatically generated based on the distro configuration.

Configuration Options#

You can customize the ROS backend behavior using the [package.build.config] section in your pixi.toml. The backend supports the following configuration options:

distro (Required)#

  • Type: String
  • Default: Not set (required)
  • Target Merge Behavior: Overwrite - Platform-specific distro takes precedence over base

The ROS distribution to build for. This affects dependency mapping and build configuration. If set the package name will be prefixed with ros-<distro>- automatically, otherwise the package name from pixi.toml or package.xml is used.

[package.build.config]
distro = "jazzy"  # or "humble", "noetic", "iron", etc.

env#

  • Type: Map<String, String>
  • Default: {}
  • Target Merge Behavior: Merge - Platform environment variables override base variables with same name, others are merged

Environment variables to set during the build process. These variables are available during compilation.

[package.build.config]
env = { AMENT_CMAKE_ENVIRONMENT_HOOKS_ENABLED = "1" }

Automatically injected environment variables#

The ROS backend keeps the following variables in sync with the selected distro, so you do not need to set them manually in env:

  • ROS_DISTRO — set to the distro name you configure in distro.
  • ROS_VERSION — set to "1" for ROS 1 distros and "2" for ROS 2 distros.

These values are available both while evaluating package.xml conditionals and during the generated build script. Any custom entries you provide in env are merged on top of these defaults. If you explicitly set ROS_DISTRO or ROS_VERSION in env, your values take precedence over the defaults.

debug-dir#

  • Type: String (path)
  • Default: Not set
  • Target Merge Behavior: Not allowed - Cannot have target specific value

If specified, internal build state and debug information will be written to this directory. Useful for troubleshooting build issues.

pixi.toml
[package.build.config]
debug-dir = ".build-debug"

extra-input-globs#

  • Type: Array<String>
  • Default: []
  • Target Merge Behavior: Overwrite - Platform-specific globs completely replace base globs

Additional glob patterns to include as input files for the build process. These patterns are added to the default input globs that include ROS-specific files.

pixi.toml
[package.build.config]
extra-input-globs = [
    "launch/**/*.py",
    "config/*.yaml",
    "msgs/**/*.msg",
    "srvs/**/*.srv"
]

Default input globs include: - Source files: **/*.{c,cpp,h,hpp,rs,sh,py,pyx} - ROS configuration: package.xml, setup.py, setup.cfg, pyproject.toml - Build files: CMakeLists.txt

extra-package-mappings#

  • Type: List<Map<String, Map<String, List<String> | RelativeFileName>>>
  • Default: []

Additional dependency mappings to apply to the dependency mapping process. These mappings are used to extend the usage of the dependencies in the package.xml file.

pixi.toml
[package.build.config]
extra-package-mappings = [
    {"ros-custom" = { ros =  ["ros-custom-msgs"] }},
    "mapping.yml"
]

Or using a toml array of tables:

pixi.toml
[[package.build.config.extra-package-mappings]]
custom_msgs = { ros = ["custom-messages"] }

Or you can use a file directly in the list:

pixi.toml
[package.build.config]
extra-package-mappings = ["mapping.yml"]

The mapping file can contain the following:

mapping.yml
package_name:  # The name of the package in the package.xml
  conda: conda-package-name # Maps to a conda package, e.g. from `conda-forge`
package_name2: # The name of the package in the package.xml
  conda: [package1, package2] # Maps to a list of conda packages
ros_package:   # The name of the package in the package.xml
  ros: ros_package # Maps to a RoboStack style package name, e.g. `ros-<distro>-ros-package` 

Build Process#

The ROS backend follows this build process:

  1. Package Detection: Parses package.xml to determine build type (ament_cmake, ament_python, catkin)
  2. Dependency Resolution: Maps ROS dependencies to conda packages using RoboStack mappings
  3. Environment Setup: Configures ROS-specific environment variables
  4. Build Execution: Uses the appropriate build template based on package type.
  5. Installation: Installs built artifacts to the conda package prefix

ROS Package Types#

The backend supports different ROS package build types:

ament_cmake (ROS2)#

For C++ packages using ament build system:

<export>
  <build_type>ament_cmake</build_type>
</export>

ament_python (ROS2)#

For Python packages using ament build system:

<export>
  <build_type>ament_python</build_type>
</export>

catkin (ROS1)#

For ROS1 packages using catkin build system:

<export>
  <build_type>catkin</build_type>
</export>

Limitations#

  • Version constraints: Dependency versions from package.xml are currently ignored
  • Conditional dependencies: Dependencies with conditions are not fully supported yet
  • Target-specific dependencies: Platform-specific dependencies in package.xml need manual handling

See Also#