Jump to content

# Rust package guidelines

3 languages

- 日本語
- Português
- 中文（简体）

From ArchWiki

This document covers standards and guidelines on writing PKGBUILDs for software written in Rust.

## Package naming

When packaging Rust projects, the package name should almost always be the same as the name of the generated binary. Note that it does not make any sense to package library crates, so only crates with bins will be packaged. For ones that generate more than one binary, the upstream crate name is usually appropriate. In any event the package name should be entirely lowercase.

## Source

Most Rust projects may be built from tarballs, source archives (e.g. source links on GitHub releases), or any other published source.

When other sources are now available, most Rust projects are published on crates.io which provides a stable download URL scheme for use with cargo. The downside of this source is that it usually does not include all the test files, license files, or other assets normally in the other sources. If needed, the PKGBUILD#source can use the following template:

    source=("$pkgname-$pkgver.tar.gz::https://static.crates.io/crates/$pkgname/$pkgname-$pkgver.crate")

## Depends

While some Rust projects have external dependencies, most just use Rust ecosystem libraries that are statically embedded in the final binary. As such most projects will not need to specify many `depends`. The usual exceptions that most Rust binaries do link against glibc libraries, so `gcc-libs` and `glibc` are typically dependencies of most Rust packages. There may be more if the build process looks for and links against any system libraries.

For `makedepends`, the vast majority of Rust projects are designed to be built using the cargo dependency manager, which both orchestrates the download of libraries to satisfy build time dependencies as well as makes all the necessary calls to `rustc`, the actual Rust compiler. Currently both cargo and rustc are provided by the rust package, but there are also alternative ways of getting both of these together or separately including the rustup package. As such, the tool most PKGBUILDs are going to call is cargo and you should depend directly on it.

    makedepends=(cargo)

If a project requires the use of a nightly version of the Rust tool chain, use:

    makedepends=(cargo-nightly)

## Prepare

The rust dependency manager cargo is able to download all the libraries required to build a project ahead of time. Running this fetch in the `prepare()` stage enables the later `build()` and other stages to be run entirely offline.

    prepare() {
        export RUSTUP_TOOLCHAIN=stable
        cargo fetch --locked --target "$(rustc -vV | sed -n 's/host: //p')"
    }

where:

- `RUSTUP_TOOLCHAIN=stable` makes sure the default tool chain is set to stable in the event users have changed their default. Of course this should be set to nightly in the event that's what the upstream project requires. This avoids a common side effect of user preferences when not building in a chroot. Also note this is not required if the upstream project has a `rust-toolchain` file or `rust-toolchain.toml` file in their sources that serves this purpose.
- `--locked` tells cargo to strictly adhere to the versions specified in the `Cargo.lock` file and prevent it from updating dependencies. This is important for reproducible builds.
- `--target "$(rustc -vV | sed -n 's/host: //p')"` tells cargo to only fetch dependencies needed for the specific target platform being built, thus reducing downloads (see PKGBUILD#arch and Rust platform support).

Note: If building a VCS package for an upstream source project that does not keep its `Cargo.lock` file in sync with `Cargo.toml` between release cycles, add `cargo update` before running `cargo fetch`. All other aspects of the build should work as documented here, although the result will not be a fully reproducible build because the dependencies will be resolved at build time.

## Build

Building a Rust package.

    build() {
        export RUSTUP_TOOLCHAIN=stable
        export CARGO_TARGET_DIR=target
        cargo build --frozen --release --all-features
    }

where:

- `--release` tells cargo to compile a release build (the default is a debug build)
- `--frozen` tells cargo to stay offline and only use the versions specified in the `Cargo.lock` file and as cached by the fetch run in the `prepare()` stage. This is functionally equivalent to `--locked --offline`, which may also be used. This is important for reproducible builds.
- `--all-features` tells cargo to compile with all features of the package enabled. Alternatively, use `--features FEATURE1,FEATURE2` if you want enable only selected features.
- `CARGO_TARGET_DIR=target` tells cargo to place the output in target relative to the current directory. This avoids a common side effect of user preferences when not building in a chroot.

Note: The two environment variables are not needed for Arch repository packages because they are always built in chroot environments with default settings. They are included here for the convenience of AUR users that may not realize the consequences of changing their user default settings and not using chroots when building packages.

## Check

Most Rust projects provide a simple way to run the test suite.

    check() {
        export RUSTUP_TOOLCHAIN=stable
        cargo test --frozen --all-features
    }

You should also check if the repository is a cargo workspace. Just open up `/Cargo.toml` and see if it contains a `[workspace]` section. If so, you should add the `--workspace` flag to `cargo test` to ensure that all tests of the workspace members are run too.

Note: Avoid using the `--release` flag when running tests, because it enables compiler optimizations and disables some features like integer overflow checks and `debug_assert!()` macro, so in theory you could end up catching less problems. Although the difference in optimization flags does mean that tests have to rebuild more crates, it still does have to rebuild anything that effects the unit tests to build in the tests and the optimization flags will slow down the build more than not having to rebuild so many crates would by matching optimization flags. Since the final binary artifacts being tested are not the same as the one we package anyway and we aren't testing the build toolchain, for most Rust projects there is nothing to gain by testing in release mode.

## Package

Rust builds binaries in `target/release` and can simply be installed to `/usr/bin`.

    package() {
        install -Dm0755 -t "$pkgdir/usr/bin/" "target/release/$pkgname"
    }

If a package has more than one executable in `/usr/bin` you can use find command:

    package() {
        find target/release \
            -maxdepth 1 \
            -executable \
            -type f \
            -exec install -Dm0755 -t "$pkgdir/usr/bin/" {} +
    }

### Notes about using cargo install

Some packages should install more files such as a man page or other assets. In the event that such a package does not have any other way to install these, one can use `cargo install`. In this case `build()` is unnecessary because `cargo install` forces rebuilding even if the package already has been built by using `cargo build`. The `prepare()` stage can still be used to fetch sources ahead of time:

    package() {
        export RUSTUP_TOOLCHAIN=stable
        cargo install --no-track --frozen --all-features --root "$pkgdir/usr/" --path .
    }

The `--no-track` argument should always be used, otherwise `cargo install` will create unwanted files such as `/usr/.crates.toml` or `/usr/.crates2.json`.

## Example packages

Click Package Actions > Source Files in the package page to see its example PKGBUILD.

- cbindgen
- ripgrep

Category:

- Arch package guidelines