This commit is contained in:
agra
2026-03-02 21:00:55 +02:00
parent 2f4f898d54
commit bbb5426777
42 changed files with 483 additions and 9023 deletions

View File

@@ -34,7 +34,25 @@ pub fn main(init: std.process.Init) !void {
if (std.mem.eql(u8, arg, "--target")) {
i += 1;
if (i >= args.len) { std.debug.print("error: --target requires a value\n", .{}); return; }
target_config.triple = (try allocator.dupeZ(u8, args[i])).ptr;
const raw = args[i];
// Shorthand aliases for common targets
const expanded = if (std.mem.eql(u8, raw, "wasm") or std.mem.eql(u8, raw, "wasm32") or std.mem.eql(u8, raw, "emscripten"))
"wasm32-unknown-emscripten"
else if (std.mem.eql(u8, raw, "wasm64"))
"wasm64-unknown-emscripten"
else if (std.mem.eql(u8, raw, "macos") or std.mem.eql(u8, raw, "macos-arm"))
"aarch64-apple-macos"
else if (std.mem.eql(u8, raw, "macos-x86"))
"x86_64-apple-macos"
else if (std.mem.eql(u8, raw, "linux") or std.mem.eql(u8, raw, "linux-x86"))
"x86_64-unknown-linux-gnu"
else if (std.mem.eql(u8, raw, "linux-arm"))
"aarch64-unknown-linux-gnu"
else if (std.mem.eql(u8, raw, "windows"))
"x86_64-windows-msvc"
else
raw;
target_config.triple = (try allocator.dupeZ(u8, expanded)).ptr;
} else if (std.mem.eql(u8, arg, "--cpu")) {
i += 1;
if (i >= args.len) { std.debug.print("error: --cpu requires a value\n", .{}); return; }
@@ -100,7 +118,6 @@ pub fn main(init: std.process.Init) !void {
break :blk base;
};
compile(allocator, io, path, output_name, target_config, show_timing, enable_cache) catch return;
std.debug.print("compiled: {s}\n", .{output_name});
} else if (std.mem.eql(u8, command, "ir")) {
emitIR(allocator, io, path, target_config) catch return;
} else if (std.mem.eql(u8, command, "ir-dump")) {
@@ -219,17 +236,17 @@ fn compileCForJIT(allocator: std.mem.Allocator, io: std.Io, comp: *sx.core.Compi
}
/// Compile C sources from #import c blocks to .o files for linking.
fn compileCForBuild(allocator: std.mem.Allocator, io: std.Io, comp: *sx.core.Compilation) ![]const []const u8 {
fn compileCForBuild(allocator: std.mem.Allocator, io: std.Io, comp: *sx.core.Compilation, tmp_dir: []const u8) ![]const []const u8 {
const c_infos = try comp.collectCImportSources();
if (c_infos.len == 0) return &.{};
// For Emscripten targets, use emcc to cross-compile C sources
if (comp.target_config.isEmscripten()) {
return try sx.c_import.compileCWithEmcc(allocator, io, c_infos);
return try sx.c_import.compileCWithEmcc(allocator, io, c_infos, comp.target_config, tmp_dir);
}
const obj_bufs = try sx.c_import.compileCToObjects(allocator, c_infos);
return try sx.c_import.writeCObjectFiles(allocator, io, obj_bufs);
return try sx.c_import.writeCObjectFiles(allocator, io, obj_bufs, tmp_dir);
}
fn parseOptLevel(s: []const u8) ?sx.target.TargetConfig.OptLevel {
@@ -252,7 +269,7 @@ fn printUsage() void {
\\ lsp Start language server (LSP)
\\
\\Options:
\\ --target <triple> Target triple (default: host)
\\ --target <target> Target triple or shorthand: wasm, macos, linux, windows (default: host)
\\ --cpu <name> CPU name (default: generic)
\\ --opt <level> Optimization: none/0, less/1, default/2, aggressive/3
\\ -o <path> Output path
@@ -408,7 +425,11 @@ fn compileWithTimer(allocator: std.mem.Allocator, io: std.Io, input_path: []cons
const root = comp.resolved_root orelse comp.root orelse return error.CompileError;
const libs = try extractLibraries(allocator, root);
const obj_path = try std.fmt.allocPrintSentinel(allocator, "{s}.o", .{output_path}, 0);
// Create temp directory for build artifacts
const tmp_dir: []const u8 = ".sx-tmp";
std.Io.Dir.createDirPath(.cwd(), io, tmp_dir) catch {};
const obj_path = try std.fmt.allocPrintSentinel(allocator, "{s}/main.o", .{tmp_dir}, 0);
// Cache: compute key and check for cached binary/.o
const key = computeCacheKey(source, &comp.import_sources, target_config);
@@ -453,15 +474,37 @@ fn compileWithTimer(allocator: std.mem.Allocator, io: std.Io, input_path: []cons
// Compile C sources from #import c blocks to .o files
timer.mark();
const c_obj_paths = compileCForBuild(allocator, io, &comp) catch {
const c_obj_paths = compileCForBuild(allocator, io, &comp, tmp_dir) catch {
std.debug.print("error: C import compilation failed\n", .{});
return error.CompileError;
};
timer.record("c-import");
// Merge build config (from #run blocks) with CLI config
var merged_config = target_config;
const build_flags = comp.getBuildLinkFlags();
if (build_flags.len > 0) {
var all_flags: std.ArrayList([]const u8) = .empty;
for (target_config.extra_link_flags) |f| try all_flags.append(allocator, f);
for (build_flags) |f| try all_flags.append(allocator, f);
merged_config.extra_link_flags = try all_flags.toOwnedSlice(allocator);
}
// Override output path from #run if set (and no explicit -o was given on CLI)
const final_output = if (target_config.output_path == null)
(comp.getBuildOutputPath() orelse output_path)
else
output_path;
// Ensure output directory exists
if (std.mem.lastIndexOfScalar(u8, final_output, '/')) |sep| {
if (sep > 0) {
std.Io.Dir.createDirPath(.cwd(), io, final_output[0..sep]) catch {};
}
}
// Link (sx .o + C .o files)
timer.mark();
sx.target.link(allocator, io, obj_path, c_obj_paths, output_path, libs, target_config) catch {
sx.target.link(allocator, io, obj_path, c_obj_paths, final_output, libs, merged_config) catch {
std.debug.print("error: linking failed\n", .{});
return error.CompileError;
};
@@ -472,11 +515,16 @@ fn compileWithTimer(allocator: std.mem.Allocator, io: std.Io, input_path: []cons
std.Io.Dir.copyFile(.cwd(), output_path, .cwd(), cache_bin, io, .{ .make_path = true }) catch {};
}
// Clean up object files
std.debug.print("compiled: {s}\n", .{final_output});
// Clean up temp directory and all build artifacts
std.Io.Dir.deleteFile(.cwd(), io, obj_path) catch {};
const shell_tmp = std.fmt.allocPrint(allocator, "{s}.shell.html", .{obj_path}) catch null;
if (shell_tmp) |sp| std.Io.Dir.deleteFile(.cwd(), io, sp) catch {};
for (c_obj_paths) |cop| {
std.Io.Dir.deleteFile(.cwd(), io, cop) catch {};
}
std.Io.Dir.deleteDir(.cwd(), io, tmp_dir) catch {};
}
fn runAOT(allocator: std.mem.Allocator, io: std.Io, input_path: []const u8, target_config: sx.target.TargetConfig, timer: *Timing, enable_cache: bool) !void {
@@ -556,13 +604,21 @@ fn hasTopLevelRun(root: *const sx.ast.Node) bool {
fn extractLibraries(allocator: std.mem.Allocator, root: *const sx.ast.Node) ![]const []const u8 {
var libs = std.ArrayList([]const u8).empty;
var seen = std.StringHashMap(void).init(allocator);
const addLib = struct {
fn f(l: *std.ArrayList([]const u8), s: *std.StringHashMap(void), a: std.mem.Allocator, name: []const u8) !void {
if (s.contains(name)) return;
try s.put(name, {});
try l.append(a, name);
}
}.f;
for (root.data.root.decls) |decl| {
switch (decl.data) {
.library_decl => |ld| try libs.append(allocator, ld.lib_name),
.library_decl => |ld| try addLib(&libs, &seen, allocator, ld.lib_name),
.namespace_decl => |ns| {
for (ns.decls) |nd| {
switch (nd.data) {
.library_decl => |ld| try libs.append(allocator, ld.lib_name),
.library_decl => |ld| try addLib(&libs, &seen, allocator, ld.lib_name),
else => {},
}
}