How to patch Rust¶
This guide details the process of fixing an existing versioned rustc
Ubuntu package.
To see the process of creating a new versioned
rustc
package, consult the How to update Rust guide instead.To see the process of backporting Rust, consult the How to backport Rust guide instead.
Background¶
Unfortunately, since rustc
is a versioned source package, we are unable to use the more modern git-ubuntu (git-ubuntu(1)) workflow. Whenever you must fix a bug in an already-released Rust source package, you must follow the legacy debdiff(1) workflow instead.
Attention
This guide assumes that you already have a basic understanding of maintaining Ubuntu packages in general. It only covers the things that make Rust package patching unique.
Substitution Terms¶
From now on, the documentation will contain certain terms within angle brackets which must be replaced with the actual value that applies to your situation.
As an example, let’s assume you are updating rustc-1.84
(upstream version 1.84.0
) to rustc-1.85
(upstream version 1.85.1
) for Noble Numbat:
<X.Y>
: The short Rust version you’re updating to.Example:
1.85
<X.Y.Z>
: The long Rust version you’re updating to.Example:
1.85.1
<X.Y_old>
: The short Rust version you’re updating from.Example:
1.84
<X.Y.Z_old>
: The long Rust version you’re updating from.Example:
1.84.0
<release>
: The target Ubuntu release adjective.Example:
noble
<lpuser>
: Your Launchpad username. This is also used to refer to your personal Launchpad Git repository’s remote name.<foundations>
: Your local Git remote name for the Foundationsrustc
Git repository.<lp_bug_number>
: The number of the Launchpad bug associated with this upload.
Setting up the Repository Locally¶
This only needs to be done once when setting up a machine for Rust toolchain maintenance for the first time.
Project directory structure¶
Since the Debian build tools generate files in the parent directory of your package source directory, it’s recommended to keep things organized by placing the cloned repository inside of a fresh directory of its own.
Clone the repository inside an existing rustc
directory so your file structure looks like the following:
rustc
├── rustc
│ ├── [...]
│ ├── Cargo.lock
│ ├── Cargo.toml
│ ├── debian
│ └── [...]
└── rustc-<...>.orig.tar.xz
Naturally, your higher-level rustc
directory won’t have any .orig.tar.xz files yet, but they will be stored there once you start working on the package.
Cloning the Git repository¶
The main repository for all versioned Rust toolchain packages is the Foundations Launchpad Git repository. A branch exists for every single upstream release and backport and serves as a central place to store all Rust toolchain code, regardless of which versioned package a particular branch belongs to.
Clone the Foundations Git repository within your existing parent directory:
$ git clone git+ssh://<lpuser>@git.launchpad.net/~canonical-foundations/ubuntu/+source/rustc
Then, create your own personal Git repository on Launchpad:
$ git remote add <lpuser> git+ssh://<lpuser>@git.launchpad.net/~<lpuser>/ubuntu/+source/rustc
Generally, it’s recommended to use your personal Git repository as a remote backup throughout the process — the update procedure involves multiple rebases, so it’s best to wait pushing to the Foundations repository until you’re done.
The Patching Process¶
You may make your changes to the package the same way you would with any other package. After that, you are ready to test the build locally.
Local Build and Bugfixing¶
You’re now ready to try to build rustc
using sbuild(1).
First, make sure that all previous build artifacts have been cleaned from your upper-level directory:
$ rm -vf ../*.{debian.tar.xz,dsc,buildinfo,changes,ppa.upload}
$ rm -vf debian/files
$ rm -rf .pc
Then, run the build! Depending on your computer, a full build tends to take about 1-3 hours.
$ sbuild -Ad <release>
Using another PPA to bootstrap¶
Not all rustc
releases are necessarily in the archive. Perhaps you’re waiting on a previous version to be upload, or you’re creating a backport which isn’t needed by the subsequent Ubuntu release.
If this applies to you, you must add your PPA as an extra repository to your sbuild
command:
$ sbuild -Ad <release> \
--extra-repository="deb [trusted=yes] http://ppa.launchpadcontent.net/<lpuser>/<ppa_name>/ubuntu/ <release> main"
Fixing bugs¶
If the build fails, it’s up to you to figure out why. This will require problem-solving skills and attention to detail.
First, try to find any upstream issues related to the problem on the Rust GitHub page. It’s quite common for non-packaging-related problems to be already known upstream, and you can often find a patch from there.
Searching for failing tests within the build log¶
sbuild
saves the build logs to your computer. You can easily jump to the standard output of each failing test by searching for the following within the log:
stdout ----
Running individual tests¶
If the build fails, then sbuild
will place you in an interactive shell for debugging. This is extremely useful, as you can change the source code and retry tests without rebuilding the whole thing.
For example, here’s how to re-run all the bootstrap tests:
$ debian/rules override_dh_auto_test-arch RUSTBUILD_TEST_FLAGS="src/bootstrap/"
Here’s how to re-run just the alias_and_path_for_library
bootstrap test:
$ debian/rules override_dh_auto_test-arch RUSTBUILD_TEST_FLAGS="src/bootstrap/ --test-args alias_and_path_for_library"
Proper patch header format¶
In order to fix certain bugs, it’s likely you’ll need to create your own patch at some point. It’s important that this patch contains enough information for other people to understand what it’s doing and why it’s doing it.
First, ensure that Debian has not already created an equivalent patch. If so, you can simply use their patch directly. If you need to modify the patch in any way, make sure to add Origin: backport, <Debian VCS patch URL>
to the patch header.
Otherwise, you must create your own patch. A template DEP-3 header can be generated using the following command:
$ quilt header -e --dep3 <path/to/patch>
For the most part, you can follow the Debian DEP-3 patch guidelines. However, there are a few extra things you must do:
Debian developers typically don’t use the
This patch header follows DEP-3 [...]
line added byquilt
. Delete this line.If this patch isn’t something needed to get the new Rust version to build, and you’re instead updating an existing source package, add a
Bug-Ubuntu:
line linking to the Launchpad bug.
Lintian Checks¶
Lintian (lintian(1)) checks your source package for bugs and Debian policy violations.
Clean up previous build artifacts then build the source package:
$ dpkg-buildpackage -S -I -i -nc -d -sa
First, check the Lintian output with just the warnings and errors:
lintian -i --tag-display-limit 0 2>&1 | tee <path_to_log_file>
Addressing warnings and errors¶
You must address all of these in one way or another. They must either be fixed or added to debian/source/lintian-overrides{,.in}
, with a few notable exceptions:
E: rustc-1.86 source: field-too-long Vendored-Sources-Rust
This is simply the length of the field. While we would like to change this in the future in
dh-cargo
, there’s nothing that can (or should) be done about this for now.
E: rustc-1.86 source: unknown-file-in-debian-source [debian/source/lintian-overrides.in]
This is just the file used to generate the Lintian overrides for a given Rust version. It’s completely harmless to have in the source tree.
E: rustc-1.86 source: version-substvar-for-external-package Depends ${binary:Version} cargo-<X.Y> -> rustc [debian/control:*]
This is just a fallback for a non-versioned
rustc
package. While it’s unlikely to ever be used, it’s not a typo, so you don’t need to worry about it.
W: rustc-1.86 source: unknown-field Vendored-Sources-Rust
This is a custom field, not a typo.
As for any other warnings or errors, you must figure out whether the lint should be ignored or remedied. Don’t be afraid to ask for help from more experienced package maintainers, or consult the existing Lintian overrides for precedence.
Extra lints¶
Now you can run Lintian with all the pedantic, experimental, and informational lints enabled. It isn’t typically necessary to fix most of the extra lints, but it’s a good idea to check everything and see if there are some ways to improve the package based on these lints.
lintian -i -I -E --pedantic
Important
Don’t forget to clean and rebuild the source package before re-running Lintian, otherwise your changes to the package won’t apply!
PPA Build¶
Once everything builds on your local machine and Lintian is satisfied, it’s time to test the package on all architectures by uploading it to a PPA.
Creating a new PPA¶
If this is your first PPA upload for this Rust version, you must create a new PPA using the ppa-dev-tools
snap. The PPA name depends on whether you are updating Rust, backporting Rust, or patching Rust.
New versioned Rust package:
$ ppa create rustc-<X.Y>-merge
Rust backport:
$ ppa create rustc-<X.Y>-release
Rust patch:
$ ppa create rustc-<X.Y>-lp<lp_bug_number>
The command should return a URL leading to the PPA. You must go to that Launchpad URL and do two things:
“Change Details” -> Enable all “Processors” (Make sure RISC-V is enabled!)
“Edit PPA Dependencies” -> Set Ubuntu dependencies to “Proposed”
If you are using another PPA to bootstrap, then you must explicitly add this PPA as a dependency in the “Edit PPA Dependencies” menu.
PPA changelog entry¶
Next, add a temporary changelog entry, appending ~ppa<N>
to your version number so the PPA version isn’t used in favour of the actual version in the archive:
Note
<N>
is just the number of the upload. You may have to fix something and re-upload to this PPA, so you should use ~ppa1
for your first PPA upload, ~ppa2
for your second, etc.
$ dch -bv <X.Y.Z>+dfsg0ubuntu1-0ubuntu1\~ppa<N> \
--distribution "<release>" \
"PPA upload"
Uploading the source package¶
Make sure that your source directory is clean (especially debian/files
), then build the source package:
$ dpkg-buildpackage -S -I -i -nc -d -sa
Finally, upload the newly-created source package:
Note
You can get the source-changes-file
script here.
New versioned Rust package:
$ dput ppa:<lpname>/rustc-<X.Y>-merge $(source-changes-file)
Rust backport:
$ dput ppa:<lpname>/rustc-<X.Y>-<release> $(source-changes-file)
Rust patch:
$ dput ppa:<lpname>/rustc-<X.Y>-lp<lp_bug_number> $(source-changes-file)
The PPA will then build the Rust package for all architectures supported by Ubuntu. These builds will highlight any architecture-specific build failures.
Handling early PPA build failures¶
Sometimes, a PPA build on a specific architecture will fail in under 15 minutes with no build log provided. If this happens, there was a Launchpad issue, and you can simply retry the build without consequence.
If the build failed and there is a build log provided, then there was indeed a build failure which you must address.
autopkgtests¶
You must also verify that none of your changes have interfered with autopkgtests in any way.
To run the autopkgtests for real, run the following command provided by the ppa-dev-tools
snap to get links to all the autopkgtests:
$ ppa tests \
ppa:<lpuser>/rustc-<X.Y>-merge \
--release <release> \
--show-url
Click all of the links except i386 to trigger the autopkgtests for each target architecture.
Re-run the same ppa tests ...
command to check the status of the autopkgtests themselves.
The infrastructure can be a little flaky at times. If you get a “BAD” reponse (instead of a “PASS” or “FAIL”), then you just need to retry it.
Creating a Reviewable Diff¶
Once you’ve verified that your updated package builds properly in a PPA, passes all autopkgtests, and meets Lintian standards, then you’re ready to create a reviewable diff using debdiff(1).
See also
To get more info on the legacy debdiff
process in general, consult the Submitting the fix section.
Essentially, since the Git history of rustc-<X.Y>
was wiped when it was uploaded as a new package, we need to manually generate a diff between the uploaded version of rustc-<X.Y>
and your updated version of rustc
that doesn’t rely on Git. To do this, we’ll need .dsc
s for both package versions.
Build the source package for both the new and old versions:
$ dpkg-buildpackage -S -I -i -nc -d -sa
After that, use debdiff
to generate a diff between the two .dsc
s. Redirect the output to an easily-accessible place:
$ debdiff <old_dsc> <new_dsc> > 1-<new_full_version_number>.debdiff
debdiff patch naming convention¶
Important
An understanding of Rust-specific version string conventions is necessary for this portion. Read the Rust version strings article before continuing.
Let’s break down an example debdiff patch name: 1-1.86.0+dfsg0ubuntu2-0ubuntu1.debdiff
1-
means that this is the first revision of this patch.1.86.0+dfsg0ubuntu2-0ubuntu1
is the full version number of your updated version.0ubuntu2
means that the orig tarball has been regenerated after the initial upload. You don’t have to increment this number unless you’ve changed the orig tarball.0ubuntu1
has been reset, no matter what the previous version number is. This is because the orig tarball was regenerated. You only have to increment this portion of the version number when the orig tarball was the same.
The
.debdiff
suffix is simply a hint that this is a patch. Launchpad will complain (but still allow you to upload the patch) if this is not here.
Here’s another example: 2-1.81.0+dfsg0ubuntu1-0ubuntu3.debdiff
2-
: It’s the second revision of this patch, meaning that the sponsor had some feedback and another patch had to be generated.0ubuntu1
: The orig tarball has been unchanged since the initial upload.0ubuntu3
: This package has already been updated once before since its initial upload.