Support installing third-party tier-3 target support with rustup
Problem you are trying to solve
I'd like to be able to install a tier-3 toolchain using rustup rather than the current solution of unzipping a file into $(rustc --print sysroot).
Currently we manually build zipfiles of the libstd for riscv32imac-unknown-xous-elf using the stable compiler and install this into $(rustc --print sysroot)/lib/rustlib. This works well as it allows for users to have the standard library without needing a C compiler (which is otherwise required to build libm).
These zipfiles are installed by the xtask pattern as part of the normal build, but it would be nice to have it managed in a standard way.
Solution you'd like
rustup supports having alternate distribution servers. These are mostly used for pre-release testing, but it would be possible to use it for tier-3 targets. For example, we could build packages for xous with ./x.py dist --target riscv32imac-unknown-xous-elf build-manifest rust-std and then use the build-manifest program to generate a dist server root.
The issue is that target add refuses to consult the dist server for the riscv32imac-unknown-xous-elf target triple, so even though that target exists and is available on that server it won't go looking for it.
Notes
Initially I was going to open an issue identical to https://github.com/rust-lang/rustup/issues/4590 because it took me a while to discover what the problem was. However I see as of https://github.com/rust-lang/rustup/issues/4006 it at least doesn't refer to rustc --print target-list anymore.
@xobs I'm sorry but I haven't fully understood your request. What was the error message you were getting?
Are you sure the entries that you need are really in the manifest file, since rustup is merely an interpreter for it?
In #4409 I explicitly didn't introduce the notion of tiered targets in rustup for cases like this, and I don't believe it had existed before.
As such, I don't believe rustup is able to detect whether a target is of tier 3. If it cannot download a target, it should normally fall into one of the following cases:
- The target was not recognized: I don't think that is the case for this issue?
- The manifest did not indicate the existence of the target.
- The download URL of the target as indicated by the manifest did not exist.
As an example:
$ RUSTUP_LOG=trace RUSTUP_DIST_SERVER=https://xous.xobs.io/dist rustup +stable target install riscv32imac-unknown-xous-elf
2025-12-09T08:58:42.927239Z DEBUG run_rustup:run_rustup_inner:main:set_globals: rustup::cli::common: read metadata version: '12' current_dir="/Users/seancross/Code/39c3-talk" args="[\"rustup\", \"+nightly\", \"target\", \"install\", \"riscv32imac-unknown-xous-elf\"]" current_dir="/Users/seancross/Code/39c3-talk" quiet=false
2025-12-09T08:58:42.927348Z TRACE run_rustup:run_rustup_inner:main:set_globals: rustup::config: `RUSTUP_DIST_SERVER` has been set to `https://xous.xobs.io/dist` current_dir="/Users/seancross/Code/39c3-talk" args="[\"rustup\", \"+nightly\", \"target\", \"install\", \"riscv32imac-unknown-xous-elf\"]" current_dir="/Users/seancross/Code/39c3-talk" quiet=false
2025-12-09T08:58:42.978499Z TRACE run_rustup:run_rustup_inner: rustup_init: error=toolchain 'nightly-aarch64-apple-darwin' does not support target 'riscv32imac-unknown-xous-elf'; did you mean 'riscv32imac-unknown-none-elf'?
note: you can see a list of supported targets with `rustc --print=target-list`
note: if you are adding support for a new target to rustc itself, see https://rustc-dev-guide.rust-lang.org/building/new-target.html
2025-12-09T08:58:42.978527Z ERROR rustup::cli::common: toolchain 'nightly-aarch64-apple-darwin' does not support target 'riscv32imac-unknown-xous-elf'; did you mean 'riscv32imac-unknown-none-elf'?
note: you can see a list of supported targets with `rustc --print=target-list`
note: if you are adding support for a new target to rustc itself, see https://rustc-dev-guide.rust-lang.org/building/new-target.html
$
I do have a https://xous.xobs.io/dist/channel-rust-stable.toml file, and I was in the process of adding https://xous.xobs.io/dist/channel-rust-stable.toml.sha256, however I noticed that rustup doesn't even try to fetch this file.
The error message in #4409 states that there are no prebuilt artifacts, however I'm building them for this target and would like for rustup to be able to install them, and the artifacts are there, I'd like rustup to ask for them.
Put another way: I'd like to have an official method to install stable tier-3 targets. rustup seems to be the way to do that. If it's not the right method, I can try taking another approach.
toolchain 'nightly-aarch64-apple-darwin' does not support target 'riscv32imac-unknown-xous-elf'
It says nightly but the manifest name you posted says stable? Also the manifest contains example.com urls (though I'd expect that to be a later error).
I'm sorry, I must have mis-copied the output. I've been switching back and forth between +nightly and +stable. I would have expected the +nightly version of rustup to have the new text, but it looks the same.
And you're right in that the manifest file contains example.com URLs. I put example.com when I generated the file using build-manifest and I was going to update them once I had proven that the file was correctly requested.
But I don't even see rustup requesting the file. I tried using +nightly so as to use the nightly version of rustup, but the same thing happens with +stable.
Working backwards, ultimately I'd like to install https://xous.xobs.io/dist/rust-std-1.91.0-riscv32imac-unknown-xous-elf.tar.gz or https://xous.xobs.io/dist/rust-std-1.91.0-riscv32imac-unknown-xous-elf.tar.xz with rustup. But my understanding is as of today that can't be done because it doesn't consult the remote dist server for this particular target, and I'd like to change that.
@xobs Okay I think I have the full picture now. Trying this on the bleeding release of rustup gives:
> RUSTUP_DIST_SERVER=https://xous.xobs.io/dist rustup +stable target install riscv32imac-unknown-xous-elf
error: toolchain 'stable-aarch64-apple-darwin' has no prebuilt artifacts available for target 'riscv32imac-unknown-xous-elf'
note: this may happen to a low-tier target as per https://doc.rust-lang.org/nightly/rustc/platform-support.html
note: you can find instructions on that page to build the target support from source
My first guess would be that it indicates a problem in your manifest file format. A correct manifest should not hit the if branch below:
https://github.com/rust-lang/rustup/blob/a1b2ad14f34744370f32c5f4c819d17cfe2535bb/src/toolchain/distributable.rs#L72-L82
It's not hitting my manifest file at all. I've made this patch:
diff --git a/src/download/mod.rs b/src/download/mod.rs
index 37ae9b32..f1053c2c 100644
--- a/src/download/mod.rs
+++ b/src/download/mod.rs
@@ -254,6 +254,7 @@ impl Backend {
callback: Option<DownloadCallback<'_>>,
timeout: Duration,
) -> anyhow::Result<()> {
+ eprintln!("Downloading {url:?} to {}", path.display());
let Err(err) = self
.download_impl(url, path, resume_from_partial, callback, timeout)
.await
So now it will print every time it tries to download. If I try to install riscv32imac-unknown-xous-elf it errors without trying to download anything. If I try to install riscv32imac-unknown-none-elf it tries to fetch the file but fails (since it doesn't exist).
$ RUSTUP_DIST_SERVER=https://xous.xobs.io RUSTUP_HOME=home CARGO_HOME=home RUSTUP_FORCE_ARG0=rustup target/debug/rustup-init target install riscv32imac-unknown-xous-elf
error: toolchain 'stable-aarch64-apple-darwin' has no prebuilt artifacts available for target 'riscv32imac-unknown-xous-elf'
note: this may happen to a low-tier target as per https://doc.rust-lang.org/nightly/rustc/platform-support.html
note: you can find instructions on that page to build the target support from source
$ RUSTUP_DIST_SERVER=https://xous.xobs.io RUSTUP_HOME=home CARGO_HOME=home RUSTUP_FORCE_ARG0=rustup target/debug/rustup-init target install riscv32imac-unknown-none-elf
info: downloading component(s)
Downloading Url { scheme: "https", cannot_be_a_base: false, username: "", password: None, host: Some(Domain("xous.xobs.io")), port: None, path: "/dist/2025-11-10/rust-std-1.91.1-riscv32imac-unknown-none-elf.tar.xz", query: None, fragment: None } to /Users/seancross/Code/rustup/home/downloads/2bb32e849ebda8329c2918a496558908a42ff60e09f013b93a212f6a88443d22.partial
rust-std download failed after 0s info: rolling back changes
error: component download failed for rust-std-riscv32imac-unknown-none-elf: could not download file from 'https://xous.xobs.io/dist/2025-11-10/rust-std-1.91.1-riscv32imac-unknown-none-elf.tar.xz' to '/Users/seancross/Code/rustup/home/downloads/2bb32e849ebda8329c2918a496558908a42ff60e09f013b93a212f6a88443d22.partial': http request returned an unsuccessful status code: 404
$
@xobs Could you try to log the line where rustup is getting the manifest file though? The log you have shown above seems to be only related to component downloading.
I think part of the problem may be that it likely uses a channel toml file from the sysroot and not the server? I could see there being security considerations here. This particular package is just a bunch of library files, so in this case it's not necessarily an issue, but I can imagine there being something else at play here.
I tried patching download_and_check() to print the URL, and it still doesn't try to download the manifest file.
@xobs We do allow mirrors or third-party release servers, for example TUNA has been using the same mechanism without problems... so your use case should be valid as well in theory.
@xobs Just in case you doubt the manifest reading doesn't work, see the log below:
> RUSTUP_LOG=rustup=debug RUSTUP_DIST_SERVER=https://xous.xobs.io/dist rustup toolchain install beta
2025-12-09T15:07:56.153260Z DEBUG rustup::config: read metadata version: 12
2025-12-09T15:07:56.154344Z DEBUG rustup::install: installing toolchain beta-aarch64-apple-darwin
[..]
2025-12-09T15:07:56.154665Z DEBUG rustup::download: downloading file url=https://xous.xobs.io/dist/dist/channel-rust-beta.toml.sha256
[..]
2025-12-09T15:07:56.749545Z DEBUG rustup::dist: manifest not found; trying legacy manifest
[..]
2025-12-09T15:07:56.749999Z DEBUG rustup::download: downloading file url=https://xous.xobs.io/dist/dist/channel-rust-beta.sha256
[..]
2025-12-09T15:07:57.329387Z ERROR rustup::cli::common: no release found for 'beta'
The thing is that this manifest resolution is done only once, during the toolchain's installation phase. (I clearly forgot about this earlier, my apologies for having led you towards the wrong direction 🙇♀️)
So indeed there is some security-related stuff going on here as you said, as well as, I assume, the effort to keep every component in sync so that they are guaranteed to work with each other, but it wouldn't be a problem if you just delegate all components (except yours) to the official?
That wouldn't be a problem, and in fact would be preferred. I'd rather have the minimum amount of stuff to specialize a tier 3 target and keep everything else upstream.
Maybe have something similar to how we do environment variables for things like CARGO_TARGET_<triple>_RUNNER where a dist server can be different for a target triple?
Maybe have something similar to how we do environment variables for things like
CARGO_TARGET_<triple>_RUNNERwhere a dist server can be different for a target triple?
@xobs I'm afraid that works against what we have for now since it breaks the current stable distribution apart even on our official release server (the stable toolchain downloaded from a mirror will have the manifest from and potentially specific to that mirror, that's more acceptable).
As an alternative, I have collected past suggestions and proposed #4636 to be a candidate on our roadmap. What do you think? Will rustc versions change if and how your new components work (I assume they will)?
I'm not sure that works, but I'm not entirely understanding the proposal. How would that allow for installing a +stable toolchain for tier-3 targets? The proposal looks like a way to have alternate toolchains, which does seem useful for some development branches but doesn't quite address the goal of being able to use tier-3 targets on stable Rust.
I'm not sure that works, but I'm not entirely understanding the proposal. How would that allow for installing a
+stabletoolchain for tier-3 targets? The proposal looks like a way to have alternate toolchains, which does seem useful for some development branches but doesn't quite address the goal of being able to use tier-3 targets on stable Rust.
@xobs The idea is that we can make addons easier without really tinkering with the official distribution.
The imaginary stable-xous-elf-addon toolchain distribution has its own manifest that points to the server you control, has a minimal set of components, and can be mixed with stable to form a new virtual toolchain through FS links for actual usage, etc.
Whether that toolchain can work on its own is another topic, as I'll need to check if we have limitations regarding the minimal requirements of a toolchain and whether we should lift it in certain cases, but anyway it can be left for further discussion.
@xobs Besides, for a more "here and now" solution you can have a look at how esp solved their distribution problem with a custom tool: https://github.com/esp-rs/espup
We already manually install it by unzipping a file to $(rustc +stable --print sysroot)/lib/rustlib which gets us the ability to use normal rustc to compile.
I opened this issue because it seems a little... rude?... to be mucking about with writing files to the rustlib directory. But given it's libstd built with the rust compiler against the same branch, it's probably fine.
@xobs Yes, I understand, I'm also really concerned about having something unpacked in a dir that's theoretically fully owned by rustup as any changes you've made can be easily overwritten at the end of the day. That's why I'm thinking about having a mock/linked/virtual component as a more elegant solution.