buildEnv: Reduce length of AMENT_PREFIX_PATH and other ROS 2 variables

The purpose of ROS-specific version of buildEnv in this overlay is to
reduce the length of environment variables when using many ROS
packages. However, currently it works only for ROS 1 and not for
ROS 2. This commit is an attempt to fix that.

Although the change looks trivial, it took me multiple full days to
figure out what to change and how. The following is my understanding
of how handling of environment variables works in ROS 2 and in
nix-ros-overlay and why the change in this commit works. I'm not
completely sure that it's all correct so feel free to complain if not.

- Every ament_cmake ROS package contains a $out/share/*/local_setup.sh
  script, generated by ament CMake macros, which is responsible for
  setting environment variables required for proper function of the
  package under ROS 2. Every package extends AMENT_PREFIX_PATH and it
  may extend other variables such as PATH. The default prefix used for
  extending the variables is specified at compile time and is equal to
  package's Nix store path ($out).

- local_setup.sh files are sourced when building dependent ROS
  packages. In nix-ros-overlay, this is accomplished by
  ament-cmake-core-setup-hook, which is automatically propagated to
  all dependents.

- ROS-specific buildEnv ensures that ROS packages are not further
  propagated downstream but non-ROS packages are.
  ament-cmake-core-setup-hook is a non-ROS package (it's a Nix native
  package) so if any package in the buildEnv depends on it, it is
  propagated out of buildEnv. Therefore dependents of the buildEnv
  source all local_setup.sh files from the buildEnv (and from other
  ROS packages outside of buildEnv, if there are any).

- The problem when sourcing local_setup.sh files now is the default
  prefix built into them, which causes every package to have a
  separate entry in the extended variables. However, if the file is
  sourced with AMENT_CURRENT_PREFIX variable set, its value overrides
  the default prefix. That's what we do in this commit. We set
  AMENT_CURRENT_PREFIX to the store path of the sourced package; in
  case of normal ROS packages it's equal to the default builtin
  prefix, but in case of buildEnv, it's different and all packages in
  the environment share the same prefix.

I'm testing this change with the following flake.nix:

    {
      inputs = {
        nix-ros-overlay.url = "/path/to/repo/with/this/commit";
        nixpkgs.follows = "nix-ros-overlay/nixpkgs";
      };
      outputs = { self, nixpkgs, nix-ros-overlay }:
        let
          pkgs = import nixpkgs {
            system = "x86_64-linux";
            overlays = [ nix-ros-overlay.overlays.default ];
          };
          rosDistro = pkgs.rosPackages.humble;
          buildEnv = rosDistro.buildEnv {
            paths = with rosDistro; [
              demo-nodes-cpp
            ];
          };
        in
        {
          devShells.x86_64-linux.default = pkgs.mkShell {
            name = "ros-env-test";
            packages = [ buildEnv ];
          };
          packages.x86_64-linux = {
            default = buildEnv;
            inherit (rosDistro) demo-nodes-cpp;
          };
        };
    }

Running `nix develop` and then:

    echo $AMENT_PREFIX_PATH | tr : \\n | wc -l

without this commit returns 71 entries, while with this commit the
result in a single entry!
This commit is contained in:
Michal Sojka 2024-01-01 23:15:35 +01:00 committed by Ben Wolsieffer
parent 5f5fc56984
commit dd9cc83369

1
distros/ament-cmake-core-setup-hook/setup-hook.sh Normal file → Executable file
View file

@ -14,6 +14,7 @@ _findAmentPackages() {
# ROS scripts use unbound variables
set +u
for setup in "$pkg"/share/*/local_setup.sh; do
AMENT_CURRENT_PREFIX=$pkg
source "$setup"
done
set -u