<a id="how-to-patch-rust"></a>

# 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](https://documentation.ubuntu.com/project/maintainers/niche-package-maintenance/rustc/update-rust.md#how-to-update-rust) guide instead.
- To see the process of [backporting](https://documentation.ubuntu.com/project/how-ubuntu-is-made/concepts/glossary.md#term-Backport) Rust, consult the [How to backport Rust](https://documentation.ubuntu.com/project/maintainers/niche-package-maintenance/rustc/backport-rust.md#how-to-backport-rust) guide 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.

## Background

`rustc` [VCS](https://documentation.ubuntu.com/project/how-ubuntu-is-made/concepts/glossary.md#term-VCS) policy is unique because it is a versioned [source package](https://documentation.ubuntu.com/project/how-ubuntu-is-made/concepts/glossary.md#term-Source-Package). This means that every new upstream Rust version corresponds to a package with a new, unique name in the [Ubuntu Archive](https://documentation.ubuntu.com/project/how-ubuntu-is-made/concepts/glossary.md#term-Ubuntu-Archive).

Therefore, there are two repositories for every `rustc` version uploaded to the
archive:

1. The
   [Foundations Rust repository](https://code.launchpad.net/~canonical-foundations/ubuntu/+source/rustc):
   this repository is used for
   [adding new Rust versions to the archive](https://documentation.ubuntu.com/project/maintainers/niche-package-maintenance/rustc/update-rust.md#how-to-update-rust). It
   tracks the complete history of all `rustc` packages in Ubuntu, but once a
   versioned package is uploaded to the archive, updates to that package must
   be synced back to this repository manually.
2. The package’s unique [git-ubuntu](https://documentation.ubuntu.com/project/how-ubuntu-is-made/concepts/glossary.md#term-git-ubuntu) () repository
   (example: [rustc-1.93](https://code.launchpad.net/ubuntu/+source/rustc-1.93)):
   this is the repository that matches the actual state of the package in the
   Ubuntu archive. Whenever an update to the package is uploaded, this
   repository gets updated automatically.

In short, patching an existing `rustc` release entails modifying and uploading
the release’s `git-ubuntu` repository, then manually copying over the changes to
the Foundations Rust repository after your changes are uploaded to the archive.

## Substitution terms

From now on, the documentation contains 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 patching `rustc-1.94` ([upstream](https://documentation.ubuntu.com/project/how-ubuntu-is-made/concepts/glossary.md#term-Upstream) version
`1.94.1`) for 26.10 Stonking Stingray:

`<X.Y>`
: The short Rust version you’re working on.
  <br/>
  - Example: `1.94`

`<X.Y.Z>`
: The long Rust version you’re on.
  <br/>
  - Example: `1.94.1`

`<release>`
: The target [Ubuntu release](https://documentation.ubuntu.com/project/how-ubuntu-is-made/concepts/glossary.md#term-Ubuntu-release) adjective.
  <br/>
  - Example: `stonking`

`<lpuser>`
: Your [Launchpad](https://documentation.ubuntu.com/project/how-ubuntu-is-made/concepts/glossary.md#term-Launchpad) username. This is also used to refer to
  your personal Launchpad Git [repository’s](https://documentation.ubuntu.com/project/how-ubuntu-is-made/concepts/glossary.md#term-Repository) remote name.

`<foundations>`
: Your local Git remote name for the [Foundations Rust repository](https://code.launchpad.net/~canonical-foundations/ubuntu/+source/rustc).

`<lp_bug_number>`
: The number of the [Launchpad](https://documentation.ubuntu.com/project/how-ubuntu-is-made/concepts/glossary.md#term-Launchpad) [bug](https://documentation.ubuntu.com/project/how-ubuntu-is-made/concepts/glossary.md#term-Bug) associated
  with this upload.

<a id="foundations-rust-repo-setup"></a>

## 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:

```none
rustc
├── rustc
│   ├── [...]
│   ├── Cargo.lock
│   ├── Cargo.toml
│   ├── debian
│   └── [...]
├── rustc-<...>.orig.tar.xz
└── rustc-<...>.orig-vendor.tar.xz
```

Naturally, your higher-level `rustc` directory won’t have the [.orig.tar.xz](https://documentation.ubuntu.com/project/how-ubuntu-is-made/concepts/glossary.md#term-orig-tarball) 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](https://git.launchpad.net/~canonical-foundations/ubuntu/+source/rustc). A branch exists for every single upstream release and [backport](https://documentation.ubuntu.com/project/how-ubuntu-is-made/concepts/glossary.md#term-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:

```none
$ git clone git+ssh://<lpuser>@git.launchpad.net/~canonical-foundations/ubuntu/+source/rustc
```

Then, create your own personal Git repository on Launchpad:

```none
$ 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.

---

## Setting up the git-ubuntu repository

Just like the Foundations Rust repository, set up a parent directory, then clone
the versioned package repository:

```none
$ mkdir rustc-<X.Y>
$ cd rustc-<X.Y>
$ git-ubuntu clone rustc-<X.Y>
$ cd rustc-<X.Y>
```

Inspect `debian/changelog` and make sure the most recent [changelog](https://documentation.ubuntu.com/project/how-ubuntu-is-made/concepts/glossary.md#term-Changelog) entry
version matches the version in the archive.

After that, get the current [orig tarball](https://documentation.ubuntu.com/project/how-ubuntu-is-made/concepts/glossary.md#term-orig-tarball) from the archive, so you are
able to build the package locally:

```none
$ git-ubuntu export-orig
```

Create your own new branch referencing the Launchpad bug you’re fixing:

```none
$ git checkout -b lp<lp_bug_number>
```

#### ATTENTION
Any patches to `rustc` must fix at least one Launchpad bug. If your patch
doesn’t have a Launchpad bug yet, create one and reference its version number in
your changelog.

## Patching process

You must make *all changes* to the package within the `git-ubuntu` repository,
*not* the Foundations Rust repository.

You may make your changes to the package the same way you would with any other
package: [How to make changes to a package](https://documentation.ubuntu.com/project/contributors/updating/make-changes-to-a-package.md#how-to-make-changes-to-a-package).

Don’t forget to update the changelog! See the
[version string format guide](https://documentation.ubuntu.com/project/how-ubuntu-is-made/concepts/version-strings.md#version-strings) for help on selecting
the proper version string.

The changelog must also reference the bug you are fixing.

After that, you are ready to test the build locally.

### Local Build and Bugfixing

You’re now ready to try to build `rustc` using .

First, make sure that all previous build artifacts have been cleaned from your upper-level directory:

<a id="updating-rust-clean-build"></a>
```none
$ 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.

```none
$ 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 uploaded, 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:

```none
$ sbuild -Ad <release> \
    --extra-repository="deb [trusted=yes] http://ppa.launchpadcontent.net/<lpuser>/<ppa_name>/ubuntu/ <release> main"
```

A common case is including the Staging PPA for bootstrapping:

```none
$ sbuild -Ad <release> \
    --extra-repository="deb [trusted=yes] http://ppa.launchpad.net/rust-toolchain/staging/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](https://github.com/rust-lang/rust/issues?q=is%3Aissue). 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:

```none
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:

```none
$ 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:

```none
$ 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, you’ll likely 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](https://salsa.debian.org/rust-team/rust/-/tree/debian/experimental?ref_type=heads) 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](https://documentation.ubuntu.com/project/how-ubuntu-is-made/concepts/glossary.md#term-DEP-3) header can be generated using the following command:

```none
$ quilt header -e --dep3 <path/to/patch>
```

For the most part, you can follow the [Debian DEP-3 patch guidelines](https://dep-team.pages.debian.net/deps/dep3/). 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 by `quilt`. 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.

### 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](https://documentation.ubuntu.com/project/how-ubuntu-is-made/concepts/glossary.md#term-PPA). This step is optional if doing a backport, but it is still recommended when doing a backport for the first time, or if there is reason to expect that the backport may need extensive troubleshooting.

#### Creating a new PPA

If this is your first PPA upload for this Rust version, you can create a new PPA using the [`ppa-dev-tools` snap](https://snapcraft.io/ppa-dev-tools) (it is also possible to use the Launchpad web UI). It is recommended to use the following naming convention for the PPA depending on whether you are updating Rust, backporting Rust, or patching Rust.

New versioned Rust package:

```none
$ ppa create rustc-<X.Y>-merge
```

Rust backport:

```none
$ ppa create rustc-<X.Y>-release
```

Rust patch:

```none
$ 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:

1. “Change Details” -> Enable all “Processors” (Make sure RISC-V is enabled!)
2. “Edit PPA Dependencies” -> Set Ubuntu dependencies to “Security” if doing a backport, or “Proposed” otherwise.

If you are using another PPA to bootstrap, then you must explicitly add this PPA as a dependency in the “Edit PPA Dependencies” menu.

#### NOTE
When a new Rust toolchain is uploaded to the Archive for the devel series, it initially goes into the [proposed pocket](https://documentation.ubuntu.com/project/how-ubuntu-is-made/concepts/package-archive.md#archive-pockets-proposed). By configuring your PPA to depend on “Proposed”, the set of available packages in the PPA aligns with what will be available in the Archive. This avoids a situation where the PPA build could succeed but then the Archive build fails due to missing dependencies, or vice versa.

On the other hand, when backporting a Rust toolchain to a stable release, it will be uploaded to the [security pocket](https://documentation.ubuntu.com/project/how-ubuntu-is-made/concepts/package-archive.md#archive-pockets-security). In this case, configuring your PPA to depend on “Security” aligns with the dependencies that will be available in the Archive. For this same reason, the Staging PPA is configured to depend on “Security” since it is used for backports.

#### 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.

```none
$ dch -bv <X.Y.Z>+dfsg-0ubuntu1\~ppa<N> \
    --distribution "<release>" \
    "PPA upload"
```

#### Uploading the source package

[Make sure that your source directory is clean](https://documentation.ubuntu.com/project/maintainers/niche-package-maintenance/rustc/update-rust.md#updating-rust-clean-build) (especially `debian/files`), then build the source package:

```none
$ dpkg-buildpackage -S -I -i -nc -d -sa
```

Finally, upload the newly-created source package.

New versioned Rust package:

```none
$ dput ppa:<lpname>/rustc-<X.Y>-merge <path_to_source_changes>
```

Rust backport:

```none
$ dput ppa:<lpname>/rustc-<X.Y>-<release> <path_to_source_changes>
```

Rust patch:

```none
$ dput ppa:<lpname>/rustc-<X.Y>-lp<lp_bug_number> <path_to_source_changes>
```

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](https://documentation.ubuntu.com/project/how-ubuntu-is-made/concepts/glossary.md#term-autopkgtest) in any way.

To run the [autopkgtests](https://documentation.ubuntu.com/project/how-ubuntu-is-made/concepts/glossary.md#term-autopkgtest) for real, run the following command provided by the [`ppa-dev-tools` snap](https://snapcraft.io/ppa-dev-tools) to get links to all the autopkgtests:

<a id="updating-rust-autopkgtests-url-command"></a>
```none
$ 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” response (instead of a “PASS” or “FAIL”), then you just need to retry it.

<a id="disable-rust-selfbuild-autopkgtest"></a>

#### Failing selfbuild autopkgtest

The selfbuild autopkgtest is often flaky because it recompiles the entire
compiler—a task too large for typical autopkgtest infrastructure. This is a
well-known problem; it is unlikely you have done anything wrong (especially if
your patch is small). The bug representing this issue is [LP: #2144934](https://bugs.launchpad.net/bugs/2144934).

For a [new toolchain upload](https://documentation.ubuntu.com/project/maintainers/niche-package-maintenance/rustc/update-rust.md#how-to-update-rust), this should be
addressed, but you are not expected to fix this when simply patching the
toolchain; the autopkgtest already passed once before.

In this case, go to [LP: #2144934](https://bugs.launchpad.net/bugs/2144934), mark the bug as affecting the toolchain
version you’re working on, and then comment out the autopkgtest in
`debian/tests/control`, referencing the bug number:

```diff
--- a/debian/tests/control
+++ b/debian/tests/control
@@ -1,6 +1,7 @@
-Test-Command: ./debian/rules build RUST_TEST_SELFBUILD=1
-Depends: @, @builddeps@
-Restrictions: rw-build-tree, allow-stderr
+# Disabled due to excessive resource usage (LP: #2144934)
+# Test-Command: ./debian/rules build RUST_TEST_SELFBUILD=1
+# Depends: @, @builddeps@
+# Restrictions: rw-build-tree, allow-stderr
 
 Tests: create-and-build-crate
 Restrictions: rw-build-tree, allow-stderr
```

Ensure you reference the bug in your commit message and changelog entry as well.

### Uploading the patched package

Once you are satisfied with your changes, upload the package.

First, push your changes to your personal branch:

```none
$ git push <lpuser>
```

Then, go to your personal repository list at
`https://code.launchpad.net/~<lpuser>/+git`. Select your repo, select your
`lp<lp_bug_number>` branch, then select “Propose for merging”. Submit your
[merge proposal](https://documentation.ubuntu.com/project/how-ubuntu-is-made/concepts/glossary.md#term-Merge-Proposal) there.

## Updating the Foundations Rust repository

After your changes have been uploaded to the archive, the Foundations Rust
repository must be manually updated to reflect the package’s actual state in the
archive.

You must add your changes to the rich Git history of the Foundations repo.
First, go to your `git-ubuntu` repo, switch to the `ubuntu/devel` branch, and
ensure it’s synced with the archive:

```none
$ git checkout ubuntu/devel
$ git pull
```

Then, create a series of patches for each commit you added since the last
version published in the archive:

```none
$ git format-patch pkg/import/<version_string_before_your_changes>..
```

This command generates a series of numbered patches, which you can then apply
to the Foundations Rust repository.

Go to the Foundations Rust repository working tree you
[set up earlier](#foundations-rust-repo-setup) and switch to the
appropriate `merge` branch:

```none
$ git checkout merge-<X.Y>
```

Then, apply each patch to the branch in numerical order:

```none
$ git am <path_to_git_ubuntu_repo>/rustc-<X.Y>/rustc-<X.Y>/0001-<patch_name>.patch
$ git am <path_to_git_ubuntu_repo>/rustc-<X.Y>/rustc-<X.Y>/0002-<patch_name>.patch
[...]
```

Once every patch has been applied, update both your personal remote and the
general Foundations remote:

```none
$ git push <lpuser>
$ git push <foundations>
```

Finally, delete the patches you generated from your `git-ubuntu` repo:

```none
$ rm <path_to_git_ubuntu_repo>/rustc-<X.Y>/rustc-<X.Y>/00*
```
