Repository navigation
The toolchain is a release a consumer can name, install and link with - #392
Merged
Merged
Conversation
gbae's CI is the first third party to build for ToyOS, and it reported what it
needed in order: a linker inside the toolchain tarball, a tag it can pin by SDK
version, the glibc floor stated, and an output it can exec.
**toyos-ld goes in the tarball.** rustc's ToyOS target spec names its linker
`toyos-ld` and finds it on `PATH` (`rust/compiler/rustc_target/src/spec/base/
toyos.rs`: `linker: Some("toyos-ld".into())`), so a consumer with only the
toolchain had nothing to link with and needed a monorepo checkout. The publish
step now copies the host binary the build already made into
`<host>/stage2/bin/`, beside `rustc`.
**And the install path prefers it — where it is this tree's linker.** The
release tag is `sha256(HEAD:rust HEAD:toyos-abi/src HEAD:toyos/src
HEAD:userland/libc/src)`, which `toyos-ld` is not part of: a branch that changes
the linker reuses an existing release, so "take the shipped binary" would have
had CI link every build with whatever linker first produced that key.
`toyos-ld-witness` travels in the tarball beside it and says which sources it
is, by the same fingerprint `std_fork_witness` uses, and `Owner::Installed`
takes the shipped one only when that witness is this checkout's — else it
builds, as it does today.
Negative control, the whole decision reverted to the base's (always build):
---- toolchain::tests::the_shipped_linker_is_taken_only_where_it_is_this_tree_s stdout ----
assertion `left == right` failed: a shipped linker whose witness is this
tree's is the one to link through
left: None
right: Some(".../toyos-standing-shipped-ld-67381/toyos-ld")
test result: FAILED. 0 passed; 1 failed
and green on the fix. The oracle is rustc's own target spec, quoted above: it
is what decides that a bare name on `PATH` is the linker at all.
**A tag a consumer can write down.** The content hash stays — it is what makes
two branches carrying the same four trees idempotent — and
`toolchain-linux-x86_64-sdk-<toyos-abi's version>` is added beside it. An asset
belongs to one release and GitHub can neither share nor move one, so the alias
is a second release carrying the `TOOLCHAIN` manifest (a few hundred bytes) and
notes naming the tag the 401 MiB tarball is on; re-uploading the tarball was the
alternative. It is never made on a `pull_request`, whose head is somebody's
branch.
`TOOLCHAIN` is also inside the tarball: the ToyOS commit, the rust submodule
commit, the host triple, the glibc floor and the five SDK crate versions, the
last read from `--sdk-versions` so the set lives in `src/sdkversion.rs` alone.
**glibc: 2.39, measured.** Over the published asset
(`toolchain-linux-x86_64-d8dbd61f8ae95241`), `readelf --dyn-syms` across
`bin/rustc`, `bin/rustdoc` and `lib/librustc_driver-37f9a7448f819635.so` names
GLIBC_2.39 at the top, from librustc_driver. That is exactly `ubuntu-24.04`'s,
and `ubuntu-24.04` is where the host half is built: run 33525742815's `build`
job carries labels `["ubuntu-24.04"]` and ran 15:26:33Z to 16:24:34Z to publish
that asset. So nothing moves — but the reasoning at `route.yml:119-123` does not
bind this job either way, because `toolchain.yml`'s `build` declares no
`container:` at all and never runs in `debian:sid`; that container exists for
QEMU's version, which a bootstrap does not use.
What can move is the machine: a `workflow_dispatch` routes `build` to the T14,
which has never run this workflow (zero `workflow_dispatch` runs of it exist)
and whose glibc is unmeasured. So the floor is declared as `GLIBC_FLOOR` and
asserted before publishing: every `GLIBC_x.y` the shipped binaries name is read
back and a build that needs more is refused. The scan is a grep over the dynamic
string table rather than `objdump`, which adds nothing to the machine's
provisioning and gives the same answer — checked against `readelf` on the asset,
both 2.39.
**toyos-ld writes an executable.** `fs::write` creates at the umask, so every
program the linker produced was 0644 and the rename carried that inode. Nothing
in this repository noticed, because every caller feeds the output to a loader
that does not read the mode. Red before, on the base:
---- a_linked_program_is_executable stdout ----
assertion `left == right` failed: /tmp/toyos-ld-mode-72205 was written 0644
left: 420
right: 493
test result: FAILED. 0 passed; 1 failed
and green after. The test drives the linker binary through the `Case` harness
the determinism suite uses, over a synthetic object, so it measures what a
consumer gets and not what a library call returns.
**The documentation is the release notes**, generated in the workflow and
carried by both releases: the install commands, the SDK crate versions, the
glibc floor and the `raw-window-handle` patch a program that opens a window
needs until rust-windowing/raw-window-handle#223 is released — that stanza read
out of `userland/Cargo.toml` so it cannot rot. The same is stage 4 of
`issues/build/toyos-is-a-normal-target.md`, once.
# Conflicts: # issues/build/toyos-is-a-normal-target.md
… back The last fork the window path goes through that was based on an upstream master now sits on one. `ToyOSOrg/raw-window-handle` has a new branch `toyos-0.6.2` — the v0.6.2 tag (5fda8e8) plus the ToyOS handle and nothing else — and the tree, the winit fork and the softbuffer fork all patch that instead of `toyos`. Nothing was rewritten: `toyos` stays at c39042b and `add-toyos-support` stays where the maintainer wants the PR. git diff --stat v0.6.2 toyos-0.6.2 CHANGELOG.md | 4 +++ src/lib.rs | 16 +++++++++++ src/toyos.rs | 92 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 112 insertions(+) Re-applied by hand rather than cherry-picked: v0.6.2 predates upstream's owned handles and the `Web*` → `WasmBindgen*` rename, so the module list and the thread-safety asserts sit differently and the pick conflicts in `src/lib.rs`. `src/toyos.rs` came over byte-identical (`git diff origin/toyos toyos-0.6.2 -- src/toyos.rs` is empty) and every ToyOS line in `src/lib.rs` is the same text. `cargo test --all-features` on the branch: 42 passed, 0 failed, including the three `src/toyos.rs` doc examples. **And that is why softbuffer's five deleted lines come back.** They renamed v0.4.8's own `RawWindowHandle::Web`, `WebCanvas`, `WebOffscreenCanvas` and `RawDisplayHandle::Web` arms to `WasmBindgen*`, and they existed only because the patch above them named a master-based fork where those variants are gone. Against `toyos-0.6.2` they are the compile error, so `toyos-0.4.8` is back on the release's own names and its delta off v0.4.8 is now purely additive: Cargo.toml | 7 +++ src/backend_dispatch.rs | 2 + src/backends/mod.rs | 2 + src/backends/toyos.rs | 142 ++++++++++++++++++++++++++++++++++++++ src/lib.rs | 2 + 5 files changed, 155 insertions(+) winit needed no such repair — it already names `RawDisplayHandle::Web` and `RawWindowHandle::WebCanvas` in `winit-web`, so the master-based patch was the thing that disagreed with it. Branch heads, all fast-forwards: ToyOSOrg/raw-window-handle toyos-0.6.2 2a5d102bdbd5dc987b653a7ffff5bedbbbfc53b3 ToyOSOrg/winit toyos 69ee7eef7c21cbd1f344f2508f8965d83693cb02 ToyOSOrg/softbuffer toyos-0.4.8 63959966e6d5bdd1d2f2a40f631bc0cdc289405c `--check-forks` reads all three back: "20 branches asked, all current", with raw-window-handle at 2a5d102bdb, winit at 69ee7eef7c and softbuffer at 63959966e6 in `userland/Cargo.lock`.
`bin/cargo` is a symlink into the runner's own rustup and the tarball excludes
it; a glob over `bin/*` would have measured another machine's binary and called
the answer this toolchain's. `-type f` is what leaves it out.
Both arms, run from the committed step against the published asset's binaries
(`toolchain-linux-x86_64-d8dbd61f8ae95241`, extracted):
=== GLIBC_FLOOR=2.39 ===
the host half names up to GLIBC_2.39; this release states 2.39
EXIT=0
=== GLIBC_FLOOR=2.30 ===
the host half names up to GLIBC_2.39; this release states 2.30
::error::the host half needs GLIBC_2.39 and this release states
::error::2.30, so a consumer on the stated floor cannot run it.
EXIT=1
The `sdk alias` step's create-else-edit-and-clobber sequence, run against a
scratch repository rather than asserted:
=== first create ===
https://github.com/.../releases/tag/toolchain-linux-x86_64-sdk-0.1.0
EXIT=0
=== second create ===
a release with the same tag name already exists: toolchain-linux-x86_64-sdk-0.1.0
EXIT=1
=== edit notes === EXIT=0
=== upload --clobber === EXIT=0
=== the release now ===
asset: TOOLCHAIN-probe
notes v2
so the `||` branch is the moving one and nothing is re-uploaded but the
manifest. The comment now says that instead of asserting what GitHub cannot do.
Japabu
marked this pull request as ready for review
September 3, 2026 18:26
Japabu
enabled auto-merge
September 3, 2026 18:26
This was referenced Sep 3, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
gbae's CI is the first third party to build for ToyOS, and it reported what it
needed in order. This is items 1, 2, 4, 5, the glibc answer, and the
raw-window-handle move.
toyos-ld goes in the tarball, and the install path prefers it
rustc's ToyOS target spec names the linker
toyos-ldand finds it onPATH(
rust/compiler/rustc_target/src/spec/base/toyos.rs:linker: Some("toyos-ld".into())), so a consumer holding only the toolchain hadnothing to link with. The publish step copies the host binary the build already
made into
<host>/stage2/bin/, besiderustc.The release tag is
sha256(HEAD:rust HEAD:toyos-abi/src HEAD:toyos/src HEAD:userland/libc/src)andtoyos-ldis not part of it, so "prefer whateverwas shipped" would have had CI link every build with whichever linker first
produced that key.
toyos-ld-witnesstravels in the tarball beside the binaryand says which sources it is;
Owner::Installedtakes the shipped one only whenthat witness is this checkout's, and builds otherwise.
Negative control — the whole decision reverted to the base's (always build):
green on the fix. Oracle: rustc's own target spec, quoted above — it is what
decides a bare name on
PATHis the linker at all.A tag a consumer can name
The content hash stays;
toolchain-linux-x86_64-sdk-<toyos-abi's version>isadded beside it. GitHub's API hangs an asset off one release id, so the
alternative was re-uploading 401 MiB on every landing; the alias is a second
release carrying the
TOOLCHAINmanifest instead — the ToyOS commit, the rustsubmodule commit, the host triple, the glibc floor and the five SDK crate
versions from
--sdk-versions— with notes naming the tag the tarball is on.Never on a
pull_request, whose head is somebody's branch.glibc: 2.39, and the brief's premise corrected
readelf --dyn-symsover the published asset(
toolchain-linux-x86_64-d8dbd61f8ae95241) acrossbin/rustc,bin/rustdocand
lib/librustc_driver-37f9a7448f819635.sotops out at GLIBC_2.39, fromlibrustc_driver. That is exactly
ubuntu-24.04's, andubuntu-24.04is wherethe host half is built: run 33525742815's
buildjob carries labels["ubuntu-24.04"]and ran 15:26:33Z to 16:24:34Z to publish that asset. Sonothing about the build host moves.
The sid-container reasoning at
route.yml:119-123does not bind this job eitherway:
toolchain.yml'sbuilddeclares nocontainer:at all and has never runin
debian:sid. That container exists for QEMU's version, which a bootstrap doesnot use.
What can move is the machine. A
workflow_dispatchroutesbuildto the T14,which has never run this workflow — zero
workflow_dispatchruns of it exist —and whose glibc is unmeasured. So the floor is declared as
GLIBC_FLOORandasserted before publishing: every
GLIBC_x.ythe shipped binaries name is readback and a build needing more is refused by name. The scan is a grep over the
dynamic string table rather than
objdump, adding nothing to the machine'sprovisioning; checked against
readelfon the asset, both 2.39.toyos-ld writes an executable
fs::writecreates at the umask, so every program the linker produced was 0644and the rename carried that inode. Nothing here noticed, because every caller
feeds the output to a loader that does not read the mode.
green after. The test drives the linker binary through the
Caseharness thedeterminism suite uses, so it measures what a consumer gets.
The documentation
Generated in the workflow and carried by both releases: the install commands,
the SDK crate versions, the glibc floor, and the
raw-window-handlepatch aprogram that opens a window needs until
rust-windowing/raw-window-handle#223 is released — that stanza read out of
userland/Cargo.tomlso it cannot rot. The same is stage 4 ofissues/build/toyos-is-a-normal-target.md, once.raw-window-handle sits on its release
ToyOSOrg/raw-window-handlehas a new branchtoyos-0.6.2— the v0.6.2 tag(5fda8e8) plus the ToyOS handle, +112 and nothing else — and the tree, the winit
fork and the softbuffer fork patch that instead of
toyos. Nothing wasrewritten. Re-applied by hand: v0.6.2 predates upstream's owned handles and the
Web*→WasmBindgen*rename, so the cherry-pick conflicts insrc/lib.rs;src/toyos.rscame over byte-identical andcargo test --all-featureson thebranch is 42 passed, 0 failed.
Softbuffer's five deleted lines come back with it. They renamed v0.4.8's own
Webarms toWasmBindgen*only because the patch above them named amaster-based fork; against
toyos-0.6.2they are the compile error.toyos-0.4.8is back on the release's names and its delta off v0.4.8 is purely additive
(+155). winit needed no repair —
winit-webalready namesRawDisplayHandle::Web.Branch heads, all fast-forwards:
The workflow change cannot be proven by a run from this branch
toolchain.ymldeclaresworkflow_dispatch, so it was dispatched(run 33790020091,
--ref toolchain-for-consumers) — and the T14's own admissionhook refused it before any step:
route.ymlsends everyworkflow_dispatchto the T14, and.github/runner/accept-trusted.shtakes a dispatch only frommain. There isno lane that runs this workflow's dispatch from a branch.
What did run is the
pull_requestevent, run 33789990056,buildgreen: themodified file parses and every new step evaluates its condition correctly —
glibc floor,manifest and notesandsdk aliasall skipped, because the tagtoolchain-linux-x86_64-d8dbd61f8ae95241is already published and apull_requestis not what moves a consumer's alias.The steps' shells were run instead, verbatim out of the committed YAML:
glibc floor, over the published asset's own binaries, both arms — green atGLIBC_FLOOR=2.39,exit 1with the named refusal at 2.30.manifest and notes, producing the exact release-note text this PR ships.sdk aliassequence against a scratch repository — firstcreate0,second
create1 with "a release with the same tag name already exists",then
edit --notes-file0 andupload --clobber0, leaving one release withthe new notes and the replaced manifest.
The first push to
mainafter this lands is what actually createstoolchain-linux-x86_64-sdk-0.1.0.