Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 135 additions & 1 deletion .github/workflows/toolchain.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ jobs:
needs: [route]
runs-on: ${{ needs.route.outputs.runner }}
timeout-minutes: 350
env:
# The oldest glibc a consumer of this toolchain needs, which is the
# `ubuntu-24.04` image the hosted lane builds the host half on. The `glibc
# floor` step refuses to publish binaries that name a newer one, so the
# number the release notes carry is measured rather than promised.
GLIBC_FLOOR: "2.39"
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0

Expand Down Expand Up @@ -203,6 +209,100 @@ jobs:
echo '```'
} >> "$GITHUB_STEP_SUMMARY"

# Every `GLIBC_x.y` the shipped host binaries name, against what this
# release states. A grep over the dynamic string table and not `objdump`:
# the answer is the same (checked against `readelf --dyn-syms` on the
# published asset) and it adds no tool to the machine's provisioning. It
# can only over-report, so the failure direction is a refused publish.
- name: glibc floor
if: steps.have.outputs.build == 'yes'
run: |
host=x86_64-unknown-linux-gnu
# `-type f`, because `bin/cargo` is a symlink into this machine's own
# rustup and the tarball excludes it for that reason.
{ find "rust/build/$host/stage2/bin" -maxdepth 1 -type f -print0
find "rust/build/$host/stage2/lib" -maxdepth 1 -type f -name '*.so*' -print0
} > /tmp/shipped
need=$(xargs -0 grep -ao 'GLIBC_[0-9][0-9]*\.[0-9][0-9]*' < /tmp/shipped \
| sed 's/.*GLIBC_//' | sort -V | tail -1)
echo "the host half names up to GLIBC_$need; this release states $GLIBC_FLOOR"
if [ "$(printf '%s\n%s\n' "$need" "$GLIBC_FLOOR" | sort -V | tail -1)" != "$GLIBC_FLOOR" ]
then
echo "::error::the host half needs GLIBC_$need and this release states"
echo "::error::$GLIBC_FLOOR, so a consumer on the stated floor cannot run it. It was"
echo "::error::built on a machine with a newer glibc than the floor allows: build the"
echo "::error::host half on the oldest supported one, or move GLIBC_FLOOR deliberately."
exit 1
fi

# **The pin a consumer writes down, and what it gets.** `TOOLCHAIN` goes
# inside the tarball and is the alias release's own asset, so the toolchain
# a tag names can be read without a 401 MiB download. `--sdk-versions` is
# asked of the build system for `publish.yml`'s reason: the set of
# published crates and their order live in `src/sdkversion.rs` alone.
- name: manifest and notes
if: steps.have.outputs.build == 'yes' || github.event_name != 'pull_request'
run: |
tag=${{ steps.key.outputs.tag }}
url=https://github.com/${{ github.repository }}/releases/download/$tag/toyos-toolchain.tar.zst
{
echo "toolchain $tag"
echo "toyos ${{ github.sha }}"
echo "rust $(git rev-parse HEAD:rust)"
echo "host x86_64-unknown-linux-gnu"
echo "glibc $GLIBC_FLOOR"
cargo run --quiet -- --sdk-versions
} > /tmp/TOOLCHAIN
cat /tmp/TOOLCHAIN

{
cat <<EOF
The \`x86_64-unknown-linux-gnu\` toolchain that cross-compiles for
\`x86_64-unknown-toyos\`, from ToyOS \`${{ github.sha }}\`.

## Install

mkdir -p toyos-toolchain
curl -sSL $url | tar --zstd -x -C toyos-toolchain
rustup toolchain link toyos toyos-toolchain/x86_64-unknown-linux-gnu/stage2
ln -s "\$(rustup which cargo)" toyos-toolchain/x86_64-unknown-linux-gnu/stage2/bin/cargo
export PATH="\$PWD/toyos-toolchain/x86_64-unknown-linux-gnu/stage2/bin:\$PATH"
cargo +toyos build --target x86_64-unknown-toyos

The last two commands before the build are not decoration. rustc's ToyOS
target names its linker \`toyos-ld\` and finds it on \`PATH\`; the tarball
carries one in that same \`bin/\`. The \`cargo\` symlink is not shipped
because its path would be the publisher's, and rustup narrates a fallback
on every invocation without it.

## glibc

The host binaries name **GLIBC_$GLIBC_FLOOR** at most, so any distribution
with glibc $GLIBC_FLOOR or newer runs them — Ubuntu 24.04, Debian 13,
Fedora 40. A build that needed more is refused rather than published.

## What this is, and the SDK crates that go with it

EOF
sed 's/^/ /' /tmp/TOOLCHAIN
cat <<EOF

The five crate versions are the ones on crates.io this toolchain's std was
built from; \`sdk-<toyos-abi's version>\` is the release tag that names
them. The same lines are the file \`TOOLCHAIN\` inside the tarball.

## raw-window-handle

Until [rust-windowing/raw-window-handle#223](https://github.com/rust-windowing/raw-window-handle/pull/223)
is released, a program that opens a window carries this patch, because the
ToyOS window handle is in no published raw-window-handle yet:

[patch.crates-io]
$(grep '^raw-window-handle = ' userland/Cargo.toml)
EOF
} > /tmp/notes.md
cat /tmp/notes.md

# `lib/rustlib/<host>` and `<host>/stage2/bin/cargo` are symlinks into
# whatever toolchain this runner has, which is not a path any artifact can
# carry. `Owner::Installed` recreates both —
Expand All @@ -216,10 +316,18 @@ jobs:
run: |
host=x86_64-unknown-linux-gnu
t0=$(date +%s)
# rustc's ToyOS target spec names the linker `toyos-ld` and finds it on
# PATH, so a consumer with only this tarball has to be given one — the
# host binary the `build` step above already made. `toyos-ld-witness`
# says which sources it is, because this tag is a function of four
# trees and `toyos-ld` is not among them.
cp "target/$host/release/toyos-ld" "rust/build/$host/stage2/bin/toyos-ld"
cp /tmp/TOOLCHAIN rust/build/TOOLCHAIN
tar -C rust/build \
--exclude="x86_64-unknown-toyos/stage2/lib/rustlib/$host" \
--exclude="$host/stage2/bin/cargo" \
-c "$host/stage2" x86_64-unknown-toyos/stage2 toyos-sysroot-witness \
toyos-ld-witness TOOLCHAIN \
| zstd -T0 -3 -o /tmp/toyos-toolchain.tar.zst
echo "PACKAGE-SECONDS: $(( $(date +%s) - t0 ))"
ls -l /tmp/toyos-toolchain.tar.zst
Expand All @@ -228,7 +336,7 @@ jobs:
# otherwise fail having produced the same bytes.
gh release create "${{ steps.key.outputs.tag }}" \
--title "${{ steps.key.outputs.tag }}" \
--notes "x86_64-unknown-linux-gnu toolchain for rust $(git rev-parse HEAD:rust), built from ${{ github.sha }}." \
--notes-file /tmp/notes.md \
/tmp/toyos-toolchain.tar.zst \
|| gh release view "${{ steps.key.outputs.tag }}"

Expand All @@ -255,3 +363,29 @@ jobs:
echo "::error::toyos-toolchain.tar.zst. Nothing can install this toolchain, and a"
echo "::error::green here is what lets ci.yml start thirteen shards."
exit 1

# **The SDK version names the ABI; the toolchain that goes with it carries
# the same number.** The content hash above makes a publish idempotent and
# is no name a consumer can write down, so `sdk-<toyos-abi's version>` is
# the one it pins, and it moves to whatever toolchain this tree's ABI
# belongs to.
#
# A second release and not a copy of the asset: GitHub's API hangs an
# asset off one release id, so the alternative was re-uploading 401 MiB on
# every landing. The alias carries the manifest instead — a few hundred
# bytes — and its notes name the tag the tarball is on. `create` refuses a
# tag that exists, which is what `edit` plus a clobbering upload is for.
# Never on a `pull_request`, whose head is somebody's branch and not what
# a consumer should be handed.
- name: sdk alias
if: github.event_name != 'pull_request'
env:
GH_TOKEN: ${{ github.token }}
run: |
v=$(grep '^toyos-abi ' /tmp/TOOLCHAIN | cut -d' ' -f2)
alias=toolchain-linux-x86_64-sdk-$v
gh release create "$alias" --title "$alias" --notes-file /tmp/notes.md \
/tmp/TOOLCHAIN \
|| { gh release edit "$alias" --notes-file /tmp/notes.md
gh release upload "$alias" /tmp/TOOLCHAIN --clobber; }
gh release view "$alias"
39 changes: 16 additions & 23 deletions forks.toml
Original file line number Diff line number Diff line change
Expand Up @@ -115,20 +115,15 @@ tier = "sibling"
why = "Adds the winit-toyos sibling crate — the model case for this tier."
pr = "none — winit-toyos must be published before upstream can depend on it by version"
followup = "winit-toyos is ToyOS's own code still living inside the fork; move it to the monorepo once published"
owed = """
`winit/Cargo.toml:101` patches `raw-window-handle` from `Japabu/`, the account
name this estate left; `ToyOSOrg/` is where it lives now and GitHub is
redirecting.
"""

[softbuffer]
upstream = "rust-windowing/softbuffer"
base = "v0.4.8"
base_note = "d871852. The `toyos` branch on master's f0d20b9 is left behind, unrewritten."
branch = "toyos-0.4.8"
delta = "+161/-5"
delta = "+155"
tier = "sibling"
why = "ToyOS surface backend. The -5 lines track the patched raw-window-handle's renames, not ToyOS work."
why = "ToyOS surface backend."
pr = "none — sendable once toyos-window is on crates.io"
release_note = """
The backend was re-applied by hand rather than cherry-picked: v0.4.8's
Expand All @@ -138,31 +133,29 @@ The backend was re-applied by hand rather than cherry-picked: v0.4.8's
row by row through `window::Framebuffer::blit`, which clips to the window.
"""
rwh_patch = """
The `[patch.crates-io] raw-window-handle` entry stays: the newest release is
0.6.2 and it has no `ToyOs` variant, so `src/backends/toyos.rs` cannot name one
without the fork. The five deleted lines stay with it — the patched fork is on
raw-window-handle's master, where `RawWindowHandle::Web` and
`RawDisplayHandle::Web` are gone, so v0.4.8's own match arms are a compile
error against it (E0599, src/lib.rs:321 and :338, measured on the tag). They go
when the raw-window-handle fork moves onto 0.6.2 the way this one moved, which
is what that entry's `owed` now records.
The `[patch.crates-io] raw-window-handle` entry stays until #223 is released:
0.6.2 has no `ToyOs` variant, so `src/backends/toyos.rs` cannot name one. It
names the fork's `toyos-0.6.2` branch — the release plus that variant — so
v0.4.8's own `Web` match arms are the right ones and the delta is additive.
"""

[raw-window-handle]
upstream = "rust-windowing/raw-window-handle"
base = "1b85948"
delta = "+109"
base = "v0.6.2"
base_note = "5fda8e8. The `toyos` branch on master's 1b85948 is left behind, unrewritten, and so is `add-toyos-support`, which is where the maintainer wants the PR."
branch = "toyos-0.6.2"
delta = "+112"
tier = "sibling"
why = "Pure addition: a ToyOS handle variant. Depends on nothing of ours, so it was sendable without publishing."
pr = "https://github.com/rust-windowing/raw-window-handle/pull/223 (open, 2026-07-28)"
pr_branch = "add-toyos-support"
pr_note = "`toyos` was moved to c39042b so its tree is byte-identical to the PR head; the delta grew +69 → +109 by adding the doc examples and CHANGELOG entry every other platform module carries. Verified 2026-08-07: `git diff origin/toyos origin/add-toyos-support` is empty, and both are +109 off 1b85948."
owed = """
`base` is master, not a release, and it is the last fork that is. v0.6.2 is an
ancestor of `toyos` (23 commits behind it), and those 23 carry the
`Web*` → `WasmBindgen*` renames every consumer then has to follow — which is
the whole of softbuffer's `-5`. A `toyos-0.6.2` branch beside `toyos` ends that,
and the PR branch stays where the maintainer wants it.
release_note = """
Re-applied by hand and not cherry-picked: v0.6.2 predates upstream's owned
handles and its `Web*` → `WasmBindgen*` rename, so the module list and the
thread-safety asserts sit differently. `src/toyos.rs` is byte-identical to
`toyos`'s and every ToyOS line in `src/lib.rs` is the same text; the +3 over
`toyos`'s +109 is the `## Unreleased` heading a release branch needs.
"""

[getrandom]
Expand Down
36 changes: 26 additions & 10 deletions issues/build/toyos-is-a-normal-target.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,32 @@ Stages, in order:
first publish. Until it is there the publish job fails by name on every
landing, which is the intended noise.
3. **The forks.** `forks.toml`'s `owed` per fork. softbuffer names
`toyos-window` and sits on the v0.4.8 release; raw-window-handle is the last
fork based on a master rather than a release, and its `owed` says what moving
it off costs. Every fork whose `pr` says "sendable once … is on crates.io"
becomes sendable.
4. **The toolchain.** A ToyOS rustc/std is what a third-party program needs
after the crates resolve, and building one is a two-hour bootstrap of the
`rust/` fork. `toolchain.yml` already builds one and uploads it as a release
asset, and `.github/install-toolchain.sh` fetches it — but the tag is a
content key CI computes for its own cache. What is owed is a release named
for a human and a one-line install anybody outside this repository can run.
`toyos-window` and sits on the v0.4.8 release, and raw-window-handle sits on
v0.6.2, so nothing the window path goes through is based on a master any
more. Every fork whose `pr` says "sendable once … is on crates.io" becomes
sendable.
4. **Done.** The toolchain is a release a consumer can name, install and link
with. `toolchain-linux-x86_64-sdk-<toyos-abi's version>` is the tag it pins —
the SDK version names the ABI, and the toolchain that goes with it carries
the same number — and that release's asset is the `TOOLCHAIN` manifest, which
names the content-keyed release the tarball is on. What a consumer runs, and
the release notes of every toolchain release carry it:

mkdir -p toyos-toolchain
curl -sSL "$asset" | tar --zstd -x -C toyos-toolchain
stage2=toyos-toolchain/x86_64-unknown-linux-gnu/stage2
rustup toolchain link toyos "$stage2"
ln -s "$(rustup which cargo)" "$stage2/bin/cargo"
export PATH="$PWD/$stage2/bin:$PATH"
cargo +toyos build --target x86_64-unknown-toyos

`toyos-ld` is in that `bin/` because rustc's ToyOS target names its linker
and finds it on `PATH`. The glibc floor is 2.39 — `ubuntu-24.04`'s, the
image the host half is built on — measured over the shipped binaries and
asserted at publish time, so a build on a newer machine is refused rather
than published. A program that opens a window also carries a `[patch]` of
`raw-window-handle` to the fork's release branch, until
rust-windowing/raw-window-handle#223 is released.
5. **Upstream.** The three backends — winit-toyos, softbuffer's ToyOS backend,
cpal's ToyOS host — become upstream pull requests rather than forks, which is
what the `sibling` tier in `forks.toml` means.
Expand Down
5 changes: 3 additions & 2 deletions src/prose-ledger
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ src/sourcegate.rs 479 1
src/stamps.rs 3 0
src/testargs.rs 97 0
src/tiers.rs 336 26
src/toolchain.rs 780 29
src/toolchain.rs 796 29
src/wallpaper.rs 124 0
src/worktree.rs 74 0
src/writinglaw.rs 24 0
Expand Down Expand Up @@ -516,12 +516,13 @@ toyos-ld/src/emit_elf.rs 116 0
toyos-ld/src/emit_macho.rs 97 0
toyos-ld/src/emit_pe.rs 17 0
toyos-ld/src/lib.rs 97 0
toyos-ld/src/main.rs 18 0
toyos-ld/src/main.rs 20 0
toyos-ld/src/reloc.rs 113 0
toyos-ld/tests/alloc_shims.rs 25 0
toyos-ld/tests/archive_dialects.rs 47 0
toyos-ld/tests/common/mod.rs 74 0
toyos-ld/tests/determinism.rs 26 0
toyos-ld/tests/output_mode.rs 3 0
toyos-manifest/src/lib.rs 122 0
toyos-mixer/src/channel.rs 38 0
toyos-mixer/src/corpus.rs 179 1
Expand Down
Loading
Loading