# Generating 'dev' and runtime artefacts from ROS packages

**URL:** <https://discourse.openrobotics.org/t/generating-dev-and-runtime-artefacts-from-ros-packages/12448>\
**Category:** Buildfarm\
**Created:** [January 22, 2020, 8:24pm UTC](https://discourse.openrobotics.org/t/generating-dev-and-runtime-artefacts-from-ros-packages/12448 "2020-01-22T20:24:27Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![gavanderhoorn](https://sea2.discourse-cdn.com/flex022/user_avatar/discourse.openrobotics.org/gavanderhoorn/32/969_2.png) [@gavanderhoorn](https://discourse.openrobotics.org/u/gavanderhoorn)\
**Post date:** [January 22, 2020, 8:24pm UTC](https://discourse.openrobotics.org/t/generating-dev-and-runtime-artefacts-from-ros-packages/12448/1 "2020-01-22T20:24:27Z")

</div>

First: I’m not entirely sure this belongs here, but I couldn’t think of a better category. It’s probably something that will touch the buildfarm if we can come up with something, hence why I posted it here.

* * *

I’ve been interested in reducing the size of deployments of ROS applications, both native and in Docker (and Singularity) images for some time now, and one thing that I have been curious about is whether it would be possible to generate separate runtime and `-dev` archives from ROS packages (to use the Debian/Ubuntu terminology).

Some Googling led me to [How to separate bloom-generate’d debian package into variants (release, -dev, -dbg)?](https://answers.ros.org/question/299788) on ROS Answers, which has an answer by @tfoote mentioning the support for automatic generation of `-dbg` symbol packages (which has been active for Melodic and newer releases), but couldn’t find additional discussions about the topic.

Using multi-stage `Dockerfile`s tremendous savings can be accomplished (going from 4GB+ to 200MB images), but this is non-trivial with ROS packages in the mix.

Thinking about it, I can see two aspects which may make this either difficult or reduce the gains significantly:

1. ROS has (very) few leaf packages: many ROS packages export libraries, headers and other resources that get consumed by others as part of a build. This means that when a package is installed, it’s most likely installed because it’s being used by another. Main contributor to package size are the libraries (this is an assumption), which would always be present and thus having a separate runtime and `-dev` package doesn’t offer much net gain.
2. it’s not necessarily something that can be automated: creating proper Debian packages can be _hard_. Part of what makes it complex is knowing what should go where, and this is not always obvious either. A single source package could spawn multiple binary packages, and the rules that govern this are typically largely hand written.

Pt 1 may not be too problematic. It’s an assumption and would be something to figure out.

Pt 2 could perhaps be approached very naively: assume everything exported by `INCLUDE_DIRS` in a `catkin_package(..)` call (in ROS 1) _and_ is local to the exporting package is what should go in a `-dev` package. Assume everything else would go into the runtime package. That information would somehow be transferred to Bloom when it populates the package structure and `debian` templates.

Another option could be to use CMake’s support for `COMPONENT` in `install(..)` rules (as that could make it work for ROS 2 as well), but I have no idea whether that would help when generating Debian/RPM/something-else packages.

* * *

I also realise that to do this right is probably going to be complex and will require quite some effort.

This post here on Discourse is to find out whether others have thought about this as well or perhaps even have implemented something in this direction.

---

<div class="post-metadata">

**Author:** ![tfoote](https://sea2.discourse-cdn.com/flex022/user_avatar/discourse.openrobotics.org/tfoote/32/3813_2.png) [@tfoote](https://discourse.openrobotics.org/u/tfoote)\
**Post date:** [January 23, 2020, 5:26am UTC](https://discourse.openrobotics.org/t/generating-dev-and-runtime-artefacts-from-ros-packages/12448/2 "2020-01-23T05:26:27Z")

</div>

This is definitely something that has been considered at various times. And it’s been thought about recently by @cottsay who has been looking into generating rpms. As rpms have stricter linters.

The automation of separating dbg symbols was eventually resolved with it becoming part of the standard debian toolchain:

[https://github.com/ros-infrastructure/bloom/pull/32](https://github.com/ros-infrastructure/bloom/pull/32)

From that issue you can find links to various other discussions of adding `-dev` packages as far back as 2011. But on reflecting on it I think that the reason that we haven’t found a solution for created them is that it doesn’t make sense to “automatically” split packages. There are many heuristics to say when to put something into the dev version or the runtime component. But in reality there’s many corner cases that will never be met so the maintainer will basically need to list out resources to land in either regular or dev variants of the package. In the Debian world this is the role of the debian package maintainer. Each resource is placed into one or the other bucket. But more importantly the downstream package maintainer then has to add the right dependency on either the runtime or devel package. That downstream maintainer also splits their package into runtime and devel packages. But more importantly they also split the dependencies conditionally on the devel and runtime dependencies appropriately.

Now you can see that we actually have 4 different cases for one dependency. There’s both devel and runtime versions of both packages. And the dependencies declared on the downstream package have to be conditionally evaluated for the subset of the package that is being used to build or made available.

We have a rich language of dependencies with at least 5 different versions of dependencies for a package including `build`, `build_export`, `buildtool`, `buildtool_export`, `exec`. And that’s excluding the extra test and doc dependencies as well as groups etc. We would then have to include a way to declare associations of each of those dependencies to the runtime or devel versions of a package.

Looking at this from a little bit further away, the main reason for having `foo` and `foo-dev` packages is because historically `foo` was released as a tarball and then the debian maintainers would pick up the tarball, and select what went to each subpackage, and add the appropriate dependency declarations. Since then the process has not evolved much, though now often it’s pulled directly from the source repository. But in general the upstream maintainers are not thinking about the impacts of releasing their software and packaging.

By comparison in the ROS ecosystem core developers actively use packages as atomic units and leverage those packages even when developing, and they use and pay attention to the package dependencies. The efficiency gains from modular packaging that are usually gained by the debian maintainers work can be leveraged in the development environment too.

As such I think that it might behoove us not to think of this as a problem to be fixed at the release stage, but as an area of efficiency that we can gain by structuring our upstream packages similiarly. The benefits of separating the build and runtime dependencies helps overall with compile times. Dependency trees do not grow as quickly if they are not the union of build and runtime dependencies. Parallel build tools can build faster when these potentials are paid attention to in the development environment.

And I think that many of our common practices that the ROS community has developed such as recommending making message only packages gets a lot to this end. They are the higher level equivalent of headers and break apart the dependency trees by providing standard interfaces. And through encouraging small modular packages we get the ability to leverage the find grain dependency control.

Specifically separating the build and runtime dependencies is something that has been shown to be quite effective that we’re not actively doing. However, instead of trying to do that at release time. I’d suggest that we consider making that a best practice for the ROS maintainers to create full packages that separate concerns into runtime and build time dependencies. This will give the developer the ability to fully specify the dependencies for each and where content goes is also clearly obvious. We could even pick up the naming convention of debian to name the content designed for build time to end with `-dev` however that would then conflict with the Fedora/RPM convention to name things with `-devel`.

In ROS 2 we’ve leveraged this separation of dependency types to enable swapping out the runtime libraries and hide the underlying implementations yet building them all against the same headers by defining a clear interface specification. There’s always a matter of tradeoffs between making things more modular and keeping things as simple as possible.

So in conclusion I think what I’d like to suggest is that we already have support for all the bells and whistles needed to very clearly define the necessary dependencies. And that if we can identify places where separating the build and runtime dependencies would be valuable that we consider simply making separate packages and not try to find ways to automatically split a package and then find ways to refer to those partial packages in downstream packages. We’re generating packages, we have packages. Keeping it one to one is basically the simplest solution.

---

<div class="post-metadata">

**Author:** ![Will\_Gardner](https://sea2.discourse-cdn.com/flex022/user_avatar/discourse.openrobotics.org/will_gardner/32/5420_2.png) [@Will\_Gardner](https://discourse.openrobotics.org/u/Will_Gardner)\
**Post date:** [January 23, 2020, 9:41am UTC](https://discourse.openrobotics.org/t/generating-dev-and-runtime-artefacts-from-ros-packages/12448/3 "2020-01-23T09:41:57Z")

</div>

At work I am in a similar situation. We extensively work with docker images and found that ROS certainly doesn’t make it easy to have small docker images. On the larger end they could easily reach 12GiB (although a lot of that was our own negligence). Even a base install of ROS resulted in close to a 800MiB compressed image.

You’re absolutely correct that there is an inherent limitation at the moment with ROS packages _always_ including the dev artifacts and that has given us real trouble in a few areas. One of the things we did actually find however is that nine times out of ten it isn’t actually the ROS packages themselves that are the problem - virtually all of the built debians contain just shared libraries and headers which is only going to be barely larger than a non-dev package. It’s the upstream dependencies that they hold which are consistently problematic.

As an example the `ros-melodic-cpp-common` deb package, which is in any core installation of ROS, has a dependency on libboost-dev. This boost metapackage is _massive_ and has massive dependencies of its own, installing it on a fresh Ubuntu docker image installs 586MB worth of stuff even with `--no-install-recommends`. Some of the dependencies that get pulled in are just plain useless for actually **running** ROS applications:

- gcc
- perl
- python3

To workaround this problem we came up with a novel solution. Before installing our ROS dependencies into our docker images we install custom, fake, metapackages. These metapackages are special in that they don’t install anything themselves, they simply “provide” (in the debian packaging sense) all of the upstream dev dependencies of which ever ROS packages we’re intending to install (plus a subset of ROS packages that we definately don’t need) without actually adding anything to the host system. That way when we later install the ROS dependencies it doesn’t actually install the dev dependencies. It turns out with this alone the size of a bare ROS docker image went from ~800MiB down to ~100MiB compressed. We’ve similarly extended this strategy to tame OpenCV and CUDA which means even our largest base images max out at about 400MiB.

The positive news is that solving this specific problem in ROS packaging more generally is probably much easier than implementing `-dev` packages in their entirety. In theory all that is needed is to produce `-dev` packages that just have extra `-dev` dependencies of their own, no extra files.

To a degree this should be straight forward as package.xml already has a mechanism for declaring these dev only transitive dependencies: `build_export_depend`. The downside of this approach is that it involves fixing dependencies on basically every package in the ROS ecosystem since that’s mainly used to export headers at the moment.

A hackier approach would be on the rosdep side. At the moment the `boost` rosdep key is declared as being the dev packages for every given OS: [rosdistro/rosdep/base.yaml at master · ros/rosdistro · GitHub](https://github.com/ros/rosdistro/blob/master/rosdep/base.yaml#L272). It should be possible to create a “parallel” rosdep file that declares the non-dev versions of packages which can then be automatically used by bloom to generate runtime packages with the non-dev dependencies.

The final approach is as @tfoote has suggested, explicitly create ‘-dev’ ROS packages. The downside of this approach is legacy, there is a substantial amount of legwork to implement this approach and tons of breaking changes to go with it. It also seems overkill since ROS already has most of the mechanisms in place to resolve this on the release side.

---

<div class="post-metadata">

**Author:** ![gavanderhoorn](https://sea2.discourse-cdn.com/flex022/user_avatar/discourse.openrobotics.org/gavanderhoorn/32/969_2.png) [@gavanderhoorn](https://discourse.openrobotics.org/u/gavanderhoorn)\
**Post date:** [January 23, 2020, 9:58am UTC](https://discourse.openrobotics.org/t/generating-dev-and-runtime-artefacts-from-ros-packages/12448/4 "2020-01-23T09:58:15Z")

</div>

> [@Will\_Gardner](#):
>
> Before installing our ROS dependencies into our docker images we install custom, fake, metapackages. These metapackages are special in that they don’t install anything themselves, they simply “provide” (in the debian packaging sense) all of the upstream dev dependencies of which ever ROS packages we’re intending to install (plus a subset of ROS packages that we definately don’t need) without actually adding anything to the host system

Are these REP-127/140/149 metapackages, or the Debian variant? If the latter, are you using `equivs` for this?

> [@Will\_Gardner](#):
>
> As an example the `ros-melodic-cpp-common` deb package, which is in any core installation of ROS, has a dependency on libboost-dev. This boost metapackage is _massive_ and has massive dependencies of its own, installing it on a fresh Ubuntu docker image installs 586MB worth of stuff even with `--no-install-recommends` .

Yes, that was something I quickly ran into as well.

> [@Will\_Gardner](#):
>
> That way when we later install the ROS dependencies it doesn’t actually install the dev dependencies. It turns out with this alone the size of a bare ROS docker image went from ~800MiB down to ~100MiB compressed. We’ve similarly extended this strategy to tame OpenCV and CUDA which means even our largest base images max out at about 400MiB.
> 
> The positive news is that solving this specific problem in ROS packaging more generally is probably much easier than implementing `-dev` packages in their entirety. In theory all that is needed is to produce `-dev` packages that just have extra `-dev` dependencies of their own, no extra files.

Could you go into a bit more detail about your approach (haven’t had my coffee yet)? Are you referring to extra ROS packages here which package the dev dependencies?

> [@Will\_Gardner](#):
>
> To a degree this should be straight forward as package.xml already has a mechanism for declaring these dev only transitive dependencies: `build_export_depend` . The downside of this approach is that it involves fixing dependencies on basically every package in the ROS ecosystem since that’s mainly used to export headers at the moment.

So if I understand you correctly you’d like to also use it to export “other” build dependencies?

* * *

Thanks for the insight, this is quite a creative way to deal with this (although technically still a work-around 😉 ).

---

<div class="post-metadata">

**Author:** ![Will\_Gardner](https://sea2.discourse-cdn.com/flex022/user_avatar/discourse.openrobotics.org/will_gardner/32/5420_2.png) [@Will\_Gardner](https://discourse.openrobotics.org/u/Will_Gardner)\
**Post date:** [January 23, 2020, 10:57am UTC](https://discourse.openrobotics.org/t/generating-dev-and-runtime-artefacts-from-ros-packages/12448/5 "2020-01-23T10:57:55Z")

</div>

> Are these REP-127/140/149 metapackages, or the Debian variant? If the latter, are you using `equivs` for this?

Debian variant. When I prototyped it originally I did use `equivs` although we create them with custom tooling nowadays.

> Could you go into a bit more detail about your approach (haven’t had my coffee yet)? Are you referring to extra ROS packages here which package the dev dependencies?

No. Ultimately we only care about the debian packages. Certainly one approach to achieve that _is_ to have separate dev ros packages but that involves overhauling basically every C++ package in the ROS ecosystem since they all invariably depend on boost in some way.

> So if I understand you correctly you’d like to also use it to export “other” build dependencies?

Yes, so for example I could say my `foo` package has `build_export_depend` on boost-dev, but `exec_depend` on `boost-non-dev`. This would then allow bloom to automatically generate two debian packages:

`foo`: depends on boost-non-dev, contains all of the foo package itself  
`foo-dev` depends on boost-dev and `foo`

This would only require a fairly procedural update to fix up everyone’s package.xml

---

<div class="post-metadata">

**Author:** ![ruffsl](https://sea2.discourse-cdn.com/flex022/user_avatar/discourse.openrobotics.org/ruffsl/32/83_2.png) [@ruffsl](https://discourse.openrobotics.org/u/ruffsl)\
**Post date:** [January 23, 2020, 10:52pm UTC](https://discourse.openrobotics.org/t/generating-dev-and-runtime-artefacts-from-ros-packages/12448/6 "2020-01-23T22:52:52Z")

</div>

> [@Will\_Gardner](#):
>
> It turns out with this alone the size of a bare ROS docker image went from ~800MiB down to ~100MiB compressed. We’ve similarly extended this strategy to tame OpenCV and CUDA which means even our largest base images max out at about 400MiB.

Nice! Do you have any Dockerfiles you could share publicly that demonstrate this approach?

---

<div class="post-metadata">

**Author:** ![marguedas](https://sea2.discourse-cdn.com/flex022/user_avatar/discourse.openrobotics.org/marguedas/32/1633_2.png) [@marguedas](https://discourse.openrobotics.org/u/marguedas)\
**Post date:** [January 23, 2020, 11:19pm UTC](https://discourse.openrobotics.org/t/generating-dev-and-runtime-artefacts-from-ros-packages/12448/7 "2020-01-23T23:19:57Z")

</div>

> [@Will\_Gardner](#):
>
> At the moment the `boost` rosdep key is declared as being the dev packages for every given OS: [rosdistro/rosdep/base.yaml at master · ros/rosdistro · GitHub](https://github.com/ros/rosdistro/blob/master/rosdep/base.yaml#L272). It should be possible to create a “parallel” rosdep file that declares the non-dev versions of packages which can then be automatically used by bloom to generate runtime packages with the non-dev dependencies.

This is something that came to mind multiple times when trying to reduce the amount of packages being pulled.  
While very convenient to have these “blanket” rosdep keys, it is very uncommon for any package to need “all of boost dev packages” or “all opencv dev packages”. A great deal of space would be saved if packages were to use more specific and targeted rosdep keys. While some stacks like OpenCV and PCL used to have a single/couple deb with everything, they now provide multiple debs and most likely we should leverage them. For some stacks there is already already a narrower set of keys available, the `-dev` and the `release` one: [rosdistro/rosdep/base.yaml at aaa5fe421f23c07234850c338ec4632d530ab746 · ros/rosdistro · GitHub](https://github.com/ros/rosdistro/blob/aaa5fe421f23c07234850c338ec4632d530ab746/rosdep/base.yaml#L1585-L1600)

In an ideal world, all packages would define separately their `build_depend` with the `-dev` version of the libraries they use and the `exec_depend` with the runtime libs they really need to run the package.

Given the number of packages using (or implicitly relying) on these blanket rules, it would be very challenging to actually apply this to an entire distro.

Maybe it’s something that could be encouraged for ROS 2 ? as the number of packages is still pretty limited, so would be auditable. We would need to find a way to discourage / warn users to steer them away from using these blanket rules.

> [@ruffsl](#):
>
> Nice! Do you have any Dockerfiles you could share publicly that demonstrate this approach?

👍 The would be great and could be inspiration to reduce the size of the official ROS images that are ginormous.

---

<div class="post-metadata">

**Author:** ![ruffsl](https://sea2.discourse-cdn.com/flex022/user_avatar/discourse.openrobotics.org/ruffsl/32/83_2.png) [@ruffsl](https://discourse.openrobotics.org/u/ruffsl)\
**Post date:** [January 24, 2020, 12:20am UTC](https://discourse.openrobotics.org/t/generating-dev-and-runtime-artefacts-from-ros-packages/12448/8 "2020-01-24T00:20:03Z")

</div>

> [@marguedas](#):
>
> Maybe it’s something that could be encouraged for ROS 2 ? as the number of packages is still pretty limited, so would be auditable. We would need to find a way to discourage / warn users to steer them away from using these blanket rules.

Hear! Hear! A fresh a proper slate, no time like the present to set the president. 👍

---

<div class="post-metadata">

**Author:** ![gavanderhoorn](https://sea2.discourse-cdn.com/flex022/user_avatar/discourse.openrobotics.org/gavanderhoorn/32/969_2.png) [@gavanderhoorn](https://discourse.openrobotics.org/u/gavanderhoorn)\
**Post date:** [January 24, 2020, 12:24pm UTC](https://discourse.openrobotics.org/t/generating-dev-and-runtime-artefacts-from-ros-packages/12448/9 "2020-01-24T12:24:40Z")

</div>

> [@tfoote](#):
>
> So in conclusion I think what I’d like to suggest is that we already have support for all the bells and whistles needed to very clearly define the necessary dependencies. And that if we can identify places where separating the build and runtime dependencies would be valuable that we consider simply making separate packages and not try to find ways to automatically split a package and then find ways to refer to those partial packages in downstream packages. We’re generating packages, we have packages. Keeping it one to one is basically the simplest solution.

This would be a nice way to go about it, but as @Will_Gardner also wrote, that’s going to be a lot of work (and will result in many more packages as well, which has an adverse affect of build times). For ROS 2 (as @marguedas suggested) this might be possible still.

For ROS 1 however I doubt it is feasible any more (nr of packages, size of maintainer/developer community & sunsetting).

What @Will_Gardner describes seems like a low-hanging fruit that could allow us to significantly reduce the footprint of ROS 1 (and potentially ROS 2) deployments without requiring too much work.

@Will_Gardner: could you provide a little more detail on what you are doing with those metapackages such that we could replicate it? I’m sure @ruffsl would be able to figure out whether we could apply a similar approach to the official `ros` and `ros2` Docker images.

---

<div class="post-metadata">

**Author:** ![simonschmeisser](https://sea2.discourse-cdn.com/flex022/user_avatar/discourse.openrobotics.org/simonschmeisser/32/27_2.png) [@simonschmeisser](https://discourse.openrobotics.org/u/simonschmeisser)\
**Post date:** [January 24, 2020, 3:37pm UTC](https://discourse.openrobotics.org/t/generating-dev-and-runtime-artefacts-from-ros-packages/12448/10 "2020-01-24T15:37:48Z")

</div>

> [@marguedas](#):
>
> In an ideal world, all packages would define separately their `build_depend` with the `-dev` version of the libraries they use and the `exec_depend` with the runtime libs they really need to run the package.
> 
> Given the number of packages using (or implicitly relying) on these blanket rules, it would be very challenging to actually apply this to an entire distro.

Maybe I or someone else can come up with a graphviz/dot script to identify the worst offenders in individual subsets of the packages? I’ll try my luck with ogre/rviz

---

<div class="post-metadata">

**Author:** ![gavanderhoorn](https://sea2.discourse-cdn.com/flex022/user_avatar/discourse.openrobotics.org/gavanderhoorn/32/969_2.png) [@gavanderhoorn](https://discourse.openrobotics.org/u/gavanderhoorn)\
**Post date:** [January 25, 2020, 4:46pm UTC](https://discourse.openrobotics.org/t/generating-dev-and-runtime-artefacts-from-ros-packages/12448/11 "2020-01-25T16:46:10Z")

</div>

There are quite a few tools available for visualising dependency trees (`debtree` and `apt-cache` can do it fi).

`ros-melodic-cpp-common` indeed brings in approx 580 MB of dependencies (when installed on a bare `ubuntu:bionic`). `libboost-all-dev` is essentially almost singularly responsible for this (207 pkgs for `libboost-all-dev` vs 210 pkgs for `ros-melodic-cpp-common`).

`ros-melodic-roscpp` already lists very specific parts of Boost as dependencies, but as it depends on `cpp-common` (as @Will_Gardner wrote), installing just `roscpp` on a bare `ubuntu:bionic` image wants to install 655 MB of packages.

Of this only 8.1 MB is actually placed inside `/opt/ros/melodic`.

* * *

Edit: @Will_Gardner: I imagine you have some way of figuring out the runtime dependencies of particular ROS nodes / packages and then generate metapackages based on that information. Have you automated this, is this a manual process, or are you relying on package manifests?

---

<div class="post-metadata">

**Author:** ![marguedas](https://sea2.discourse-cdn.com/flex022/user_avatar/discourse.openrobotics.org/marguedas/32/1633_2.png) [@marguedas](https://discourse.openrobotics.org/u/marguedas)\
**Post date:** [January 25, 2020, 10:40pm UTC](https://discourse.openrobotics.org/t/generating-dev-and-runtime-artefacts-from-ros-packages/12448/12 "2020-01-25T22:40:26Z")

</div>

> [@gavanderhoorn](#):
>
> `ros-melodic-roscpp` already lists very specific parts of Boost as dependencies

I guess that’s what I meant by “relying on blanket rules”. While roscpp `find_package`’s and uses specific parts of boost, it doesn’t declare dependencies on any part of it in it’s `package.xml`, relying on lower level packages to provide all the parts of boost it needs.

Not sure how the proper subset of boost packages appeared in the control file though..

> [@gavanderhoorn](#):
>
> Edit: @Will_Gardner: I imagine you have some way of figuring out the runtime dependencies of particular ROS nodes / packages and then generate metapackages based on that information. Have you automated this, is this a manual process, or are you relying on package manifests?

Definitely tools helping to extract this information will be very useful

> [@marguedas](#):
>
> > Maybe it’s something that could be encouraged for ROS 2 ? as the number of packages is still pretty limited, so would be auditable. We would need to find a way to discourage / warn users to steer them away from using these blanket rules.
> 
> Hear! Hear! A fresh a proper slate, no time like the present to set the president.

Yeah getting these split for ROS 2 and allow to install only the nondev version of ROS packages would be great.  
Looks like ROS 2 is facing other challenges as the core is completely boost-free and the `eloquent-ros-core` image [is still 700MB](https://github.com/osrf/docker_templates/pull/82#issue-366610342), my guess is from the way message packages are currently packaged.

---

<div class="post-metadata">

**Author:** ![dirk-thomas](https://sea2.discourse-cdn.com/flex022/user_avatar/discourse.openrobotics.org/dirk-thomas/32/4328_2.png) [@dirk-thomas](https://discourse.openrobotics.org/u/dirk-thomas)\
**Post date:** [January 25, 2020, 11:10pm UTC](https://discourse.openrobotics.org/t/generating-dev-and-runtime-artefacts-from-ros-packages/12448/13 "2020-01-25T23:10:51Z")

</div>

> [@marguedas](#):
>
> While very convenient to have these “blanket” rosdep keys, it is very uncommon for any package to need “all of boost dev packages” or “all opencv dev packages”. A great deal of space would be saved if packages were to use more specific and targeted rosdep keys.

There is nothing holding us back to do exactly that already. All it takes is a minute to make a PR to update any packages which do use these “blanket” rosdep keys (see below for an example).

> [@marguedas](#):
>
> Maybe it’s something that could be encouraged for ROS 2 ?

I don’t see why ROS 1 packages couldn’t be updated too. I doubt the number of packages (which use these “blanket” rosdep keys) and enough people care about is actually that high.

> [@gavanderhoorn](#):
>
> For ROS 1 however I doubt it is feasible any more (nr of packages, size of maintainer/developer community & sunsetting).

ROS Noetic is not even released yet and will be supported for 5 years. I would think that is motivation enough to still update package you care about. Also it doesn’t mean that the maintainer of a package has to do it. Anyone can create a pull request with this kind of change - the maintainer only has to accept it and roll a new release.

And as mentioned above an example how easy it is to improve the dependency size: [ros/roscpp\_core#117](https://github.com/ros/roscpp_core/pull/117) uses specific rosdep keys (`libboost-date-time-dev`, `libboost-system-dev`, `libboost-thread-dev`) instead of `boost` (which maps to `libboost-all-dev`). The effect on installing `ros-*-roscpp_core` (assuming you have `build-essential` installed anyway):

- Before it pulled in ~500MB
- Afterwards it only pulls in ~200MB

While dev/non-dev ROS packages would certainly get us further we could already do something today. So please consider to create PRs to replace the usage of “blanket” rosdep keys with something more specific.

---

<div class="post-metadata">

**Author:** ![Will\_Gardner](https://sea2.discourse-cdn.com/flex022/user_avatar/discourse.openrobotics.org/will_gardner/32/5420_2.png) [@Will\_Gardner](https://discourse.openrobotics.org/u/Will_Gardner)\
**Post date:** [January 26, 2020, 12:34am UTC](https://discourse.openrobotics.org/t/generating-dev-and-runtime-artefacts-from-ros-packages/12448/14 "2020-01-26T00:34:08Z")

</div>

Our Dockerfiles by themselves aren’t all that useful since most of what they do is install fake packages.

The generation of the fake packages is the interesting part, I’m not sure how much I can share so I’ll describe it in high level terms. The entire process operates entirely within the realms of Debian packaging, nothing ROS specific.

When we want to build an image we declare its high level debian dependencies, typically this will be a set of ROS packages (e.g. ros-kinetic-ros-core) and some other libraries that we want to use with it. We then have a Python script which takes this list and traverses the dependency graph. As it traverses the graph it will start to prune off dependencies based off of certain criteria, some of this will be hard coded rules based on package names but mostly it’s automated heuristics. The main one is the one which eliminates packages based on their [debian section](https://www.debian.org/doc/debian-policy/ch-archive.html#sections). While all the ROS packages sit in the `misc` section (since bloom doesn’t know any better) the packages in the upstream Ubuntu repos are nicely split up which makes it easy to eliminate all the packages in the `devel` section or the `graphics` section. As we prune off packages we add them to a list of packages to “provide” in the metapackage we’re going to generate.

Along with this we also explicitly add certain non-dev dependencies to the images. For example we explicitly add _all_ of the non-dev boost packages (which turn out to be not that big) to our images to make up for not having the boost-dev meta-package.

Here’s an example of the pruned graph for ros-kinetic-ros-core (nodes are only shown the first time they’re found in the graph):

```auto
* 113.5MB / 658.9MB ros-kinetic-ros-core
* 31.9MB / 81.6MB ros-kinetic-catkin
      0.0MB / 36.0MB cmake
      0.0MB / 7.3MB google-mock
      0.0MB / 0.0MB libgtest-dev
* 30.0MB / 36.5MB python-catkin-pkg
* 26.6MB / 33.1MB python-dateutil
* 23.7MB / 30.2MB python-six
* 23.6MB / 30.1MB python
* 23.0MB / 29.5MB python2.7
* 6.3MB / 6.3MB python2.7-minimal
* 2.7MB / 2.7MB libpython2.7-minimal
* 0.2MB / 0.2MB zlib1g
* 16.3MB / 22.8MB libpython2.7-stdlib
* 0.1MB / 0.1MB mime-support
* 0.1MB / 0.1MB libbz2-1.0
* 1.7MB / 1.7MB libdb5.3
* 0.4MB / 0.4MB libexpat1
* 0.1MB / 0.1MB libffi6
* 0.8MB / 0.8MB libncursesw5
* 0.5MB / 0.5MB libtinfo5
* 0.4MB / 6.9MB libreadline6
* 0.1MB / 6.6MB readline-common
      0.0MB / 6.5MB dpkg
* 0.9MB / 0.9MB libsqlite3-0
* 3.3MB / 3.3MB libssl1.0.0
* 0.0MB / 0.0MB libpython-stdlib
* 2.7MB / 2.7MB tzdata
* 2.9MB / 2.9MB python-docutils
* 0.0MB / 0.0MB python-roman
* 1.2MB / 1.2MB docutils-common
* 0.1MB / 0.1MB sgml-base
* 0.5MB / 0.5MB xml-core
* 0.3MB / 0.3MB sed
* 0.2MB / 0.2MB python-pyparsing
* 0.2MB / 0.2MB python-catkin-pkg-modules
* 0.3MB / 0.3MB python-empy
* 1.0MB / 1.0MB python-nose
* 0.4MB / 0.4MB python-pkg-resources
      0.0MB / 0.1MB ros-kinetic-cmake-modules
* 11.0MB / 385.4MB ros-kinetic-common-msgs
* 4.9MB / 379.3MB ros-kinetic-actionlib-msgs
      0.0MB / 0.3MB ros-kinetic-message-generation
* 3.6MB / 377.7MB ros-kinetic-message-runtime
* 2.1MB / 376.3MB ros-kinetic-cpp-common
* 2.0MB / 2.0MB libconsole-bridge0.2v5
* 2.0MB / 2.0MB libstdc++6
* 0.1MB / 0.1MB gcc-5-base
      0.0MB / 308.0MB libboost-all-dev
      0.0MB / 66.1MB libconsole-bridge-dev
* 1.0MB / 1.0MB ros-kinetic-genpy
* 0.6MB / 0.6MB python-yaml
* 0.2MB / 0.2MB libyaml-0-2
* 0.2MB / 0.2MB ros-kinetic-genmsg
* 0.4MB / 0.4MB ros-kinetic-roscpp-serialization
* 0.4MB / 0.4MB ros-kinetic-roscpp-traits
* 0.3MB / 0.3MB ros-kinetic-rostime
* 0.1MB / 0.1MB libboost-system1.58.0
* 1.1MB / 1.1MB ros-kinetic-std-msgs
* 0.3MB / 0.3MB ros-kinetic-diagnostic-msgs
* 1.2MB / 1.2MB ros-kinetic-geometry-msgs
* 1.0MB / 1.0MB ros-kinetic-nav-msgs
* 1.8MB / 1.8MB ros-kinetic-sensor-msgs
* 0.2MB / 0.2MB ros-kinetic-shape-msgs
* 0.1MB / 0.1MB ros-kinetic-stereo-msgs
* 0.3MB / 0.3MB ros-kinetic-trajectory-msgs
      0.0MB / 0.0MB ros-kinetic-rosbag-migration-rule
* 1.1MB / 1.1MB ros-kinetic-visualization-msgs
* 0.1MB / 0.1MB ros-kinetic-gencpp
* 0.1MB / 0.1MB ros-kinetic-geneus
      0.0MB / 0.1MB ros-kinetic-genlisp
      0.0MB / 0.1MB ros-kinetic-gennodejs
* 7.9MB / 31.9MB ros-kinetic-ros
* 0.0MB / 0.2MB ros-kinetic-mk
      0.0MB / 0.1MB ros-kinetic-rosbuild
* 0.1MB / 0.1MB ros-kinetic-rosbash
      0.0MB / 0.1MB ros-kinetic-rosboost-cfg
* 0.2MB / 23.2MB ros-kinetic-rosclean
* 0.1MB / 23.1MB python-rospkg
* 0.1MB / 23.1MB python-rospkg-modules
      0.0MB / 23.0MB lsb-release
* 0.1MB / 0.1MB ros-kinetic-roscreate
* 0.0MB / 0.0MB ros-kinetic-roslang
* 7.5MB / 7.9MB ros-kinetic-roslib
* 0.0MB / 0.0MB ros-kinetic-ros-environment
* 7.0MB / 7.4MB ros-kinetic-rospack
* 0.1MB / 0.1MB libboost-filesystem1.58.0
* 0.5MB / 0.5MB libboost-program-options1.58.0
* 3.5MB / 3.5MB libpython2.7
* 0.1MB / 0.1MB libtinyxml2.6.2v5
      0.0MB / 0.2MB libtinyxml-dev
      0.0MB / 0.2MB pkg-config
      0.0MB / 0.0MB python-dev
* 2.4MB / 2.4MB python-rosdep
* 2.1MB / 2.1MB python-rosdistro
* 1.3MB / 1.3MB ca-certificates
* 0.9MB / 0.9MB openssl
* 0.7MB / 0.7MB python-rosdistro-modules
* 0.5MB / 0.5MB python-setuptools
      0.0MB / 0.1MB ros-kinetic-rosmake
      0.0MB / 0.2MB ros-kinetic-rosunit
* 62.5MB / 159.5MB ros-kinetic-ros-comm
* 36.5MB / 68.0MB ros-kinetic-message-filters
* 33.3MB / 64.7MB ros-kinetic-rosconsole
* 30.5MB / 30.5MB libboost-regex1.58.0
* 29.4MB / 29.4MB libicu55
* 2.4MB / 6.2MB liblog4cxx10v5
* 0.4MB / 4.1MB libapr1
* 0.1MB / 3.8MB libuuid1
      0.0MB / 3.7MB passwd
* 0.2MB / 0.2MB libaprutil1
      0.0MB / 8.2MB libapr1-dev
      0.0MB / 12.2MB libaprutil1-dev
      0.0MB / 7.3MB liblog4cxx10-dev
* 3.1MB / 3.1MB ros-kinetic-roscpp
* 0.1MB / 0.1MB libboost-chrono1.58.0
* 0.2MB / 0.2MB libboost-thread1.58.0
* 0.2MB / 0.2MB ros-kinetic-rosgraph-msgs
* 0.2MB / 0.2MB ros-kinetic-xmlrpcpp
* 21.3MB / 21.5MB ros-kinetic-rosbag
* 0.6MB / 0.8MB ros-kinetic-rosbag-storage
      0.0MB / 0.1MB libbz2-dev
* 0.2MB / 0.4MB ros-kinetic-roslz4
* 0.1MB / 0.1MB liblz4-1
      0.0MB / 0.2MB liblz4-dev
* 18.1MB / 18.1MB ros-kinetic-rospy
* 17.1MB / 17.1MB python-numpy
* 0.5MB / 0.5MB libblas3
* 0.0MB / 0.0MB libblas-common
* 7.5MB / 7.5MB liblapack3
* 1.5MB / 1.5MB libgfortran3
* 0.3MB / 0.3MB libquadmath0
* 0.3MB / 0.3MB ros-kinetic-rosgraph
* 0.0MB / 0.0MB python-netifaces
* 0.2MB / 0.2MB ros-kinetic-std-srvs
* 1.1MB / 1.1MB ros-kinetic-topic-tools
* 3.9MB / 3.9MB ros-kinetic-roslaunch
* 2.6MB / 2.6MB python-paramiko
* 1.9MB / 1.9MB python-crypto
* 0.5MB / 0.5MB libgmp10
* 0.1MB / 0.1MB python-ecdsa
* 0.4MB / 0.4MB ros-kinetic-rosmaster
* 0.1MB / 0.1MB python-defusedxml
* 0.1MB / 0.1MB ros-kinetic-rosout
* 0.1MB / 0.1MB ros-kinetic-rosparam
      0.0MB / 65.3MB ros-kinetic-roslisp
* 0.1MB / 0.1MB ros-kinetic-rosmsg
* 0.3MB / 0.3MB ros-kinetic-rosnode
* 0.2MB / 0.2MB ros-kinetic-rostopic
* 0.1MB / 0.1MB ros-kinetic-rosservice
* 0.1MB / 0.1MB ros-kinetic-rostest
* 0.2MB / 0.2MB ros-kinetic-roswtf
* 0.0MB / 0.0MB ros-kinetic-rosconsole-bridge
* 0.0MB / 0.0MB ros-kinetic-roscpp-core

```

---

<div class="post-metadata">

**Author:** ![gavanderhoorn](https://sea2.discourse-cdn.com/flex022/user_avatar/discourse.openrobotics.org/gavanderhoorn/32/969_2.png) [@gavanderhoorn](https://discourse.openrobotics.org/u/gavanderhoorn)\
**Post date:** [January 26, 2020, 10:31am UTC](https://discourse.openrobotics.org/t/generating-dev-and-runtime-artefacts-from-ros-packages/12448/15 "2020-01-26T10:31:54Z")

</div>

> [@dirk-thomas](#):
>
> > [@marguedas](#):
> >
> > Maybe it’s something that could be encouraged for ROS 2 ?
> 
> I don’t see why ROS 1 packages couldn’t be updated too. I doubt the number of packages (which use these “blanket” rosdep keys) and enough people care about is actually that high.
> 
> > [@gavanderhoorn](#):
> >
> > For ROS 1 however I doubt it is feasible any more (nr of packages, size of maintainer/developer community & sunsetting).
> 
> ROS Noetic is not even released yet and will be supported for 5 years. I would think that is motivation enough to still update package you care about. Also it doesn’t mean that the maintainer of a package has to do it. Anyone can create a pull request with this kind of change - the maintainer only has to accept it and roll a new release.
> 
> And as mentioned above an example how easy it is to improve the dependency size: [ros/roscpp\_core#117](https://github.com/ros/roscpp_core/pull/117) uses specific rosdep keys (`libboost-date-time-dev`, `libboost-system-dev`, `libboost-thread-dev`) instead of `boost` (which maps to `libboost-all-dev`). The effect on installing `ros-*-roscpp_core` (assuming you have `build-essential` installed anyway):
> 
> - Before it pulled in ~500MB
> - Afterwards it only pulls in ~200MB

Reason I was hesitant to start doing this is that we risk breaking downstream packages that may be (implicitly/unknowingly) depending on `cpp_common` to bring in `libboost-all-dev`.

This seems to have happened before (from: [ros/roscpp\_core#24](https://github.com/ros/roscpp_core/issues/24)):

> Before the ROS package `console_bridge` was bringing in the boost dependency transitively. Since `roscpp_core` now uses the system package of `console_bridge` boost is not exposed as a transitive dependency anymore.
> 
> Anyway the packages in `roscpp_core` should state their direct dependencies explicitly anyway and not rely on transitive dependencies for this. Please see [PR #25](https://github.com/ros/roscpp_core/pull/25) for the proposed fix.

Technically those downstream packages would need to be fixed of course. I just wasn’t sure that was a feasible thing to do in ROS 1 still.

That was all really. I’m more than happy to start submitting PRs. But I don’t like breaking things and potentially problematic changes like this have been met with hesitation from OR maintainers in the past, so it’s not too strange for us to first look at approaches that would result in similar gains without causing too much trouble “upstream”.

---

<div class="post-metadata">

**Author:** ![marguedas](https://sea2.discourse-cdn.com/flex022/user_avatar/discourse.openrobotics.org/marguedas/32/1633_2.png) [@marguedas](https://discourse.openrobotics.org/u/marguedas)\
**Post date:** [January 26, 2020, 10:54am UTC](https://discourse.openrobotics.org/t/generating-dev-and-runtime-artefacts-from-ros-packages/12448/16 "2020-01-26T10:54:58Z")

</div>

> [@gavanderhoorn](#):
>
> Reason I was hesitant to start doing this is that we risk breaking downstream packages that may be (implicitly/unknowingly) depending on `cpp_common` to bring in `libboost-all-dev` .

👍 Same reason here.  
In the above example of `roscpp` not declaring its’ boost dependency. It’s likely that this `cpp_common` PR broke `roscpp` in the process.  
In the case where the changes are targeting noetic only, it wouldn’t be too much of an issue as maintainers would update their packages before releasing. But for people not having a dedicated `noetic` branch it may propagate build failures every time one layer fixes the dep declaration.

If that’s a road we’re comfortable going down for Noetic, I’m happy to help submitting PRs.

---

<div class="post-metadata">

**Author:** ![gavanderhoorn](https://sea2.discourse-cdn.com/flex022/user_avatar/discourse.openrobotics.org/gavanderhoorn/32/969_2.png) [@gavanderhoorn](https://discourse.openrobotics.org/u/gavanderhoorn)\
**Post date:** [January 26, 2020, 3:33pm UTC](https://discourse.openrobotics.org/t/generating-dev-and-runtime-artefacts-from-ros-packages/12448/17 "2020-01-26T15:33:11Z")

</div>

> [@marguedas](#):
>
> It’s likely that this `cpp_common` PR broke `roscpp` in the process.

Seeing as `roscpp` hasn’t been released yet for Noetic that isn’t much of a problem right now.

Noetic would seem like the time to start pruning these package manifests.

re: replacing blanket `depend`s: moving dependencies on keys like `boost` to `build_depend` et al. would keep builds working. The runtime dependencies would be much lighter though.

Perhaps a two-step process could be used: first tighten up the `exec_depend`s, then change build dependencies.

* * *

And a related PR for `rosdep`:

[https://github.com/ros-infrastructure/rosdep/pull/727](https://github.com/ros-infrastructure/rosdep/pull/727)

---

<div class="post-metadata">

**Author:** ![gavanderhoorn](https://sea2.discourse-cdn.com/flex022/user_avatar/discourse.openrobotics.org/gavanderhoorn/32/969_2.png) [@gavanderhoorn](https://discourse.openrobotics.org/u/gavanderhoorn)\
**Post date:** [January 26, 2020, 3:54pm UTC](https://discourse.openrobotics.org/t/generating-dev-and-runtime-artefacts-from-ros-packages/12448/18 "2020-01-26T15:54:59Z")

</div>

> [@Will\_Gardner](#):
>
> As it traverses the graph it will start to prune off dependencies based off of certain criteria, some of this will be hard coded rules based on package names but mostly it’s automated heuristics. The main one is the one which eliminates packages based on their [debian section](https://www.debian.org/doc/debian-policy/ch-archive.html#sections). While all the ROS packages sit in the `misc` section (since bloom doesn’t know any better) the packages in the upstream Ubuntu repos are nicely split up which makes it easy to eliminate all the packages in the `devel` section or the `graphics` section.

Bloom’s patch support could perhaps be used to change the default `misc` to more appropriate values. That would require some work, but would seem doable for at least stuff in `ros_core` and similar metapackages.

But should we perhaps consider adding support for categories to ROS package manifests? Packages could be placed in `misc` by default, but if the author/maintainer provides the metadata, Bloom could use that to populate the `Section` field.

RPM did have “groups”, but those seem to have been deprecated some time ago.

> [@Will\_Gardner](#):
>
> Here’s an example of the pruned graph for ros-kinetic-ros-core (nodes are only shown the first time they’re found in the graph):

Entries without a `*` are pruned then, I gather? And the second column shows the cumulative sub total of the packages installed by (and for) that branch of the tree (but the trees only include a dependency if it wasn’t added somewhere else earlier (so `lsb_release` shows up with 23MB, but that’s probably because it depends on all sorts of Python dependencies which aren’t shown a second time))?

With some primitive scripting and a very manual process I’d arrived at a similar set of packages, but I hadn’t gotten `ros-kinetic-ros-core` down to `650` MB yet.

---

<div class="post-metadata">

**Author:** ![marguedas](https://sea2.discourse-cdn.com/flex022/user_avatar/discourse.openrobotics.org/marguedas/32/1633_2.png) [@marguedas](https://discourse.openrobotics.org/u/marguedas)\
**Post date:** [January 26, 2020, 5:12pm UTC](https://discourse.openrobotics.org/t/generating-dev-and-runtime-artefacts-from-ros-packages/12448/19 "2020-01-26T17:12:12Z")

</div>

> [@gavanderhoorn](#):
>
> Perhaps a two-step process could be used: first tighten up the `exec_depend` s, then change build dependencies.

I started part of the process and opened PRs to `ros_comm` and its’ dependencies to get rid of the blanket `boost` key (in both build and exec). This was pretty straight forward (though not automated) and did not include any audit/tightening of the runtime dependencies. This allowed to propagate the ~300MB space gain to the all of `ros_comm`.

re: “first tighten up the `exec_depend` s”: Migrating all these packages to format 2 or 3 would also allow us to leverage the `exec_depend` versus `build_export_depend` and tighten them accordingly. Bloom could then use that information to segregate `-dev` vs actual runtime dependencies.

> [@gavanderhoorn](#):
>
> And a related PR for `rosdep` :
> 
> [github.com/ros-infrastructure/rosdep](https://github.com/ros-infrastructure/rosdep/pull/727)

Great !

---

<div class="post-metadata">

**Author:** ![dirk-thomas](https://sea2.discourse-cdn.com/flex022/user_avatar/discourse.openrobotics.org/dirk-thomas/32/4328_2.png) [@dirk-thomas](https://discourse.openrobotics.org/u/dirk-thomas)\
**Post date:** [January 27, 2020, 6:23pm UTC](https://discourse.openrobotics.org/t/generating-dev-and-runtime-artefacts-from-ros-packages/12448/20 "2020-01-27T18:23:37Z")

</div>

> [@gavanderhoorn](#):
>
> Reason I was hesitant to start doing this is that we risk breaking downstream packages that may be (implicitly/unknowingly) depending on `cpp_common` to bring in `libboost-all-dev` .

Assuming that almost all packages depend on e.g. `roscpp` the only chance to breaking downstream packages is if all packages including and below `roscpp` are switching from `boost` to something more specific for an already released distro. If only one package limits the change to Noetic (since it already needs a separate branch for other reasons) downstream packages will still have `boost` available in Kinetic / Melodic. Therefore I think the chance for regressions is fairly low.

> [@gavanderhoorn](#):
>
> Perhaps a two-step process could be used: first tighten up the `exec_depend` s, then change build dependencies.

What would be the advantage of that? The first step already affects downstream packages. The second step is local to the package and is easy to test and doesn’t affect other packages - so why should it not be done at the same time?

[Next page](https://discourse.openrobotics.org/t/generating-dev-and-runtime-artefacts-from-ros-packages/12448.md?page=2)
