From b7bff3c02e657cf34e0d0b7d3aa1c4562816f4cd Mon Sep 17 00:00:00 2001 From: Chad Smith Date: Tue, 24 Feb 2026 13:19:12 -0800 Subject: [PATCH 1/2] add chad target subcommand for managing cross-compilation SDKs --- src/chad-native.ts | 72 ++++++++++++++++++++++++++++++++++++++++++++++ src/chad-node.ts | 47 +++++++++++++++++++++++++++++- 2 files changed, 118 insertions(+), 1 deletion(-) diff --git a/src/chad-native.ts b/src/chad-native.ts index 45f798ac..a3141b4f 100644 --- a/src/chad-native.ts +++ b/src/chad-native.ts @@ -12,6 +12,8 @@ import { ArgumentParser } from "./argparse.js"; declare const fs: { existsSync(filename: string): boolean; writeFileSync(filename: string, data: string): number; + readdirSync(dirname: string): string[]; + unlinkSync(filename: string): number; }; declare const path: { @@ -24,6 +26,8 @@ declare const process: { exit(code: number): void; argv: string[]; argv0: string; + platform: string; + env: { [key: string]: string }; }; declare const child_process: { @@ -49,6 +53,7 @@ parser.addSubcommand("ir", "Emit LLVM IR only"); parser.addSubcommand("init", "Generate starter project"); parser.addSubcommand("watch", "Watch for changes and recompile+run"); parser.addSubcommand("clean", "Remove the .build directory"); +parser.addSubcommand("target", "Manage cross-compilation target SDKs"); parser.addFlag("version", "", "Show version"); parser.addScopedOption("output", "o", "Specify output file", "", "build,run,ir"); @@ -111,6 +116,73 @@ if (command === "clean") { process.exit(0); } +if (command === "target") { + const action = parser.getPositional(0); + const home = process.env.HOME; + const baseDir = home + "/.chadscript/targets"; + + if (action === "add") { + const name = parser.getPositional(1); + if (name.length === 0) { + console.log("Usage: chad target add "); + console.log("Example: chad target add linux-x64"); + process.exit(1); + throw new Error("unreachable"); + } + const sdkDir = baseDir + "/" + name; + child_process.execSync("mkdir -p " + sdkDir); + const url = + "https://github.com/cs01/ChadScript/releases/download/latest/chadscript-target-" + name + ".tar.gz"; + console.log("Downloading target SDK '" + name + "'..."); + cs_exec_passthrough("curl -fsSL \"" + url + "\" | tar xzf - -C \"" + sdkDir + "\""); + // Validate the download produced a valid SDK + if (!fs.existsSync(sdkDir + "/sdk.json")) { + child_process.execSync("rm -rf " + sdkDir); + console.log("chad: error: downloaded SDK '" + name + "' is invalid (missing sdk.json)"); + process.exit(1); + throw new Error("unreachable"); + } + console.log("Target SDK '" + name + "' installed to " + sdkDir); + } else if (action === "list") { + if (!fs.existsSync(baseDir)) { + process.exit(0); + } + const entries = fs.readdirSync(baseDir); + let ei = 0; + while (ei < entries.length) { + if (fs.existsSync(baseDir + "/" + entries[ei] + "/sdk.json")) { + console.log(entries[ei]); + } + ei = ei + 1; + } + } else if (action === "remove") { + const name = parser.getPositional(1); + if (name.length === 0) { + console.log("Usage: chad target remove "); + process.exit(1); + throw new Error("unreachable"); + } + const sdkDir = baseDir + "/" + name; + if (!fs.existsSync(sdkDir)) { + console.log("chad: target SDK '" + name + "' is not installed"); + process.exit(1); + throw new Error("unreachable"); + } + child_process.execSync("rm -rf " + sdkDir); + console.log("Removed target SDK '" + name + "'"); + } else { + console.log("Usage: chad target "); + console.log(""); + console.log("Actions:"); + console.log(" add Download and install a target SDK"); + console.log(" list List installed target SDKs"); + console.log(" remove Remove a target SDK"); + console.log(""); + console.log("Example: chad target add linux-x64"); + } + process.exit(0); +} + if (command === "watch") { const watchInput = parser.getPositional(0); if (watchInput.length === 0) { diff --git a/src/chad-node.ts b/src/chad-node.ts index 8a4ca109..7f1fcfa0 100644 --- a/src/chad-node.ts +++ b/src/chad-node.ts @@ -19,6 +19,7 @@ import { ArgumentParser } from "./argparse.js"; import * as path from "path"; import * as fs from "fs"; import { execSync, spawn as spawnProc, ChildProcess } from "child_process"; +import { installTargetSDK, listInstalledSDKs, getSDKBaseDir } from "./cross-compile.js"; const parser = new ArgumentParser("chad", "compile TypeScript to native binaries via LLVM"); parser.addSubcommand("build", "Compile to a native binary"); @@ -27,6 +28,7 @@ parser.addSubcommand("ir", "Emit LLVM IR only"); parser.addSubcommand("init", "Generate starter project (chadscript.d.ts, tsconfig.json, hello.ts)"); parser.addSubcommand("watch", "Watch for changes and recompile+run"); parser.addSubcommand("clean", "Remove the .build directory"); +parser.addSubcommand("target", "Manage cross-compilation target SDKs"); parser.addFlag("version", "", "Show version"); parser.addScopedOption("output", "o", "Specify output file", "", "build,run,ir"); @@ -86,6 +88,48 @@ if (command === "clean") { process.exit(0); } +if (command === "target") { + const action = parser.getPositional(0); + + if (action === "add") { + const name = parser.getPositional(1); + if (!name) { + console.error("Usage: chad target add "); + console.error("Example: chad target add linux-x64"); + process.exit(1); + } + installTargetSDK(name); + } else if (action === "list") { + const sdks = listInstalledSDKs(); + for (const sdk of sdks) { + console.log(sdk); + } + } else if (action === "remove") { + const name = parser.getPositional(1); + if (!name) { + console.error("Usage: chad target remove "); + process.exit(1); + } + const sdkDir = path.join(getSDKBaseDir(), name); + if (!fs.existsSync(sdkDir)) { + console.error(`chad: target SDK '${name}' is not installed`); + process.exit(1); + } + fs.rmSync(sdkDir, { recursive: true }); + console.log(`Removed target SDK '${name}'`); + } else { + console.log("Usage: chad target "); + console.log(""); + console.log("Actions:"); + console.log(" add Download and install a target SDK"); + console.log(" list List installed target SDKs"); + console.log(" remove Remove a target SDK"); + console.log(""); + console.log("Example: chad target add linux-x64"); + } + process.exit(0); +} + if (command === "watch") { const watchFile = parser.getPositional(0); if (!watchFile) { @@ -181,7 +225,8 @@ if ( command !== "run" && command !== "ir" && command !== "init" && - command !== "watch" + command !== "watch" && + command !== "target" ) { if (command.endsWith(".ts") || command.endsWith(".js")) { console.error(`chad: error: missing command. did you mean 'chad build ${command}'?`); From cddf8182e067905399831134ef2bcb4882c9bbaa Mon Sep 17 00:00:00 2001 From: Chad Smith Date: Tue, 24 Feb 2026 13:31:34 -0800 Subject: [PATCH 2/2] add linux-x64 target sdk to release and use ubuntu 20.04 for older glibc --- .github/workflows/ci.yml | 9 +++++++++ src/chad-native.ts | 6 ++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 893b7f56..3ba20fc3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -89,6 +89,9 @@ jobs: run: node --import tsx --test tests/self-hosting.test.ts timeout-minutes: 15 + - name: Build target SDK + run: bash scripts/build-target-sdk.sh + - name: Package release artifact run: | mkdir -p release/lib @@ -117,6 +120,12 @@ jobs: name: chadscript-linux-x64 path: chadscript-linux-x64.tar.gz + - name: Upload target SDK + uses: actions/upload-artifact@v4 + with: + name: chadscript-target-linux-x64 + path: chadscript-target-linux-x64.tar.gz + build-macos: runs-on: macos-latest diff --git a/src/chad-native.ts b/src/chad-native.ts index a3141b4f..ed4ee576 100644 --- a/src/chad-native.ts +++ b/src/chad-native.ts @@ -132,9 +132,11 @@ if (command === "target") { const sdkDir = baseDir + "/" + name; child_process.execSync("mkdir -p " + sdkDir); const url = - "https://github.com/cs01/ChadScript/releases/download/latest/chadscript-target-" + name + ".tar.gz"; + "https://github.com/cs01/ChadScript/releases/download/latest/chadscript-target-" + + name + + ".tar.gz"; console.log("Downloading target SDK '" + name + "'..."); - cs_exec_passthrough("curl -fsSL \"" + url + "\" | tar xzf - -C \"" + sdkDir + "\""); + cs_exec_passthrough('curl -fsSL "' + url + '" | tar xzf - -C "' + sdkDir + '"'); // Validate the download produced a valid SDK if (!fs.existsSync(sdkDir + "/sdk.json")) { child_process.execSync("rm -rf " + sdkDir);