ADR-076 — std Rust natif sur la libc d’Air : cible env = "musl" + vendor = "air" (chantier B, option A)
Statut : Accepté (2026-07-08, décision BDFL directe). Amende ADR-050 (choix env = "air" → env = "musl") et amende ADR-075 (« pal pur, zéro libc » → « std sur la libc d’Air »). Met en œuvre le chantier B de la roadmap PAL (docs/notes/roadmap-pal-cloture-fr.md).
Catégorie : Décision de toolchain (toit std). N’altère aucune API couche 0/1 scellée.
Contexte
Objectif du chantier B : produire la toolchain Rust *-linux-air avec std natif, pour que nos exécutables et tests soient full Rust, sans tirer la glibc.
En amorçant le portage, un mur d’architecture est apparu : std — pour une cible target_family = "unix" + target_os = "linux" (ce qu’Air est, ADR-004/050) — compile son API publique std::os::unix / std::os::linux, indissociable de libc (extern crate libc, types libc::*, helpers sys::cvt, sys::linux). Un « pal sur couche 1 pure, sans libc » (l’ambition d’ADR-075) ne suffit pas : la structure de std exige une libc.
Par ailleurs, target_env = "air" (ADR-050 D1, « 3ᵉ saveur de libc ») n’est reconnu par aucun crate de l’écosystème (libc, std) : la crate libc échoue à compiler pour env = "air" (elle ne sélectionne aucun module d’environnement). Or Air est ABI-musl — son llvm-target est déjà *-linux-musl (ADR-050 D-1.3).
Décision — option A
std utilise son pal unix, et les symboles libc sont fournis par la libc pure-Rust d’Air (air-libc, libair_c) au lieu de la glibc. Concrètement :
-
La cible porte
env = "musl"(au lieu de"air") dans son spec JSON. Alorslibc,stdet tout cratecfg(musl)/cfg(unix)compilent pour Air sans le moindre patch (ils voient une cible musl, ce qu’Air est). C’est le modèle éprouvé d’eyra/mustang (cibles à base musl). -
L’identité d’Air passe par
vendor = "air". Le code Air qui doit savoir « je bâtis la cible Air réelle » (couche 1 :errno#[thread_local], destructeursthread_local,yield_now/set_thread_name, feature nightlythread_local, sélections per-cible) utilise désormaiscfg(target_vendor = "air")au lieu decfg(target_env = "air"). Déclaré valide dans[workspace.lints.rust](check-cfg). Renommage mécanique, sémantiquement invariant (faux sur l’hôte gnu, vrai sur la cible Air). -
Le lien résout les symboles libc vers
libair_c(statique), pas la glibc :-nostdlib+ CRT Air +-lair_c.-lunwind= stub (la cible estpanic = abort, l’unwinder n’est jamais invoqué).
« Sans glibc » ≠ « sans libc ». L’objectif est zéro glibc, avec notre libc (pure Rust, couche 1 → C-ABI). La campagne air-libc-* (déjà en production, cf. la libc scopée OpenSSH) devient aussi le socle de std : les deux grands chantiers (libc et toolchain) convergent.
Conséquences
build-std=stdcompile ENTIÈREMENT pour Air, sans un seul patch delibc/std(prouvé le 2026-07-08 :libcconfig musl +std+std_detect+ unhello-stdcompilent ; ne reste que le lien, qui réclame-lc/-lunwind). L’option A est validée.- Validation on-target préservée : selftest
rtx86-air = 42 ET aarch64-air = 42 après le renommagecfg(couche 1 intacte viavendor = "air"). Barrière verte. - Cible de production du chantier B : compléter
air-libcpour couvrir la surfacestd::sys::unix(superset des 55 manques révélés par la sonde OpenSSH), configurer le lien (libair static + CRT +-lunwindstub), et faire TOURNERhello-stdon-target 2 arches. C’est désormais le cœur du chantier (le portagestdest acquis ; reste la complétion de libair). - Ce qui est abandonné vs ADR-075 : le pal
aircustom (on réutilise le palunixdestd). Le patchset rust-src d’ADR-075 devient quasi vide (l’env = "musl"fait tout compiler). - Réversibilité : le renommage
cfget le champenvdu spec sont localisés ; aucun impact sur les couches scellées.
Alternatives rejetées
- (B) Cible non-unix (os custom) :
stdne compilerait pasos::unix(pal pur possible), mais coût écosystème massif (tout cratecfg(unix)exclut Air) et perte de la logique unix destd. Rejeté. - (C) Patcher
std/libclourdement pour découpleros::unixd’Air, en gardantenv = "air": gros patch fragile (38+ cfgmusldanslibc), dette de rebase à chaque nightly. Rejeté au profit deenv = "musl"(zéro patch). - Garder
env = "air": rejeté — non reconnu par l’écosystème, force le portage à se battre contrelibc/std.