diff --git a/bootstrap/Compilation.zig b/bootstrap/Compilation.zig index 3f9a3f8..798b6a6 100644 --- a/bootstrap/Compilation.zig +++ b/bootstrap/Compilation.zig @@ -87,6 +87,7 @@ pub fn compileBuildExecutable(context: *const Context, arguments: [][*:0]u8) !vo .generate_debug_information = true, .name = "build", .is_test = false, + .c_source_files = &.{}, }, }; @@ -102,13 +103,18 @@ pub fn compileBuildExecutable(context: *const Context, arguments: [][*:0]u8) !vo else => false, }; if (!success) { - // std.debug.print("The following command terminated with failure ({s}): {s}\n", .{ @tagName(result.term), argv }); - // if (result.stdout.len > 0) { - // std.debug.print("STDOUT:\n{s}\n", .{result.stdout}); - // } - // if (result.stderr.len > 0) { - // std.debug.print("STDOUT:\n{s}\n", .{result.stderr}); - // } + try write(.panic, "The following command terminated with failure: ("); + try write(.panic, @tagName(result.term)); + try write(.panic, "):\n"); + for (argv) |arg| { + try write(.panic, arg); + try write(.panic, ", "); + } + try write(.panic, "\n"); + try write(.panic, result.stdout); + try write(.panic, "\n"); + try write(.panic, result.stderr); + try write(.panic, "\n"); std.os.abort(); } } @@ -2181,6 +2187,7 @@ pub fn buildExecutable(context: *const Context, arguments: [][*:0]u8, options: E var maybe_only_parse: ?bool = null; var link_libc = false; var maybe_executable_name: ?[]const u8 = null; + var c_source_files = UnpinnedArray([*:0]u8){}; const generate_debug_information = true; if (arguments.len == 0) return error.InvalidInput; @@ -2287,6 +2294,17 @@ pub fn buildExecutable(context: *const Context, arguments: [][*:0]u8, options: E } else { reportUnterminatedArgumentError(current_argument); } + } else if (byte_equal(current_argument, "-c_source_files")) { + if (i + 1 != arguments.len) { + i += 1; + + try c_source_files.ensure_capacity(context.my_allocator, @intCast(arguments.len - i)); + while (i < arguments.len) : (i += 1) { + c_source_files.append_with_capacity(arguments[i]); + } + } else { + reportUnterminatedArgumentError(current_argument); + } } else { @panic(current_argument); // std.debug.panic("Unrecognized argument: {s}", .{current_argument}); @@ -2328,6 +2346,7 @@ pub fn buildExecutable(context: *const Context, arguments: [][*:0]u8, options: E .generate_debug_information = generate_debug_information, .name = executable_name, .is_test = options.is_test, + .c_source_files = c_source_files.slice(), }, }; @@ -9886,7 +9905,7 @@ pub const Builder = struct { break :blk switch (result.value) { .@"comptime" => |ct| switch (ct) { .global => |global| switch (global.initial_value) { - .function_definition => .{ + .function_definition, .function_declaration => .{ .value = .{ .@"comptime" = .{ .global = global, @@ -12163,6 +12182,7 @@ pub const Unit = struct { main_package: ?*Package = null, all_errors: Type.Index = .null, descriptor: Descriptor, + object_files: UnpinnedArray([*:0]const u8) = .{}, discard_identifiers: usize = 0, anon_i: usize = 0, anon_arr: usize = 0, @@ -12885,6 +12905,20 @@ pub const Unit = struct { } if (!unit.descriptor.only_parse) { + try unit.object_files.ensure_capacity(context.my_allocator, @intCast(unit.descriptor.c_source_files.len)); + for (unit.descriptor.c_source_files) |c_source_file| { + const c_src = span(c_source_file); + const dot_index = data_structures.last(c_src, '.') orelse unreachable; + const path_without_extension = c_src[0..dot_index]; + const basename = std.fs.path.basename(path_without_extension); + const basename_z = try std.mem.joinZ(context.allocator, "/", &.{"nat", basename}); + var c_flag = "-c".*; + var o_flag = "-o".*; + var arguments = [_][*:0]u8{&c_flag, c_source_file, &o_flag, basename_z}; + try compileCSourceFile(context, &arguments); + unit.object_files.append_with_capacity(basename_z); + } + try unit.analyze(context); try llvm.codegen(unit, context); @@ -12938,6 +12972,7 @@ pub const Descriptor = struct { link_libc: bool, is_test: bool, generate_debug_information: bool, + c_source_files: []const [*:0]u8, name: []const u8, }; diff --git a/bootstrap/backend/llvm.zig b/bootstrap/backend/llvm.zig index c8b56ec..b7e9bf5 100644 --- a/bootstrap/backend/llvm.zig +++ b/bootstrap/backend/llvm.zig @@ -3062,6 +3062,11 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo try arguments.append(context.my_allocator, object_file_path.ptr); + try arguments.append_slice(context.my_allocator, unit.object_files.slice()); + // for (unit.object_files.slice()) |object_file| { + // _ = object_file; // autofix + // } + switch (unit.descriptor.os) { .macos => { try arguments.append(context.my_allocator, "-dynamic"); @@ -3126,13 +3131,14 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo } if (!linking_result) { + try write(.panic, "\n"); for (arguments.slice()) |argument| { const arg = data_structures.span(argument); - try write(.llvm, arg); - try write(.llvm, " "); + try write(.panic, arg); + try write(.panic, " "); } - try write(.llvm, "\n"); + try write(.panic, "\n"); - @panic("Above linker invokation failed"); + @panic(stderr_ptr[0..stderr_len]); } } diff --git a/bootstrap/library.zig b/bootstrap/library.zig index ac245ac..4b98986 100644 --- a/bootstrap/library.zig +++ b/bootstrap/library.zig @@ -723,3 +723,16 @@ pub fn span(ptr: [*:0]const u8) [:0]const u8 { } return ptr[0..len :0]; } + +pub fn last(bytes: []const u8, byte: u8) ?usize { + var i = bytes.len; + while (i > 0) { + i -= 1; + + if (bytes[i] == byte) { + return i; + } + } + + return null; +} diff --git a/bootstrap/main.zig b/bootstrap/main.zig index 72d6b3d..f2eeb6c 100644 --- a/bootstrap/main.zig +++ b/bootstrap/main.zig @@ -29,10 +29,19 @@ pub export fn main(c_argc: c_int, c_argv: [*][*:0]c_char, c_envp: [*:null]?[*:0] if (entry_point(arguments)) |_| { return 0; } else |err| { - const error_name: []const u8 = @errorName(err); - Compilation.write(.panic, "Error: ") catch {}; - Compilation.write(.panic, error_name) catch {}; - Compilation.write(.panic, "\n") catch {}; + const print_stack_trace = @import("configuration").print_stack_trace; + switch (print_stack_trace) { + true => if (@errorReturnTrace()) |trace| { + std.debug.dumpStackTrace(trace.*); + }, + false => { + const error_name: []const u8 = @errorName(err); + Compilation.write(.panic, "Error: ") catch {}; + Compilation.write(.panic, error_name) catch {}; + Compilation.write(.panic, "\n") catch {}; + }, + } + return 1; } } diff --git a/lib/std/build.nat b/lib/std/build.nat index fd9fc65..f8ad4f2 100644 --- a/lib/std/build.nat +++ b/lib/std/build.nat @@ -8,7 +8,7 @@ const Executable = struct{ main_source_path: [:0]const u8, link_libc: bool = false, name: [:0]const u8, - c_source_files: []const []const u8 = .{}.&, + c_source_files: []const [:0]const u8 = .{}.&, const compile = fn(executable: Executable) *!void { const argument_count = std.start.argument_count; @@ -35,8 +35,15 @@ const Executable = struct{ } else { link_libc_arg = "false"; } - const argv = [_:null] ?[&:0]const u8{ compiler_path, "exe", "-main_source_file", executable.main_source_path.ptr, "-link_libc", link_libc_arg, "-name", executable.name.ptr }; - try std.os.execute(path = compiler_path, argv = argv.&, env = std.start.environment_values); + + if (executable.c_source_files.len > 0) { + assert(executable.c_source_files.len == 1); + const argv = [_:null] ?[&:0]const u8{ compiler_path, "exe", "-main_source_file", executable.main_source_path.ptr, "-link_libc", link_libc_arg, "-name", executable.name.ptr, "-c_source_files", executable.c_source_files[0].ptr }; + try std.os.execute(path = compiler_path, argv = argv.&, env = std.start.environment_values); + } else { + const argv = [_:null] ?[&:0]const u8{ compiler_path, "exe", "-main_source_file", executable.main_source_path.ptr, "-link_libc", link_libc_arg, "-name", executable.name.ptr }; + try std.os.execute(path = compiler_path, argv = argv.&, env = std.start.environment_values); + } } else { const raw_status = try std.os.waitpid(pid, flags = 0); if (std.os.ifexited(status = raw_status)) { diff --git a/lib/std/testing.nat b/lib/std/testing.nat index d910872..289377e 100644 --- a/lib/std/testing.nat +++ b/lib/std/testing.nat @@ -2,7 +2,7 @@ const Error = error{ unexpected_result, }; -const expect(ok: bool) Error!void { +const expect = fn (ok: bool) Error!void { if (!ok) { return Error.unexpected_result; } diff --git a/test/c-abi/first/build.nat b/test/build/c-abi/build.nat similarity index 72% rename from test/c-abi/first/build.nat rename to test/build/c-abi/build.nat index 5a3fda9..6afe512 100644 --- a/test/c-abi/first/build.nat +++ b/test/build/c-abi/build.nat @@ -1,5 +1,5 @@ const std = #import("std"); -const build = std.build; +const Executable = std.build.Executable; const main = fn() *!void { const executable = Executable{ @@ -9,7 +9,8 @@ const main = fn() *!void { .abi = .gnu, }, .main_source_path = "main.nat", - .name = "first", + .name = "c-abi", + .c_source_files = .{ "foo.c" }.&, }; try executable.compile(); diff --git a/test/build/c-abi/foo.c b/test/build/c-abi/foo.c new file mode 100644 index 0000000..6c420c2 --- /dev/null +++ b/test/build/c-abi/foo.c @@ -0,0 +1,5 @@ +typedef unsigned u32; +u32 foo(u32 arg) +{ + return arg; +} diff --git a/test/c-abi/first/main.nat b/test/build/c-abi/main.nat similarity index 81% rename from test/c-abi/first/main.nat rename to test/build/c-abi/main.nat index e2d2017..2d7b711 100644 --- a/test/c-abi/first/main.nat +++ b/test/build/c-abi/main.nat @@ -1,5 +1,5 @@ const std = #import("std"); -const expect = std.test.expect; +const expect = std.testing.expect; const foo :: extern = fn(arg: u32) u32; const main = fn () *!void { const arg = 0xabcdef;