Rust packaging on Exherbo

At Clever Cloud, we love giving new technologies a try. Since its inception, rust has always been quite appealing and we always wanted to find projects for which rust would be an adequate solution.

Now that rust is mature enough (to our taste), we started giving it a go, and we needed a way to easily package rust software for our distribution of choice: Exherbo.

Rust package manager: cargo

Amongst the tooling shipped with rust is cargo. Cargo allows you to simply build, test and install a package and its dependencies. It's actually very nice to use when developing.

If we were to install software with cargo, there are two solutions:

  • Either we want to install something like a private or local projects and we just
    have to go to the sources directory, where the Cargo.toml file is, and run
    cargo install
  • Or we want to install something publicly available, on crates.io
    for example, we can just run cargo install crate_name, crate_name being the actual
    crate (cargo package) name. We could also pass it a git URL as an alternative.

Regarding production environment and system-wide utilities, we want everything to be managed by the distribution package manager, paludis, so we had to hook up those two and try to make them work together as well as possible.

How packaging rust is different from packaging other software

Currently, rust doesn't support installing libraries system-wide as they're not confident enough in the stability of their ABI yet. What that concretely means is that it's not yet possible to install any rust library, and then use it to build dependent stuff. You have to rebuild all the dependencies each time you build a rust binary.

That particular point is the most noticeable difference on the packaging point of view. Instead of building and installing the dependencies, then the dependents, you only install the final product with everything built in.

What a "traditional" install process looks like in a source-based distribution

For traditional packages, the build and install process is composed of several sequential steps, that we can summarize like this, in that order:

Fetch

This is the step where we download everything, usually the software sources.

This is the only step for which network sandboxing is disabled.

Unpack

Here we unpack the sources in a sandboxed environment. Network sandboxing is on as well as file system sandboxing for this step and all the steps onwards.

Prepare

This is an optional phase, not needed by all packages. It's the moment when we generate build-system files if needed and apply patches to the software sources.

Configure

We decide there which features we want to enable or not for the build.

Build

Where the actual build happens.

Install

We install the resulting binaries/files in a sandboxed location replicating the / hierarchy.

Merge

Once everything gets validated in terms of file system access, everything gets installed to the root file system.

What a rust install process looks like

A rust install pretty much ressembles a traditional one, with one huge exception.

As I said before, cargo install supports either reading a local Cargo.toml, or being passed a crate name or a git URL.

On the other hand, cargo fetch, which is the command that downloads all the dependencies doesn't have these options, it only supports reading a local Cargo.toml and fetching the dependencies listed in it. If libraries were installable system-wide, we wouldn't need this step at all, but since they're not, we have to do this step after unpacking the sources, to be able to access the Cargo.toml.

This has serious implications, it means that we extend the unpack phase with three additional actions:

  • disable the network sandboxing, which is really sad and we should never do that
  • cargo fecth
  • re-enable the network sandboxing

Of course, we don't have to download the dependencies at each reinstall, cargo supports a CARGO_HOME environment variable allowing us to store the downloads in a shared location with all other distfiles.

We'll eventually be able to install libraries system-wide, but I don't see that happening any time soon. The better workaround I could come up with to clean up this hack is to make cargo fetch support being given a crate name or a URL, matching its companion cargo install. Here is the corresponding github issue but I'm not sure whether it will be implemented nor when.

Actual implementation and examples

The implementation of my exlib (which is to exherbo packages what libraries are to software) can be found here.

cargo-watch is a good example of what a rust exherbo package using this exlib looks like. You can see it's fairly trivial and everything is automatic.

Blog

À lire également

MateriaDB KV, Functions: discover the future of Clever Cloud at Devoxx Paris 2024

Clever Cloud is proud to present its new range of serverless products: Materia!
Company

Our new logs interface is available in public beta

You can now discover our new log stack interface and its new features!
Company

Deploy from GitLab or GitHub

Over the past few months, some customers have raised questions about CI/CD building to deploy…

Engineering