Make build work

This commit is contained in:
David Gonzalez Martin 2024-02-20 22:55:34 -06:00
parent 30c56e317e
commit 6ae42e05c5
17 changed files with 530 additions and 236 deletions

View File

@ -36,7 +36,7 @@ fn reportUnterminatedArgumentError(string: []const u8) noreturn {
std.debug.panic("Unterminated argument: {s}", .{string}); std.debug.panic("Unterminated argument: {s}", .{string});
} }
pub fn buildExecutable(allocator: Allocator, arguments: [][:0]u8) !void { pub fn createContext(allocator: Allocator) !*const Context{
const context: *Context = try allocator.create(Context); const context: *Context = try allocator.create(Context);
const self_exe_path = try std.fs.selfExePathAlloc(allocator); const self_exe_path = try std.fs.selfExePathAlloc(allocator);
@ -52,6 +52,42 @@ pub fn buildExecutable(allocator: Allocator, arguments: [][:0]u8) !void {
try context.build_directory.makePath(cache_dir_name); try context.build_directory.makePath(cache_dir_name);
try context.build_directory.makePath(installation_dir_name); try context.build_directory.makePath(installation_dir_name);
return context;
}
pub fn compileBuildExecutable(context: *const Context, arguments: [][:0]u8) !void {
_ = arguments; // autofix
const unit = try context.allocator.create(Unit);
const target_query = try std.Target.Query.parse(.{});
const target = try std.zig.system.resolveTargetQuery(target_query);
unit.* = .{
.descriptor = .{
.main_package_path = "build.nat",
.target = target,
.only_parse = false,
.executable_path = "nat/build",
.link_libc = @import("builtin").os.tag == .macos,
.generate_debug_information = true,
.name = "build",
},
};
try unit.compile(context);
const result = try std.ChildProcess.run(.{
.allocator = context.allocator,
.argv = &.{ "nat/build", "-compiler_path", context.executable_absolute_path },
});
switch (result.term) {
.Exited => |exit_code| {
if (exit_code != 0) @panic("Bad exit code");
},
.Signal => @panic("Signaled"),
.Stopped => @panic("Stopped"),
.Unknown => @panic("Unknown"),
}
}
pub fn buildExecutable(context: *const Context, arguments: [][:0]u8) !void {
var maybe_executable_path: ?[]const u8 = null; var maybe_executable_path: ?[]const u8 = null;
var maybe_main_package_path: ?[]const u8 = null; var maybe_main_package_path: ?[]const u8 = null;
var target_triplet: []const u8 = switch (@import("builtin").os.tag) { var target_triplet: []const u8 = switch (@import("builtin").os.tag) {
@ -63,121 +99,112 @@ pub fn buildExecutable(allocator: Allocator, arguments: [][:0]u8) !void {
var link_libc = false; var link_libc = false;
var maybe_executable_name: ?[]const u8 = null; var maybe_executable_name: ?[]const u8 = null;
const generate_debug_information = true; const generate_debug_information = true;
var is_build = false;
if (arguments.len == 0) { if (arguments.len == 0) return error.InvalidInput;
is_build = true;
} else if (equal(u8, arguments[0], "init")) {
if (arguments.len == 1) {
unreachable;
} else {
@panic("Init does not take arguments");
}
} else {
var i: usize = 0;
while (i < arguments.len) : (i += 1) {
const current_argument = arguments[i];
if (equal(u8, current_argument, "-o")) {
if (i + 1 != arguments.len) {
maybe_executable_path = arguments[i + 1];
assert(maybe_executable_path.?.len != 0);
i += 1;
} else {
reportUnterminatedArgumentError(current_argument);
}
} else if (equal(u8, current_argument, "-target")) {
if (i + 1 != arguments.len) {
target_triplet = arguments[i + 1];
i += 1;
} else {
reportUnterminatedArgumentError(current_argument);
}
} else if (equal(u8, current_argument, "-log")) {
if (i + 1 != arguments.len) {
i += 1;
var log_argument_iterator = std.mem.splitScalar(u8, arguments[i], ','); var i: usize = 0;
while (i < arguments.len) : (i += 1) {
const current_argument = arguments[i];
if (equal(u8, current_argument, "-o")) {
if (i + 1 != arguments.len) {
maybe_executable_path = arguments[i + 1];
assert(maybe_executable_path.?.len != 0);
i += 1;
} else {
reportUnterminatedArgumentError(current_argument);
}
} else if (equal(u8, current_argument, "-target")) {
if (i + 1 != arguments.len) {
target_triplet = arguments[i + 1];
i += 1;
} else {
reportUnterminatedArgumentError(current_argument);
}
} else if (equal(u8, current_argument, "-log")) {
if (i + 1 != arguments.len) {
i += 1;
while (log_argument_iterator.next()) |log_argument| { var log_argument_iterator = std.mem.splitScalar(u8, arguments[i], ',');
var log_argument_splitter = std.mem.splitScalar(u8, log_argument, '.');
const log_scope_candidate = log_argument_splitter.next() orelse unreachable;
var recognized_scope = false;
inline for (@typeInfo(LoggerScope).Enum.fields) |logger_scope_enum_field| { while (log_argument_iterator.next()) |log_argument| {
const log_scope = @field(LoggerScope, logger_scope_enum_field.name); var log_argument_splitter = std.mem.splitScalar(u8, log_argument, '.');
const log_scope_candidate = log_argument_splitter.next() orelse unreachable;
var recognized_scope = false;
if (equal(u8, @tagName(log_scope), log_scope_candidate)) { inline for (@typeInfo(LoggerScope).Enum.fields) |logger_scope_enum_field| {
const LogScope = getLoggerScopeType(log_scope); const log_scope = @field(LoggerScope, logger_scope_enum_field.name);
if (log_argument_splitter.next()) |particular_log_candidate| { if (equal(u8, @tagName(log_scope), log_scope_candidate)) {
var recognized_particular = false; const LogScope = getLoggerScopeType(log_scope);
inline for (@typeInfo(LogScope.Logger).Enum.fields) |particular_log_field| {
const particular_log = @field(LogScope.Logger, particular_log_field.name);
if (equal(u8, particular_log_candidate, @tagName(particular_log))) { if (log_argument_splitter.next()) |particular_log_candidate| {
LogScope.Logger.bitset.setPresent(particular_log, true); var recognized_particular = false;
recognized_particular = true; inline for (@typeInfo(LogScope.Logger).Enum.fields) |particular_log_field| {
} const particular_log = @field(LogScope.Logger, particular_log_field.name);
} else if (!recognized_particular) std.debug.panic("Unrecognized particular log \"{s}\" in scope {s}", .{ particular_log_candidate, @tagName(log_scope) });
} else {
// LogScope.Logger.bitset = @TypeOf(LogScope.Logger.bitset).initFull();
}
logger_bitset.setPresent(log_scope, true); if (equal(u8, particular_log_candidate, @tagName(particular_log))) {
LogScope.Logger.bitset.setPresent(particular_log, true);
recognized_scope = true; recognized_particular = true;
}
} else if (!recognized_particular) std.debug.panic("Unrecognized particular log \"{s}\" in scope {s}", .{ particular_log_candidate, @tagName(log_scope) });
} else {
// LogScope.Logger.bitset = @TypeOf(LogScope.Logger.bitset).initFull();
} }
} else if (!recognized_scope) std.debug.panic("Unrecognized log scope: {s}", .{log_scope_candidate});
}
} else {
reportUnterminatedArgumentError(current_argument);
}
} else if (equal(u8, current_argument, "-parse")) {
if (i + 1 != arguments.len) {
i += 1;
const arg = arguments[i]; logger_bitset.setPresent(log_scope, true);
maybe_main_package_path = arg;
maybe_only_parse = true;
} else {
reportUnterminatedArgumentError(current_argument);
}
} else if (equal(u8, current_argument, "-link_libc")) {
if (i + 1 != arguments.len) {
i += 1;
const arg = arguments[i]; recognized_scope = true;
if (std.mem.eql(u8, arg, "true")) { }
link_libc = true; } else if (!recognized_scope) std.debug.panic("Unrecognized log scope: {s}", .{log_scope_candidate});
} else if (std.mem.eql(u8, arg, "false")) {
link_libc = false;
} else {
unreachable;
}
} else {
reportUnterminatedArgumentError(current_argument);
}
} else if (equal(u8, current_argument, "-main_source_file")) {
if (i + 1 != arguments.len) {
i += 1;
const arg = arguments[i];
maybe_main_package_path = arg;
} else {
reportUnterminatedArgumentError(current_argument);
}
} else if (equal(u8, current_argument, "-name")) {
if (i + 1 != arguments.len) {
i += 1;
const arg = arguments[i];
maybe_executable_name = arg;
} else {
reportUnterminatedArgumentError(current_argument);
} }
} else { } else {
std.debug.panic("Unrecognized argument: {s}", .{current_argument}); reportUnterminatedArgumentError(current_argument);
} }
} else if (equal(u8, current_argument, "-parse")) {
if (i + 1 != arguments.len) {
i += 1;
const arg = arguments[i];
maybe_main_package_path = arg;
maybe_only_parse = true;
} else {
reportUnterminatedArgumentError(current_argument);
}
} else if (equal(u8, current_argument, "-link_libc")) {
if (i + 1 != arguments.len) {
i += 1;
const arg = arguments[i];
if (std.mem.eql(u8, arg, "true")) {
link_libc = true;
} else if (std.mem.eql(u8, arg, "false")) {
link_libc = false;
} else {
unreachable;
}
} else {
reportUnterminatedArgumentError(current_argument);
}
} else if (equal(u8, current_argument, "-main_source_file")) {
if (i + 1 != arguments.len) {
i += 1;
const arg = arguments[i];
maybe_main_package_path = arg;
} else {
reportUnterminatedArgumentError(current_argument);
}
} else if (equal(u8, current_argument, "-name")) {
if (i + 1 != arguments.len) {
i += 1;
const arg = arguments[i];
maybe_executable_name = arg;
} else {
reportUnterminatedArgumentError(current_argument);
}
} else {
std.debug.panic("Unrecognized argument: {s}", .{current_argument});
} }
} }
@ -190,25 +217,13 @@ pub fn buildExecutable(allocator: Allocator, arguments: [][:0]u8) !void {
file.close(); file.close();
break :blk path; break :blk path;
} else blk: { } else unreachable;
const build_file = "build.nat";
const file = std.fs.cwd().openFile(build_file, .{}) catch return error.main_package_path_not_specified;
file.close();
is_build = true;
break :blk build_file; const executable_name = if (maybe_executable_name) |name| name else std.fs.path.basename(main_package_path[0 .. main_package_path.len - "/main.nat".len]);
};
const executable_name = if (is_build) b: {
assert(maybe_executable_name == null);
break :b "build";
} else b: {
break :b if (maybe_executable_name) |name| name else std.fs.path.basename(main_package_path[0 .. main_package_path.len - "/main.nat".len]);
};
const executable_path = maybe_executable_path orelse blk: { const executable_path = maybe_executable_path orelse blk: {
assert(executable_name.len > 0); assert(executable_name.len > 0);
const result = try std.mem.concat(allocator, u8, &.{ "nat/", executable_name }); const result = try std.mem.concat(context.allocator, u8, &.{ "nat/", executable_name });
break :blk result; break :blk result;
}; };
@ -218,7 +233,6 @@ pub fn buildExecutable(allocator: Allocator, arguments: [][:0]u8) !void {
.main_package_path = main_package_path, .main_package_path = main_package_path,
.executable_path = executable_path, .executable_path = executable_path,
.target = target, .target = target,
.is_build = is_build,
.only_parse = only_parse, .only_parse = only_parse,
.link_libc = switch (target.os.tag) { .link_libc = switch (target.os.tag) {
.linux => link_libc, .linux => link_libc,
@ -372,6 +386,12 @@ fn getTypeBitSize(ty: *Type, unit: *Unit) u32 {
} }
}, },
.pointer => 64, .pointer => 64,
.@"enum" => |enum_index| b: {
const enum_type = unit.enums.get(enum_index);
const backing_type = unit.types.get(enum_type.backing_type);
break :b getTypeBitSize(backing_type, unit);
},
.slice => 2 * @bitSizeOf(usize),
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
}; };
} }
@ -397,7 +417,7 @@ pub const Type = union(enum) {
fn getByteSize(ty: *Type, unit: *Unit) u32 { fn getByteSize(ty: *Type, unit: *Unit) u32 {
_ = unit; // autofix _ = unit; // autofix
return switch (ty.*) { return switch (ty.*) {
.integer => |integer| integer.bit_count, .integer => |integer| @divExact(integer.bit_count, @bitSizeOf(u8)),
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
}; };
} }
@ -1312,6 +1332,18 @@ pub const Builder = struct {
.pointer => |source_pointer| { .pointer => |source_pointer| {
if (destination_pointer.type == source_pointer.type) { if (destination_pointer.type == source_pointer.type) {
if (destination_pointer.mutability == source_pointer.mutability) { if (destination_pointer.mutability == source_pointer.mutability) {
if (destination_pointer.nullable != source_pointer.nullable) {
std.debug.print("Dst: {} Src: {}\n",.{destination_pointer.nullable, source_pointer.nullable});
if (destination_pointer.nullable) {
assert(destination_pointer.termination != source_pointer.termination);
unreachable;
} else {
unreachable;
}
}
if (destination_pointer.termination != source_pointer.termination) {
unreachable;
}
unreachable; unreachable;
} else { } else {
break :b .pointer_const_to_var; break :b .pointer_const_to_var;
@ -1435,6 +1467,7 @@ pub const Builder = struct {
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
}; };
}, },
.none => .usize,
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
}; };
@ -2255,10 +2288,16 @@ pub const Builder = struct {
return .pointer_to_nullable; return .pointer_to_nullable;
} }
} else {
if (destination_pointer.termination != .none) {
unreachable;
} else {
unreachable;
}
} }
} }
unreachable; std.debug.panic("Pointer unknown typecheck:\nDst: {}\n Src: {}", .{destination_pointer, source_pointer});
}, },
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
} }
@ -2387,27 +2426,20 @@ pub const Builder = struct {
} }
}, },
.array => |destination_array| { .array => |destination_array| {
switch (source.*) { switch (source.*) {
.array => |source_array| { .array => |source_array| {
assert(destination_array.type == source_array.type); assert(destination_array.type == source_array.type);
assert(destination_array.count == source_array.count); assert(destination_array.count == source_array.count);
if (destination_array.termination != source_array.termination) { if (destination_array.termination != source_array.termination) {
if (destination_array.termination == .none) { if (destination_array.termination == .none) {
unreachable; unreachable;
} else { } else {
std.debug.panic("Expected {s} array termination, got {s}", .{@tagName(destination_array.termination), @tagName(source_array.termination)}); std.debug.panic("Expected {s} array termination, got {s}", .{@tagName(destination_array.termination), @tagName(source_array.termination)});
} }
} else unreachable; } else unreachable;
}, },
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
} }
// .array => |array| switch (unit.types.get(array_type).*) {
// .array => |expected_array| {
// },
// else => |t| @panic(@tagName(t)),
// },
// else => |t| @panic(@tagName(t)),
// }
}, },
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
} }
@ -3928,6 +3960,19 @@ pub const Builder = struct {
const result = try builder.resolveContainerLiteral(unit, context, initialization_nodes, container_type_index); const result = try builder.resolveContainerLiteral(unit, context, initialization_nodes, container_type_index);
break :block result; break :block result;
}, },
.anonymous_container_literal => block: {
switch (type_expect) {
.type => |type_index| {
assert(node.left == .null);
assert(node.right != .null);
const initialization_nodes = unit.getNodeList(node.right);
const result = try builder.resolveContainerLiteral(unit, context, initialization_nodes, type_index);
break :block result;
},
else => |t| @panic(@tagName(t)),
}
unreachable;
},
.enum_literal => block: { .enum_literal => block: {
switch (type_expect) { switch (type_expect) {
.type => |type_index| { .type => |type_index| {
@ -5191,52 +5236,15 @@ pub const Builder = struct {
.type = gep_type, .type = gep_type,
}; };
}, },
.pointer => |child_pointer| switch (unit.types.get(child_pointer.type).*) { .pointer => |child_pointer| switch (child_pointer.many) {
.array => |array| b: { true => b: {
const load = try unit.instructions.append(context.allocator, .{ const load = try unit.instructions.append(context.allocator, .{
.load = .{ .load = .{
.value = array_like_expression, .value = array_like_expression,
.type = pointer.type, .type = pointer.type,
}, },
}); });
try builder.appendInstruction(unit, context, load); try builder.appendInstruction(unit, context, load);
const gep = try unit.instructions.append(context.allocator, .{
.get_element_pointer = .{
.pointer = load,
.base_type = array.type,
.is_struct = false,
.index = index,
},
});
try builder.appendInstruction(unit, context, gep);
const gep_type = try unit.getPointerType(context, .{
.type = array.type,
.termination = .none,
.mutability = pointer.mutability,
.many = false,
.nullable = false,
});
break :b .{
.value = .{
.runtime = gep,
},
.type = gep_type,
};
},
.integer => b: {
assert(child_pointer.many);
const load = try unit.instructions.append(context.allocator, .{
.load = .{
.value = array_like_expression,
.type = pointer.type,
},
});
try builder.appendInstruction(unit, context, load);
const gep = try unit.instructions.append(context.allocator, .{ const gep = try unit.instructions.append(context.allocator, .{
.get_element_pointer = .{ .get_element_pointer = .{
.pointer = load, .pointer = load,
@ -5249,8 +5257,8 @@ pub const Builder = struct {
const gep_type = try unit.getPointerType(context, .{ const gep_type = try unit.getPointerType(context, .{
.type = child_pointer.type, .type = child_pointer.type,
.termination = .none, .termination = child_pointer.termination,
.mutability = pointer.mutability, .mutability = child_pointer.mutability,
.many = false, .many = false,
.nullable = false, .nullable = false,
}); });
@ -5262,7 +5270,79 @@ pub const Builder = struct {
.type = gep_type, .type = gep_type,
}; };
}, },
else => |t| @panic(@tagName(t)), false => switch (unit.types.get(child_pointer.type).*) {
.array => |array| b: {
const load = try unit.instructions.append(context.allocator, .{
.load = .{
.value = array_like_expression,
.type = pointer.type,
},
});
try builder.appendInstruction(unit, context, load);
const gep = try unit.instructions.append(context.allocator, .{
.get_element_pointer = .{
.pointer = load,
.base_type = array.type,
.is_struct = false,
.index = index,
},
});
try builder.appendInstruction(unit, context, gep);
const gep_type = try unit.getPointerType(context, .{
.type = array.type,
.termination = .none,
.mutability = pointer.mutability,
.many = false,
.nullable = false,
});
break :b .{
.value = .{
.runtime = gep,
},
.type = gep_type,
};
},
.integer => b: {
assert(child_pointer.many);
const load = try unit.instructions.append(context.allocator, .{
.load = .{
.value = array_like_expression,
.type = pointer.type,
},
});
try builder.appendInstruction(unit, context, load);
const gep = try unit.instructions.append(context.allocator, .{
.get_element_pointer = .{
.pointer = load,
.base_type = child_pointer.type,
.is_struct = false,
.index = index,
},
});
try builder.appendInstruction(unit, context, gep);
const gep_type = try unit.getPointerType(context, .{
.type = child_pointer.type,
.termination = .none,
.mutability = pointer.mutability,
.many = false,
.nullable = false,
});
break :b .{
.value = .{
.runtime = gep,
},
.type = gep_type,
};
},
else => |t| @panic(@tagName(t)),
},
}, },
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
}, },
@ -5658,7 +5738,11 @@ pub const Builder = struct {
if (array_type.count != expression_element_count) @panic("Array element count mismatch"); if (array_type.count != expression_element_count) @panic("Array element count mismatch");
var is_comptime = true; var is_comptime = true;
var values = try ArrayList(V).initCapacity(context.allocator, nodes.len); const is_terminated = switch (array_type.termination) {
.none => false,
else => true,
};
var values = try ArrayList(V).initCapacity(context.allocator, nodes.len + @intFromBool(is_terminated));
for (nodes) |node_index| { for (nodes) |node_index| {
const value = try builder.resolveRuntimeValue(unit, context, Type.Expect{ .type = array_type.type }, node_index, .right); const value = try builder.resolveRuntimeValue(unit, context, Type.Expect{ .type = array_type.type }, node_index, .right);
// assert(value.value == .@"comptime"); // assert(value.value == .@"comptime");
@ -5666,6 +5750,26 @@ pub const Builder = struct {
values.appendAssumeCapacity(value); values.appendAssumeCapacity(value);
} }
switch (array_type.termination) {
.none => {},
.zero => values.appendAssumeCapacity(.{
.value = .{
.@"comptime" = .{
.constant_int = .{
.value = 0,
},
},
},
.type = array_type.type,
}),
.null => values.appendAssumeCapacity(.{
.value = .{
.@"comptime" = .null_pointer,
},
.type = array_type.type,
}),
}
if (is_comptime) { if (is_comptime) {
const constant_array = try unit.constant_arrays.append(context.allocator, .{ const constant_array = try unit.constant_arrays.append(context.allocator, .{
.values = blk: { .values = blk: {
@ -7662,30 +7766,47 @@ pub const Builder = struct {
.constant_int = .{ .constant_int = .{
.value = ct_int.value, .value = ct_int.value,
}, },
},
},
.type = ti,
};
},
.signed => {
if (destination_integer_type.signedness == .unsigned) {
unreachable;
} else {
const value = -@as(i64, @intCast(ct_int.value));
return .{
.value = .{
.@"comptime" = .{
.constant_int = .{
.value = @bitCast(value),
},
}, },
}, },
.type = ti, .type = ti,
}; };
}, }
.signed => { },
if (destination_integer_type.signedness == .unsigned) { }
unreachable; },
} else { .pointer_to_nullable => {
const value = -@as(i64, @intCast(ct_int.value)); const cast = try unit.instructions.append(context.allocator, .{
return .{ .cast = .{
.value = .{ .id = .pointer_to_nullable,
.@"comptime" = .{ .value = result,
.constant_int = .{ .type = ti,
.value = @bitCast(value), },
}, });
}, try builder.appendInstruction(unit, context, cast);
},
.type = ti, return .{
}; .value = .{
} .runtime = cast,
}, },
} .type = ti,
}, };
},
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
} }
}, },
@ -8513,7 +8634,6 @@ pub const Descriptor = struct {
main_package_path: []const u8, main_package_path: []const u8,
executable_path: []const u8, executable_path: []const u8,
target: std.Target, target: std.Target,
is_build: bool,
only_parse: bool, only_parse: bool,
link_libc: bool, link_libc: bool,
generate_debug_information: bool, generate_debug_information: bool,

View File

@ -1255,7 +1255,11 @@ pub const LLVM = struct {
}, },
.array => |array| blk: { .array => |array| blk: {
const element_type = try llvm.getType(unit, context, array.type); const element_type = try llvm.getType(unit, context, array.type);
const array_type = LLVM.Type.Array.get(element_type, array.count) orelse return Type.Error.array; const extra_element = switch (array.termination) {
.none => false,
else => true,
};
const array_type = LLVM.Type.Array.get(element_type, array.count + @intFromBool(extra_element)) orelse return Type.Error.array;
break :blk array_type.toType(); break :blk array_type.toType();
}, },
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
@ -2223,6 +2227,7 @@ pub const LLVM = struct {
}; };
list.appendAssumeCapacity(value); list.appendAssumeCapacity(value);
} }
const result = array_type.getConstant(list.items.ptr, list.items.len) orelse unreachable; const result = array_type.getConstant(list.items.ptr, list.items.len) orelse unreachable;
return result; return result;
} }
@ -3193,17 +3198,55 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo
var arguments = ArrayList([*:0]const u8){}; var arguments = ArrayList([*:0]const u8){};
try arguments.append(context.allocator, driver_program); try arguments.append(context.allocator, driver_program);
try arguments.append(context.allocator, object_file_path.ptr); try arguments.append(context.allocator, "--error-limit=0");
try arguments.append(context.allocator, "-o"); try arguments.append(context.allocator, "-o");
try arguments.append(context.allocator, destination_file_path.ptr); try arguments.append(context.allocator, destination_file_path.ptr);
if (format == .macho) { try arguments.append(context.allocator, object_file_path.ptr);
try arguments.append(context.allocator, "-dynamic");
try arguments.appendSlice(context.allocator, &.{ "-platform_version", "macos", "13.4.1", "13.3" }); switch (unit.descriptor.target.os.tag) {
try arguments.appendSlice(context.allocator, &.{ "-arch", "arm64" }); .macos => {
try arguments.appendSlice(context.allocator, &.{ "-syslibroot", "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk" }); try arguments.append(context.allocator, "-dynamic");
try arguments.appendSlice(context.allocator, &.{ "-e", "_main" }); try arguments.appendSlice(context.allocator, &.{ "-platform_version", "macos", "13.4.1", "13.3" });
try arguments.append(context.allocator, "-lSystem"); try arguments.append(context.allocator, "-arch");
try arguments.append(context.allocator, switch (unit.descriptor.target.cpu.arch) {
.aarch64 => "arm64",
else => |t| @panic(@tagName(t)),
});
try arguments.appendSlice(context.allocator, &.{ "-syslibroot", "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk" });
try arguments.appendSlice(context.allocator, &.{ "-e", "_main" });
try arguments.append(context.allocator, "-lSystem");
},
.linux => {
try arguments.appendSlice(context.allocator, &.{"--entry", "_start"});
try arguments.append(context.allocator, "-m");
try arguments.append(context.allocator, switch (unit.descriptor.target.cpu.arch) {
.x86_64 => "elf_x86_64",
else => |t| @panic(@tagName(t)),
});
if (unit.descriptor.link_libc) {
try arguments.append(context.allocator, "/usr/lib/crt1.o");
try arguments.append(context.allocator, "/usr/lib/crti.o");
try arguments.appendSlice(context.allocator, &.{"-L", "/usr/lib"});
try arguments.appendSlice(context.allocator, &.{"-dynamic-linker", "/lib64/ld-linux-x86-64.so.2"});
try arguments.append(context.allocator, "--as-needed");
try arguments.append(context.allocator, "-lm");
try arguments.append(context.allocator, "-lpthread");
try arguments.append(context.allocator, "-lc");
try arguments.append(context.allocator, "-ldl");
try arguments.append(context.allocator, "-lrt");
try arguments.append(context.allocator, "-lutil");
try arguments.append(context.allocator, "/usr/lib/crtn.o");
}
// if (unit.descriptor.link_libc) {
// try arguments.appendSlice(context.allocator, &.{ "-lc" });
// }
},
.windows => {},
else => |t| @panic(@tagName(t)),
} }
var stdout_ptr: [*]const u8 = undefined; var stdout_ptr: [*]const u8 = undefined;

View File

@ -29,7 +29,8 @@ pub fn main() !void {
const command_arguments = arguments[2..]; const command_arguments = arguments[2..];
if (equal(u8, command, "build")) { if (equal(u8, command, "build")) {
todo(); const context = try Compilation.createContext(allocator);
try Compilation.compileBuildExecutable(context, command_arguments);
} else if (equal(u8, command, "clang") or equal(u8, command, "-cc1") or equal(u8, command, "-cc1as")) { } else if (equal(u8, command, "clang") or equal(u8, command, "-cc1") or equal(u8, command, "-cc1as")) {
const exit_code = try clangMain(allocator, arguments); const exit_code = try clangMain(allocator, arguments);
std.process.exit(exit_code); std.process.exit(exit_code);
@ -40,7 +41,8 @@ pub fn main() !void {
// TODO: transform our arguments to Clang and invoke it // TODO: transform our arguments to Clang and invoke it
todo(); todo();
} else if (equal(u8, command, "exe")) { } else if (equal(u8, command, "exe")) {
try Compilation.buildExecutable(allocator, command_arguments); const context = try Compilation.createContext(allocator);
try Compilation.buildExecutable(context, command_arguments);
} else if (equal(u8, command, "lib")) { } else if (equal(u8, command, "lib")) {
todo(); todo();
} else if (equal(u8, command, "obj")) { } else if (equal(u8, command, "obj")) {

View File

@ -1,4 +1,5 @@
const std = @import("std"); const std = @import("std");
const Allocator = std.mem.Allocator;
const TestError = error{ const TestError = error{
junk_in_test_directory, junk_in_test_directory,
@ -9,36 +10,38 @@ const TestError = error{
fail, fail,
}; };
pub fn main() !void { fn collectDirectoryDirEntries(allocator: Allocator, path: []const u8) ![]const []const u8{
std.debug.print("\n",.{}); var dir = try std.fs.cwd().openDir(path, .{
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
const allocator = arena.allocator();
const standalone_test_dir_path = "test/standalone";
var standalone_test_dir = try std.fs.cwd().openDir(standalone_test_dir_path, .{
.iterate = true, .iterate = true,
}); });
var standalone_iterator = standalone_test_dir.iterate(); var dir_iterator = dir.iterate();
var standalone_test_names = std.ArrayListUnmanaged([]const u8){}; var dir_entries = std.ArrayListUnmanaged([]const u8){};
while (try standalone_iterator.next()) |entry| { while (try dir_iterator.next()) |entry| {
switch (entry.kind) { switch (entry.kind) {
.directory => try standalone_test_names.append(allocator, try allocator.dupe(u8, entry.name)), .directory => try dir_entries.append(allocator, try allocator.dupe(u8, entry.name)),
else => return error.junk_in_test_directory, else => return error.junk_in_test_directory,
} }
} }
standalone_test_dir.close(); dir.close();
const total_compilation_count = standalone_test_names.items.len; return dir_entries.items;
}
fn runStandaloneTests(allocator: Allocator) !void {
const standalone_test_dir_path = "test/standalone";
const standalone_test_names = try collectDirectoryDirEntries(allocator, standalone_test_dir_path);
const total_compilation_count = standalone_test_names.len;
var ran_compilation_count: usize = 0; var ran_compilation_count: usize = 0;
var failed_compilation_count: usize = 0; var failed_compilation_count: usize = 0;
var ran_test_count: usize = 0; var ran_test_count: usize = 0;
var failed_test_count: usize = 0; var failed_test_count: usize = 0;
const total_test_count = standalone_test_names.items.len; const total_test_count = standalone_test_names.len;
for (standalone_test_names.items) |standalone_test_name| { for (standalone_test_names) |standalone_test_name| {
std.debug.assert(!std.mem.eql(u8, standalone_test_name, "else_ifrld"));
std.debug.print("{s}... ", .{standalone_test_name}); std.debug.print("{s}... ", .{standalone_test_name});
const source_file_path = try std.mem.concat(allocator, u8, &.{standalone_test_dir_path, "/", standalone_test_name, "/main.nat"}); const source_file_path = try std.mem.concat(allocator, u8, &.{standalone_test_dir_path, "/", standalone_test_name, "/main.nat"});
const compile_run = try std.ChildProcess.run(.{ const compile_run = try std.ChildProcess.run(.{
@ -106,3 +109,100 @@ pub fn main() !void {
return error.fail; return error.fail;
} }
} }
fn runBuildTests(allocator: Allocator) !void {
std.debug.print("\n[BUILD TESTS]\n\n", .{});
const test_dir_path = "test/build";
const test_names = try collectDirectoryDirEntries(allocator, test_dir_path);
const test_dir_realpath = try std.fs.cwd().realpathAlloc(allocator, test_dir_path);
const compiler_realpath = try std.fs.cwd().realpathAlloc(allocator, "zig-out/bin/nat");
try std.os.chdir(test_dir_realpath);
const total_compilation_count = test_names.len;
var ran_compilation_count: usize = 0;
var failed_compilation_count: usize = 0;
var ran_test_count: usize = 0;
var failed_test_count: usize = 0;
const total_test_count = test_names.len;
for (test_names) |test_name| {
std.debug.print("{s}... ", .{test_name});
try std.os.chdir(test_name);
const compile_run = try std.ChildProcess.run(.{
.allocator = allocator,
// TODO: delete -main_source_file?
.argv = &.{compiler_realpath, "build"},
});
ran_compilation_count += 1;
const compilation_result: TestError!bool = switch (compile_run.term) {
.Exited => |exit_code| if (exit_code == 0) true else error.abnormal_exit_code,
.Signal => error.signaled,
.Stopped => error.stopped,
.Unknown => error.unknown,
};
const compilation_success = compilation_result catch b: {
failed_compilation_count += 1;
break :b false;
};
std.debug.print("[COMPILATION {s}] ", .{if (compilation_success) "\x1b[32mOK\x1b[0m" else "\x1b[31mFAILED\x1b[0m"});
if (compile_run.stdout.len > 0) {
std.debug.print("STDOUT:\n\n{s}\n\n", .{compile_run.stdout});
}
if (compile_run.stderr.len > 0) {
std.debug.print("STDERR:\n\n{s}\n\n", .{compile_run.stderr});
}
if (compilation_success) {
const test_path = try std.mem.concat(allocator, u8, &.{"nat/", test_name});
const test_run = try std.ChildProcess.run(.{
.allocator = allocator,
// TODO: delete -main_source_file?
.argv = &.{ test_path },
});
ran_test_count += 1;
const test_result: TestError!bool = switch (test_run.term) {
.Exited => |exit_code| if (exit_code == 0) true else error.abnormal_exit_code,
.Signal => error.signaled,
.Stopped => error.stopped,
.Unknown => error.unknown,
};
const test_success = test_result catch b: {
failed_test_count += 1;
break :b false;
};
std.debug.print("[TEST {s}]\n", .{if (test_success) "\x1b[32mOK\x1b[0m" else "\x1b[31mFAILED\x1b[0m"});
if (test_run.stdout.len > 0) {
std.debug.print("STDOUT:\n\n{s}\n\n", .{test_run.stdout});
}
if (test_run.stderr.len > 0) {
std.debug.print("STDERR:\n\n{s}\n\n", .{test_run.stderr});
}
} else {
std.debug.print("\n", .{});
}
try std.os.chdir(test_dir_realpath);
}
std.debug.print("\nTOTAL COMPILATIONS: {}. FAILED: {}\n", .{total_compilation_count, failed_compilation_count});
std.debug.print("TOTAL TESTS: {}. RAN: {}. FAILED: {}\n", .{total_test_count, ran_test_count, failed_test_count});
if (failed_compilation_count > 0 or failed_test_count > 0) {
return error.fail;
}
}
pub fn main() !void {
std.debug.print("\n",.{});
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
const allocator = arena.allocator();
try runStandaloneTests(allocator);
try runBuildTests(allocator);
}

View File

@ -7,7 +7,7 @@ const Executable = struct{
target: Target, target: Target,
main_source_path: [:0]const u8, main_source_path: [:0]const u8,
link_libc: bool = false, link_libc: bool = false,
name: []const u8, name: [:0]const u8,
const compile = fn(executable: Executable) bool { const compile = fn(executable: Executable) bool {
const argument_count = std.start.argument_count; const argument_count = std.start.argument_count;
@ -22,7 +22,13 @@ const Executable = struct{
const compile_with_compiler_path = fn(executable: Executable, compiler_path: [&:0]const u8) bool { const compile_with_compiler_path = fn(executable: Executable, compiler_path: [&:0]const u8) bool {
if (std.os.duplicate_process()) |pid| { if (std.os.duplicate_process()) |pid| {
if (pid == 0) { if (pid == 0) {
const argv = [_:null] ?[&:0]const u8{ compiler_path, "-main_source_file", #cast(executable.main_source_path.ptr), "-link_libc", if (executable.link_libc) "true" else "false", "-name", #cast(executable.name.ptr) }; var link_libc_arg: [&:0]const u8 = undefined;
if (executable.link_libc) {
link_libc_arg = "true";
} 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 };
std.os.execute(path = compiler_path, argv = argv.&, env = std.start.environment_values); std.os.execute(path = compiler_path, argv = argv.&, env = std.start.environment_values);
return true; return true;
} else { } else {

View File

@ -406,7 +406,7 @@ const waitpid = fn(pid: Process.Id, flags: u32) ?u32 {
while (true) { while (true) {
const raw_syscall_result = linux.waitpid(pid, status = status.&, flags, resource_usage = 0); const raw_syscall_result = linux.waitpid(pid, status = status.&, flags, resource_usage = 0);
const signed_syscall_result: ssize = #cast(raw_syscall_result); const signed_syscall_result: ssize = #cast(raw_syscall_result);
if (raw_syscall_result != -4) { if (signed_syscall_result != -4) {
if (linux.unwrap_syscall(syscall_result = raw_syscall_result)) |_| { if (linux.unwrap_syscall(syscall_result = raw_syscall_result)) |_| {
return status; return status;
} else { } else {

View File

@ -478,7 +478,8 @@ const pipe2 = fn (pipe_pointer: &[2]s32, flags: u32) usize {
} }
const waitpid = fn(pid: ProcessId, status: &u32, flags: u32, resource_usage: usize) usize { const waitpid = fn(pid: ProcessId, status: &u32, flags: u32, resource_usage: usize) usize {
const result = #syscall(#cast(Syscall.wait4), pid, #cast(status), flags, resource_usage); const pid_unsigned: u32 = #cast(pid);
const result = #syscall(#cast(Syscall.wait4), pid_unsigned, #cast(status), flags, resource_usage);
return result; return result;
} }

View File

@ -27,8 +27,10 @@ const start :: export = fn (argc_argv_address: usize) noreturn {
argument_count = argument_count_ptr.@; argument_count = argument_count_ptr.@;
argument_address_iterator += #size(usize); argument_address_iterator += #size(usize);
argument_values = #cast(argument_address_iterator); argument_values = #cast(argument_address_iterator);
const argv = argument_values;
argument_address_iterator += #size(usize) * (argument_count + 1); argument_address_iterator += #size(usize) * (argument_count + 1);
environment_values = #cast(argument_address_iterator); environment_values = #cast(argument_address_iterator);
const env = environment_values;
const result = #import("main").main(); const result = #import("main").main();
std.os.exit(exit_code = result); std.os.exit(exit_code = result);
} }

View File

@ -0,0 +1,7 @@
const std = #import("std");
const assert = std.assert;
const main = fn() s32 {
var foo = [2:null] ?[&:0]const u8 {"Hi", "Ho"};
assert(foo[2] == null);
return 0;
}

View File

@ -0,0 +1,6 @@
const std = #import("std");
const assert = std.assert;
const main = fn () s32 {
assert(#size(usize) == 8);
return 0;
}

View File

@ -0,0 +1,7 @@
const std = #import("std");
const assert = std.assert;
const main = fn () s32 {
var a = [2:0]u8 {1, 2};
assert(a[2] == 0);
return 0;
}