openSUSE:Packaging Rust Software
Build Service Tutorial · Tips & Tricks · Cross Distribution Howto · Packaging checks
Desktop menu categories · RPM Macros · Scriptlets · Init scripts · How to write good changes
Maintaining/Creating packages that use Rust code
Dependencies
You should install the following packages to help build rust packages for OpenSUSE.
zypper install obs-service-cargo osc obs-service-tar obs-service-obs_scm \ obs-service-recompress obs-service-set_version obs-service-format_spec_file \ cargo sudo
Optionally, you can install cargo lock2rpmprovides to help generate the license string.
cargo install cargo-lock2rpmprovides
If you want to cache build artefacts, as of osc-0.173.0 you can use sccache to help reduce repeat build times.
# ~/.config/osc/oscrc sccache = 1 sccache_uri = file:///var/cache/obs_sccache.tar
Alternately you can use a network sccache
# ~/.config/osc/oscrc sccache = 1 sccache_uri = redis://127.0.0.1:6379
Setting up the redis cache is beyond the scope of this page.
Creating the Package
You should checkout your blank package with:
osc co home:<username>/package cd home:<username>/package
A skeleton RPM spec file for a rust package is:
Name: hellorust # This will be set by osc services, that will run after this. Version: 0.0.0 Release: 0 Summary: A hello world with a number of the day printer # If you know the license, put it's SPDX string here. # Alternately, you can use cargo lock2rpmprovides to help generate this. License: Unknown Url: https://github.com/Firstyear/hellorust Source0: %{name}-%{version}.tar.zst Source1: vendor.tar.zst # Pull in the latest rust/cargo toolchain BuildRequires: cargo # This contains rpm macros to assist with building BuildRequires: cargo-packaging # Disable this line if you wish to support all platforms. # In most situations, you will likely only target tier1 arches for user facing components. ExclusiveArch: %{rust_tier1_arches} %description A hello world with a number of the day printer. %prep # The number passed to -a (a stands for "after") should be equivalent to the Source tag number # of the vendor tarball, 1 in this case (from Source1). %autosetup -p1 -a1 # Remove exec bits to prevent an issue in fedora shebang checking. Uncomment only if required. # find vendor -type f -name \*.rs -exec chmod -x '{}' \; %build %{cargo_build} %install # using cargo_install (only supports bindir) %{cargo_install} # manual process, this can place binaries in other directories like sbindin # install -D -d -m 0755 %{buildroot}%{_bindir} # install -m 0755 %{_builddir}/%{name}-%{version}/target/release/%{name} %{buildroot}%{_bindir}/%{name} %check %{cargo_test} %files %license LICENSE-FILE %{_bindir}/%{name} %changelog
There are a few commented areas you’ll need to fill in and check. Note that %{cargo_build} is redundant if you use %{cargo_install} as %{cargo_install} will rebuild and install at the same time.
Some projects may need custom rust flags as build arguments, which can be set with the following variable at the head of the spec file.
%define __rustflags --cfg tokio_unstable
But next we will create a service file that allows OBS to help get our sources and bundle them for us. This should go in a file called _service
<services> <service mode="disabled" name="obs_scm"> <param name="url">https://github.com/Firstyear/hellorust.git</param> <param name="versionformat">@PARENT_TAG@~@TAG_OFFSET@</param> <param name="scm">git</param> <param name="revision">v0.1.1</param> <param name="match-tag">*</param> <param name="versionrewrite-pattern">v(\d+\.\d+\.\d+)</param> <param name="versionrewrite-replacement">\1</param> <param name="changesgenerate">enable</param> <param name="changesauthor"> YOUR EMAIL HERE </param> </service> <service mode="disabled" name="tar" /> <service mode="disabled" name="recompress"> <param name="file">*.tar</param> <param name="compression">zst</param> </service> <service mode="disabled" name="set_version"/> <service name="cargo_vendor" mode="disabled"> <param name="src">hellorust</param> <param name="compression">zst</param> <param name="update">true</param> </service> </services>
This service file does a lot of the work for us:
- It will fetch the sources from git, based on the version we set.
- It will turn them into a tar.zst for us.
- It will update the changelog for the rpm, and set the correct version in the spec file.
- It will download our rust dependencies, and then bundle them to vendor.tar.zst.
- It scans our project for any known vulnerabilities These come from the RustSec advisory database.
You can run this with:
osc service ra
Optionally, you can now run the lock2rpmprovides:
cd hellorust cargo lock2rpmprovides
This will generate a license string you can copy into the spec file.
You can then add the needed sources and commit to OBS
osc add _service _servicedata cargo_config hellorust-0.1.1~git0.db340ad.tar.zst hellorust.spec vendor.tar.zst osc ci
If you run into any issues while building your package that mention crate dependency conflicts then you may disable the automatic update of dependencies like so:
<param name="update">false</param>
to see if that fixes it. Since crates on git forges will normally only run CI over their older Cargo.lock files rather than the newest semver compatible dependencies.
From here, you can follow the How to contribute to Factory guide.
Projects with Cargo workspaces
Workspaces is a Cargo feature to build multiple projects from the same git tree. You can make the following changes to your spec to build them in the same project:
BuildRequires: cargo-packaging >= 1.2.0 [...] %build %{cargo_build} --all [...] %install %{cargo_install -p project1} %{cargo_install -p project2}
Updating your Package
Once your package has been accepted, you can then update it with the following steps:
osc bco devel:project/pkgname cd home:username:branches:devel:project/pkgname osc service ra osc status
Then inspect the changes, and osc rm any old files, and osc add any updated source files.
From there you can then do a build and a ci/sr
osc build osc ci osc results osc sr
Patching vendored crates
Sometimes it is necessary to patch vendored crates for one reason or another, usually because of CVEs that are not yet in an upstream release. Or if you want to fix CVEs in Leap which have older versions.
Even though, theoretically, you can modify and repackage your vendor-tarball, we try to leave the vendor-tarball untouched, and work with patch-files, to make it clear what was modified by us.
This is a bit more tricky, but can be done in the following manner:
First, extract your sources- and vendor-tarballs, either manually or with
quilt setup your.spec
or, if you are using a service-file with
osc service run obs_scm
But note that you have to extract your vendor-tarball manually, then.
Then cd into the source-dir.
Now you can run optionally an initial
cargo tree | less
to identify which sub(-subsub)package you need to change/bump.
Depending on what you have to do, continue with the corresponding instructions below.
Forcing newer versions of direct dependency
Assume, we have a dependency-tree like this:
your_package v1.0 ├── dep_lvl_1 v4.4 ...
and you want to bump dep_lvl_1 to v4.5
For this you need your top-level Cargo.toml-file, Cargo.lock if it exists, as well as vendor/dep_lvl_1/ under some kind of version control (quilt may be unsuitable for this, since you probably don't know in advance, which files will get modified by updating the crate).
Edit your Cargo.toml-file, to make cargo use v4.5 for dep_lvl_1.
Verify with
cargo tree | less
then run
cargo vendor
to update the crate dep_lvl_1.
Jump to Final steps for all.
Forcing newer versions of sub-dependency
Assume, we have a dependency-tree like this:
your_package v1.0 ├── dep_lvl_1 v4.4 │ ├── dep_lvl_2 v1.3 ...
and you want to bump dep_lvl_2 to v1.4.
For this you need your top-level Cargo.toml-file, Cargo.lock if it exists, as well as vendor/dep_lvl_1/Cargo.toml and vendor/dep_lvl_2/ under some kind of version control (quilt may be unsuitable for this, since you probably don't know in advance, which files will get modified by updating the crate).
Edit the Cargo.toml-file you want to change:
vi vendor/dep_lvl_1/Cargo.toml
and find where dep_lvl_2 is requested. Change the version accordingly to v1.4.
Add to the toplevel Cargo.toml-file
[patch.crates-io] dep_lvl_1 = { path="vendor/dep_lvl_1" }
Run
cargo vendor
to download the new version of dep_lvl_2
Jump to Final steps for all.
Actually modifying dependency-code
Assume, we have a dependency-tree like this:
your_package v1.0 ├── dep_lvl_1 v4.4 │ ├── dep_lvl_2 v1.3 ...
and you want to modify the code of dep_lvl_2 directly.
For this you need your top-level Cargo.toml-file, Cargo.lock if it exists, as well as the files you need to edit, e.g. vendor/dep_lvl_2/src/lib.rs under some kind of version control.
Edit the source-file you want to change:
vi vendor/dep_lvl_2/src/lib.rs
Add to the toplevel Cargo.toml-file
[patch.crates-io] dep_lvl_2 = { path="vendor/dep_lvl_2" }
Jump to Final steps for all.
Final steps for all
If you ran cargo vendor before coming here, check if you have a .cargo/config.toml-file. If not, create the directory and copy the cargo_config-file from your additional sources there.
Then put it under version control, too, and write the output of cargo vendor to .cargo/config.toml (in most cases this file will not be modified, but in rare cases it can happen, when you play with the dependency-tree. So we want to also have this modification in our patch).
Verify you have the correct versions for everything with
cargo tree | less
Create a patch from the modifications you did (using your version control, e.g. git diff > ../modifying_vendored_crate.patch or quilt refresh), and add it to your spec-file.
References
- openSUSE:Package_dependencies Advanced Package Dependencies
- obs-service-cargo_vendor
- obs-service-cargo_audit