|
| 1 | +# Building `ros2_rust` packages |
| 2 | + |
| 3 | +This is a more detailed guide on how to build ROS 2 packages written in Rust that expands on the minimal steps in the README. |
| 4 | + |
| 5 | +In this guide, the Foxy distribution of ROS 2 is used, but newer distributions can be used by simply replacing 'foxy' with the distribution name everywhere. |
| 6 | + |
| 7 | + |
| 8 | +## Environment setup |
| 9 | + |
| 10 | +Building `rclrs` requires a standard [ROS 2 installation](https://docs.ros.org/en/foxy/Installation.html), and a few Rust-specific extensions. |
| 11 | +These extensions are: `colcon-cargo`, `colcon-ros-cargo`, `cargo-ament-build`. The former two are `colcon` plugins, and the latter is a `cargo` plugin. |
| 12 | + |
| 13 | +It is recommended to use the premade Docker image that contains all the necessary dependencies. |
| 14 | +If you do not want to use Docker, see the `Dockerfile` in the `ros2_rust` repo for how dependencies can be installed. |
| 15 | + |
| 16 | + |
| 17 | +### Choosing a workspace directory and cloning `ros2_rust` |
| 18 | + |
| 19 | +A "workspace directory", or just "workspace", is simply a directory of your choosing that contains your `ros2_rust` checkout and potentially other ROS 2 packages. It will also usually be your working directory for building. There is only one limitation: It must not contain the ROS 2 installation, so it can't be `/`, for instance. Note that this has nothing to do with a [`cargo` workspace](https://doc.rust-lang.org/book/ch14-03-cargo-workspaces.html). |
| 20 | + |
| 21 | +If you don't have a workspace directory already, simply create an empty directory in a convenient location. |
| 22 | + |
| 23 | +Next, clone `ros2_rust` into it. The rest of this article will assume that you have cloned it into a subdirectory named `src` like this: |
| 24 | + |
| 25 | +```shell |
| 26 | +# Make sure to run this in the workspace directory |
| 27 | +mkdir src |
| 28 | +git clone https://github.com/ros2-rust/ros2_rust.git src/ros2_rust |
| 29 | +``` |
| 30 | + |
| 31 | +*Note: Once `rclrs` is published on crates.io, it's not technically needed anymore to clone the `ros2_rust` repo, and this section should be adapted to reflect that.* |
| 32 | + |
| 33 | +### Using the Docker image |
| 34 | + |
| 35 | +Build the Docker image with |
| 36 | + |
| 37 | +```shell |
| 38 | +# Make sure to run this in the workspace directory |
| 39 | +docker build -t ros2_rust_dev - < src/ros2_rust/Dockerfile |
| 40 | +``` |
| 41 | + |
| 42 | +and then run it with |
| 43 | + |
| 44 | +```shell |
| 45 | +# Make sure to run this in the workspace directory |
| 46 | +docker run --rm -it --volume $(pwd):/workspace ros2_rust_dev /bin/bash |
| 47 | +``` |
| 48 | + |
| 49 | +As you can see, this maps the workspace directory to `/workspace` inside the container. So, if you're using Docker, that is now the "workspace directory". |
| 50 | + |
| 51 | + |
| 52 | +### Importing repositories |
| 53 | + |
| 54 | +`ros2_rust` also expects a few other repositories to be in the workspace. They can be imported from a `repos` file contained in `ros2_rust`, like this: |
| 55 | + |
| 56 | +```shell |
| 57 | +# Make sure to run this in the workspace directory |
| 58 | +vcs import src < src/ros2_rust/ros2_rust_foxy.repos |
| 59 | +``` |
| 60 | + |
| 61 | +This uses the [`vcs` tool](https://github.com/dirk-thomas/vcstool), which is preinstalled in the Docker image. |
| 62 | + |
| 63 | +The new repositories are now in `src/ros2`. |
| 64 | +The list of needed repositories should very rarely change, so you typically only need to do this once. |
| 65 | + |
| 66 | + |
| 67 | +### Sourcing the ROS 2 installation |
| 68 | + |
| 69 | +Before building, the ROS 2 installation, and ideally _only_ the ROS 2 installation, should be sourced. |
| 70 | +Sourcing an installation populates a few environment variables such as `$AMENT_PREFIX_PATH`, so if you are not sure, you can check that the `$AMENT_PREFIX_PATH` environment variable contains the ROS 2 installation, e.g. `/opt/ros/foxy`. |
| 71 | + |
| 72 | +In the pre-made Docker image, sourcing is already done for you. Otherwise, if `$AMENT_PREFIX_PATH` is empty, you need to source the ROS 2 installation yourself: |
| 73 | + |
| 74 | +```shell |
| 75 | +# You can also do `source /opt/ros/foxy/setup.bash` in bash |
| 76 | +. /opt/ros/foxy/setup.sh |
| 77 | +```` |
| 78 | + |
| 79 | +If `$AMENT_PREFIX_PATH` contains undesired paths, exit and reopen your shell. If the problem persists, it's probably because of a sourcing command in your `~/.bashrc` or similar. |
| 80 | +
|
| 81 | +It's convenient to install `tmux`, especially in Docker, and open separate windows or panes for building and running. |
| 82 | + |
| 83 | + |
| 84 | +### Checking your setup |
| 85 | + |
| 86 | +To verify that you've correctly installed dependencies and sourced your ROS 2 installation, you should be able to run |
| 87 | +
|
| 88 | +```shell |
| 89 | +colcon list |
| 90 | +``` |
| 91 | +
|
| 92 | +without errors and see a line like this in the output: |
| 93 | +
|
| 94 | +``` |
| 95 | +rclrs src/ros2_rust/rclrs (ament_cargo) |
| 96 | +``` |
| 97 | +
|
| 98 | +The build type `ament_cargo` means that the `colcon-ros-cargo` plugin works as expected. |
| 99 | +
|
| 100 | +
|
| 101 | +## Building with `colcon` |
| 102 | +
|
| 103 | +Now that everything is set up, you can build with `colcon`. The basic command to build a package and its dependencies is |
| 104 | +
|
| 105 | +```shell |
| 106 | +# Make sure to run this in the workspace directory |
| 107 | +colcon build --packages-up-to $YOUR_PACKAGE |
| 108 | +``` |
| 109 | +
|
| 110 | +where `$YOUR_PACKAGE` could be e.g. `rclrs_examples`. See `colcon build --help` for a complete list of options. |
| 111 | +
|
| 112 | +It's normal to see a `Some selected packages are already built in one or more underlay workspace` warning. This is because the standard message definitions that are part of ROS 2 need to be regenerated in order to create Rust bindings. If and when `ros2_rust` becomes an officially supported client library, this issue will disappear. |
| 113 | + |
| 114 | +In addition to the standard `build`, `install` and `log` directories, `colcon` will have created a `.cargo/config.toml` file. |
| 115 | + |
| 116 | + |
| 117 | +### Tips and limitations |
| 118 | + |
| 119 | +Don't start two build processes involving Rust packages in parallel; they might overwrite each other's `.cargo/config.toml`. |
| 120 | + |
| 121 | +A clean build will always be much slower than an incremental rebuild. |
| 122 | + |
| 123 | +`colcon test` is not currently implemented for Rust packages. |
| 124 | + |
| 125 | +The plugin to build `cargo` projects with `colcon` currently has the issue that both `cargo` and `colcon` will [rebuild all dependencies for each package](https://github.com/colcon/colcon-ros-cargo/). This makes building large projects with `colcon` less efficient, but if this is an issue, you can build with `cargo` instead. |
| 126 | + |
| 127 | + |
| 128 | +## Building with `cargo` |
| 129 | + |
| 130 | +Rust packages for ROS 2 can also be built with pure `cargo`, and still integrate with ROS 2 tools like `ros2 run`. |
| 131 | + |
| 132 | + |
| 133 | +### Learning by doing |
| 134 | + |
| 135 | +*Note: The following needs to be adapted once we publish `rclrs` on crates.io.* |
| 136 | + |
| 137 | +If you `cd` into e.g. `rclrs` before ever having built it with `colcon` and try to `cargo build` it, you'll see an error like |
| 138 | +
|
| 139 | +``` |
| 140 | + Updating crates.io index |
| 141 | +error: no matching package named `rosidl_runtime_rs` found |
| 142 | +location searched: registry `crates-io` |
| 143 | +required by package `rclrs v0.2.0 (/workspace/ros2_rust/rclrs)` |
| 144 | +``` |
| 145 | +
|
| 146 | +Why is that? It's because `rclrs/Cargo.toml` contains `rosidl_runtime_rs = "*"` instead of `rosidl_runtime_rs = { path = "../rosidl_runtime_rs" }`, even though the package at that path is the one that is meant. If that's confusing, please read on. The reason is that it is a principle of ROS 2 and `colcon` for packages to reference their dependencies only by their _name_, and not by their path. This allows packages to be moved around freely in a workspace, or replaced by versions installed through `apt`, with no changes to the source code. |
| 147 | +
|
| 148 | +Unfortunately, referring to a package only by its name makes `cargo` assume that it should download that package from `crates.io`. `colcon-ros-cargo` works around this with a little hack: At build-time, it resolves these names to concrete paths to the local package, which are then written into `.cargo/config.toml`. The entries in that file override the original locations on `crates.io`, and therefore the _local_ packages will be used instead. |
| 149 | +
|
| 150 | +So, the problem above is fixed by doing one initial build of the package, or the whole workspace, with `colcon`. This creates the `.cargo/config.toml` file, and `cargo` will now use it to find `rosidl_runtime_rs`. Of course, if you don't want to install/use `colcon` for some reason, you can also create that file yourself, or replace the name-only dependencies in `rclrs/Cargo.toml` with paths. |
| 151 | +
|
| 152 | +Running `cargo build` in `rclrs` will now work, as well as `cargo doc`, `cargo test` and all the other `cargo` commands. Unfortunately, `cargo` may sometimes print messages saying |
| 153 | +
|
| 154 | +> warning: Patch `rclrs v0.1.0 (/workspace/install/rclrs/share/rclrs/rust)` was not used in the crate graph. |
| 155 | +
|
| 156 | +This can be ignored. |
| 157 | +
|
| 158 | +However, `cargo build` will not yet work for Rust packages that depend on message packages, like `rclrs_examples`: |
| 159 | +
|
| 160 | +
|
| 161 | +``` |
| 162 | +/usr/bin/ld: cannot find -lrclrs_example_msgs__rosidl_typesupport_c |
| 163 | +/usr/bin/ld: cannot find -lrclrs_example_msgs__rosidl_generator_c |
| 164 | +collect2: error: ld returned 1 exit status |
| 165 | +``` |
| 166 | +
|
| 167 | +That is, the linker does not know where to look for the `rclrs_example_msgs` libraries. This location is contained in an environment variable set by the `setup.sh` script for `rclrs_example_msgs`. So we can just source the `install/setup.sh`, and after that, `rclrs_examples` can be built. |
| 168 | +
|
| 169 | +To summarize: |
| 170 | +
|
| 171 | +```shell |
| 172 | +# Initial build of the package with colcon |
| 173 | +# The --lookup-in-workspace flag is optional |
| 174 | +# Compare .cargo/config.toml with and without it to see its effect |
| 175 | +colcon build --packages-up-to rclrs_examples --lookup-in-workspace |
| 176 | +. install/setup.sh |
| 177 | +cd src/ros2_rust/rclrs_examples |
| 178 | +# Run cargo build, or cargo check, cargo doc, etc. |
| 179 | +cargo build |
| 180 | +``` |
| 181 | +
|
| 182 | +If you'd like to not have any of that "overriding dependencies through `.cargo/config.toml`", you can do that too of course. You just need to use concrete paths for any dependency that isn't published on `crates.io`, such as message packages. For instance, you might have to write `std_msgs = {path = '/workspace/install/std_msgs/share/std_msgs/rust'}`. |
| 183 | +
|
| 184 | +
|
| 185 | +### Integration with ROS 2 tools |
| 186 | +
|
| 187 | +How can a binary created in Rust be made available to `ros2 run`, `ros2 launch` etc.? And how can other ROS 2 packages use a `cdylib` created in Rust? For that to work, the correct files must be placed at the correct location in the install directory, see [REP 122](https://www.ros.org/reps/rep-0122.html). |
| 188 | +
|
| 189 | +It's not necessary to learn about which marker file goes where. The functionality to properly set up the install directory was extracted from `colcon-ros-cargo` into a `cargo` plugin, so that `colcon` is not required: [`cargo-ament-build`](https://github.com/ros2-rust/cargo-ament-build). |
| 190 | +
|
| 191 | +Simply use `cargo ament-build --install-base <path to install dir>` as a drop-in replacement for `cargo build`. After building, simply `. install/setup.sh` and you're good to run your executable with `ros2 run`. |
| 192 | +
|
| 193 | +
|
| 194 | +## Troubleshooting |
| 195 | +
|
| 196 | +If something goes very wrong and you want to start fresh, make sure to delete all `install*`, `build*`, `target`, and `.cargo` directories. Possibly update the Docker container if it is out of date. |
| 197 | +
|
| 198 | +
|
| 199 | +### Package identification |
| 200 | +
|
| 201 | +When you forgot to source the ROS 2 installation, you'll get this error: |
| 202 | +
|
| 203 | +> ERROR:colcon.colcon_core.package_identification:Exception in package identification extension 'python_setup_py' |
| 204 | +
|
| 205 | +Source the ROS 2 installation and try again. |
| 206 | +
|
| 207 | +
|
| 208 | +### Failed to resolve patches |
| 209 | +
|
| 210 | +When you've deleted your `install` directory but not your `.cargo` directory, you'll get this error: |
| 211 | +
|
| 212 | +> error: failed to resolve patches for `https://github.com/rust-lang/crates.io-index` |
| 213 | +
|
| 214 | +Delete the `.cargo` directory, and rebuild. |
0 commit comments