Skip to content

Commit d66ca78

Browse files
authored
Merge pull request #9623 from roc-lang/roc-build-default-app
Add build-only default app platform runtime
2 parents 9d2ba0d + 4b3593a commit d66ca78

22 files changed

Lines changed: 1630 additions & 21 deletions

File tree

build.zig

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4892,6 +4892,32 @@ fn addMainExe(
48924892
b.pathJoin(&.{ "src/cli/targets", cross_target.name, builtins_extern_ext }),
48934893
);
48944894
exe.step.dependOn(&copy_cross_builtins_extern.step);
4895+
4896+
if (std.mem.eql(u8, cross_target.name, "x64musl") or std.mem.eql(u8, cross_target.name, "arm64musl")) {
4897+
const default_platform_runtime_obj = b.addObject(.{
4898+
.name = b.fmt("roc_default_platform_{s}", .{cross_target.name}),
4899+
.root_module = b.createModule(.{
4900+
.root_source_file = b.path("src/default_platform/linux_runtime.zig"),
4901+
.target = cross_resolved_target,
4902+
.optimize = .ReleaseFast,
4903+
.strip = false,
4904+
.omit_frame_pointer = false,
4905+
.pic = true,
4906+
.single_threaded = true,
4907+
}),
4908+
});
4909+
default_platform_runtime_obj.root_module.stack_check = false;
4910+
default_platform_runtime_obj.root_module.link_libc = false;
4911+
default_platform_runtime_obj.bundle_compiler_rt = false;
4912+
configureBackend(default_platform_runtime_obj, cross_resolved_target);
4913+
4914+
const copy_default_platform_runtime = b.addUpdateSourceFiles();
4915+
copy_default_platform_runtime.addCopyFileToSource(
4916+
default_platform_runtime_obj.getEmittedBin(),
4917+
b.pathJoin(&.{ "src/cli/targets", cross_target.name, "roc_default_platform.o" }),
4918+
);
4919+
exe.step.dependOn(&copy_default_platform_runtime.step);
4920+
}
48954921
}
48964922

48974923
const config = b.addOptions();

src/backend/llvm/MonoLlvmCodeGen.zig

Lines changed: 424 additions & 2 deletions
Large diffs are not rendered by default.

src/cli/cli_args.zig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ pub const BuildArgs = struct {
114114
warning_count_out: ?*usize = null, // optionally receive the total warning count
115115
require_executable_output: bool = false, // reject static/shared library targets
116116
suppress_build_status: bool = false, // suppress "Built..." output (used by roc run)
117+
synthetic_default_platform: bool = false, // internal: build rewrote a headerless app to the default platform
117118
};
118119

119120
/// Arguments for `roc test`

src/cli/main.zig

Lines changed: 124 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,23 @@ const BuiltinsObjects = struct {
257257
}
258258
};
259259

260+
const DefaultPlatformRuntimeObjects = struct {
261+
const x64musl = if (builtin.is_test) &[_]u8{} else @embedFile("targets/x64musl/roc_default_platform.o");
262+
const arm64musl = if (builtin.is_test) &[_]u8{} else @embedFile("targets/arm64musl/roc_default_platform.o");
263+
264+
pub fn forTarget(target: RocTarget) ?[]const u8 {
265+
return switch (target) {
266+
.x64musl => x64musl,
267+
.arm64musl => arm64musl,
268+
else => null,
269+
};
270+
}
271+
272+
pub fn filename() []const u8 {
273+
return "roc_default_platform.o";
274+
}
275+
};
276+
260277
// Workaround for Zig standard library compilation issue on macOS ARM64.
261278
//
262279
// The Problem:
@@ -1814,6 +1831,7 @@ fn rocRun(ctx: *CliCtx, args: cli_args.RunArgs) anyerror!void {
18141831
host_input_paths.items,
18151832
try hostedSymbolsFromLir(ctx.arena, &view.store),
18161833
target_name,
1834+
false,
18171835
);
18181836
}
18191837

@@ -3570,13 +3588,9 @@ fn rocBuild(ctx: *CliCtx, args: cli_args.BuildArgs) anyerror!void {
35703588
return;
35713589
}
35723590

3573-
// Headerless apps use a simple builtin platform and cannot be compiled
3591+
// Headerless apps build through a synthetic default platform.
35743592
if (try readDefaultAppSource(ctx, args.path)) |source| {
3575-
ctx.gpa.free(source);
3576-
try renderProblem(ctx.gpa, ctx.io.stderr(), .{
3577-
.build_not_supported_for_headerless = .{ .app_path = args.path },
3578-
});
3579-
return error.UnsupportedTarget;
3593+
return rocBuildDefaultApp(ctx, args, source);
35803594
}
35813595

35823596
// Select build path based on optimization level
@@ -3587,6 +3601,63 @@ fn rocBuild(ctx: *CliCtx, args: cli_args.BuildArgs) anyerror!void {
35873601
}
35883602
}
35893603

3604+
fn rocBuildDefaultApp(ctx: *CliCtx, args: cli_args.BuildArgs, original_source: []const u8) anyerror!void {
3605+
defer ctx.gpa.free(original_source);
3606+
3607+
const temp_dir = createUniqueTempDir(ctx) catch |err| {
3608+
return ctx.fail(.{ .temp_dir_failed = .{ .err = err } });
3609+
};
3610+
defer std.Io.Dir.cwd().deleteTree(ctx.io.std_io, temp_dir) catch {};
3611+
3612+
const platform_dir = try std.fs.path.join(ctx.arena, &.{ temp_dir, ".roc_echo_platform" });
3613+
try std.Io.Dir.cwd().createDirPath(ctx.io.std_io, platform_dir);
3614+
3615+
const app_filename = std.fs.path.basename(args.path);
3616+
const app_path = try std.fs.path.join(ctx.arena, &.{ temp_dir, app_filename });
3617+
const platform_main_path = try std.fs.path.join(ctx.arena, &.{ platform_dir, "main.roc" });
3618+
const echo_module_path = try std.fs.path.join(ctx.arena, &.{ platform_dir, "Echo.roc" });
3619+
3620+
const header =
3621+
"app [main!] { pf: platform \"./.roc_echo_platform/main.roc\" }\n\n" ++
3622+
"import pf.Echo\n\n" ++
3623+
"echo! = |msg| Echo.line!(msg)\n\n";
3624+
const synthetic_source = try std.mem.concat(ctx.gpa, u8, &.{ header, original_source });
3625+
defer ctx.gpa.free(synthetic_source);
3626+
3627+
try std.Io.Dir.cwd().writeFile(ctx.io.std_io, .{ .sub_path = app_path, .data = synthetic_source });
3628+
try std.Io.Dir.cwd().writeFile(ctx.io.std_io, .{ .sub_path = platform_main_path, .data = defaultBuildPlatformSource(args) });
3629+
try std.Io.Dir.cwd().writeFile(ctx.io.std_io, .{ .sub_path = echo_module_path, .data = echo_platform.echo_module_source });
3630+
3631+
var synthetic_args = args;
3632+
synthetic_args.path = app_path;
3633+
synthetic_args.synthetic_default_platform = true;
3634+
if (synthetic_args.output == null) {
3635+
synthetic_args.output = try base.module_path.getModuleNameAlloc(ctx.arena, args.path);
3636+
}
3637+
3638+
switch (synthetic_args.opt) {
3639+
.dev => try rocBuildNative(ctx, synthetic_args),
3640+
.interpreter => try rocBuildEmbedded(ctx, synthetic_args),
3641+
.size, .speed => try rocBuildLlvm(ctx, synthetic_args),
3642+
}
3643+
}
3644+
3645+
fn defaultBuildPlatformSource(args: cli_args.BuildArgs) []const u8 {
3646+
const target = if (args.target) |target_str|
3647+
RocTarget.fromString(target_str)
3648+
else
3649+
RocTarget.detectNative();
3650+
3651+
if (target) |roc_build_target| {
3652+
return switch (roc_build_target.toOsTag()) {
3653+
.macos, .windows => echo_platform.build_c_platform_main_source,
3654+
else => echo_platform.build_platform_main_source,
3655+
};
3656+
}
3657+
3658+
return echo_platform.build_platform_main_source;
3659+
}
3660+
35903661
/// Build using the dev backend to generate native machine code.
35913662
/// This produces truly compiled executables without an interpreter.
35923663
fn nativeBuildEntrypoints(
@@ -3764,7 +3835,12 @@ fn verifyHostInputSymbols(
37643835
host_input_paths: []const []const u8,
37653836
hosted_symbols: []const []const u8,
37663837
target_name: []const u8,
3838+
synthetic_default_platform: bool,
37673839
) anyerror!void {
3840+
if (host_input_paths.len == 0 and synthetic_default_platform) {
3841+
return;
3842+
}
3843+
37683844
var needed = std.ArrayList([]const u8).empty;
37693845
try needed.appendSlice(ctx.arena, &host_symbols.runtime_symbols);
37703846
try needed.appendSlice(ctx.arena, hosted_symbols);
@@ -3778,6 +3854,16 @@ fn verifyHostInputSymbols(
37783854
}
37793855
}
37803856

3857+
fn writeDefaultPlatformRuntimeObject(ctx: *CliCtx, build_cache_dir: []const u8, target: RocTarget) anyerror!?[]const u8 {
3858+
const bytes = DefaultPlatformRuntimeObjects.forTarget(target) orelse return null;
3859+
const runtime_path = try std.fs.path.join(ctx.arena, &.{ build_cache_dir, DefaultPlatformRuntimeObjects.filename() });
3860+
backend.writeFileWindowsAvSafe(ctx.io.std_io, runtime_path, bytes) catch |err| {
3861+
std.log.err("Failed to write default platform runtime object {s}: {}", .{ runtime_path, err });
3862+
return err;
3863+
};
3864+
return runtime_path;
3865+
}
3866+
37813867
/// The host inputs of a link, in link order.
37823868
fn hostInputPaths(ctx: *CliCtx, link_inputs: PlatformLinkInputs) std.mem.Allocator.Error![]const []const u8 {
37833869
var paths = try std.array_list.Managed([]const u8).initCapacity(
@@ -4459,6 +4545,8 @@ fn compileLlvmAppObject(
44594545
link_type: roc_target.OutputKind,
44604546
lowered: *const lir.CheckedPipeline.LoweredProgram,
44614547
entrypoints: []const backend.Entrypoint,
4548+
enable_default_platform_runtime: bool,
4549+
enable_default_platform_hosted_calls: bool,
44624550
) anyerror!LlvmObjectPaths {
44634551
const std_target = try stdTargetForLlvmBuild(ctx, target);
44644552
const llvm_cpu = llvmCpuNameForTarget(std_target);
@@ -4471,6 +4559,9 @@ fn compileLlvmAppObject(
44714559
);
44724560
codegen.layout_store = &lowered.lir_result.layouts;
44734561
codegen.emit_debug_info = true;
4562+
codegen.enable_default_platform_runtime = enable_default_platform_runtime;
4563+
codegen.enable_default_platform_hosted_calls = enable_default_platform_hosted_calls;
4564+
codegen.enable_default_platform_diagnostics = enable_default_platform_hosted_calls and args.debug;
44744565
codegen.debug_producer = "roc " ++ build_options.compiler_version;
44754566
defer codegen.deinit();
44764567

@@ -4624,7 +4715,7 @@ fn rocBuildWasmLlvm(
46244715
unreachable;
46254716
}
46264717

4627-
const app_object = try compileLlvmAppObject(ctx, args, build_cache_dir, .wasm32, link_type, lowered, entrypoints);
4718+
const app_object = try compileLlvmAppObject(ctx, args, build_cache_dir, .wasm32, link_type, lowered, entrypoints, false, false);
46284719

46294720
var owned_inputs: std.ArrayList([]u8) = .empty;
46304721
defer freeOwnedWasmInputs(ctx, &owned_inputs);
@@ -4890,6 +4981,7 @@ fn rocBuildLlvm(ctx: *CliCtx, args: cli_args.BuildArgs) anyerror!void {
48904981
.{
48914982
.target_usize = target_usize,
48924983
.list_in_place_map = listInPlaceMapForOpt(args.opt),
4984+
.proc_debug_names = args.synthetic_default_platform,
48934985
},
48944986
);
48954987
defer lowered.deinit();
@@ -4929,7 +5021,20 @@ fn rocBuildLlvm(ctx: *CliCtx, args: cli_args.BuildArgs) anyerror!void {
49295021
static_data_exports,
49305022
);
49315023
} else {
4932-
const app_object = try compileLlvmAppObject(ctx, args, build_cache_dir, target, link_type, &lowered, entrypoints);
5024+
const hosted_symbols = try hostedSymbolsFromLir(ctx.arena, &lowered.lir_result.store);
5025+
const enable_default_platform_runtime = target_os == .linux and args.synthetic_default_platform;
5026+
5027+
const app_object = try compileLlvmAppObject(
5028+
ctx,
5029+
args,
5030+
build_cache_dir,
5031+
target,
5032+
link_type,
5033+
&lowered,
5034+
entrypoints,
5035+
enable_default_platform_runtime,
5036+
args.synthetic_default_platform,
5037+
);
49335038

49345039
var static_data_obj_path: ?[]const u8 = null;
49355040
if (static_data_exports.len > 0) {
@@ -4952,15 +5057,23 @@ fn rocBuildLlvm(ctx: *CliCtx, args: cli_args.BuildArgs) anyerror!void {
49525057
if (static_data_obj_path) |path| {
49535058
try object_files.append(path);
49545059
}
5060+
if (enable_default_platform_runtime) {
5061+
if (try writeDefaultPlatformRuntimeObject(ctx, build_cache_dir, target)) |runtime_path| {
5062+
try object_files.append(runtime_path);
5063+
} else {
5064+
return error.UnsupportedTarget;
5065+
}
5066+
}
49555067

49565068
if (link_type == .archive) {
49575069
try writeArchiveOutput(ctx, target, final_output_path, link_inputs, object_files.items);
49585070
} else {
49595071
try verifyHostInputSymbols(
49605072
ctx,
49615073
try hostInputPaths(ctx, link_inputs),
4962-
try hostedSymbolsFromLir(ctx.arena, &lowered.lir_result.store),
5074+
hosted_symbols,
49635075
link_inputs.target_name,
5076+
args.synthetic_default_platform,
49645077
);
49655078

49665079
const force_undefined_symbols = try staticDataLinkRootSymbols(ctx, static_data_exports);
@@ -5179,6 +5292,7 @@ fn rocBuildNative(ctx: *CliCtx, args: cli_args.BuildArgs) anyerror!void {
51795292
.target_usize = target_usize,
51805293
.inline_mode = postCheckInlineModeForOpt(args.opt),
51815294
.list_in_place_map = listInPlaceMapForOpt(args.opt),
5295+
.proc_debug_names = args.synthetic_default_platform,
51825296
},
51835297
);
51845298
defer lowered.deinit();
@@ -5292,6 +5406,7 @@ fn rocBuildNative(ctx: *CliCtx, args: cli_args.BuildArgs) anyerror!void {
52925406
try hostInputPaths(ctx, link_inputs),
52935407
try hostedSymbolsFromLir(ctx.arena, &lowered.lir_result.store),
52945408
link_inputs.target_name,
5409+
false,
52955410
);
52965411

52975412
const force_undefined_symbols = try staticDataLinkRootSymbols(ctx, static_data_exports);
117 KB
Binary file not shown.

0 commit comments

Comments
 (0)