Implement first C ABI test

This commit is contained in:
David Gonzalez Martin 2024-03-14 08:09:05 -06:00
parent fd859efc42
commit 3f0e8079eb
9 changed files with 99 additions and 23 deletions

View File

@ -87,6 +87,7 @@ pub fn compileBuildExecutable(context: *const Context, arguments: [][*:0]u8) !vo
.generate_debug_information = true, .generate_debug_information = true,
.name = "build", .name = "build",
.is_test = false, .is_test = false,
.c_source_files = &.{},
}, },
}; };
@ -102,13 +103,18 @@ pub fn compileBuildExecutable(context: *const Context, arguments: [][*:0]u8) !vo
else => false, else => false,
}; };
if (!success) { if (!success) {
// std.debug.print("The following command terminated with failure ({s}): {s}\n", .{ @tagName(result.term), argv }); try write(.panic, "The following command terminated with failure: (");
// if (result.stdout.len > 0) { try write(.panic, @tagName(result.term));
// std.debug.print("STDOUT:\n{s}\n", .{result.stdout}); try write(.panic, "):\n");
// } for (argv) |arg| {
// if (result.stderr.len > 0) { try write(.panic, arg);
// std.debug.print("STDOUT:\n{s}\n", .{result.stderr}); 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(); 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 maybe_only_parse: ?bool = null;
var link_libc = false; var link_libc = false;
var maybe_executable_name: ?[]const u8 = null; var maybe_executable_name: ?[]const u8 = null;
var c_source_files = UnpinnedArray([*:0]u8){};
const generate_debug_information = true; const generate_debug_information = true;
if (arguments.len == 0) return error.InvalidInput; if (arguments.len == 0) return error.InvalidInput;
@ -2287,6 +2294,17 @@ pub fn buildExecutable(context: *const Context, arguments: [][*:0]u8, options: E
} else { } else {
reportUnterminatedArgumentError(current_argument); 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 { } else {
@panic(current_argument); @panic(current_argument);
// std.debug.panic("Unrecognized argument: {s}", .{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, .generate_debug_information = generate_debug_information,
.name = executable_name, .name = executable_name,
.is_test = options.is_test, .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) { break :blk switch (result.value) {
.@"comptime" => |ct| switch (ct) { .@"comptime" => |ct| switch (ct) {
.global => |global| switch (global.initial_value) { .global => |global| switch (global.initial_value) {
.function_definition => .{ .function_definition, .function_declaration => .{
.value = .{ .value = .{
.@"comptime" = .{ .@"comptime" = .{
.global = global, .global = global,
@ -12163,6 +12182,7 @@ pub const Unit = struct {
main_package: ?*Package = null, main_package: ?*Package = null,
all_errors: Type.Index = .null, all_errors: Type.Index = .null,
descriptor: Descriptor, descriptor: Descriptor,
object_files: UnpinnedArray([*:0]const u8) = .{},
discard_identifiers: usize = 0, discard_identifiers: usize = 0,
anon_i: usize = 0, anon_i: usize = 0,
anon_arr: usize = 0, anon_arr: usize = 0,
@ -12885,6 +12905,20 @@ pub const Unit = struct {
} }
if (!unit.descriptor.only_parse) { 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 unit.analyze(context);
try llvm.codegen(unit, context); try llvm.codegen(unit, context);
@ -12938,6 +12972,7 @@ pub const Descriptor = struct {
link_libc: bool, link_libc: bool,
is_test: bool, is_test: bool,
generate_debug_information: bool, generate_debug_information: bool,
c_source_files: []const [*:0]u8,
name: []const u8, name: []const u8,
}; };

View File

@ -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(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) { switch (unit.descriptor.os) {
.macos => { .macos => {
try arguments.append(context.my_allocator, "-dynamic"); 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) { if (!linking_result) {
try write(.panic, "\n");
for (arguments.slice()) |argument| { for (arguments.slice()) |argument| {
const arg = data_structures.span(argument); const arg = data_structures.span(argument);
try write(.llvm, arg); try write(.panic, arg);
try write(.llvm, " "); try write(.panic, " ");
} }
try write(.llvm, "\n"); try write(.panic, "\n");
@panic("Above linker invokation failed"); @panic(stderr_ptr[0..stderr_len]);
} }
} }

View File

@ -723,3 +723,16 @@ pub fn span(ptr: [*:0]const u8) [:0]const u8 {
} }
return ptr[0..len :0]; 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;
}

View File

@ -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)) |_| { if (entry_point(arguments)) |_| {
return 0; return 0;
} else |err| { } else |err| {
const error_name: []const u8 = @errorName(err); const print_stack_trace = @import("configuration").print_stack_trace;
Compilation.write(.panic, "Error: ") catch {}; switch (print_stack_trace) {
Compilation.write(.panic, error_name) catch {}; true => if (@errorReturnTrace()) |trace| {
Compilation.write(.panic, "\n") catch {}; 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; return 1;
} }
} }

View File

@ -8,7 +8,7 @@ const Executable = struct{
main_source_path: [:0]const u8, main_source_path: [:0]const u8,
link_libc: bool = false, link_libc: bool = false,
name: [:0]const u8, name: [:0]const u8,
c_source_files: []const []const u8 = .{}.&, c_source_files: []const [:0]const u8 = .{}.&,
const compile = fn(executable: Executable) *!void { const compile = fn(executable: Executable) *!void {
const argument_count = std.start.argument_count; const argument_count = std.start.argument_count;
@ -35,8 +35,15 @@ const Executable = struct{
} else { } else {
link_libc_arg = "false"; 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 { } else {
const raw_status = try std.os.waitpid(pid, flags = 0); const raw_status = try std.os.waitpid(pid, flags = 0);
if (std.os.ifexited(status = raw_status)) { if (std.os.ifexited(status = raw_status)) {

View File

@ -2,7 +2,7 @@ const Error = error{
unexpected_result, unexpected_result,
}; };
const expect(ok: bool) Error!void { const expect = fn (ok: bool) Error!void {
if (!ok) { if (!ok) {
return Error.unexpected_result; return Error.unexpected_result;
} }

View File

@ -1,5 +1,5 @@
const std = #import("std"); const std = #import("std");
const build = std.build; const Executable = std.build.Executable;
const main = fn() *!void { const main = fn() *!void {
const executable = Executable{ const executable = Executable{
@ -9,7 +9,8 @@ const main = fn() *!void {
.abi = .gnu, .abi = .gnu,
}, },
.main_source_path = "main.nat", .main_source_path = "main.nat",
.name = "first", .name = "c-abi",
.c_source_files = .{ "foo.c" }.&,
}; };
try executable.compile(); try executable.compile();

5
test/build/c-abi/foo.c Normal file
View File

@ -0,0 +1,5 @@
typedef unsigned u32;
u32 foo(u32 arg)
{
return arg;
}

View File

@ -1,5 +1,5 @@
const std = #import("std"); const std = #import("std");
const expect = std.test.expect; const expect = std.testing.expect;
const foo :: extern = fn(arg: u32) u32; const foo :: extern = fn(arg: u32) u32;
const main = fn () *!void { const main = fn () *!void {
const arg = 0xabcdef; const arg = 0xabcdef;