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
9 changes: 9 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down
74 changes: 74 additions & 0 deletions src/chad-native.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand All @@ -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: {
Expand All @@ -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");
Expand Down Expand Up @@ -111,6 +116,75 @@ 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 <name>");
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 <name>");
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 <action>");
console.log("");
console.log("Actions:");
console.log(" add <name> Download and install a target SDK");
console.log(" list List installed target SDKs");
console.log(" remove <name> 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) {
Expand Down
47 changes: 46 additions & 1 deletion src/chad-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand All @@ -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");
Expand Down Expand Up @@ -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 <name>");
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 <name>");
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 <action>");
console.log("");
console.log("Actions:");
console.log(" add <name> Download and install a target SDK");
console.log(" list List installed target SDKs");
console.log(" remove <name> 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) {
Expand Down Expand Up @@ -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}'?`);
Expand Down
Loading