buninu-linux 0.9.56 → 0.10.56

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/LICENSES/MIT-ZilchOS-bootstrap-from-tcc.txt +21 -0
  2. package/NOTICE.md +34 -0
  3. package/README.md +3 -0
  4. package/initramfs/usr/lib/bootstrap/1.prepare-rootfs.js +106 -0
  5. package/initramfs/usr/lib/bootstrap/10.build-m4.js +40 -0
  6. package/initramfs/usr/lib/bootstrap/11.build-flex.js +50 -0
  7. package/initramfs/usr/lib/bootstrap/12.build-gcc.js +261 -0
  8. package/initramfs/usr/lib/bootstrap/2.enter-rootfs.js +49 -0
  9. package/initramfs/usr/lib/bootstrap/3.gen-musl-dev.js +141 -0
  10. package/initramfs/usr/lib/bootstrap/4.build-tcc.js +61 -0
  11. package/initramfs/usr/lib/bootstrap/5.build-make.js +58 -0
  12. package/initramfs/usr/lib/bootstrap/6.build-binutils.js +77 -0
  13. package/initramfs/usr/lib/bootstrap/7.build-gmp.js +68 -0
  14. package/initramfs/usr/lib/bootstrap/8.build-mpfr.js +59 -0
  15. package/initramfs/usr/lib/bootstrap/9.build-mpc.js +55 -0
  16. package/initramfs/usr/lib/bootstrap/LICENSE +21 -0
  17. package/initramfs/usr/lib/bootstrap/LICENSES/MIT-ZilchOS-bootstrap-from-tcc.txt +21 -0
  18. package/initramfs/usr/lib/bootstrap/NOTICE.md +65 -0
  19. package/initramfs/usr/lib/bootstrap/README.md +204 -0
  20. package/initramfs/usr/lib/bootstrap/downloads.md +49 -0
  21. package/initramfs/usr/lib/bootstrap/lib/build.js +155 -0
  22. package/initramfs/usr/lib/bootstrap/lib/common.js +134 -0
  23. package/initramfs/usr/lib/bootstrap/lib/compile-object.c +14 -0
  24. package/initramfs/usr/lib/bootstrap/lib/musl-headers.js +59 -0
  25. package/initramfs/usr/lib/bootstrap/lib/seed.js +148 -0
  26. package/initramfs/usr/lib/bootstrap/lib/startup-check.c +14 -0
  27. package/initramfs/usr/lib/bootstrap/lib/tcc-build.js +53 -0
  28. package/initramfs/usr/lib/bootstrap/sources/musl-1.2.6.tar.gz +0 -0
  29. package/initramfs/usr/lib/bootstrap/sources/tinycc-05f0faf-upstream.tar.gz +0 -0
  30. package/initramfs/usr/lib/bootstrap/sources/tinycc-local-bootstrap-files.tar.gz +0 -0
  31. package/package.json +1 -1
  32. package/test/bootstrap.test.js +65 -0
@@ -0,0 +1,204 @@
1
+ # Bootstrap from Bun
2
+
3
+ - Route and recipe reference: [ZilchOS/bootstrap-from-tcc](https://github.com/ZilchOS/bootstrap-from-tcc),
4
+ by Alexander Sosedkin. See [NOTICE.md](NOTICE.md) for full credit and component licenses.
5
+
6
+ - Requires Bun with `Bun.cc`, `Bun.Archive` and `Bun.YAML` (tested with 1.4.2).
7
+ - Supported targets: native ARM64 and x86-64 Linux with musl.
8
+ - Run stages 1 and 2 outside the new rootfs; run stages 3–12 inside it.
9
+ - The numbered filenames are the execution order.
10
+ - Inside the new rootfs, use `cd /bootstrap`; scripts, `lib/`, `README.md` and
11
+ `downloads.md` live alongside `src`, `toolchain`, `deps`, `build` and `result`.
12
+ - Copies of scripts 1/2 are included for reference; run preparation/entry from
13
+ the outer package, which also contains the bundled seed archives.
14
+
15
+ ## 1. Prepare a new rootfs
16
+
17
+ ```sh
18
+ bun /usr/lib/bootstrap/1.prepare-rootfs.js
19
+ ```
20
+
21
+ - Default architecture: the running Bun's architecture.
22
+ - Default destination: `./roota` for ARM64, `./rootx` for x86-64.
23
+ - Optional arguments: `[ARCH] [ROOTFS]`.
24
+ - Architecture aliases: `a`, `arm64`, `aarch64`; `x`, `x64`, `x86_64`, `x86-64`.
25
+ - A single directory argument uses the default architecture.
26
+ - Destination must be new; its parent directory must already exist.
27
+
28
+ ```sh
29
+ bun /usr/lib/bootstrap/1.prepare-rootfs.js a ./my-root
30
+ bun /usr/lib/bootstrap/1.prepare-rootfs.js x
31
+ bun /usr/lib/bootstrap/1.prepare-rootfs.js ./my-root
32
+ ```
33
+
34
+ - On Buninu Linux (`ID=buninu-linux` in `/etc/os-release`), reuse the local
35
+ Bun, musl loader, libgcc, libstdc++, DNS configuration, bunmsh and extraction tools.
36
+ - For a different target architecture, or outside Buninu Linux:
37
+ - Fetch `buninu-linux@0.9.56` for the runtime libraries and JavaScript tools.
38
+ - Fetch musl Bun 1.4.2 for the target architecture (x64 uses baseline).
39
+ - Verify SHA-256 before using either download.
40
+ - Pins live in `downloads.md`; despite its extension, it is YAML with the same
41
+ URL/hash pair format as `scripts/npm-packages.md`.
42
+ - Cache: `${XDG_CACHE_HOME:-$HOME/.cache}/buninu-bootstrap/`; cached files are
43
+ verified again. A bad cache entry causes an error, not an unchecked fallback.
44
+ - No musl-dev APK, prebuilt CRT, installed headers or standalone compiler required.
45
+ - Bundled sources are hash-checked and extracted:
46
+ - `sources/musl-1.2.6.tar.gz` -> `/bootstrap/src/musl`.
47
+ - `sources/tinycc-05f0faf-upstream.tar.gz` -> `/bootstrap/src/tinycc`.
48
+ - `sources/tinycc-local-bootstrap-files.tar.gz` -> TinyCC recipes and `/bin/cc`.
49
+ - `/bin/sh` is an executable JavaScript file with `#!/bin/bun`.
50
+ - It prepends `/bootstrap/result/bin` and `/bootstrap/toolchain/bin` to PATH,
51
+ removes duplicates, then dynamically imports bunmsh. Missing bin directories
52
+ are harmless; child processes inherit the environment.
53
+ - Build stages use a separate shell launcher and explicit PATH so installed
54
+ result tools do not silently replace bootstrap tools during a rebuild.
55
+ - Scripts 1–12 and helpers are copied to `/bootstrap` in the new rootfs.
56
+ - Preparation can select another architecture; entering/building requires a
57
+ matching CPU or a separately configured emulator. These scripts do not set up emulation.
58
+
59
+ ## 2. Print the entry command
60
+
61
+ ```sh
62
+ bun /usr/lib/bootstrap/2.enter-rootfs.js
63
+ ```
64
+
65
+ - Accepts `[ARCH] [ROOTFS]`, with the same architecture aliases as stage 1.
66
+ - Without a destination (including an architecture-only invocation), prints
67
+ `/path/to/your/rootfs` as a placeholder instead of assuming `roota` or `rootx`.
68
+ - Prints commands only; it does not enter, modify or read the rootfs or its
69
+ marker file. The destination does not need to exist to print instructions.
70
+ - Reads the host's os-release only to choose chroot versus bunproot.
71
+ - Buninu Linux example (requires chroot privileges):
72
+
73
+ ```sh
74
+ chroot '/absolute/path/rootx' /bin/sh
75
+ ```
76
+
77
+ - Android/Termux and other hosts with bunproot:
78
+
79
+ ```sh
80
+ bun x bunproot -koe -r '/absolute/path/roota'
81
+ ```
82
+
83
+ ## 3. Generate musl development files, inside the rootfs
84
+
85
+ ```sh
86
+ cd /bootstrap
87
+ bun 3.gen-musl-dev.js
88
+ ```
89
+
90
+ - Generate 217 public headers from musl's source and architecture templates.
91
+ - Build TinyCC in memory using `Bun.cc`; emit `crt1.o`, `crti.o`, `crtn.o`.
92
+ - Install headers under `/usr/include`, CRT objects under `/usr/lib`.
93
+ - Preserve the original musl sources.
94
+ - ARM64 only: use a temporary startup-header override omitting the unused
95
+ `_DYNAMIC` argument, because TinyCC's ARM64 assembler does not support `adrp`.
96
+ - This override applies only to ordinary `crt1`; static PIE `rcrt1` and the
97
+ dynamic loader are not built here.
98
+ - Refuse existing headers/CRT instead of overwriting them.
99
+ - If this stage fails after headers were generated, preserve the rootfs for
100
+ diagnosis and prepare a fresh destination before repeating the full flow.
101
+
102
+ ## 4. Build a standalone C compiler, inside the rootfs
103
+
104
+ ```sh
105
+ bun /bootstrap/4.build-tcc.js
106
+ cc /bootstrap/src/tinycc/bun-hello-libc.c -o /tmp/hello
107
+ /tmp/hello
108
+ ```
109
+
110
+ - Build TinyCC stage1 through `Bun.cc`, using the generated CRT.
111
+ - Build the x64 compiler runtime helpers when needed.
112
+ - Rebuild stage2 with stage1 and require byte-identical executables.
113
+ - Verify libc, x64 conversions/variadic calls, and CRT startup behavior.
114
+ - `/bin/cc` launches `/bootstrap/toolchain/bin/tcc` with the headers, libraries and helpers.
115
+ - Stages 3/4 are offline; no host build tools are used.
116
+ - The seed Bun, musl shared library, libgcc and libstdc++ remain prebuilt inputs.
117
+ - Source licenses remain in the archives and extracted trees:
118
+ - musl: `/bootstrap/src/musl/COPYRIGHT`.
119
+ - TinyCC: `/bootstrap/src/tinycc/COPYING` and individual source-file notices.
120
+
121
+ ## 5–12. Build through GCC 4.7
122
+
123
+ - Run each numbered script inside the same rootfs, in order.
124
+ - Each stage fetches its source on demand and checks the SHA-256 in `downloads.md`.
125
+ - These eight large source archives are **not** included in the npm package or initramfs.
126
+ - Fixed version/commit URLs are used, never `latest` or a moving branch.
127
+ - Cache: `/root/.cache/buninu-bootstrap` by default.
128
+ - Source trees: `/bootstrap/src`; build trees and logs: `/bootstrap/build`.
129
+ - Reserve at least 3 GB of writable workspace; avoid a small RAM-only rootfs.
130
+ - Set `BOOTSTRAP_JOBS=1` to reduce build concurrency (default: 2).
131
+
132
+ | Stage | Command after `bun /bootstrap/` | Source |
133
+ | --- | --- | --- |
134
+ | 5 | `5.build-make.js` | GNU Make 4.4.1, official GNU release |
135
+ | 6 | `6.build-binutils.js` | Binutils 2.39, official GNU release |
136
+ | 7 | `7.build-gmp.js` | GMP 4.3.2, official GNU release |
137
+ | 8 | `8.build-mpfr.js` | MPFR 2.4.2, official version directory |
138
+ | 9 | `9.build-mpc.js` | MPC 0.8.1, GCC infrastructure archive |
139
+ | 10 | `10.build-m4.js` | GNU m4 1.4.18, official GNU release |
140
+ | 11 | `11.build-flex.js` | Flex 2.6.4, official GitHub release |
141
+ | 12 | `12.build-gcc.js` | GCC mirror, commit `9d2b63d100ae7c99ee8b128fb41086fd85c8f57d` |
142
+
143
+ - GNU Make first uses its shipped no-make builder, then rebuilds itself.
144
+ - Binutils supplies the assembler, linker and archive tools; stage 6 also
145
+ builds/tests the additional TinyCC runtime helpers needed by later stages.
146
+ - GMP/MPFR/MPC are static prerequisite libraries; MPFR runs upstream arithmetic
147
+ tests, and MPC's stage verifies all three libraries together.
148
+ - m4 and Flex are built from source and tested before GCC uses them.
149
+ - `configure` and build shell commands use bunmsh. External utility adapters
150
+ call its JavaScript builtins in POSIX mode; no host awk/sed/coreutils enter the build.
151
+ - Logs are streamed to the terminal and retained under `/bootstrap/build/logs/`.
152
+ - Re-running a failed stage revalidates the download, reconfigures and resumes
153
+ its existing build tree. Source compatibility patches are idempotent.
154
+ - GCC uses a vendor 4.7 branch containing both ARM64 and x64 backends. The pinned
155
+ GitHub snapshot's 76,720 regular source files match the earlier local archive.
156
+ - Stage 12 builds the C frontend and libgcc, then tests `-O0`/`-O2`, the musl
157
+ interpreter, architecture/type macros and 128-bit runtime arithmetic.
158
+ - Stage 12 runs GCC's `install-gcc` and `install-target-libgcc` rules with
159
+ `--prefix=/bootstrap/result`, and includes native Binutils executables.
160
+ - GCC's private `include` and `include-fixed` directories are installed using
161
+ JavaScript with symlinks dereferenced, avoiding the tar pipe and recursive
162
+ `cp -p` requirements and retaining headers after source/build removal.
163
+ - The installed compiler is `/bootstrap/result/bin/gcc`; it is not an `xgcc`
164
+ wrapper and needs no `-B` pointing into a build directory.
165
+ - The final check temporarily renames `src`, `toolchain`, `deps` and `build`
166
+ to sibling `*.verification-hidden` directories, compiles/runs ordinary and
167
+ shared-library checks using only result tools, then restores the directories.
168
+ - If interrupted by a forced kill, restore any `*.verification-hidden` directory
169
+ to its original name before resuming. Ordinary errors restore them automatically.
170
+ - Successful verification writes `/bootstrap/result/verified.json` and
171
+ `/bootstrap/result/verification.log`; stage 12's build log remains in `build/logs`.
172
+ - After stage 12 succeeds, `/bootstrap/build` can be deleted. The installed GCC
173
+ also does not need `src`, `deps` or `toolchain`; keep these to rebuild or use
174
+ the intermediate tools such as Make and TinyCC.
175
+ - Result still uses the rootfs's musl loader/libc, `/usr/include`, `/usr/lib`
176
+ startup objects and seed runtime libraries. It is not a self-contained rootfs.
177
+ - The installation prefix is fixed at `/bootstrap/result`; moving it into `/usr`
178
+ is not supported by simply copying it. A system installation should instead
179
+ configure `--prefix=/usr` and stage the installation with `DESTDIR`.
180
+ - This is the GCC 4.7 C + libgcc stopping milestone, not a full GCC three-stage
181
+ bootstrap or a completed Bun rebuild.
182
+ - This layout/install revision has only received static checks; validate it
183
+ by running the numbered stages in a new rootfs.
184
+
185
+ ## Directory layout
186
+
187
+ | Directory | Contents |
188
+ | --- | --- |
189
+ | `/bootstrap/src` | musl, TinyCC and downloaded package source trees |
190
+ | `/bootstrap/toolchain` | intermediate compiler, runtime helpers, Make, Binutils, m4, Flex and JS utility adapters |
191
+ | `/bootstrap/deps` | static GMP, MPFR and MPC libraries and headers |
192
+ | `/bootstrap/build` | intermediate objects, build trees and build logs |
193
+ | `/bootstrap/result` | installed GCC, libgcc, Binutils and final verification records |
194
+
195
+ ```sh
196
+ # Entering through /bin/sh already sets PATH.
197
+ gcc --version
198
+ gcc hello.c -o hello
199
+ ./hello
200
+ ```
201
+
202
+ - The generated musl headers and CRT remain in `/usr/include` and `/usr/lib`.
203
+ - The source archive download cache remains under `/root/.cache/buninu-bootstrap`.
204
+
@@ -0,0 +1,49 @@
1
+ # YAML: [immutable download URL, SHA-256]
2
+ # npm SHA-256 verified against registry SHA-512 integrity.
3
+ # Bun 1.4.2 pins match scripts/arch.js.
4
+
5
+ "buninu-linux@0.9.56":
6
+ - https://registry.npmjs.org/buninu-linux/-/buninu-linux-0.9.56.tgz
7
+ - sha256:972b46b5f0b1caaa0e64e352de68e37b28cc6955ed4240e91f0fe358b0d6f73d
8
+
9
+ "bun-linux-aarch64-musl@1.4.2":
10
+ - https://github.com/oven-sh/bun/releases/download/bun-v1.4.2/bun-linux-aarch64-musl.zip
11
+ - sha256:71760b6c8ea30623b81a4907cb815d48e2ea266f2e73e751534a44a0607950df
12
+
13
+ "bun-linux-x64-musl-baseline@1.4.2":
14
+ - https://github.com/oven-sh/bun/releases/download/bun-v1.4.2/bun-linux-x64-musl-baseline.zip
15
+ - sha256:76e1db84e98f22f78de0a87e309bfbbf297732847f9720db36750646c85c8c18
16
+
17
+ # Stages 5–12: fetched on demand, never bundled in the npm package.
18
+
19
+ "make@4.4.1":
20
+ - https://ftp.gnu.org/gnu/make/make-4.4.1.tar.gz
21
+ - sha256:dd16fb1d67bfab79a72f5e8390735c49e3e8e70b4945a15ab1f81ddb78658fb3
22
+
23
+ "binutils@2.39":
24
+ - https://ftp.gnu.org/gnu/binutils/binutils-2.39.tar.gz
25
+ - sha256:d12ea6f239f1ffe3533ea11ad6e224ffcb89eb5d01bbea589e9158780fa11f10
26
+
27
+ "gmp@4.3.2":
28
+ - https://ftp.gnu.org/gnu/gmp/gmp-4.3.2.tar.gz
29
+ - sha256:7be3ad1641b99b17f6a8be6a976f1f954e997c41e919ad7e0c418fe848c13c97
30
+
31
+ "mpfr@2.4.2":
32
+ - https://www.mpfr.org/mpfr-2.4.2/mpfr-2.4.2.tar.gz
33
+ - sha256:246d7e184048b1fc48d3696dd302c9774e24e921204221540745e5464022b637
34
+
35
+ "mpc@0.8.1":
36
+ - https://gcc.gnu.org/pub/gcc/infrastructure/mpc-0.8.1.tar.gz
37
+ - sha256:e664603757251fd8a352848276497a4c79b7f8b21fd8aedd5cc0598a38fee3e4
38
+
39
+ "m4@1.4.18":
40
+ - https://ftp.gnu.org/gnu/m4/m4-1.4.18.tar.gz
41
+ - sha256:ab2633921a5cd38e48797bf5521ad259bdc4b979078034a3b790d7fec5493fab
42
+
43
+ "flex@2.6.4":
44
+ - https://github.com/westes/flex/releases/download/v2.6.4/flex-2.6.4.tar.gz
45
+ - sha256:e87aae032bf07c26f85ac0ed3250998c37621d95f8bd748b31f15b33c45ee995
46
+
47
+ "gcc@4.7-9d2b63d":
48
+ - https://codeload.github.com/gcc-mirror/gcc/tar.gz/9d2b63d100ae7c99ee8b128fb41086fd85c8f57d
49
+ - sha256:8ba051b9c7e2de8f3dd97d0092cffd17cafec2e2e2f70f1ec17a6c94972c8f80
@@ -0,0 +1,155 @@
1
+ import {
2
+ appendFileSync, chmodSync, copyFileSync, existsSync, mkdirSync,
3
+ readFileSync, readdirSync, utimesSync, writeFileSync,
4
+ } from "node:fs";
5
+ import { join } from "node:path";
6
+ import { bootstrap, bun, download, quote, requireRootfs } from "./common.js";
7
+
8
+ export const tools = "/bootstrap/toolchain/bin";
9
+ export const toolchain = "/bootstrap/toolchain";
10
+ export const deps = "/bootstrap/deps";
11
+ export const sourceRoot = "/bootstrap/src";
12
+ export const buildRoot = "/bootstrap/build";
13
+ export const resultRoot = "/bootstrap/result";
14
+ export function useLog(path) { log = path; }
15
+ // Build shells preserve the explicit build PATH, without the guest entry
16
+ // shell's preference for installed result tools.
17
+ export const shell = tools + "/sh";
18
+ export const make = tools + "/make";
19
+ let log;
20
+
21
+ export function stage(number, name, args) {
22
+ if (args.length) throw new Error(`Usage: bun ${number}.build-${name}.js`);
23
+ const { arch } = requireRootfs();
24
+ mkdirSync(buildRoot + "/logs", { recursive: true });
25
+ log = `${buildRoot}/logs/${number}-${name}.log`;
26
+ note(`\n${new Date().toISOString()} Stage ${number}: ${name} (${arch})`);
27
+ return { arch, triple: `${arch}-unknown-linux-musl` };
28
+ }
29
+
30
+ function note(text) {
31
+ console.log(text);
32
+ if (log) appendFileSync(log, text + "\n");
33
+ }
34
+
35
+ export function environment(extra = {}) {
36
+ return {
37
+ PATH: `${tools}:/bin:/usr/bin`, HOME: "/root", LC_ALL: "C",
38
+ SHELL: shell, CONFIG_SHELL: shell, CC: tools + "/cc", CC_FOR_BUILD: tools + "/cc",
39
+ AR: existsSync(toolchain + "/bin/ar") ? toolchain + "/bin/ar" : tools + "/bootstrap-ar",
40
+ LD: existsSync(toolchain + "/bin/ld") ? toolchain + "/bin/ld" : tools + "/bootstrap-ld",
41
+ RANLIB: existsSync(toolchain + "/bin/ranlib") ? toolchain + "/bin/ranlib" : ":",
42
+ NM: toolchain + "/bin/nm", MAKE: make, MAKEINFO: ":", CFLAGS: "-O2",
43
+ CFLAGS_FOR_TARGET: "-O2", lt_cv_sys_max_cmd_len: "32768",
44
+ ...extra,
45
+ };
46
+ }
47
+
48
+ export async function command(argv, cwd = buildRoot, extra = {}, capture = false) {
49
+ mkdirSync(cwd, { recursive: true });
50
+ note("$ " + argv.map(quote).join(" "));
51
+ const child = Bun.spawn(argv, {
52
+ cwd, env: environment(extra), stdin: "ignore", stdout: "pipe", stderr: "pipe",
53
+ });
54
+ const chunks = [];
55
+ async function drain(stream, output, collect) {
56
+ for await (const bytes of stream) {
57
+ const data = Buffer.from(bytes);
58
+ output.write(data);
59
+ if (log) appendFileSync(log, data);
60
+ if (collect) chunks.push(data);
61
+ }
62
+ }
63
+ const [status] = await Promise.all([
64
+ child.exited, drain(child.stdout, process.stdout, capture), drain(child.stderr, process.stderr, false),
65
+ ]);
66
+ if (status !== 0) throw new Error(`Command exited ${status}; see ${log}: ${argv[0]}`);
67
+ return capture ? Buffer.concat(chunks).toString() : "";
68
+ }
69
+
70
+ export async function source(pin, directory) {
71
+ // Verify on every stage invocation, including a cached archive.
72
+ const archive = await download(pin);
73
+ const target = join(sourceRoot, directory);
74
+ if (!existsSync(target)) {
75
+ mkdirSync(sourceRoot, { recursive: true });
76
+ const tar = new Bun.Archive(readFileSync(archive));
77
+ await tar.extract(sourceRoot);
78
+ // Preserve release-generated parsers/Makefiles: extraction time must
79
+ // not accidentally request autoconf, automake or bison regeneration.
80
+ for (const [name, file] of await tar.files()) {
81
+ const seconds = file.lastModified / 1000;
82
+ utimesSync(join(sourceRoot, name), seconds, seconds);
83
+ }
84
+ }
85
+ if (!existsSync(join(target, "configure"))) throw new Error(`Incomplete source tree: ${target}`);
86
+ return target;
87
+ }
88
+
89
+ export function patch(path, before, after) {
90
+ const old = readFileSync(path, "utf8");
91
+ if (old.includes(after)) return;
92
+ if (!old.includes(before)) throw new Error(`Patch context missing: ${path}`);
93
+ writeFileSync(path, old.replaceAll(before, after));
94
+ }
95
+
96
+ export function script(path, text) {
97
+ writeFileSync(path, "#!/bin/bun\n" + text, { mode: 0o755 });
98
+ chmodSync(path, 0o755);
99
+ }
100
+
101
+ export function installAdapters() {
102
+ mkdirSync(tools, { recursive: true });
103
+ script(shell, "await import('/buninu/apps/bunmsh/src/main.js');\n");
104
+ // Native Make/configure can exec these names; implementations remain JS.
105
+ const names = "awk basename cat chmod cmp cp cut date diff dirname echo egrep expr false fgrep grep head ln ls mkdir mktemp mv printf pwd rm rmdir sed sleep sort tail test touch tr true uname uniq wc".split(" ");
106
+ for (const name of names) script(`${tools}/${name}`, `
107
+ import {executeArgv,createState,finishShell} from '/buninu/apps/bunmsh/src/shell.js';
108
+ const state=createState({shellOptions:{posix:true}});
109
+ const result=await executeArgv(['builtin',${JSON.stringify(name)},...process.argv.slice(2)],state);
110
+ process.exitCode=(await finishShell(state,result.status)).status;
111
+ `);
112
+ script(tools + "/bootstrap-ar", `import {existsSync} from 'node:fs';const ar='/bootstrap/toolchain/bin/ar';const p=Bun.spawn([...(existsSync(ar)?[ar]:['/bootstrap/toolchain/bin/tcc','-ar']),...Bun.argv.slice(2)],{stdin:'inherit',stdout:'inherit',stderr:'inherit'});process.exitCode=await p.exited;\n`);
113
+ script(tools + "/bootstrap-ld", `import {existsSync} from 'node:fs';const ld='/bootstrap/toolchain/bin/ld';const p=Bun.spawn([existsSync(ld)?ld:'/bootstrap/toolchain/bin/tcc',...Bun.argv.slice(2)],{stdin:'inherit',stdout:'inherit',stderr:'inherit'});process.exitCode=await p.exited;\n`);
114
+ installCc();
115
+ }
116
+
117
+ export function installCc() {
118
+ script(tools + "/cc", `
119
+ import {existsSync} from 'node:fs';
120
+ const args=Bun.argv.slice(2);
121
+ const info=args.length===0 || args.every(a=>['-v','--version','-h','-hh','--help','-dumpversion','-dumpmachine','-print-search-dirs'].includes(a));
122
+ const linking=!info && !args.some(a=>['-c','-E','-S','-r'].includes(a));
123
+ const archive=process.arch==='x64'?'/bootstrap/toolchain/lib/tcc/libbootstrap-tcc-x64.a':'/bootstrap/toolchain/lib/tcc/libbootstrap-tcc-arm64.a';
124
+ const runtime=linking&&existsSync(archive)?[archive]:[];
125
+ const p=Bun.spawn(['/bootstrap/toolchain/bin/tcc',...args,...(info?[]:['-B/bootstrap/toolchain/lib/tcc','-nostdinc','-I/usr/include','-I/bootstrap/toolchain/lib/tcc/include','-L/usr/lib','-L/lib',...runtime,...(linking?['-lgcc_s']:[])])],{stdin:'inherit',stdout:'inherit',stderr:'inherit'});
126
+ process.exitCode=await p.exited;
127
+ `);
128
+ }
129
+
130
+ export async function configure(src, build, flags, extra = {}) {
131
+ await command([bun(), "/buninu/apps/bunmsh/index.js", src + "/configure", ...flags], build, extra);
132
+ }
133
+
134
+ export async function makeIn(build, targets = ["all"], extra = {}) {
135
+ const jobs = process.env.BOOTSTRAP_JOBS || "2";
136
+ if (!/^[1-9][0-9]*$/.test(jobs)) throw new Error("BOOTSTRAP_JOBS must be a positive integer");
137
+ await command([make, "SHELL=" + shell, "MAKEINFO=:", "-j" + jobs, ...targets], build, extra);
138
+ }
139
+
140
+ export function canonicalizers(src) {
141
+ const binutils = sourceRoot + "/binutils-2.39";
142
+ for (const name of ["config.sub", "config.guess"]) {
143
+ copyFileSync(join(binutils, name), join(src, name));
144
+ chmodSync(join(src, name), 0o755);
145
+ }
146
+ }
147
+
148
+ export function ready(number, message) {
149
+ note(`Stage ${number} verified: ${message}`);
150
+ if (number < 12) {
151
+ const next = readdirSync(bootstrap).find(name => name.startsWith(`${number + 1}.`) && name.endsWith(".js"));
152
+ if (!next) throw new Error(`Missing stage ${number + 1}`);
153
+ note(`Next: bun /bootstrap/${next}`);
154
+ }
155
+ }
@@ -0,0 +1,134 @@
1
+ import {
2
+ existsSync, lstatSync, mkdirSync, readFileSync, writeFileSync,
3
+ } from "node:fs";
4
+ import { homedir } from "node:os";
5
+ import { dirname, join, resolve } from "node:path";
6
+ import { createHash } from "node:crypto";
7
+
8
+ export const bootstrap = resolve(import.meta.dir, "..");
9
+ export const bun = () => Bun.which("bun") || process.argv0;
10
+ export const quote = value => "'" + value.replaceAll("'", "'\\''") + "'";
11
+
12
+ export function normalizeArch(name) {
13
+ if (["x", "x64", "x86_64", "x86-64"].includes(name)) return "x86_64";
14
+ if (["a", "aarch64", "arm64"].includes(name)) return "aarch64";
15
+ throw new Error(`Unsupported architecture: ${name}`);
16
+ }
17
+
18
+ export function options(args) {
19
+ if (args.includes("--help") || args.includes("-h")) {
20
+ console.log("Usage: bun SCRIPT [ARCH] [ROOTFS]");
21
+ console.log("ARCH: a/arm64/aarch64 or x/x64/x86_64/x86-64; default: host architecture.");
22
+ console.log("ROOTFS: default ./roota (arm64) or ./rootx (x64). A lone path is accepted.");
23
+ return null;
24
+ }
25
+ let arch = normalizeArch(process.arch);
26
+ const rest = [...args];
27
+ if (rest.length && /^(x|x64|x86_64|x86-64|a|arm64|aarch64)$/.test(rest[0])) {
28
+ arch = normalizeArch(rest.shift());
29
+ }
30
+ if (rest.length > 1 || rest.some(arg => arg.startsWith("-"))) {
31
+ throw new Error("Expected [ARCH] [ROOTFS]; use --help");
32
+ }
33
+ return { arch, rootfs: resolve(rest[0] ?? (arch === "aarch64" ? "roota" : "rootx")) };
34
+ }
35
+
36
+ // Parse data only: do not execute os-release as shell code.
37
+ export function isBuninuLinux() {
38
+ let text;
39
+ try { text = readFileSync("/etc/os-release", "utf8"); }
40
+ catch (error) {
41
+ if (error.code !== "ENOENT") throw error;
42
+ try { text = readFileSync("/usr/lib/os-release", "utf8"); }
43
+ catch (fallback) { if (fallback.code === "ENOENT") return false; throw fallback; }
44
+ }
45
+ return hasBuninuId(text);
46
+ }
47
+
48
+ export function hasBuninuId(text) {
49
+ const ids = [...text.matchAll(/^ID=(.*)\r?$/gm)];
50
+ const value = ids.at(-1)?.[1].trim();
51
+ return ["buninu-linux", '"buninu-linux"', "'buninu-linux'"].includes(value);
52
+ }
53
+
54
+ export function assertAbsent(path) {
55
+ try { lstatSync(path); }
56
+ catch (error) { if (error.code === "ENOENT") return; throw error; }
57
+ throw new Error(`Refusing existing destination: ${path}`);
58
+ }
59
+
60
+ export function write(path, bytes, mode = 0o644) {
61
+ mkdirSync(dirname(path), { recursive: true });
62
+ writeFileSync(path, bytes, { flag: "wx", mode });
63
+ }
64
+
65
+ export const sha256 = bytes => createHash("sha256").update(bytes).digest("hex");
66
+
67
+ export async function download(name) {
68
+ const pins = Bun.YAML.parse(readFileSync(join(bootstrap, "downloads.md"), "utf8"));
69
+ const pin = pins[name];
70
+ if (!Array.isArray(pin) || !/^sha256:[a-f0-9]{64}$/.test(pin[1])) {
71
+ throw new Error(`Missing or invalid download pin: ${name}`);
72
+ }
73
+ const [url, hash] = pin;
74
+ const expected = hash.slice(7);
75
+ const cache = join(process.env.XDG_CACHE_HOME || join(homedir(), ".cache"), "buninu-bootstrap");
76
+ const path = join(cache, expected + "-" + new URL(url).pathname.split("/").at(-1));
77
+ if (existsSync(path)) {
78
+ const bytes = readFileSync(path);
79
+ if (sha256(bytes) !== expected) throw new Error(`Cached file hash mismatch: ${path}`);
80
+ console.log(`Verified cache: ${name}`);
81
+ return path;
82
+ }
83
+ console.log(`Fetching ${name}`);
84
+ const response = await fetch(url);
85
+ if (!response.ok) throw new Error(`${url}: HTTP ${response.status}`);
86
+ const bytes = Buffer.from(await response.arrayBuffer());
87
+ if (sha256(bytes) !== expected) throw new Error(`Download hash mismatch: ${name}`);
88
+ mkdirSync(cache, { recursive: true });
89
+ writeFileSync(path, bytes, { flag: "wx", mode: 0o644 });
90
+ return path;
91
+ }
92
+
93
+ export async function run(argv) {
94
+ console.log("$ " + argv.map(quote).join(" "));
95
+ const child = Bun.spawn(argv, { stdin: "inherit", stdout: "inherit", stderr: "inherit" });
96
+ const status = await child.exited;
97
+ if (status !== 0) throw new Error(`Command exited ${status}: ${argv[0]}`);
98
+ }
99
+
100
+ export async function cli(main) {
101
+ const mask = process.umask(0o022);
102
+ try { await main(Bun.argv.slice(2)); }
103
+ catch (error) { console.error(error.message); process.exitCode = 1; }
104
+ finally { process.umask(mask); }
105
+ }
106
+
107
+ export function requireRootfs() {
108
+ // Prevent an accidental stage 3/4 invocation from writing host /usr/include.
109
+ const marker = JSON.parse(readFileSync("/etc/buninu-bootstrap.json", "utf8"));
110
+ if (marker.format !== 1 || marker.arch !== normalizeArch(process.arch)) {
111
+ throw new Error("Enter the prepared rootfs for the matching architecture first");
112
+ }
113
+ if (bootstrap !== "/bootstrap") throw new Error("Run this stage inside the prepared rootfs");
114
+ return marker;
115
+ }
116
+
117
+ export function validateElf(name, data, arch) {
118
+ if (data.length < 64 || data.toString("hex", 0, 4) !== "7f454c46"
119
+ || data[4] !== 2 || data[5] !== 1 || data.readUInt16LE(18) !== (arch === "aarch64" ? 183 : 62)) {
120
+ throw new Error(`Not a ${arch} little-endian ELF64 file: ${name}`);
121
+ }
122
+ }
123
+
124
+ export function elfInterpreter(data) {
125
+ const start = Number(data.readBigUInt64LE(32)), stride = data.readUInt16LE(54), count = data.readUInt16LE(56);
126
+ for (let i = 0; i < count; i++) {
127
+ const p = start + i * stride;
128
+ if (!Number.isSafeInteger(p) || stride < 56 || p + 56 > data.length) throw new Error("Invalid Bun ELF program headers");
129
+ if (data.readUInt32LE(p) !== 3) continue;
130
+ const at = Number(data.readBigUInt64LE(p + 8)), size = Number(data.readBigUInt64LE(p + 32));
131
+ if (!Number.isSafeInteger(at) || !Number.isSafeInteger(size) || at + size > data.length) throw new Error("Invalid Bun ELF interpreter range");
132
+ return data.subarray(at, at + size).toString().replace(/\0.*$/s, "");
133
+ }
134
+ }
@@ -0,0 +1,14 @@
1
+ #include "/bootstrap/src/tinycc/libtcc.c"
2
+
3
+ int compile_object(const char *source, const char *output, const char *flags) {
4
+ TCCState *s = tcc_new();
5
+ if (!s) return 10;
6
+ tcc_set_lib_path(s, "/bootstrap/src/tinycc");
7
+ int result = tcc_set_options(s, flags);
8
+ if (!result) result = tcc_set_output_type(s, TCC_OUTPUT_OBJ);
9
+ if (!result) result = tcc_add_file(s, source);
10
+ if (!result) result = tcc_output_file(s, output);
11
+ tcc_delete(s);
12
+ return result;
13
+ }
14
+ #include "/bootstrap/src/tinycc/bun-x64-runtime.c"
@@ -0,0 +1,59 @@
1
+ // Generate musl 1.2.6 public headers without Make, sed or a C compiler.
2
+ import {
3
+ existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync,
4
+ } from "node:fs";
5
+ import { dirname, join } from "node:path";
6
+
7
+ export function generateHeaders(arch, source, output) {
8
+ if (readFileSync(join(source, "VERSION"), "utf8").trim() !== "1.2.6") {
9
+ throw new Error("This generator follows musl 1.2.6's header rules");
10
+ }
11
+ if (existsSync(output)) throw new Error(`Refusing existing output: ${output}`);
12
+ const headers = new Map();
13
+ const copyHeaders = (directory, prefix = "") => {
14
+ for (const entry of readdirSync(directory, { withFileTypes: true })) {
15
+ if (entry.isFile() && entry.name.endsWith(".h")) {
16
+ headers.set(prefix + entry.name, readFileSync(join(directory, entry.name)));
17
+ }
18
+ }
19
+ };
20
+
21
+ // Makefile's include/*.h and include/*/*.h public headers.
22
+ copyHeaders(join(source, "include"));
23
+ for (const entry of readdirSync(join(source, "include"), { withFileTypes: true })) {
24
+ if (entry.isDirectory()) copyHeaders(join(source, "include", entry.name), entry.name + "/");
25
+ }
26
+ // Architecture-specific bits override generic fallbacks.
27
+ copyHeaders(join(source, "arch/generic/bits"), "bits/");
28
+ copyHeaders(join(source, "arch", arch, "bits"), "bits/");
29
+
30
+ // Translate tools/mkalltypes.sed's three substitutions literally. The
31
+ // replacement's final newline is intentional, in addition to the input's.
32
+ const types = readFileSync(join(source, "arch", arch, "bits/alltypes.h.in"), "utf8")
33
+ + readFileSync(join(source, "include/alltypes.h.in"), "utf8");
34
+ headers.set("bits/alltypes.h", types
35
+ .replace(/^TYPEDEF (.*) ([^ ]*);$/gm, (_, type, name) =>
36
+ `#if defined(__NEED_${name}) && !defined(__DEFINED_${name})\ntypedef ${type} ${name};\n#define __DEFINED_${name}\n#endif\n`)
37
+ .replace(/^STRUCT * ([^ ]*) (.*);$/gm, (_, name, body) =>
38
+ `#if defined(__NEED_struct_${name}) && !defined(__DEFINED_struct_${name})\nstruct ${name} ${body};\n#define __DEFINED_struct_${name}\n#endif\n`)
39
+ .replace(/^UNION * ([^ ]*) (.*);$/gm, (_, name, body) =>
40
+ `#if defined(__NEED_union_${name}) && !defined(__DEFINED_union_${name})\nunion ${name} ${body};\n#define __DEFINED_union_${name}\n#endif\n`));
41
+
42
+ // Makefile: copy the input, then append sed -n 's/__NR_/SYS_/p' output.
43
+ const syscalls = readFileSync(join(source, "arch", arch, "bits/syscall.h.in"), "utf8");
44
+ const aliases = syscalls.split("\n").filter(line => line.includes("__NR_"))
45
+ .map(line => line.replace("__NR_", "SYS_") + "\n").join("");
46
+ headers.set("bits/syscall.h", syscalls + aliases);
47
+
48
+ const previousUmask = process.umask(0o022);
49
+ try {
50
+ mkdirSync(output); // Parent must exist; refuse races with another writer.
51
+ for (const [name, data] of headers) {
52
+ const path = join(output, name);
53
+ mkdirSync(dirname(path), { recursive: true });
54
+ writeFileSync(path, data, { flag: "wx", mode: 0o644 });
55
+ }
56
+ } finally { process.umask(previousUmask); }
57
+ console.log(`Generated ${headers.size} ${arch} headers at ${output}`);
58
+ }
59
+