User:schievel/packaging rust

From Gentoo Wiki
Jump to:navigation Jump to:search

Schievel
Schievel
Contact info


deThis user is a native speaker of German.

Packaging Rust projects

Rust has it's own package manager cargo, which Gentoos cargo.eclass utilizes. This is very convenient for software developers. However it comes with some caveats for maintainers. This section will grow every time I encounter something new that is worth mentioning.

Making a versioned ebuild

Gentoo has it's own programs to make ebuilds out of Rust projects automatically: https://packages.gentoo.org/packages/dev-util/cargo-ebuild and https://packages.gentoo.org/packages/app-portage/pycargoebuild.

To use cargo ebuild, get the sources of the Rust program. This is usually done by cloning the git repository of the program. Then go into that repository and run

user $cargo ebuild

Cargo ebuild automatically checks dependencies for vulnerabilities, which takes a while. One can run `

user $cargo ebuild --noaudit

instead to speed up this process.

In the same sense run

user $pycargoebuild ./

in the repository of the program to use pycargoebuild to generate that ebuild.

If everything goes well, cargo ebuild (or pycargoebuild) spits out an ebuild into the rust programs repository folder, which can then be moved into an ebuild repository like ::gentoo or the guru.

However, this will only give a blank frame to work with. The homepage and description needs to be added and if the Rust program is not on crates.io the SRC_URI as well.

Cargo ebuild also spits out the licenses, but it can not guarantee that these are correct. So run `cargo license` in the Rust repository to get a list of licenses. Be aware that `cargo license` will give the licenses in SPDX format, which Gentoo does not always uses. So some effort needs to be put into 'translating' them.

Common problems with dependencies

crates.io dependencies

If all dependencies are fetched from crates.io the ebuild from cargo ebuild often works from scratch. Dependencies that are fetched from crates.io are declared like them in cargo.toml of the Rust project:

[dependencies]

itertools = "0.10.3"

'Cargo ebuild' will put them into the CRATES variable and they are put into the SRC_URI variables via $(cargo_crate_uris) which should already be added by 'cargo ebuild'. They are then automatically fetched and unpacked into the right place via cargo.eclass' cargo_src_unpack

github/ gitlab etc. dependencies

Sometimes software developers are not happy with the version of a crate on crates.io or a crate is not available there and they want to get it from some git repository instead. Then they declare a dependency in cargo.toml like this:

tree-sitter-haxe = { git = "https://github.com/vantreeseba/tree-sitter-haxe", version = "0.2.2", optional = true }

Which means that the release 0.19.0 from the tree-sitter-elixir github repo should be checked out. We can not clone a git repository without git-r3.eclass but we can simulate this in the ebuild. (Be aware that if cargo.toml of a Rust program says something like `tree-sitter-c-sharp = { git = "https://github.com/tree-sitter/tree-sitter-c-sharp", branch = "master" }` it wants to fetch the latest commit on the master branch of that github repository. This means we can only make a live ebuild out of this package. (is this correct?))

For this example we would put SRC_URI="https://github.com/vantreeseba/tree-sitter-haxe/archive/v0.2.2.tar.gz" into SRC_URI. The cargo.eclass notices, that it does not end with .crate and will just unpack it like any other thing in SRC_URI into ${WORKDIR}. But we need to tell cargo where to find that dependency. Therefore we patch the cargo.toml file and change the line of that dependency like this: tree-sitter-haxe = { path = "../tree-sitter-haxe-v0.2.2" } Look at this wiki page on how to write patches.

The path is relative to the cargo.toml file, so it could be something like ../../tree-sitter-haxe-v0.2.2 as well, if that dependency is not in the top cargo.toml.

The path points to the dependencies' folder in ${WORKDIR}

Writing live ebuilds

Writing live ebuilds for Rust packages is way easier than writing non-live ones. The first frame of the ebuild can be created via `cargo ebuild` as well. Then set the ebuilds version in the file name to 9999.

Live ebuilds do not need the CRATES variable, so we can remove that. Also SRC_URI is not used, they usually need to fetch the sources via git-r3.eclass or similar.

In src_unpack() put cargo_live_src_unpack. This will fetch the needed dependency crates using cargo.

Problems with live ebuilds

can't update a git repository in the offline mode

When a Rust project uses unpinned dependencies in their cargo.toml, e.g. tree-sitter-c-sharp = { git = "https://github.com/tree-sitter/tree-sitter-c-sharp", branch = "master" } cargo will always want to check if that dependency is up do date in later stages of the ebuild. This will cause the error can't update a git repository in the offline mode

In this case we can add cargo_src_configure --frozen into src_configure(). This will stop cargo from checking off what it just fetched is up to date.

Use flags

Use flags for rust are usually added via the cargo.eclass:

src_configure() {
local myfeatures=(
       barfeature
       $(usev foo)
)
       cargo_src_configure

Switch off default features

Often Rust programs do not allow to switch off single features. So in order to make it possible to choose certain features, we need to disable them all with cargo_src_configure --no-default-features and then enable only the features we want with myfeatures like mentioned above.

Using a vendor tarball like in Go ebuilds

The go.eclass and cargo.eclass are very similar regarding the functionality of loading statically linked dependencies. In a Go ebuild you define the variable EGO_SUM with a list of dependencies you get from upstream, in a Rust ebuild you would do the same with the CRATES variable. The eclasses then proceed to download those dependencies and put them in the correct places in the working directory and, in the cargo.eclasses case, create a config file for cargo so it searches for dependencies in the right places.

While it is a ongoing and controversial discussion whether the EGO_SUM-functionality should be deprecated or not in the go.eclass, the cargo.eclass is built with the CRATES variable in mind. The go.eclass offers the possibility to use a vendor-tarball instead of the EGO_SUM-functionality. That is a tar archive that contains all the dependencies. While Cargo also offers the possibility to create such a vendor-tarball as easily as Go, the cargo.eclass does not offer to use such a vendor-tarball without doing some little tricks.

Using a vendor-tarball instead of using the CRATES variable has some benefits. For example you don't need to recreate the list of dependencies (e.g. by using pycargoebuild in the upstream repo) and copy it into the new ebuild when updating. If the vendor tarball is hosted right, bumping a Rust-ebuild is as easy as renaming the ebuilds filename with the new version.

Creating a vendor tarball

Creating a vendor tarball can be done by running cargo vendor in the upstream repository. It will create a vendor folder, then tar that folder with tar -czvf vendor.tar.gz vendor.

Using a vendor tarball in an ebuild

Because the cargo.eclass is somewhat built with the CRATES variable in mind, it will complain when this variable is not set when the eclass in inherited. To work around this, we can define the variable as CRATES=" ". The cargo.eclass automatically sets up the build to look in $CARGO_HOME/gentoo to find dependencies. This folder is the folder cargo_home in $WORKDIR. So you either set up the ebuild to unpack the vendor-tarball into that directory, (by not calling default or cargo_src_unpack in src_unpack() and writing src_unpack() with your own instructions.) or you let portage do its default thing and let it unpack the vendor tarball into $S, then do ln -s "${S}/vendor/"* "${CARGO_HOME}/gentoo/".