Commit e1d4b59c5b
Changed files (7)
src-self-hosted
src-self-hosted/ir/text.zig
@@ -20,7 +20,7 @@ pub const Inst = struct {
name: []const u8,
/// Slice into the source of the part after the = and before the next instruction.
- contents: []const u8,
+ contents: []const u8 = &[0]u8{},
/// These names are used directly as the instruction names in the text format.
pub const Tag = enum {
@@ -825,7 +825,6 @@ const Parser = struct {
.name = inst_name,
.src = self.i,
.tag = InstType.base_tag,
- .contents = undefined,
};
if (@hasField(InstType, "ty")) {
@@ -960,7 +959,6 @@ const Parser = struct {
.name = try self.generateName(),
.src = src,
.tag = Inst.Str.base_tag,
- .contents = undefined,
},
.positionals = .{ .bytes = ident },
.kw_args = .{},
@@ -971,7 +969,6 @@ const Parser = struct {
.name = try self.generateName(),
.src = src,
.tag = Inst.DeclRef.base_tag,
- .contents = undefined,
},
.positionals = .{ .name = &name.base },
.kw_args = .{},
src-self-hosted/compilation.zig
@@ -1,1457 +0,0 @@
-const std = @import("std");
-const io = std.io;
-const mem = std.mem;
-const Allocator = mem.Allocator;
-const ArrayListSentineled = std.ArrayListSentineled;
-const llvm = @import("llvm.zig");
-const c = @import("c.zig");
-const builtin = std.builtin;
-const Target = std.Target;
-const warn = std.debug.warn;
-const Token = std.zig.Token;
-const ArrayList = std.ArrayList;
-const errmsg = @import("errmsg.zig");
-const ast = std.zig.ast;
-const event = std.event;
-const assert = std.debug.assert;
-const AtomicRmwOp = builtin.AtomicRmwOp;
-const AtomicOrder = builtin.AtomicOrder;
-const Scope = @import("scope.zig").Scope;
-const Decl = @import("decl.zig").Decl;
-const ir = @import("ir.zig");
-const Value = @import("value.zig").Value;
-const Type = Value.Type;
-const Span = errmsg.Span;
-const Msg = errmsg.Msg;
-const codegen = @import("codegen.zig");
-const Package = @import("package.zig").Package;
-const link = @import("link.zig").link;
-const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
-const CInt = @import("c_int.zig").CInt;
-const fs = std.fs;
-
-pub const Visib = enum {
- Private,
- Pub,
-};
-
-const max_src_size = 2 * 1024 * 1024 * 1024; // 2 GiB
-
-/// Data that is local to the event loop.
-pub const ZigCompiler = struct {
- llvm_handle_pool: std.atomic.Stack(*llvm.Context),
- lld_lock: event.Lock,
- allocator: *Allocator,
-
- /// TODO pool these so that it doesn't have to lock
- prng: event.Locked(std.rand.DefaultPrng),
-
- native_libc: event.Future(LibCInstallation),
-
- var lazy_init_targets = std.once(initializeAllTargets);
-
- pub fn init(allocator: *Allocator) !ZigCompiler {
- lazy_init_targets.call();
-
- var seed_bytes: [@sizeOf(u64)]u8 = undefined;
- try std.crypto.randomBytes(seed_bytes[0..]);
- const seed = mem.readIntNative(u64, &seed_bytes);
-
- return ZigCompiler{
- .allocator = allocator,
- .lld_lock = event.Lock.init(),
- .llvm_handle_pool = std.atomic.Stack(*llvm.Context).init(),
- .prng = event.Locked(std.rand.DefaultPrng).init(std.rand.DefaultPrng.init(seed)),
- .native_libc = event.Future(LibCInstallation).init(),
- };
- }
-
- /// Must be called only after EventLoop.run completes.
- fn deinit(self: *ZigCompiler) void {
- self.lld_lock.deinit();
- while (self.llvm_handle_pool.pop()) |node| {
- llvm.ContextDispose(node.data);
- self.allocator.destroy(node);
- }
- }
-
- /// Gets an exclusive handle on any LlvmContext.
- /// Caller must release the handle when done.
- pub fn getAnyLlvmContext(self: *ZigCompiler) !LlvmHandle {
- if (self.llvm_handle_pool.pop()) |node| return LlvmHandle{ .node = node };
-
- const context_ref = llvm.ContextCreate() orelse return error.OutOfMemory;
- errdefer llvm.ContextDispose(context_ref);
-
- const node = try self.allocator.create(std.atomic.Stack(*llvm.Context).Node);
- node.* = std.atomic.Stack(*llvm.Context).Node{
- .next = undefined,
- .data = context_ref,
- };
- errdefer self.allocator.destroy(node);
-
- return LlvmHandle{ .node = node };
- }
-
- pub fn getNativeLibC(self: *ZigCompiler) !*LibCInstallation {
- if (self.native_libc.start()) |ptr| return ptr;
- self.native_libc.data = try LibCInstallation.findNative(.{ .allocator = self.allocator });
- self.native_libc.resolve();
- return &self.native_libc.data;
- }
-
- /// Must be called only once, ever. Sets global state.
- pub fn setLlvmArgv(allocator: *Allocator, llvm_argv: []const []const u8) !void {
- if (llvm_argv.len != 0) {
- var c_compatible_args = try std.cstr.NullTerminated2DArray.fromSlices(allocator, &[_][]const []const u8{
- &[_][]const u8{"zig (LLVM option parsing)"},
- llvm_argv,
- });
- defer c_compatible_args.deinit();
- c.ZigLLVMParseCommandLineOptions(llvm_argv.len + 1, c_compatible_args.ptr);
- }
- }
-};
-
-pub const LlvmHandle = struct {
- node: *std.atomic.Stack(*llvm.Context).Node,
-
- pub fn release(self: LlvmHandle, zig_compiler: *ZigCompiler) void {
- zig_compiler.llvm_handle_pool.push(self.node);
- }
-};
-
-pub const Compilation = struct {
- pub const FnLinkSet = std.TailQueue(?*Value.Fn);
-
- zig_compiler: *ZigCompiler,
- name: ArrayListSentineled(u8, 0),
- llvm_triple: ArrayListSentineled(u8, 0),
- root_src_path: ?[]const u8,
- target: std.Target,
- llvm_target: *llvm.Target,
- build_mode: builtin.Mode,
- zig_lib_dir: []const u8,
- zig_std_dir: []const u8,
-
- /// lazily created when we need it
- tmp_dir: event.Future(BuildError![]u8) = event.Future(BuildError![]u8).init(),
-
- version: builtin.Version = builtin.Version{ .major = 0, .minor = 0, .patch = 0 },
-
- linker_script: ?[]const u8 = null,
- out_h_path: ?[]const u8 = null,
-
- is_test: bool = false,
- strip: bool = false,
- is_static: bool,
- linker_rdynamic: bool = false,
-
- clang_argv: []const []const u8 = &[_][]const u8{},
- assembly_files: []const []const u8 = &[_][]const u8{},
-
- /// paths that are explicitly provided by the user to link against
- link_objects: []const []const u8 = &[_][]const u8{},
-
- /// functions that have their own objects that we need to link
- /// it uses an optional pointer so that tombstone removals are possible
- fn_link_set: event.Locked(FnLinkSet) = event.Locked(FnLinkSet).init(FnLinkSet.init()),
-
- link_libs_list: ArrayList(*LinkLib),
- libc_link_lib: ?*LinkLib = null,
-
- err_color: errmsg.Color = .Auto,
-
- verbose_tokenize: bool = false,
- verbose_ast_tree: bool = false,
- verbose_ast_fmt: bool = false,
- verbose_cimport: bool = false,
- verbose_ir: bool = false,
- verbose_llvm_ir: bool = false,
- verbose_link: bool = false,
-
- link_eh_frame_hdr: bool = false,
-
- darwin_version_min: DarwinVersionMin = .None,
-
- test_filters: []const []const u8 = &[_][]const u8{},
- test_name_prefix: ?[]const u8 = null,
-
- emit_bin: bool = true,
- emit_asm: bool = false,
- emit_llvm_ir: bool = false,
- emit_h: bool = false,
-
- kind: Kind,
-
- events: *event.Channel(Event),
-
- exported_symbol_names: event.Locked(Decl.Table),
-
- /// Before code generation starts, must wait on this group to make sure
- /// the build is complete.
- prelink_group: event.Group(BuildError!void),
-
- compile_errors: event.Locked(CompileErrList),
-
- meta_type: *Type.MetaType,
- void_type: *Type.Void,
- bool_type: *Type.Bool,
- noreturn_type: *Type.NoReturn,
- comptime_int_type: *Type.ComptimeInt,
- u8_type: *Type.Int,
-
- void_value: *Value.Void,
- true_value: *Value.Bool,
- false_value: *Value.Bool,
- noreturn_value: *Value.NoReturn,
-
- target_machine: *llvm.TargetMachine,
- target_data_ref: *llvm.TargetData,
- target_layout_str: [*:0]u8,
- target_ptr_bits: u32,
-
- /// for allocating things which have the same lifetime as this Compilation
- arena_allocator: std.heap.ArenaAllocator,
-
- root_package: *Package,
- std_package: *Package,
-
- override_libc: ?*LibCInstallation = null,
-
- /// need to wait on this group before deinitializing
- deinit_group: event.Group(void),
-
- destroy_frame: *@Frame(createAsync),
- main_loop_frame: *@Frame(Compilation.mainLoop),
- main_loop_future: event.Future(void) = event.Future(void).init(),
-
- have_err_ret_tracing: bool = false,
-
- /// not locked because it is read-only
- primitive_type_table: TypeTable,
-
- int_type_table: event.Locked(IntTypeTable),
- array_type_table: event.Locked(ArrayTypeTable),
- ptr_type_table: event.Locked(PtrTypeTable),
- fn_type_table: event.Locked(FnTypeTable),
-
- c_int_types: [CInt.list.len]*Type.Int,
-
- fs_watch: *fs.Watch(*Scope.Root),
-
- cancelled: bool = false,
-
- const IntTypeTable = std.HashMap(*const Type.Int.Key, *Type.Int, Type.Int.Key.hash, Type.Int.Key.eql);
- const ArrayTypeTable = std.HashMap(*const Type.Array.Key, *Type.Array, Type.Array.Key.hash, Type.Array.Key.eql);
- const PtrTypeTable = std.HashMap(*const Type.Pointer.Key, *Type.Pointer, Type.Pointer.Key.hash, Type.Pointer.Key.eql);
- const FnTypeTable = std.HashMap(*const Type.Fn.Key, *Type.Fn, Type.Fn.Key.hash, Type.Fn.Key.eql);
- const TypeTable = std.StringHashMap(*Type);
-
- const CompileErrList = std.ArrayList(*Msg);
-
- // TODO handle some of these earlier and report them in a way other than error codes
- pub const BuildError = error{
- OutOfMemory,
- EndOfStream,
- IsDir,
- Unexpected,
- SystemResources,
- SharingViolation,
- PathAlreadyExists,
- FileNotFound,
- AccessDenied,
- PipeBusy,
- FileTooBig,
- SymLinkLoop,
- ProcessFdQuotaExceeded,
- NameTooLong,
- SystemFdQuotaExceeded,
- NoDevice,
- NoSpaceLeft,
- NotDir,
- FileSystem,
- OperationAborted,
- IoPending,
- BrokenPipe,
- WouldBlock,
- FileClosed,
- DestinationAddressRequired,
- DiskQuota,
- InputOutput,
- NoStdHandles,
- Overflow,
- NotSupported,
- BufferTooSmall,
- Unimplemented, // TODO remove this one
- SemanticAnalysisFailed, // TODO remove this one
- ReadOnlyFileSystem,
- LinkQuotaExceeded,
- EnvironmentVariableNotFound,
- AppDataDirUnavailable,
- LinkFailed,
- LibCRequiredButNotProvidedOrFound,
- LibCMissingDynamicLinker,
- InvalidDarwinVersionString,
- UnsupportedLinkArchitecture,
- UserResourceLimitReached,
- InvalidUtf8,
- BadPathName,
- DeviceBusy,
- CurrentWorkingDirectoryUnlinked,
- };
-
- pub const Event = union(enum) {
- Ok,
- Error: BuildError,
- Fail: []*Msg,
- };
-
- pub const DarwinVersionMin = union(enum) {
- None,
- MacOS: []const u8,
- Ios: []const u8,
- };
-
- pub const Kind = enum {
- Exe,
- Lib,
- Obj,
- };
-
- pub const LinkLib = struct {
- name: []const u8,
- path: ?[]const u8,
-
- /// the list of symbols we depend on from this lib
- symbols: ArrayList([]u8),
- provided_explicitly: bool,
- };
-
- pub const Emit = enum {
- Binary,
- Assembly,
- LlvmIr,
- };
-
- pub fn create(
- zig_compiler: *ZigCompiler,
- name: []const u8,
- root_src_path: ?[]const u8,
- target: std.zig.CrossTarget,
- kind: Kind,
- build_mode: builtin.Mode,
- is_static: bool,
- zig_lib_dir: []const u8,
- ) !*Compilation {
- var optional_comp: ?*Compilation = null;
- var frame = try zig_compiler.allocator.create(@Frame(createAsync));
- errdefer zig_compiler.allocator.destroy(frame);
- frame.* = async createAsync(
- &optional_comp,
- zig_compiler,
- name,
- root_src_path,
- target,
- kind,
- build_mode,
- is_static,
- zig_lib_dir,
- );
- // TODO causes segfault
- // return optional_comp orelse if (await frame) |_| unreachable else |err| err;
- if (optional_comp) |comp| {
- return comp;
- } else if (await frame) |_| unreachable else |err| return err;
- }
- fn createAsync(
- out_comp: *?*Compilation,
- zig_compiler: *ZigCompiler,
- name: []const u8,
- root_src_path: ?[]const u8,
- cross_target: std.zig.CrossTarget,
- kind: Kind,
- build_mode: builtin.Mode,
- is_static: bool,
- zig_lib_dir: []const u8,
- ) callconv(.Async) !void {
- const allocator = zig_compiler.allocator;
-
- // TODO merge this line with stage2.zig crossTargetToTarget
- const target_info = try std.zig.system.NativeTargetInfo.detect(std.heap.c_allocator, cross_target);
- const target = target_info.target;
-
- var comp = Compilation{
- .arena_allocator = std.heap.ArenaAllocator.init(allocator),
- .zig_compiler = zig_compiler,
- .events = undefined,
- .root_src_path = root_src_path,
- .target = target,
- .llvm_target = undefined,
- .kind = kind,
- .build_mode = build_mode,
- .zig_lib_dir = zig_lib_dir,
- .zig_std_dir = undefined,
- .destroy_frame = @frame(),
- .main_loop_frame = undefined,
-
- .name = undefined,
- .llvm_triple = undefined,
- .is_static = is_static,
- .link_libs_list = undefined,
- .exported_symbol_names = event.Locked(Decl.Table).init(Decl.Table.init(allocator)),
- .prelink_group = event.Group(BuildError!void).init(allocator),
- .deinit_group = event.Group(void).init(allocator),
- .compile_errors = event.Locked(CompileErrList).init(CompileErrList.init(allocator)),
- .int_type_table = event.Locked(IntTypeTable).init(IntTypeTable.init(allocator)),
- .array_type_table = event.Locked(ArrayTypeTable).init(ArrayTypeTable.init(allocator)),
- .ptr_type_table = event.Locked(PtrTypeTable).init(PtrTypeTable.init(allocator)),
- .fn_type_table = event.Locked(FnTypeTable).init(FnTypeTable.init(allocator)),
- .c_int_types = undefined,
-
- .meta_type = undefined,
- .void_type = undefined,
- .void_value = undefined,
- .bool_type = undefined,
- .true_value = undefined,
- .false_value = undefined,
- .noreturn_type = undefined,
- .noreturn_value = undefined,
- .comptime_int_type = undefined,
- .u8_type = undefined,
-
- .target_machine = undefined,
- .target_data_ref = undefined,
- .target_layout_str = undefined,
- .target_ptr_bits = target.cpu.arch.ptrBitWidth(),
-
- .root_package = undefined,
- .std_package = undefined,
-
- .primitive_type_table = undefined,
-
- .fs_watch = undefined,
- };
- comp.link_libs_list = ArrayList(*LinkLib).init(comp.arena());
- comp.primitive_type_table = TypeTable.init(comp.arena());
-
- defer {
- comp.int_type_table.private_data.deinit();
- comp.array_type_table.private_data.deinit();
- comp.ptr_type_table.private_data.deinit();
- comp.fn_type_table.private_data.deinit();
- comp.arena_allocator.deinit();
- }
-
- comp.name = try ArrayListSentineled(u8, 0).init(comp.arena(), name);
- comp.llvm_triple = try getLLVMTriple(comp.arena(), target);
- comp.llvm_target = try llvmTargetFromTriple(comp.llvm_triple);
- comp.zig_std_dir = try fs.path.join(comp.arena(), &[_][]const u8{ zig_lib_dir, "std" });
-
- const opt_level = switch (build_mode) {
- .Debug => llvm.CodeGenLevelNone,
- else => llvm.CodeGenLevelAggressive,
- };
-
- const reloc_mode = if (is_static) llvm.RelocStatic else llvm.RelocPIC;
-
- var target_specific_cpu_args: ?[*:0]u8 = null;
- var target_specific_cpu_features: ?[*:0]u8 = null;
- defer llvm.DisposeMessage(target_specific_cpu_args);
- defer llvm.DisposeMessage(target_specific_cpu_features);
-
- // TODO detect native CPU & features here
-
- comp.target_machine = llvm.CreateTargetMachine(
- comp.llvm_target,
- comp.llvm_triple.span(),
- target_specific_cpu_args orelse "",
- target_specific_cpu_features orelse "",
- opt_level,
- reloc_mode,
- llvm.CodeModelDefault,
- false, // TODO: add -ffunction-sections option
- ) orelse return error.OutOfMemory;
- defer llvm.DisposeTargetMachine(comp.target_machine);
-
- comp.target_data_ref = llvm.CreateTargetDataLayout(comp.target_machine) orelse return error.OutOfMemory;
- defer llvm.DisposeTargetData(comp.target_data_ref);
-
- comp.target_layout_str = llvm.CopyStringRepOfTargetData(comp.target_data_ref) orelse return error.OutOfMemory;
- defer llvm.DisposeMessage(comp.target_layout_str);
-
- comp.events = try allocator.create(event.Channel(Event));
- defer allocator.destroy(comp.events);
-
- comp.events.init(&[0]Event{});
- defer comp.events.deinit();
-
- if (root_src_path) |root_src| {
- const dirname = fs.path.dirname(root_src) orelse ".";
- const basename = fs.path.basename(root_src);
-
- comp.root_package = try Package.create(comp.arena(), dirname, basename);
- comp.std_package = try Package.create(comp.arena(), comp.zig_std_dir, "std.zig");
- try comp.root_package.add("std", comp.std_package);
- } else {
- comp.root_package = try Package.create(comp.arena(), ".", "");
- }
-
- comp.fs_watch = try fs.Watch(*Scope.Root).init(allocator, 16);
- defer comp.fs_watch.deinit();
-
- try comp.initTypes();
- defer comp.primitive_type_table.deinit();
-
- comp.main_loop_frame = try allocator.create(@Frame(mainLoop));
- defer allocator.destroy(comp.main_loop_frame);
-
- comp.main_loop_frame.* = async comp.mainLoop();
- // Set this to indicate that initialization completed successfully.
- // from here on out we must not return an error.
- // This must occur before the first suspend/await.
- out_comp.* = ∁
- // This suspend is resumed by destroy()
- suspend;
- // From here on is cleanup.
-
- comp.deinit_group.wait();
-
- if (comp.tmp_dir.getOrNull()) |tmp_dir_result|
- if (tmp_dir_result.*) |tmp_dir| {
- fs.cwd().deleteTree(tmp_dir) catch {};
- } else |_| {};
- }
-
- /// it does ref the result because it could be an arbitrary integer size
- pub fn getPrimitiveType(comp: *Compilation, name: []const u8) !?*Type {
- if (name.len >= 2) {
- switch (name[0]) {
- 'i', 'u' => blk: {
- for (name[1..]) |byte|
- switch (byte) {
- '0'...'9' => {},
- else => break :blk,
- };
- const is_signed = name[0] == 'i';
- const bit_count = std.fmt.parseUnsigned(u32, name[1..], 10) catch |err| switch (err) {
- error.Overflow => return error.Overflow,
- error.InvalidCharacter => unreachable, // we just checked the characters above
- };
- const int_type = try Type.Int.get(comp, Type.Int.Key{
- .bit_count = bit_count,
- .is_signed = is_signed,
- });
- errdefer int_type.base.base.deref();
- return &int_type.base;
- },
- else => {},
- }
- }
-
- if (comp.primitive_type_table.get(name)) |entry| {
- entry.value.base.ref();
- return entry.value;
- }
-
- return null;
- }
-
- fn initTypes(comp: *Compilation) !void {
- comp.meta_type = try comp.arena().create(Type.MetaType);
- comp.meta_type.* = Type.MetaType{
- .base = Type{
- .name = "type",
- .base = Value{
- .id = .Type,
- .typ = undefined,
- .ref_count = std.atomic.Int(usize).init(3), // 3 because it references itself twice
- },
- .id = .Type,
- .abi_alignment = Type.AbiAlignment.init(),
- },
- .value = undefined,
- };
- comp.meta_type.value = &comp.meta_type.base;
- comp.meta_type.base.base.typ = &comp.meta_type.base;
- assert((try comp.primitive_type_table.put(comp.meta_type.base.name, &comp.meta_type.base)) == null);
-
- comp.void_type = try comp.arena().create(Type.Void);
- comp.void_type.* = Type.Void{
- .base = Type{
- .name = "void",
- .base = Value{
- .id = .Type,
- .typ = &Type.MetaType.get(comp).base,
- .ref_count = std.atomic.Int(usize).init(1),
- },
- .id = .Void,
- .abi_alignment = Type.AbiAlignment.init(),
- },
- };
- assert((try comp.primitive_type_table.put(comp.void_type.base.name, &comp.void_type.base)) == null);
-
- comp.noreturn_type = try comp.arena().create(Type.NoReturn);
- comp.noreturn_type.* = Type.NoReturn{
- .base = Type{
- .name = "noreturn",
- .base = Value{
- .id = .Type,
- .typ = &Type.MetaType.get(comp).base,
- .ref_count = std.atomic.Int(usize).init(1),
- },
- .id = .NoReturn,
- .abi_alignment = Type.AbiAlignment.init(),
- },
- };
- assert((try comp.primitive_type_table.put(comp.noreturn_type.base.name, &comp.noreturn_type.base)) == null);
-
- comp.comptime_int_type = try comp.arena().create(Type.ComptimeInt);
- comp.comptime_int_type.* = Type.ComptimeInt{
- .base = Type{
- .name = "comptime_int",
- .base = Value{
- .id = .Type,
- .typ = &Type.MetaType.get(comp).base,
- .ref_count = std.atomic.Int(usize).init(1),
- },
- .id = .ComptimeInt,
- .abi_alignment = Type.AbiAlignment.init(),
- },
- };
- assert((try comp.primitive_type_table.put(comp.comptime_int_type.base.name, &comp.comptime_int_type.base)) == null);
-
- comp.bool_type = try comp.arena().create(Type.Bool);
- comp.bool_type.* = Type.Bool{
- .base = Type{
- .name = "bool",
- .base = Value{
- .id = .Type,
- .typ = &Type.MetaType.get(comp).base,
- .ref_count = std.atomic.Int(usize).init(1),
- },
- .id = .Bool,
- .abi_alignment = Type.AbiAlignment.init(),
- },
- };
- assert((try comp.primitive_type_table.put(comp.bool_type.base.name, &comp.bool_type.base)) == null);
-
- comp.void_value = try comp.arena().create(Value.Void);
- comp.void_value.* = Value.Void{
- .base = Value{
- .id = .Void,
- .typ = &Type.Void.get(comp).base,
- .ref_count = std.atomic.Int(usize).init(1),
- },
- };
-
- comp.true_value = try comp.arena().create(Value.Bool);
- comp.true_value.* = Value.Bool{
- .base = Value{
- .id = .Bool,
- .typ = &Type.Bool.get(comp).base,
- .ref_count = std.atomic.Int(usize).init(1),
- },
- .x = true,
- };
-
- comp.false_value = try comp.arena().create(Value.Bool);
- comp.false_value.* = Value.Bool{
- .base = Value{
- .id = .Bool,
- .typ = &Type.Bool.get(comp).base,
- .ref_count = std.atomic.Int(usize).init(1),
- },
- .x = false,
- };
-
- comp.noreturn_value = try comp.arena().create(Value.NoReturn);
- comp.noreturn_value.* = Value.NoReturn{
- .base = Value{
- .id = .NoReturn,
- .typ = &Type.NoReturn.get(comp).base,
- .ref_count = std.atomic.Int(usize).init(1),
- },
- };
-
- for (CInt.list) |cint, i| {
- const c_int_type = try comp.arena().create(Type.Int);
- c_int_type.* = Type.Int{
- .base = Type{
- .name = cint.zig_name,
- .base = Value{
- .id = .Type,
- .typ = &Type.MetaType.get(comp).base,
- .ref_count = std.atomic.Int(usize).init(1),
- },
- .id = .Int,
- .abi_alignment = Type.AbiAlignment.init(),
- },
- .key = Type.Int.Key{
- .is_signed = cint.is_signed,
- .bit_count = cint.sizeInBits(comp.target),
- },
- .garbage_node = undefined,
- };
- comp.c_int_types[i] = c_int_type;
- assert((try comp.primitive_type_table.put(cint.zig_name, &c_int_type.base)) == null);
- }
- comp.u8_type = try comp.arena().create(Type.Int);
- comp.u8_type.* = Type.Int{
- .base = Type{
- .name = "u8",
- .base = Value{
- .id = .Type,
- .typ = &Type.MetaType.get(comp).base,
- .ref_count = std.atomic.Int(usize).init(1),
- },
- .id = .Int,
- .abi_alignment = Type.AbiAlignment.init(),
- },
- .key = Type.Int.Key{
- .is_signed = false,
- .bit_count = 8,
- },
- .garbage_node = undefined,
- };
- assert((try comp.primitive_type_table.put(comp.u8_type.base.name, &comp.u8_type.base)) == null);
- }
-
- pub fn destroy(self: *Compilation) void {
- const allocator = self.gpa();
- self.cancelled = true;
- await self.main_loop_frame;
- resume self.destroy_frame;
- allocator.destroy(self.destroy_frame);
- }
-
- fn start(self: *Compilation) void {
- self.main_loop_future.resolve();
- }
- fn mainLoop(self: *Compilation) callconv(.Async) void {
- // wait until start() is called
- _ = self.main_loop_future.get();
-
- var build_result = self.initialCompile();
-
- while (!self.cancelled) {
- const link_result = if (build_result) blk: {
- break :blk self.maybeLink();
- } else |err| err;
- // this makes a handy error return trace and stack trace in debug mode
- if (std.debug.runtime_safety) {
- link_result catch unreachable;
- }
-
- const compile_errors = blk: {
- const held = self.compile_errors.acquire();
- defer held.release();
- break :blk held.value.toOwnedSlice();
- };
-
- if (link_result) |_| {
- if (compile_errors.len == 0) {
- self.events.put(Event.Ok);
- } else {
- self.events.put(Event{ .Fail = compile_errors });
- }
- } else |err| {
- // if there's an error then the compile errors have dangling references
- self.gpa().free(compile_errors);
-
- self.events.put(Event{ .Error = err });
- }
-
- // First, get an item from the watch channel, waiting on the channel.
- var group = event.Group(BuildError!void).init(self.gpa());
- {
- const ev = (self.fs_watch.channel.get()) catch |err| {
- build_result = err;
- continue;
- };
- const root_scope = ev.data;
- group.call(rebuildFile, .{ self, root_scope }) catch |err| {
- build_result = err;
- continue;
- };
- }
- // Next, get all the items from the channel that are buffered up.
- while (self.fs_watch.channel.getOrNull()) |ev_or_err| {
- if (ev_or_err) |ev| {
- const root_scope = ev.data;
- group.call(rebuildFile, .{ self, root_scope }) catch |err| {
- build_result = err;
- continue;
- };
- } else |err| {
- build_result = err;
- continue;
- }
- }
- build_result = group.wait();
- }
- }
- fn rebuildFile(self: *Compilation, root_scope: *Scope.Root) callconv(.Async) BuildError!void {
- const tree_scope = blk: {
- const source_code = fs.cwd().readFileAlloc(
- self.gpa(),
- root_scope.realpath,
- max_src_size,
- ) catch |err| {
- try self.addCompileErrorCli(root_scope.realpath, "unable to open: {}", .{@errorName(err)});
- return;
- };
- errdefer self.gpa().free(source_code);
-
- const tree = try std.zig.parse(self.gpa(), source_code);
- errdefer {
- tree.deinit();
- }
-
- break :blk try Scope.AstTree.create(self, tree, root_scope);
- };
- defer tree_scope.base.deref(self);
-
- var error_it = tree_scope.tree.errors.iterator(0);
- while (error_it.next()) |parse_error| {
- const msg = try Msg.createFromParseErrorAndScope(self, tree_scope, parse_error);
- errdefer msg.destroy();
-
- try self.addCompileErrorAsync(msg);
- }
- if (tree_scope.tree.errors.len != 0) {
- return;
- }
-
- const locked_table = root_scope.decls.table.acquireWrite();
- defer locked_table.release();
-
- var decl_group = event.Group(BuildError!void).init(self.gpa());
-
- try self.rebuildChangedDecls(
- &decl_group,
- locked_table.value,
- root_scope.decls,
- &tree_scope.tree.root_node.decls,
- tree_scope,
- );
-
- try decl_group.wait();
- }
-
- fn rebuildChangedDecls(
- self: *Compilation,
- group: *event.Group(BuildError!void),
- locked_table: *Decl.Table,
- decl_scope: *Scope.Decls,
- ast_decls: *ast.Node.Root.DeclList,
- tree_scope: *Scope.AstTree,
- ) !void {
- var existing_decls = try locked_table.clone();
- defer existing_decls.deinit();
-
- var ast_it = ast_decls.iterator(0);
- while (ast_it.next()) |decl_ptr| {
- const decl = decl_ptr.*;
- switch (decl.id) {
- .Comptime => {
- const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", decl);
-
- // TODO connect existing comptime decls to updated source files
-
- try self.prelink_group.call(addCompTimeBlock, .{ self, tree_scope, &decl_scope.base, comptime_node });
- },
- .VarDecl => @panic("TODO"),
- .FnProto => {
- const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);
-
- const name = if (fn_proto.name_token) |name_token| tree_scope.tree.tokenSlice(name_token) else {
- try self.addCompileError(tree_scope, Span{
- .first = fn_proto.fn_token,
- .last = fn_proto.fn_token + 1,
- }, "missing function name", .{});
- continue;
- };
-
- if (existing_decls.remove(name)) |entry| {
- // compare new code to existing
- if (entry.value.cast(Decl.Fn)) |existing_fn_decl| {
- // Just compare the old bytes to the new bytes of the top level decl.
- // Even if the AST is technically the same, we want error messages to display
- // from the most recent source.
- const old_decl_src = existing_fn_decl.base.tree_scope.tree.getNodeSource(
- &existing_fn_decl.fn_proto.base,
- );
- const new_decl_src = tree_scope.tree.getNodeSource(&fn_proto.base);
- if (mem.eql(u8, old_decl_src, new_decl_src)) {
- // it's the same, we can skip this decl
- continue;
- } else {
- @panic("TODO decl changed implementation");
- // Add the new thing before dereferencing the old thing. This way we don't end
- // up pointlessly re-creating things we end up using in the new thing.
- }
- } else {
- @panic("TODO decl changed kind");
- }
- } else {
- // add new decl
- const fn_decl = try self.gpa().create(Decl.Fn);
- fn_decl.* = Decl.Fn{
- .base = Decl{
- .id = Decl.Id.Fn,
- .name = name,
- .visib = parseVisibToken(tree_scope.tree, fn_proto.visib_token),
- .resolution = event.Future(BuildError!void).init(),
- .parent_scope = &decl_scope.base,
- .tree_scope = tree_scope,
- },
- .value = .Unresolved,
- .fn_proto = fn_proto,
- };
- tree_scope.base.ref();
- errdefer self.gpa().destroy(fn_decl);
-
- try group.call(addTopLevelDecl, .{ self, &fn_decl.base, locked_table });
- }
- },
- .TestDecl => @panic("TODO"),
- else => unreachable,
- }
- }
-
- var existing_decl_it = existing_decls.iterator();
- while (existing_decl_it.next()) |entry| {
- // this decl was deleted
- const existing_decl = entry.value;
- @panic("TODO handle decl deletion");
- }
- }
-
- fn initialCompile(self: *Compilation) !void {
- if (self.root_src_path) |root_src_path| {
- const root_scope = blk: {
- // TODO async/await fs.realpath
- const root_src_real_path = fs.realpathAlloc(self.gpa(), root_src_path) catch |err| {
- try self.addCompileErrorCli(root_src_path, "unable to open: {}", .{@errorName(err)});
- return;
- };
- errdefer self.gpa().free(root_src_real_path);
-
- break :blk try Scope.Root.create(self, root_src_real_path);
- };
- defer root_scope.base.deref(self);
-
- // assert((try self.fs_watch.addFile(root_scope.realpath, root_scope)) == null);
- try self.rebuildFile(root_scope);
- }
- }
-
- fn maybeLink(self: *Compilation) !void {
- (self.prelink_group.wait()) catch |err| switch (err) {
- error.SemanticAnalysisFailed => {},
- else => return err,
- };
-
- const any_prelink_errors = blk: {
- const compile_errors = self.compile_errors.acquire();
- defer compile_errors.release();
-
- break :blk compile_errors.value.len != 0;
- };
-
- if (!any_prelink_errors) {
- try link(self);
- }
- }
- /// caller takes ownership of resulting Code
- fn genAndAnalyzeCode(
- comp: *Compilation,
- tree_scope: *Scope.AstTree,
- scope: *Scope,
- node: *ast.Node,
- expected_type: ?*Type,
- ) callconv(.Async) !*ir.Code {
- const unanalyzed_code = try ir.gen(
- comp,
- node,
- tree_scope,
- scope,
- );
- defer unanalyzed_code.destroy(comp.gpa());
-
- if (comp.verbose_ir) {
- std.debug.warn("unanalyzed:\n", .{});
- unanalyzed_code.dump();
- }
-
- const analyzed_code = try ir.analyze(
- comp,
- unanalyzed_code,
- expected_type,
- );
- errdefer analyzed_code.destroy(comp.gpa());
-
- if (comp.verbose_ir) {
- std.debug.warn("analyzed:\n", .{});
- analyzed_code.dump();
- }
-
- return analyzed_code;
- }
- fn addCompTimeBlock(
- comp: *Compilation,
- tree_scope: *Scope.AstTree,
- scope: *Scope,
- comptime_node: *ast.Node.Comptime,
- ) callconv(.Async) BuildError!void {
- const void_type = Type.Void.get(comp);
- defer void_type.base.base.deref(comp);
-
- const analyzed_code = genAndAnalyzeCode(
- comp,
- tree_scope,
- scope,
- comptime_node.expr,
- &void_type.base,
- ) catch |err| switch (err) {
- // This poison value should not cause the errdefers to run. It simply means
- // that comp.compile_errors is populated.
- error.SemanticAnalysisFailed => return {},
- else => return err,
- };
- analyzed_code.destroy(comp.gpa());
- }
- fn addTopLevelDecl(
- self: *Compilation,
- decl: *Decl,
- locked_table: *Decl.Table,
- ) callconv(.Async) BuildError!void {
- const is_export = decl.isExported(decl.tree_scope.tree);
-
- if (is_export) {
- try self.prelink_group.call(verifyUniqueSymbol, .{ self, decl });
- try self.prelink_group.call(resolveDecl, .{ self, decl });
- }
-
- const gop = try locked_table.getOrPut(decl.name);
- if (gop.found_existing) {
- try self.addCompileError(decl.tree_scope, decl.getSpan(), "redefinition of '{}'", .{decl.name});
- // TODO note: other definition here
- } else {
- gop.kv.value = decl;
- }
- }
-
- fn addCompileError(self: *Compilation, tree_scope: *Scope.AstTree, span: Span, comptime fmt: []const u8, args: var) !void {
- const text = try std.fmt.allocPrint(self.gpa(), fmt, args);
- errdefer self.gpa().free(text);
-
- const msg = try Msg.createFromScope(self, tree_scope, span, text);
- errdefer msg.destroy();
-
- try self.prelink_group.call(addCompileErrorAsync, .{ self, msg });
- }
-
- fn addCompileErrorCli(self: *Compilation, realpath: []const u8, comptime fmt: []const u8, args: var) !void {
- const text = try std.fmt.allocPrint(self.gpa(), fmt, args);
- errdefer self.gpa().free(text);
-
- const msg = try Msg.createFromCli(self, realpath, text);
- errdefer msg.destroy();
-
- try self.prelink_group.call(addCompileErrorAsync, .{ self, msg });
- }
- fn addCompileErrorAsync(
- self: *Compilation,
- msg: *Msg,
- ) callconv(.Async) BuildError!void {
- errdefer msg.destroy();
-
- const compile_errors = self.compile_errors.acquire();
- defer compile_errors.release();
-
- try compile_errors.value.append(msg);
- }
- fn verifyUniqueSymbol(self: *Compilation, decl: *Decl) callconv(.Async) BuildError!void {
- const exported_symbol_names = self.exported_symbol_names.acquire();
- defer exported_symbol_names.release();
-
- if (try exported_symbol_names.value.put(decl.name, decl)) |other_decl| {
- try self.addCompileError(decl.tree_scope, decl.getSpan(), "exported symbol collision: '{}'", .{
- decl.name,
- });
- // TODO add error note showing location of other symbol
- }
- }
-
- pub fn haveLibC(self: *Compilation) bool {
- return self.libc_link_lib != null;
- }
-
- pub fn addLinkLib(self: *Compilation, name: []const u8, provided_explicitly: bool) !*LinkLib {
- const is_libc = mem.eql(u8, name, "c");
-
- if (is_libc) {
- if (self.libc_link_lib) |libc_link_lib| {
- return libc_link_lib;
- }
- }
-
- for (self.link_libs_list.span()) |existing_lib| {
- if (mem.eql(u8, name, existing_lib.name)) {
- return existing_lib;
- }
- }
-
- const link_lib = try self.gpa().create(LinkLib);
- link_lib.* = LinkLib{
- .name = name,
- .path = null,
- .provided_explicitly = provided_explicitly,
- .symbols = ArrayList([]u8).init(self.gpa()),
- };
- try self.link_libs_list.append(link_lib);
- if (is_libc) {
- self.libc_link_lib = link_lib;
-
- // get a head start on looking for the native libc
- // TODO this is missing a bunch of logic related to whether the target is native
- // and whether we can build libc
- if (self.override_libc == null) {
- try self.deinit_group.call(startFindingNativeLibC, .{self});
- }
- }
- return link_lib;
- }
- fn startFindingNativeLibC(self: *Compilation) callconv(.Async) void {
- event.Loop.startCpuBoundOperation();
- // we don't care if it fails, we're just trying to kick off the future resolution
- _ = self.zig_compiler.getNativeLibC() catch return;
- }
-
- /// General Purpose Allocator. Must free when done.
- fn gpa(self: Compilation) *mem.Allocator {
- return self.zig_compiler.allocator;
- }
-
- /// Arena Allocator. Automatically freed when the Compilation is destroyed.
- fn arena(self: *Compilation) *mem.Allocator {
- return &self.arena_allocator.allocator;
- }
-
- /// If the temporary directory for this compilation has not been created, it creates it.
- /// Then it creates a random file name in that dir and returns it.
- pub fn createRandomOutputPath(self: *Compilation, suffix: []const u8) !ArrayListSentineled(u8, 0) {
- const tmp_dir = try self.getTmpDir();
- const file_prefix = self.getRandomFileName();
-
- const file_name = try std.fmt.allocPrint(self.gpa(), "{}{}", .{ file_prefix[0..], suffix });
- defer self.gpa().free(file_name);
-
- const full_path = try fs.path.join(self.gpa(), &[_][]const u8{ tmp_dir, file_name[0..] });
- errdefer self.gpa().free(full_path);
-
- return ArrayListSentineled(u8, 0).fromOwnedSlice(self.gpa(), full_path);
- }
-
- /// If the temporary directory for this Compilation has not been created, creates it.
- /// Then returns it. The directory is unique to this Compilation and cleaned up when
- /// the Compilation deinitializes.
- fn getTmpDir(self: *Compilation) ![]const u8 {
- if (self.tmp_dir.start()) |ptr| return ptr.*;
- self.tmp_dir.data = self.getTmpDirImpl();
- self.tmp_dir.resolve();
- return self.tmp_dir.data;
- }
-
- fn getTmpDirImpl(self: *Compilation) ![]u8 {
- const comp_dir_name = self.getRandomFileName();
- const zig_dir_path = try getZigDir(self.gpa());
- defer self.gpa().free(zig_dir_path);
-
- const tmp_dir = try fs.path.join(self.arena(), &[_][]const u8{ zig_dir_path, comp_dir_name[0..] });
- try fs.cwd().makePath(tmp_dir);
- return tmp_dir;
- }
-
- fn getRandomFileName(self: *Compilation) [12]u8 {
- // here we replace the standard +/ with -_ so that it can be used in a file name
- const b64_fs_encoder = std.base64.Base64Encoder.init(
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_",
- std.base64.standard_pad_char,
- );
-
- var rand_bytes: [9]u8 = undefined;
-
- {
- const held = self.zig_compiler.prng.acquire();
- defer held.release();
-
- held.value.random.bytes(rand_bytes[0..]);
- }
-
- var result: [12]u8 = undefined;
- b64_fs_encoder.encode(result[0..], &rand_bytes);
- return result;
- }
-
- fn registerGarbage(comp: *Compilation, comptime T: type, node: *std.atomic.Stack(*T).Node) void {
- // TODO put the garbage somewhere
- }
-
- /// Returns a value which has been ref()'d once
- fn analyzeConstValue(
- comp: *Compilation,
- tree_scope: *Scope.AstTree,
- scope: *Scope,
- node: *ast.Node,
- expected_type: *Type,
- ) !*Value {
- var frame = try comp.gpa().create(@Frame(genAndAnalyzeCode));
- defer comp.gpa().destroy(frame);
- frame.* = async comp.genAndAnalyzeCode(tree_scope, scope, node, expected_type);
- const analyzed_code = try await frame;
- defer analyzed_code.destroy(comp.gpa());
-
- return analyzed_code.getCompTimeResult(comp);
- }
-
- fn analyzeTypeExpr(comp: *Compilation, tree_scope: *Scope.AstTree, scope: *Scope, node: *ast.Node) !*Type {
- const meta_type = &Type.MetaType.get(comp).base;
- defer meta_type.base.deref(comp);
-
- const result_val = try comp.analyzeConstValue(tree_scope, scope, node, meta_type);
- errdefer result_val.base.deref(comp);
-
- return result_val.cast(Type).?;
- }
-
- /// This declaration has been blessed as going into the final code generation.
- pub fn resolveDecl(comp: *Compilation, decl: *Decl) callconv(.Async) BuildError!void {
- if (decl.resolution.start()) |ptr| return ptr.*;
-
- decl.resolution.data = try generateDecl(comp, decl);
- decl.resolution.resolve();
- return decl.resolution.data;
- }
-};
-
-fn parseVisibToken(tree: *ast.Tree, optional_token_index: ?ast.TokenIndex) Visib {
- if (optional_token_index) |token_index| {
- const token = tree.tokens.at(token_index);
- assert(token.id == Token.Id.Keyword_pub);
- return Visib.Pub;
- } else {
- return Visib.Private;
- }
-}
-
-/// The function that actually does the generation.
-fn generateDecl(comp: *Compilation, decl: *Decl) !void {
- switch (decl.id) {
- .Var => @panic("TODO"),
- .Fn => {
- const fn_decl = @fieldParentPtr(Decl.Fn, "base", decl);
- return generateDeclFn(comp, fn_decl);
- },
- .CompTime => @panic("TODO"),
- }
-}
-
-fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {
- const tree_scope = fn_decl.base.tree_scope;
-
- const body_node = fn_decl.fn_proto.body_node orelse return generateDeclFnProto(comp, fn_decl);
-
- const fndef_scope = try Scope.FnDef.create(comp, fn_decl.base.parent_scope);
- defer fndef_scope.base.deref(comp);
-
- const fn_type = try analyzeFnType(comp, tree_scope, fn_decl.base.parent_scope, fn_decl.fn_proto);
- defer fn_type.base.base.deref(comp);
-
- var symbol_name = try std.ArrayListSentineled(u8, 0).init(comp.gpa(), fn_decl.base.name);
- var symbol_name_consumed = false;
- errdefer if (!symbol_name_consumed) symbol_name.deinit();
-
- // The Decl.Fn owns the initial 1 reference count
- const fn_val = try Value.Fn.create(comp, fn_type, fndef_scope, symbol_name);
- fn_decl.value = .{ .Fn = fn_val };
- symbol_name_consumed = true;
-
- // Define local parameter variables
- for (fn_type.key.data.Normal.params) |param, i| {
- //AstNode *param_decl_node = get_param_decl_node(fn_table_entry, i);
- const param_decl = @fieldParentPtr(ast.Node.ParamDecl, "base", fn_decl.fn_proto.params.at(i).*);
- const name_token = param_decl.name_token orelse {
- try comp.addCompileError(tree_scope, Span{
- .first = param_decl.firstToken(),
- .last = param_decl.type_node.firstToken(),
- }, "missing parameter name", .{});
- return error.SemanticAnalysisFailed;
- };
- const param_name = tree_scope.tree.tokenSlice(name_token);
-
- // if (is_noalias && get_codegen_ptr_type(param_type) == nullptr) {
- // add_node_error(g, param_decl_node, buf_sprintf("noalias on non-pointer parameter"));
- // }
-
- // TODO check for shadowing
-
- const var_scope = try Scope.Var.createParam(
- comp,
- fn_val.child_scope,
- param_name,
- ¶m_decl.base,
- i,
- param.typ,
- );
- fn_val.child_scope = &var_scope.base;
-
- try fn_type.non_key.Normal.variable_list.append(var_scope);
- }
-
- var frame = try comp.gpa().create(@Frame(Compilation.genAndAnalyzeCode));
- defer comp.gpa().destroy(frame);
- frame.* = async comp.genAndAnalyzeCode(
- tree_scope,
- fn_val.child_scope,
- body_node,
- fn_type.key.data.Normal.return_type,
- );
- const analyzed_code = try await frame;
- errdefer analyzed_code.destroy(comp.gpa());
-
- assert(fn_val.block_scope != null);
-
- // Kick off rendering to LLVM module, but it doesn't block the fn decl
- // analysis from being complete.
- try comp.prelink_group.call(codegen.renderToLlvm, .{ comp, fn_val, analyzed_code });
- try comp.prelink_group.call(addFnToLinkSet, .{ comp, fn_val });
-}
-fn addFnToLinkSet(comp: *Compilation, fn_val: *Value.Fn) callconv(.Async) Compilation.BuildError!void {
- fn_val.base.ref();
- defer fn_val.base.deref(comp);
-
- fn_val.link_set_node.data = fn_val;
-
- const held = comp.fn_link_set.acquire();
- defer held.release();
-
- held.value.append(fn_val.link_set_node);
-}
-
-fn getZigDir(allocator: *mem.Allocator) ![]u8 {
- return fs.getAppDataDir(allocator, "zig");
-}
-
-fn analyzeFnType(
- comp: *Compilation,
- tree_scope: *Scope.AstTree,
- scope: *Scope,
- fn_proto: *ast.Node.FnProto,
-) !*Type.Fn {
- const return_type_node = switch (fn_proto.return_type) {
- .Explicit => |n| n,
- .InferErrorSet => |n| n,
- };
- const return_type = try comp.analyzeTypeExpr(tree_scope, scope, return_type_node);
- return_type.base.deref(comp);
-
- var params = ArrayList(Type.Fn.Param).init(comp.gpa());
- var params_consumed = false;
- defer if (!params_consumed) {
- for (params.span()) |param| {
- param.typ.base.deref(comp);
- }
- params.deinit();
- };
-
- {
- var it = fn_proto.params.iterator(0);
- while (it.next()) |param_node_ptr| {
- const param_node = param_node_ptr.*.cast(ast.Node.ParamDecl).?;
- const param_type = try comp.analyzeTypeExpr(tree_scope, scope, param_node.type_node);
- errdefer param_type.base.deref(comp);
- try params.append(Type.Fn.Param{
- .typ = param_type,
- .is_noalias = param_node.noalias_token != null,
- });
- }
- }
-
- const key = Type.Fn.Key{
- .alignment = null,
- .data = Type.Fn.Key.Data{
- .Normal = Type.Fn.Key.Normal{
- .return_type = return_type,
- .params = params.toOwnedSlice(),
- .is_var_args = false, // TODO
- .cc = .Unspecified, // TODO
- },
- },
- };
- params_consumed = true;
- var key_consumed = false;
- defer if (!key_consumed) {
- for (key.data.Normal.params) |param| {
- param.typ.base.deref(comp);
- }
- comp.gpa().free(key.data.Normal.params);
- };
-
- const fn_type = try Type.Fn.get(comp, key);
- key_consumed = true;
- errdefer fn_type.base.base.deref(comp);
-
- return fn_type;
-}
-
-fn generateDeclFnProto(comp: *Compilation, fn_decl: *Decl.Fn) !void {
- const fn_type = try analyzeFnType(
- comp,
- fn_decl.base.tree_scope,
- fn_decl.base.parent_scope,
- fn_decl.fn_proto,
- );
- defer fn_type.base.base.deref(comp);
-
- var symbol_name = try std.ArrayListSentineled(u8, 0).init(comp.gpa(), fn_decl.base.name);
- var symbol_name_consumed = false;
- defer if (!symbol_name_consumed) symbol_name.deinit();
-
- // The Decl.Fn owns the initial 1 reference count
- const fn_proto_val = try Value.FnProto.create(comp, fn_type, symbol_name);
- fn_decl.value = .{ .FnProto = fn_proto_val };
- symbol_name_consumed = true;
-}
-
-pub fn llvmTargetFromTriple(triple: [:0]const u8) !*llvm.Target {
- var result: *llvm.Target = undefined;
- var err_msg: [*:0]u8 = undefined;
- if (llvm.GetTargetFromTriple(triple, &result, &err_msg) != 0) {
- std.debug.warn("triple: {s} error: {s}\n", .{ triple, err_msg });
- return error.UnsupportedTarget;
- }
- return result;
-}
-
-pub fn initializeAllTargets() void {
- llvm.InitializeAllTargets();
- llvm.InitializeAllTargetInfos();
- llvm.InitializeAllTargetMCs();
- llvm.InitializeAllAsmPrinters();
- llvm.InitializeAllAsmParsers();
-}
-
-pub fn getLLVMTriple(allocator: *std.mem.Allocator, target: std.Target) ![:0]u8 {
- var result = try std.ArrayListSentineled(u8, 0).initSize(allocator, 0);
- defer result.deinit();
-
- try result.outStream().print(
- "{}-unknown-{}-{}",
- .{ @tagName(target.cpu.arch), @tagName(target.os.tag), @tagName(target.abi) },
- );
-
- return result.toOwnedSlice();
-}
src-self-hosted/errmsg.zig
@@ -1,284 +0,0 @@
-const std = @import("std");
-const mem = std.mem;
-const fs = std.fs;
-const process = std.process;
-const Token = std.zig.Token;
-const ast = std.zig.ast;
-const TokenIndex = std.zig.ast.TokenIndex;
-const Compilation = @import("compilation.zig").Compilation;
-const Scope = @import("scope.zig").Scope;
-
-pub const Color = enum {
- Auto,
- Off,
- On,
-};
-
-pub const Span = struct {
- first: ast.TokenIndex,
- last: ast.TokenIndex,
-
- pub fn token(i: TokenIndex) Span {
- return Span{
- .first = i,
- .last = i,
- };
- }
-
- pub fn node(n: *ast.Node) Span {
- return Span{
- .first = n.firstToken(),
- .last = n.lastToken(),
- };
- }
-};
-
-pub const Msg = struct {
- text: []u8,
- realpath: []u8,
- data: Data,
-
- const Data = union(enum) {
- Cli: Cli,
- PathAndTree: PathAndTree,
- ScopeAndComp: ScopeAndComp,
- };
-
- const PathAndTree = struct {
- span: Span,
- tree: *ast.Tree,
- allocator: *mem.Allocator,
- };
-
- const ScopeAndComp = struct {
- span: Span,
- tree_scope: *Scope.AstTree,
- compilation: *Compilation,
- };
-
- const Cli = struct {
- allocator: *mem.Allocator,
- };
-
- pub fn destroy(self: *Msg) void {
- switch (self.data) {
- .Cli => |cli| {
- cli.allocator.free(self.text);
- cli.allocator.free(self.realpath);
- cli.allocator.destroy(self);
- },
- .PathAndTree => |path_and_tree| {
- path_and_tree.allocator.free(self.text);
- path_and_tree.allocator.free(self.realpath);
- path_and_tree.allocator.destroy(self);
- },
- .ScopeAndComp => |scope_and_comp| {
- scope_and_comp.tree_scope.base.deref(scope_and_comp.compilation);
- scope_and_comp.compilation.gpa().free(self.text);
- scope_and_comp.compilation.gpa().free(self.realpath);
- scope_and_comp.compilation.gpa().destroy(self);
- },
- }
- }
-
- fn getAllocator(self: *const Msg) *mem.Allocator {
- switch (self.data) {
- .Cli => |cli| return cli.allocator,
- .PathAndTree => |path_and_tree| {
- return path_and_tree.allocator;
- },
- .ScopeAndComp => |scope_and_comp| {
- return scope_and_comp.compilation.gpa();
- },
- }
- }
-
- pub fn getTree(self: *const Msg) *ast.Tree {
- switch (self.data) {
- .Cli => unreachable,
- .PathAndTree => |path_and_tree| {
- return path_and_tree.tree;
- },
- .ScopeAndComp => |scope_and_comp| {
- return scope_and_comp.tree_scope.tree;
- },
- }
- }
-
- pub fn getSpan(self: *const Msg) Span {
- return switch (self.data) {
- .Cli => unreachable,
- .PathAndTree => |path_and_tree| path_and_tree.span,
- .ScopeAndComp => |scope_and_comp| scope_and_comp.span,
- };
- }
-
- /// Takes ownership of text
- /// References tree_scope, and derefs when the msg is freed
- pub fn createFromScope(comp: *Compilation, tree_scope: *Scope.AstTree, span: Span, text: []u8) !*Msg {
- const realpath = try mem.dupe(comp.gpa(), u8, tree_scope.root().realpath);
- errdefer comp.gpa().free(realpath);
-
- const msg = try comp.gpa().create(Msg);
- msg.* = Msg{
- .text = text,
- .realpath = realpath,
- .data = Data{
- .ScopeAndComp = ScopeAndComp{
- .tree_scope = tree_scope,
- .compilation = comp,
- .span = span,
- },
- },
- };
- tree_scope.base.ref();
- return msg;
- }
-
- /// Caller owns returned Msg and must free with `allocator`
- /// allocator will additionally be used for printing messages later.
- pub fn createFromCli(comp: *Compilation, realpath: []const u8, text: []u8) !*Msg {
- const realpath_copy = try mem.dupe(comp.gpa(), u8, realpath);
- errdefer comp.gpa().free(realpath_copy);
-
- const msg = try comp.gpa().create(Msg);
- msg.* = Msg{
- .text = text,
- .realpath = realpath_copy,
- .data = Data{
- .Cli = Cli{ .allocator = comp.gpa() },
- },
- };
- return msg;
- }
-
- pub fn createFromParseErrorAndScope(
- comp: *Compilation,
- tree_scope: *Scope.AstTree,
- parse_error: *const ast.Error,
- ) !*Msg {
- const loc_token = parse_error.loc();
- var text_buf = std.ArrayList(u8).init(comp.gpa());
- defer text_buf.deinit();
-
- const realpath_copy = try mem.dupe(comp.gpa(), u8, tree_scope.root().realpath);
- errdefer comp.gpa().free(realpath_copy);
-
- try parse_error.render(&tree_scope.tree.tokens, text_buf.outStream());
-
- const msg = try comp.gpa().create(Msg);
- msg.* = Msg{
- .text = undefined,
- .realpath = realpath_copy,
- .data = Data{
- .ScopeAndComp = ScopeAndComp{
- .tree_scope = tree_scope,
- .compilation = comp,
- .span = Span{
- .first = loc_token,
- .last = loc_token,
- },
- },
- },
- };
- tree_scope.base.ref();
- msg.text = text_buf.toOwnedSlice();
- return msg;
- }
-
- /// `realpath` must outlive the returned Msg
- /// `tree` must outlive the returned Msg
- /// Caller owns returned Msg and must free with `allocator`
- /// allocator will additionally be used for printing messages later.
- pub fn createFromParseError(
- allocator: *mem.Allocator,
- parse_error: *const ast.Error,
- tree: *ast.Tree,
- realpath: []const u8,
- ) !*Msg {
- const loc_token = parse_error.loc();
- var text_buf = std.ArrayList(u8).init(allocator);
- defer text_buf.deinit();
-
- const realpath_copy = try mem.dupe(allocator, u8, realpath);
- errdefer allocator.free(realpath_copy);
-
- try parse_error.render(&tree.tokens, text_buf.outStream());
-
- const msg = try allocator.create(Msg);
- msg.* = Msg{
- .text = undefined,
- .realpath = realpath_copy,
- .data = Data{
- .PathAndTree = PathAndTree{
- .allocator = allocator,
- .tree = tree,
- .span = Span{
- .first = loc_token,
- .last = loc_token,
- },
- },
- },
- };
- msg.text = text_buf.toOwnedSlice();
- errdefer allocator.destroy(msg);
-
- return msg;
- }
-
- pub fn printToStream(msg: *const Msg, stream: var, color_on: bool) !void {
- switch (msg.data) {
- .Cli => {
- try stream.print("{}:-:-: error: {}\n", .{ msg.realpath, msg.text });
- return;
- },
- else => {},
- }
-
- const allocator = msg.getAllocator();
- const tree = msg.getTree();
-
- const cwd = try process.getCwdAlloc(allocator);
- defer allocator.free(cwd);
-
- const relpath = try fs.path.relative(allocator, cwd, msg.realpath);
- defer allocator.free(relpath);
-
- const path = if (relpath.len < msg.realpath.len) relpath else msg.realpath;
- const span = msg.getSpan();
-
- const first_token = tree.tokens.at(span.first);
- const last_token = tree.tokens.at(span.last);
- const start_loc = tree.tokenLocationPtr(0, first_token);
- const end_loc = tree.tokenLocationPtr(first_token.end, last_token);
- if (!color_on) {
- try stream.print("{}:{}:{}: error: {}\n", .{
- path,
- start_loc.line + 1,
- start_loc.column + 1,
- msg.text,
- });
- return;
- }
-
- try stream.print("{}:{}:{}: error: {}\n{}\n", .{
- path,
- start_loc.line + 1,
- start_loc.column + 1,
- msg.text,
- tree.source[start_loc.line_start..start_loc.line_end],
- });
- try stream.writeByteNTimes(' ', start_loc.column);
- try stream.writeByteNTimes('~', last_token.end - first_token.start);
- try stream.writeAll("\n");
- }
-
- pub fn printToFile(msg: *const Msg, file: fs.File, color: Color) !void {
- const color_on = switch (color) {
- .Auto => file.isTty(),
- .On => true,
- .Off => false,
- };
- return msg.printToStream(file.outStream(), color_on);
- }
-};
src-self-hosted/ir.zig
@@ -922,7 +922,6 @@ pub const Module = struct {
if (self.decl_table.get(hash)) |kv| {
return kv.value;
} else {
- std.debug.warn("creating new decl for {}\n", .{old_inst.name});
const new_decl = blk: {
try self.decl_table.ensureCapacity(self.decl_table.size + 1);
const new_decl = try self.allocator.create(Decl);
@@ -2161,101 +2160,3 @@ pub const ErrorMsg = struct {
self.* = undefined;
}
};
-
-pub fn main() anyerror!void {
- var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
- defer arena.deinit();
- const allocator = if (std.builtin.link_libc) std.heap.c_allocator else &arena.allocator;
-
- const args = try std.process.argsAlloc(allocator);
- defer std.process.argsFree(allocator, args);
-
- const src_path = args[1];
- const bin_path = args[2];
- const debug_error_trace = false;
- const output_zir = false;
- const object_format: ?std.builtin.ObjectFormat = null;
-
- const native_info = try std.zig.system.NativeTargetInfo.detect(allocator, .{});
-
- var bin_file = try link.openBinFilePath(allocator, std.fs.cwd(), bin_path, .{
- .target = native_info.target,
- .output_mode = .Exe,
- .link_mode = .Static,
- .object_format = object_format orelse native_info.target.getObjectFormat(),
- });
- defer bin_file.deinit();
-
- var module = blk: {
- const root_pkg = try Package.create(allocator, std.fs.cwd(), ".", src_path);
- errdefer root_pkg.destroy();
-
- const root_scope = try allocator.create(Module.Scope.ZIRModule);
- errdefer allocator.destroy(root_scope);
- root_scope.* = .{
- .sub_file_path = root_pkg.root_src_path,
- .source = .{ .unloaded = {} },
- .contents = .{ .not_available = {} },
- .status = .never_loaded,
- };
-
- break :blk Module{
- .allocator = allocator,
- .root_pkg = root_pkg,
- .root_scope = root_scope,
- .bin_file = &bin_file,
- .optimize_mode = .Debug,
- .decl_table = std.AutoHashMap(Module.Decl.Hash, *Module.Decl).init(allocator),
- .decl_exports = std.AutoHashMap(*Module.Decl, []*Module.Export).init(allocator),
- .export_owners = std.AutoHashMap(*Module.Decl, []*Module.Export).init(allocator),
- .failed_decls = std.AutoHashMap(*Module.Decl, *ErrorMsg).init(allocator),
- .failed_files = std.AutoHashMap(*Module.Scope.ZIRModule, *ErrorMsg).init(allocator),
- .failed_exports = std.AutoHashMap(*Module.Export, *ErrorMsg).init(allocator),
- .work_queue = std.fifo.LinearFifo(Module.WorkItem, .Dynamic).init(allocator),
- };
- };
- defer module.deinit();
-
- const stdin = std.io.getStdIn().inStream();
- const stderr = std.io.getStdErr().outStream();
- var repl_buf: [1024]u8 = undefined;
-
- while (true) {
- try module.update();
-
- var errors = try module.getAllErrorsAlloc();
- defer errors.deinit(allocator);
-
- if (errors.list.len != 0) {
- for (errors.list) |full_err_msg| {
- std.debug.warn("{}:{}:{}: error: {}\n", .{
- full_err_msg.src_path,
- full_err_msg.line + 1,
- full_err_msg.column + 1,
- full_err_msg.msg,
- });
- }
- if (debug_error_trace) return error.AnalysisFail;
- }
-
- try stderr.print("๐ฆ ", .{});
- if (try stdin.readUntilDelimiterOrEof(&repl_buf, '\n')) |line| {
- if (mem.eql(u8, line, "update")) {
- continue;
- } else {
- try stderr.print("unknown command: {}\n", .{line});
- }
- } else {
- break;
- }
- }
-
- if (output_zir) {
- var new_zir_module = try text.emit_zir(allocator, module);
- defer new_zir_module.deinit(allocator);
-
- var bos = std.io.bufferedOutStream(std.io.getStdOut().outStream());
- try new_zir_module.writeToStream(allocator, bos.outStream());
- try bos.flush();
- }
-}
src-self-hosted/main.zig
@@ -1,29 +1,30 @@
const std = @import("std");
-const builtin = @import("builtin");
-
-const event = std.event;
-const os = std.os;
const io = std.io;
const fs = std.fs;
const mem = std.mem;
const process = std.process;
const Allocator = mem.Allocator;
const ArrayList = std.ArrayList;
+const ast = std.zig.ast;
+const ir = @import("ir.zig");
+const link = @import("link.zig");
+const Package = @import("Package.zig");
-const c = @import("c.zig");
-const introspect = @import("introspect.zig");
-const ZigCompiler = @import("compilation.zig").ZigCompiler;
-const Compilation = @import("compilation.zig").Compilation;
-const Target = std.Target;
-const errmsg = @import("errmsg.zig");
const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
-pub const io_mode = .evented;
+// TODO Improve async I/O enough that we feel comfortable doing this.
+//pub const io_mode = .evented;
pub const max_src_size = 2 * 1024 * 1024 * 1024; // 2 GiB
+pub const Color = enum {
+ Auto,
+ Off,
+ On,
+};
+
const usage =
- \\usage: zig [command] [options]
+ \\Usage: zig [command] [options]
\\
\\Commands:
\\
@@ -39,175 +40,154 @@ const usage =
\\
;
-const Command = struct {
- name: []const u8,
- exec: async fn (*Allocator, []const []const u8) anyerror!void,
-};
-
pub fn main() !void {
- const allocator = std.heap.c_allocator;
-
- const stderr = io.getStdErr().outStream();
+ // TODO general purpose allocator in the zig std lib
+ const gpa = if (std.builtin.link_libc) std.heap.c_allocator else std.heap.page_allocator;
+ var arena_instance = std.heap.ArenaAllocator.init(gpa);
+ defer arena_instance.deinit();
+ const arena = &arena_instance.allocator;
- const args = try process.argsAlloc(allocator);
- defer process.argsFree(allocator, args);
+ const args = try process.argsAlloc(arena);
if (args.len <= 1) {
- try stderr.writeAll("expected command argument\n\n");
- try stderr.writeAll(usage);
+ std.debug.warn("expected command argument\n\n{}", .{usage});
process.exit(1);
}
const cmd = args[1];
const cmd_args = args[2..];
if (mem.eql(u8, cmd, "build-exe")) {
- return buildOutputType(allocator, cmd_args, .Exe);
+ return buildOutputType(gpa, arena, cmd_args, .Exe);
} else if (mem.eql(u8, cmd, "build-lib")) {
- return buildOutputType(allocator, cmd_args, .Lib);
+ return buildOutputType(gpa, arena, cmd_args, .Lib);
} else if (mem.eql(u8, cmd, "build-obj")) {
- return buildOutputType(allocator, cmd_args, .Obj);
+ return buildOutputType(gpa, arena, cmd_args, .Obj);
} else if (mem.eql(u8, cmd, "fmt")) {
- return cmdFmt(allocator, cmd_args);
+ return cmdFmt(gpa, cmd_args);
} else if (mem.eql(u8, cmd, "libc")) {
- return cmdLibC(allocator, cmd_args);
+ return cmdLibC(gpa, cmd_args);
} else if (mem.eql(u8, cmd, "targets")) {
- const info = try std.zig.system.NativeTargetInfo.detect(allocator, .{});
+ const info = try std.zig.system.NativeTargetInfo.detect(arena, .{});
const stdout = io.getStdOut().outStream();
- return @import("print_targets.zig").cmdTargets(allocator, cmd_args, stdout, info.target);
+ return @import("print_targets.zig").cmdTargets(arena, cmd_args, stdout, info.target);
} else if (mem.eql(u8, cmd, "version")) {
- return cmdVersion(allocator, cmd_args);
+ // Need to set up the build script to give the version as a comptime value.
+ std.debug.warn("TODO version command not implemented yet\n", .{});
+ return error.Unimplemented;
} else if (mem.eql(u8, cmd, "zen")) {
- return cmdZen(allocator, cmd_args);
+ try io.getStdOut().writeAll(info_zen);
} else if (mem.eql(u8, cmd, "help")) {
- return cmdHelp(allocator, cmd_args);
- } else if (mem.eql(u8, cmd, "internal")) {
- return cmdInternal(allocator, cmd_args);
+ try io.getStdOut().writeAll(usage);
} else {
- try stderr.print("unknown command: {}\n\n", .{args[1]});
- try stderr.writeAll(usage);
+ std.debug.warn("unknown command: {}\n\n{}", .{ args[1], usage });
process.exit(1);
}
}
const usage_build_generic =
- \\usage: zig build-exe <options> [file]
- \\ zig build-lib <options> [file]
- \\ zig build-obj <options> [file]
+ \\Usage: zig build-exe <options> [files]
+ \\ zig build-lib <options> [files]
+ \\ zig build-obj <options> [files]
+ \\
+ \\Supported file types:
+ \\ (planned) .zig Zig source code
+ \\ .zir Zig Intermediate Representation code
+ \\ (planned) .o ELF object file
+ \\ (planned) .o MACH-O (macOS) object file
+ \\ (planned) .obj COFF (Windows) object file
+ \\ (planned) .lib COFF (Windows) static library
+ \\ (planned) .a ELF static library
+ \\ (planned) .so ELF shared object (dynamic link)
+ \\ (planned) .dll Windows Dynamic Link Library
+ \\ (planned) .dylib MACH-O (macOS) dynamic library
+ \\ (planned) .s Target-specific assembly source code
+ \\ (planned) .S Assembly with C preprocessor (requires LLVM extensions)
+ \\ (planned) .c C source code (requires LLVM extensions)
+ \\ (planned) .cpp C++ source code (requires LLVM extensions)
+ \\ Other C++ extensions: .C .cc .cxx
\\
\\General Options:
- \\ --help Print this help and exit
- \\ --color [auto|off|on] Enable or disable colored error messages
+ \\ -h, --help Print this help and exit
+ \\ --watch Enable compiler REPL
+ \\ --color [auto|off|on] Enable or disable colored error messages
+ \\ -femit-bin[=path] (default) output machine code
+ \\ -fno-emit-bin Do not output machine code
\\
\\Compile Options:
- \\ --libc [file] Provide a file which specifies libc paths
- \\ --assembly [source] Add assembly file to build
- \\ --emit [filetype] Emit a specific file format as compilation output
- \\ --enable-timing-info Print timing diagnostics
- \\ --name [name] Override output name
- \\ --output [file] Override destination path
- \\ --output-h [file] Override generated header file path
- \\ --pkg-begin [name] [path] Make package available to import and push current pkg
- \\ --pkg-end Pop current pkg
- \\ --mode [mode] Set the build mode
- \\ debug (default) optimizations off, safety on
- \\ release-fast optimizations on, safety off
- \\ release-safe optimizations on, safety on
- \\ release-small optimize for small binary, safety off
- \\ --static Output will be statically linked
- \\ --strip Exclude debug symbols
- \\ -target [name] <arch><sub>-<os>-<abi> see the targets command
- \\ --eh-frame-hdr enable C++ exception handling by passing --eh-frame-hdr to linker
- \\ --verbose-tokenize Turn on compiler debug output for tokenization
- \\ --verbose-ast-tree Turn on compiler debug output for parsing into an AST (tree view)
- \\ --verbose-ast-fmt Turn on compiler debug output for parsing into an AST (render source)
- \\ --verbose-link Turn on compiler debug output for linking
- \\ --verbose-ir Turn on compiler debug output for Zig IR
- \\ --verbose-llvm-ir Turn on compiler debug output for LLVM IR
- \\ --verbose-cimport Turn on compiler debug output for C imports
- \\ -dirafter [dir] Same as -isystem but do it last
- \\ -isystem [dir] Add additional search path for other .h files
- \\ -mllvm [arg] Additional arguments to forward to LLVM's option processing
+ \\ -target [name] <arch><sub>-<os>-<abi> see the targets command
+ \\ -mcpu [cpu] Specify target CPU and feature set
+ \\ --name [name] Override output name
+ \\ --mode [mode] Set the build mode
+ \\ Debug (default) optimizations off, safety on
+ \\ ReleaseFast optimizations on, safety off
+ \\ ReleaseSafe optimizations on, safety on
+ \\ ReleaseSmall optimize for small binary, safety off
+ \\ --dynamic Force output to be dynamically linked
+ \\ --strip Exclude debug symbols
\\
\\Link Options:
- \\ --ar-path [path] Set the path to ar
- \\ --each-lib-rpath Add rpath for each used dynamic library
- \\ --library [lib] Link against lib
- \\ --forbid-library [lib] Make it an error to link against lib
- \\ --library-path [dir] Add a directory to the library search path
- \\ --linker-script [path] Use a custom linker script
- \\ --object [obj] Add object file to build
- \\ -rdynamic Add all symbols to the dynamic symbol table
- \\ -rpath [path] Add directory to the runtime library search path
- \\ -framework [name] (darwin) link against framework
- \\ -mios-version-min [ver] (darwin) set iOS deployment target
- \\ -mmacosx-version-min [ver] (darwin) set Mac OS X deployment target
- \\ --ver-major [ver] Dynamic library semver major version
- \\ --ver-minor [ver] Dynamic library semver minor version
- \\ --ver-patch [ver] Dynamic library semver patch version
+ \\ -l[lib], --library [lib] Link against system library
+ \\ --dynamic-linker [path] Set the dynamic interpreter path (usually ld.so)
+ \\ --version [ver] Dynamic library semver
\\
+ \\Debug Options (Zig Compiler Development):
+ \\ -ftime-report Print timing diagnostics
+ \\ --debug-tokenize verbose tokenization
+ \\ --debug-ast-tree verbose parsing into an AST (tree view)
+ \\ --debug-ast-fmt verbose parsing into an AST (render source)
+ \\ --debug-ir verbose Zig IR
+ \\ --debug-link verbose linking
+ \\ --debug-codegen verbose machine code generation
\\
;
-fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Compilation.Kind) !void {
- const stderr = io.getStdErr().outStream();
+const Emit = union(enum) {
+ no,
+ yes_default_path,
+ yes: []const u8,
+};
- var color: errmsg.Color = .Auto;
+fn buildOutputType(
+ gpa: *Allocator,
+ arena: *Allocator,
+ args: []const []const u8,
+ output_mode: std.builtin.OutputMode,
+) !void {
+ var color: Color = .Auto;
var build_mode: std.builtin.Mode = .Debug;
- var emit_bin = true;
- var emit_asm = false;
- var emit_llvm_ir = false;
- var emit_h = false;
var provided_name: ?[]const u8 = null;
var is_dynamic = false;
var root_src_file: ?[]const u8 = null;
- var libc_arg: ?[]const u8 = null;
var version: std.builtin.Version = .{ .major = 0, .minor = 0, .patch = 0 };
- var linker_script: ?[]const u8 = null;
var strip = false;
- var verbose_tokenize = false;
- var verbose_ast_tree = false;
- var verbose_ast_fmt = false;
- var verbose_link = false;
- var verbose_ir = false;
- var verbose_llvm_ir = false;
- var verbose_cimport = false;
- var linker_rdynamic = false;
- var link_eh_frame_hdr = false;
- var macosx_version_min: ?[]const u8 = null;
- var ios_version_min: ?[]const u8 = null;
-
- var assembly_files = ArrayList([]const u8).init(allocator);
- defer assembly_files.deinit();
-
- var link_objects = ArrayList([]const u8).init(allocator);
- defer link_objects.deinit();
-
- var clang_argv_buf = ArrayList([]const u8).init(allocator);
- defer clang_argv_buf.deinit();
-
- var mllvm_flags = ArrayList([]const u8).init(allocator);
- defer mllvm_flags.deinit();
-
- var cur_pkg = try CliPkg.init(allocator, "", "", null);
- defer cur_pkg.deinit();
-
- var system_libs = ArrayList([]const u8).init(allocator);
+ var watch = false;
+ var debug_tokenize = false;
+ var debug_ast_tree = false;
+ var debug_ast_fmt = false;
+ var debug_link = false;
+ var debug_ir = false;
+ var debug_codegen = false;
+ var time_report = false;
+ var emit_bin: Emit = .yes_default_path;
+ var emit_zir: Emit = .no;
+ var target_arch_os_abi: []const u8 = "native";
+ var target_mcpu: ?[]const u8 = null;
+ var target_dynamic_linker: ?[]const u8 = null;
+
+ var system_libs = std.ArrayList([]const u8).init(gpa);
defer system_libs.deinit();
- var c_src_files = ArrayList([]const u8).init(allocator);
- defer c_src_files.deinit();
-
{
var i: usize = 0;
while (i < args.len) : (i += 1) {
const arg = args[i];
if (mem.startsWith(u8, arg, "-")) {
- if (mem.eql(u8, arg, "--help")) {
+ if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
try io.getStdOut().writeAll(usage_build_generic);
process.exit(0);
} else if (mem.eql(u8, arg, "--color")) {
if (i + 1 >= args.len) {
- try stderr.writeAll("expected [auto|on|off] after --color\n");
+ std.debug.warn("expected [auto|on|off] after --color\n", .{});
process.exit(1);
}
i += 1;
@@ -219,12 +199,12 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
} else if (mem.eql(u8, next_arg, "off")) {
color = .Off;
} else {
- try stderr.print("expected [auto|on|off] after --color, found '{}'\n", .{next_arg});
+ std.debug.warn("expected [auto|on|off] after --color, found '{}'\n", .{next_arg});
process.exit(1);
}
} else if (mem.eql(u8, arg, "--mode")) {
if (i + 1 >= args.len) {
- try stderr.writeAll("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode\n");
+ std.debug.warn("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode\n", .{});
process.exit(1);
}
i += 1;
@@ -238,289 +218,317 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
} else if (mem.eql(u8, next_arg, "ReleaseSmall")) {
build_mode = .ReleaseSmall;
} else {
- try stderr.print("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode, found '{}'\n", .{next_arg});
+ std.debug.warn("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode, found '{}'\n", .{next_arg});
process.exit(1);
}
} else if (mem.eql(u8, arg, "--name")) {
if (i + 1 >= args.len) {
- try stderr.writeAll("expected parameter after --name\n");
+ std.debug.warn("expected parameter after --name\n", .{});
process.exit(1);
}
i += 1;
provided_name = args[i];
- } else if (mem.eql(u8, arg, "--ver-major")) {
- if (i + 1 >= args.len) {
- try stderr.writeAll("expected parameter after --ver-major\n");
- process.exit(1);
- }
- i += 1;
- version.major = try std.fmt.parseInt(u32, args[i], 10);
- } else if (mem.eql(u8, arg, "--ver-minor")) {
+ } else if (mem.eql(u8, arg, "--library")) {
if (i + 1 >= args.len) {
- try stderr.writeAll("expected parameter after --ver-minor\n");
+ std.debug.warn("expected parameter after --library\n", .{});
process.exit(1);
}
i += 1;
- version.minor = try std.fmt.parseInt(u32, args[i], 10);
- } else if (mem.eql(u8, arg, "--ver-patch")) {
+ try system_libs.append(args[i]);
+ } else if (mem.eql(u8, arg, "--version")) {
if (i + 1 >= args.len) {
- try stderr.writeAll("expected parameter after --ver-patch\n");
+ std.debug.warn("expected parameter after --version\n", .{});
process.exit(1);
}
i += 1;
- version.patch = try std.fmt.parseInt(u32, args[i], 10);
- } else if (mem.eql(u8, arg, "--linker-script")) {
- if (i + 1 >= args.len) {
- try stderr.writeAll("expected parameter after --linker-script\n");
- process.exit(1);
- }
- i += 1;
- linker_script = args[i];
- } else if (mem.eql(u8, arg, "--libc")) {
- if (i + 1 >= args.len) {
- try stderr.writeAll("expected parameter after --libc\n");
+ version = std.builtin.Version.parse(args[i]) catch |err| {
+ std.debug.warn("unable to parse --version '{}': {}\n", .{ args[i], @errorName(err) });
process.exit(1);
- }
- i += 1;
- libc_arg = args[i];
- } else if (mem.eql(u8, arg, "-mllvm")) {
+ };
+ } else if (mem.eql(u8, arg, "-target")) {
if (i + 1 >= args.len) {
- try stderr.writeAll("expected parameter after -mllvm\n");
+ std.debug.warn("expected parameter after -target\n", .{});
process.exit(1);
}
i += 1;
- try clang_argv_buf.append("-mllvm");
- try clang_argv_buf.append(args[i]);
-
- try mllvm_flags.append(args[i]);
- } else if (mem.eql(u8, arg, "-mmacosx-version-min")) {
+ target_arch_os_abi = args[i];
+ } else if (mem.eql(u8, arg, "-mcpu")) {
if (i + 1 >= args.len) {
- try stderr.writeAll("expected parameter after -mmacosx-version-min\n");
+ std.debug.warn("expected parameter after -mcpu\n", .{});
process.exit(1);
}
i += 1;
- macosx_version_min = args[i];
- } else if (mem.eql(u8, arg, "-mios-version-min")) {
+ target_mcpu = args[i];
+ } else if (mem.startsWith(u8, arg, "-mcpu=")) {
+ target_mcpu = arg["-mcpu=".len..];
+ } else if (mem.eql(u8, arg, "--dynamic-linker")) {
if (i + 1 >= args.len) {
- try stderr.writeAll("expected parameter after -mios-version-min\n");
+ std.debug.warn("expected parameter after --dynamic-linker\n", .{});
process.exit(1);
}
i += 1;
- ios_version_min = args[i];
+ target_dynamic_linker = args[i];
+ } else if (mem.eql(u8, arg, "--watch")) {
+ watch = true;
+ } else if (mem.eql(u8, arg, "-ftime-report")) {
+ time_report = true;
} else if (mem.eql(u8, arg, "-femit-bin")) {
- emit_bin = true;
+ emit_bin = .yes_default_path;
+ } else if (mem.startsWith(u8, arg, "-femit-bin=")) {
+ emit_bin = .{ .yes = arg["-femit-bin=".len..] };
} else if (mem.eql(u8, arg, "-fno-emit-bin")) {
- emit_bin = false;
- } else if (mem.eql(u8, arg, "-femit-asm")) {
- emit_asm = true;
- } else if (mem.eql(u8, arg, "-fno-emit-asm")) {
- emit_asm = false;
- } else if (mem.eql(u8, arg, "-femit-llvm-ir")) {
- emit_llvm_ir = true;
- } else if (mem.eql(u8, arg, "-fno-emit-llvm-ir")) {
- emit_llvm_ir = false;
+ emit_bin = .no;
+ } else if (mem.eql(u8, arg, "-femit-zir")) {
+ emit_zir = .yes_default_path;
+ } else if (mem.startsWith(u8, arg, "-femit-zir=")) {
+ emit_zir = .{ .yes = arg["-femit-zir=".len..] };
+ } else if (mem.eql(u8, arg, "-fno-emit-zir")) {
+ emit_zir = .no;
} else if (mem.eql(u8, arg, "-dynamic")) {
is_dynamic = true;
} else if (mem.eql(u8, arg, "--strip")) {
strip = true;
- } else if (mem.eql(u8, arg, "--verbose-tokenize")) {
- verbose_tokenize = true;
- } else if (mem.eql(u8, arg, "--verbose-ast-tree")) {
- verbose_ast_tree = true;
- } else if (mem.eql(u8, arg, "--verbose-ast-fmt")) {
- verbose_ast_fmt = true;
- } else if (mem.eql(u8, arg, "--verbose-link")) {
- verbose_link = true;
- } else if (mem.eql(u8, arg, "--verbose-ir")) {
- verbose_ir = true;
- } else if (mem.eql(u8, arg, "--verbose-llvm-ir")) {
- verbose_llvm_ir = true;
- } else if (mem.eql(u8, arg, "--eh-frame-hdr")) {
- link_eh_frame_hdr = true;
- } else if (mem.eql(u8, arg, "--verbose-cimport")) {
- verbose_cimport = true;
- } else if (mem.eql(u8, arg, "-rdynamic")) {
- linker_rdynamic = true;
- } else if (mem.eql(u8, arg, "--pkg-begin")) {
- if (i + 2 >= args.len) {
- try stderr.writeAll("expected [name] [path] after --pkg-begin\n");
- process.exit(1);
- }
- i += 1;
- const new_pkg_name = args[i];
- i += 1;
- const new_pkg_path = args[i];
-
- var new_cur_pkg = try CliPkg.init(allocator, new_pkg_name, new_pkg_path, cur_pkg);
- try cur_pkg.children.append(new_cur_pkg);
- cur_pkg = new_cur_pkg;
- } else if (mem.eql(u8, arg, "--pkg-end")) {
- if (cur_pkg.parent) |parent| {
- cur_pkg = parent;
- } else {
- try stderr.writeAll("encountered --pkg-end with no matching --pkg-begin\n");
- process.exit(1);
- }
+ } else if (mem.eql(u8, arg, "--debug-tokenize")) {
+ debug_tokenize = true;
+ } else if (mem.eql(u8, arg, "--debug-ast-tree")) {
+ debug_ast_tree = true;
+ } else if (mem.eql(u8, arg, "--debug-ast-fmt")) {
+ debug_ast_fmt = true;
+ } else if (mem.eql(u8, arg, "--debug-link")) {
+ debug_link = true;
+ } else if (mem.eql(u8, arg, "--debug-ir")) {
+ debug_ir = true;
+ } else if (mem.eql(u8, arg, "--debug-codegen")) {
+ debug_codegen = true;
} else if (mem.startsWith(u8, arg, "-l")) {
try system_libs.append(arg[2..]);
} else {
- try stderr.print("unrecognized parameter: '{}'", .{arg});
+ std.debug.warn("unrecognized parameter: '{}'", .{arg});
process.exit(1);
}
- } else if (mem.endsWith(u8, arg, ".s")) {
- try assembly_files.append(arg);
+ } else if (mem.endsWith(u8, arg, ".s") or mem.endsWith(u8, arg, ".S")) {
+ std.debug.warn("assembly files not supported yet", .{});
+ process.exit(1);
} else if (mem.endsWith(u8, arg, ".o") or
mem.endsWith(u8, arg, ".obj") or
mem.endsWith(u8, arg, ".a") or
mem.endsWith(u8, arg, ".lib"))
{
- try link_objects.append(arg);
+ std.debug.warn("object files and static libraries not supported yet", .{});
+ process.exit(1);
} else if (mem.endsWith(u8, arg, ".c") or
mem.endsWith(u8, arg, ".cpp"))
{
- try c_src_files.append(arg);
- } else if (mem.endsWith(u8, arg, ".zig")) {
+ std.debug.warn("compilation of C and C++ source code requires LLVM extensions which are not implemented yet", .{});
+ process.exit(1);
+ } else if (mem.endsWith(u8, arg, ".so") or
+ mem.endsWith(u8, arg, ".dylib") or
+ mem.endsWith(u8, arg, ".dll"))
+ {
+ std.debug.warn("linking against dynamic libraries not yet supported", .{});
+ process.exit(1);
+ } else if (mem.endsWith(u8, arg, ".zig") or mem.endsWith(u8, arg, ".zir")) {
if (root_src_file) |other| {
- try stderr.print("found another zig file '{}' after root source file '{}'", .{
- arg,
- other,
- });
+ std.debug.warn("found another zig file '{}' after root source file '{}'", .{ arg, other });
process.exit(1);
} else {
root_src_file = arg;
}
} else {
- try stderr.print("unrecognized file extension of parameter '{}'", .{arg});
+ std.debug.warn("unrecognized file extension of parameter '{}'", .{arg});
}
}
}
- if (cur_pkg.parent != null) {
- try stderr.print("unmatched --pkg-begin\n", .{});
- process.exit(1);
- }
-
const root_name = if (provided_name) |n| n else blk: {
if (root_src_file) |file| {
const basename = fs.path.basename(file);
var it = mem.split(basename, ".");
break :blk it.next() orelse basename;
} else {
- try stderr.writeAll("--name [name] not provided and unable to infer\n");
+ std.debug.warn("--name [name] not provided and unable to infer\n", .{});
process.exit(1);
}
};
- if (root_src_file == null and link_objects.len == 0 and assembly_files.len == 0) {
- try stderr.writeAll("Expected source file argument or at least one --object or --assembly argument\n");
+ if (system_libs.items.len != 0) {
+ std.debug.warn("linking against system libraries not yet supported", .{});
process.exit(1);
}
- if (out_type == Compilation.Kind.Obj and link_objects.len != 0) {
- try stderr.writeAll("When building an object file, --object arguments are invalid\n");
+ var diags: std.zig.CrossTarget.ParseOptions.Diagnostics = .{};
+ const cross_target = std.zig.CrossTarget.parse(.{
+ .arch_os_abi = target_arch_os_abi,
+ .cpu_features = target_mcpu,
+ .dynamic_linker = target_dynamic_linker,
+ .diagnostics = &diags,
+ }) catch |err| switch (err) {
+ error.UnknownCpuModel => {
+ std.debug.warn("Unknown CPU: '{}'\nAvailable CPUs for architecture '{}':\n", .{
+ diags.cpu_name.?,
+ @tagName(diags.arch.?),
+ });
+ for (diags.arch.?.allCpuModels()) |cpu| {
+ std.debug.warn(" {}\n", .{cpu.name});
+ }
+ process.exit(1);
+ },
+ error.UnknownCpuFeature => {
+ std.debug.warn(
+ \\Unknown CPU feature: '{}'
+ \\Available CPU features for architecture '{}':
+ \\
+ , .{
+ diags.unknown_feature_name,
+ @tagName(diags.arch.?),
+ });
+ for (diags.arch.?.allFeaturesList()) |feature| {
+ std.debug.warn(" {}: {}\n", .{ feature.name, feature.description });
+ }
+ process.exit(1);
+ },
+ else => |e| return e,
+ };
+
+ const object_format: ?std.builtin.ObjectFormat = null;
+ var target_info = try std.zig.system.NativeTargetInfo.detect(gpa, cross_target);
+ if (target_info.cpu_detection_unimplemented) {
+ // TODO We want to just use detected_info.target but implementing
+ // CPU model & feature detection is todo so here we rely on LLVM.
+ std.debug.warn("CPU features detection is not yet available for this system without LLVM extensions\n", .{});
process.exit(1);
}
- try ZigCompiler.setLlvmArgv(allocator, mllvm_flags.span());
-
- const zig_lib_dir = introspect.resolveZigLibDir(allocator) catch process.exit(1);
- defer allocator.free(zig_lib_dir);
-
- var override_libc: LibCInstallation = undefined;
+ const src_path = root_src_file orelse {
+ std.debug.warn("expected at least one file argument", .{});
+ process.exit(1);
+ };
- var zig_compiler = try ZigCompiler.init(allocator);
- defer zig_compiler.deinit();
+ const bin_path = switch (emit_bin) {
+ .no => {
+ std.debug.warn("-fno-emit-bin not supported yet", .{});
+ process.exit(1);
+ },
+ .yes_default_path => try std.fmt.allocPrint(arena, "{}{}", .{ root_name, target_info.target.exeFileExt() }),
+ .yes => |p| p,
+ };
- var comp = try Compilation.create(
- &zig_compiler,
- root_name,
- root_src_file,
- .{},
- out_type,
- build_mode,
- !is_dynamic,
- zig_lib_dir,
- );
- defer comp.destroy();
+ const zir_out_path: ?[]const u8 = switch (emit_zir) {
+ .no => null,
+ .yes_default_path => blk: {
+ if (root_src_file) |rsf| {
+ if (mem.endsWith(u8, rsf, ".zir")) {
+ break :blk try std.fmt.allocPrint(arena, "{}.out.zir", .{root_name});
+ }
+ }
+ break :blk try std.fmt.allocPrint(arena, "{}.zir", .{root_name});
+ },
+ .yes => |p| p,
+ };
- if (libc_arg) |libc_path| {
- parseLibcPaths(allocator, &override_libc, libc_path);
- comp.override_libc = &override_libc;
- }
+ var bin_file = try link.openBinFilePath(gpa, fs.cwd(), bin_path, .{
+ .target = target_info.target,
+ .output_mode = output_mode,
+ .link_mode = if (is_dynamic) .Dynamic else .Static,
+ .object_format = object_format orelse target_info.target.getObjectFormat(),
+ });
+ defer bin_file.deinit();
+
+ var module = blk: {
+ const root_pkg = try Package.create(gpa, fs.cwd(), ".", src_path);
+ errdefer root_pkg.destroy();
+
+ const root_scope = try gpa.create(ir.Module.Scope.ZIRModule);
+ errdefer gpa.destroy(root_scope);
+ root_scope.* = .{
+ .sub_file_path = root_pkg.root_src_path,
+ .source = .{ .unloaded = {} },
+ .contents = .{ .not_available = {} },
+ .status = .never_loaded,
+ };
- for (system_libs.span()) |lib| {
- _ = try comp.addLinkLib(lib, true);
+ break :blk ir.Module{
+ .allocator = gpa,
+ .root_pkg = root_pkg,
+ .root_scope = root_scope,
+ .bin_file = &bin_file,
+ .optimize_mode = .Debug,
+ .decl_table = std.AutoHashMap(ir.Module.Decl.Hash, *ir.Module.Decl).init(gpa),
+ .decl_exports = std.AutoHashMap(*ir.Module.Decl, []*ir.Module.Export).init(gpa),
+ .export_owners = std.AutoHashMap(*ir.Module.Decl, []*ir.Module.Export).init(gpa),
+ .failed_decls = std.AutoHashMap(*ir.Module.Decl, *ir.ErrorMsg).init(gpa),
+ .failed_files = std.AutoHashMap(*ir.Module.Scope.ZIRModule, *ir.ErrorMsg).init(gpa),
+ .failed_exports = std.AutoHashMap(*ir.Module.Export, *ir.ErrorMsg).init(gpa),
+ .work_queue = std.fifo.LinearFifo(ir.Module.WorkItem, .Dynamic).init(gpa),
+ };
+ };
+ defer module.deinit();
+
+ const stdin = std.io.getStdIn().inStream();
+ const stderr = std.io.getStdErr().outStream();
+ var repl_buf: [1024]u8 = undefined;
+
+ try updateModule(gpa, &module, zir_out_path);
+
+ while (watch) {
+ try stderr.print("๐ฆ ", .{});
+ if (stdin.readUntilDelimiterOrEof(&repl_buf, '\n') catch |err| {
+ try stderr.print("\nUnable to parse command: {}\n", .{@errorName(err)});
+ continue;
+ }) |line| {
+ if (mem.eql(u8, line, "update")) {
+ try updateModule(gpa, &module, zir_out_path);
+ } else if (mem.eql(u8, line, "exit")) {
+ break;
+ } else if (mem.eql(u8, line, "help")) {
+ try stderr.writeAll(repl_help);
+ } else {
+ try stderr.print("unknown command: {}\n", .{line});
+ }
+ } else {
+ break;
+ }
}
+}
- comp.version = version;
- comp.is_test = false;
- comp.linker_script = linker_script;
- comp.clang_argv = clang_argv_buf.span();
- comp.strip = strip;
-
- comp.verbose_tokenize = verbose_tokenize;
- comp.verbose_ast_tree = verbose_ast_tree;
- comp.verbose_ast_fmt = verbose_ast_fmt;
- comp.verbose_link = verbose_link;
- comp.verbose_ir = verbose_ir;
- comp.verbose_llvm_ir = verbose_llvm_ir;
- comp.verbose_cimport = verbose_cimport;
-
- comp.link_eh_frame_hdr = link_eh_frame_hdr;
-
- comp.err_color = color;
+fn updateModule(gpa: *Allocator, module: *ir.Module, zir_out_path: ?[]const u8) !void {
+ try module.update();
- comp.linker_rdynamic = linker_rdynamic;
+ var errors = try module.getAllErrorsAlloc();
+ defer errors.deinit(module.allocator);
- if (macosx_version_min != null and ios_version_min != null) {
- try stderr.writeAll("-mmacosx-version-min and -mios-version-min options not allowed together\n");
- process.exit(1);
+ if (errors.list.len != 0) {
+ for (errors.list) |full_err_msg| {
+ std.debug.warn("{}:{}:{}: error: {}\n", .{
+ full_err_msg.src_path,
+ full_err_msg.line + 1,
+ full_err_msg.column + 1,
+ full_err_msg.msg,
+ });
+ }
}
- if (macosx_version_min) |ver| {
- comp.darwin_version_min = Compilation.DarwinVersionMin{ .MacOS = ver };
- }
- if (ios_version_min) |ver| {
- comp.darwin_version_min = Compilation.DarwinVersionMin{ .Ios = ver };
- }
+ if (zir_out_path) |zop| {
+ var new_zir_module = try ir.text.emit_zir(gpa, module.*);
+ defer new_zir_module.deinit(gpa);
- comp.emit_bin = emit_bin;
- comp.emit_asm = emit_asm;
- comp.emit_llvm_ir = emit_llvm_ir;
- comp.emit_h = emit_h;
- comp.assembly_files = assembly_files.span();
- comp.link_objects = link_objects.span();
+ const baf = try io.BufferedAtomicFile.create(gpa, fs.cwd(), zop, .{});
+ defer baf.destroy();
- comp.start();
- processBuildEvents(comp, color);
-}
+ try new_zir_module.writeToStream(gpa, baf.stream());
-fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void {
- const stderr_file = io.getStdErr();
- const stderr = stderr_file.outStream();
- var count: usize = 0;
- while (!comp.cancelled) {
- const build_event = comp.events.get();
- count += 1;
-
- switch (build_event) {
- .Ok => {
- stderr.print("Build {} succeeded\n", .{count}) catch process.exit(1);
- },
- .Error => |err| {
- stderr.print("Build {} failed: {}\n", .{ count, @errorName(err) }) catch process.exit(1);
- },
- .Fail => |msgs| {
- stderr.print("Build {} compile errors:\n", .{count}) catch process.exit(1);
- for (msgs) |msg| {
- defer msg.destroy();
- msg.printToFile(stderr_file, color) catch process.exit(1);
- }
- },
- }
+ try baf.finish();
}
}
+const repl_help =
+ \\Commands:
+ \\ update Detect changes to source files and update output files.
+ \\ help Print this text
+ \\ exit Quit this repl
+ \\
+;
+
pub const usage_fmt =
\\usage: zig fmt [file]...
\\
@@ -539,17 +547,17 @@ pub const usage_fmt =
;
const Fmt = struct {
- seen: event.Locked(SeenMap),
+ seen: SeenMap,
any_error: bool,
- color: errmsg.Color,
- allocator: *Allocator,
+ color: Color,
+ gpa: *Allocator,
- const SeenMap = std.StringHashMap(void);
+ const SeenMap = std.BufSet;
};
-fn parseLibcPaths(allocator: *Allocator, libc: *LibCInstallation, libc_paths_file: []const u8) void {
+fn parseLibcPaths(gpa: *Allocator, libc: *LibCInstallation, libc_paths_file: []const u8) void {
const stderr = io.getStdErr().outStream();
- libc.* = LibCInstallation.parse(allocator, libc_paths_file, stderr) catch |err| {
+ libc.* = LibCInstallation.parse(gpa, libc_paths_file, stderr) catch |err| {
stderr.print("Unable to parse libc path file '{}': {}.\n" ++
"Try running `zig libc` to see an example for the native target.\n", .{
libc_paths_file,
@@ -559,13 +567,13 @@ fn parseLibcPaths(allocator: *Allocator, libc: *LibCInstallation, libc_paths_fil
};
}
-fn cmdLibC(allocator: *Allocator, args: []const []const u8) !void {
+fn cmdLibC(gpa: *Allocator, args: []const []const u8) !void {
const stderr = io.getStdErr().outStream();
switch (args.len) {
0 => {},
1 => {
var libc_installation: LibCInstallation = undefined;
- parseLibcPaths(allocator, &libc_installation, args[0]);
+ parseLibcPaths(gpa, &libc_installation, args[0]);
return;
},
else => {
@@ -574,23 +582,20 @@ fn cmdLibC(allocator: *Allocator, args: []const []const u8) !void {
},
}
- var zig_compiler = try ZigCompiler.init(allocator);
- defer zig_compiler.deinit();
-
- const libc = zig_compiler.getNativeLibC() catch |err| {
+ const libc = LibCInstallation.findNative(.{ .allocator = gpa }) catch |err| {
stderr.print("unable to find libc: {}\n", .{@errorName(err)}) catch {};
process.exit(1);
};
+
libc.render(io.getStdOut().outStream()) catch process.exit(1);
}
-fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
+pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
const stderr_file = io.getStdErr();
- const stderr = stderr_file.outStream();
- var color: errmsg.Color = .Auto;
+ var color: Color = .Auto;
var stdin_flag: bool = false;
var check_flag: bool = false;
- var input_files = ArrayList([]const u8).init(allocator);
+ var input_files = ArrayList([]const u8).init(gpa);
{
var i: usize = 0;
@@ -603,7 +608,7 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
process.exit(0);
} else if (mem.eql(u8, arg, "--color")) {
if (i + 1 >= args.len) {
- try stderr.writeAll("expected [auto|on|off] after --color\n");
+ std.debug.warn("expected [auto|on|off] after --color\n", .{});
process.exit(1);
}
i += 1;
@@ -615,7 +620,7 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
} else if (mem.eql(u8, next_arg, "off")) {
color = .Off;
} else {
- try stderr.print("expected [auto|on|off] after --color, found '{}'\n", .{next_arg});
+ std.debug.warn("expected [auto|on|off] after --color, found '{}'\n", .{next_arg});
process.exit(1);
}
} else if (mem.eql(u8, arg, "--stdin")) {
@@ -623,7 +628,7 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
} else if (mem.eql(u8, arg, "--check")) {
check_flag = true;
} else {
- try stderr.print("unrecognized parameter: '{}'", .{arg});
+ std.debug.warn("unrecognized parameter: '{}'", .{arg});
process.exit(1);
}
} else {
@@ -633,60 +638,55 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
}
if (stdin_flag) {
- if (input_files.len != 0) {
- try stderr.writeAll("cannot use --stdin with positional arguments\n");
+ if (input_files.items.len != 0) {
+ std.debug.warn("cannot use --stdin with positional arguments\n", .{});
process.exit(1);
}
const stdin = io.getStdIn().inStream();
- const source_code = try stdin.readAllAlloc(allocator, max_src_size);
- defer allocator.free(source_code);
+ const source_code = try stdin.readAllAlloc(gpa, max_src_size);
+ defer gpa.free(source_code);
- const tree = std.zig.parse(allocator, source_code) catch |err| {
- try stderr.print("error parsing stdin: {}\n", .{err});
+ const tree = std.zig.parse(gpa, source_code) catch |err| {
+ std.debug.warn("error parsing stdin: {}\n", .{err});
process.exit(1);
};
defer tree.deinit();
var error_it = tree.errors.iterator(0);
while (error_it.next()) |parse_error| {
- const msg = try errmsg.Msg.createFromParseError(allocator, parse_error, tree, "<stdin>");
- defer msg.destroy();
-
- try msg.printToFile(io.getStdErr(), color);
+ try printErrMsgToFile(gpa, parse_error, tree, "<stdin>", stderr_file, color);
}
if (tree.errors.len != 0) {
process.exit(1);
}
if (check_flag) {
- const anything_changed = try std.zig.render(allocator, io.null_out_stream, tree);
- const code: u8 = if (anything_changed) 1 else 0;
+ const anything_changed = try std.zig.render(gpa, io.null_out_stream, tree);
+ const code = if (anything_changed) @as(u8, 1) else @as(u8, 0);
process.exit(code);
}
const stdout = io.getStdOut().outStream();
- _ = try std.zig.render(allocator, stdout, tree);
+ _ = try std.zig.render(gpa, stdout, tree);
return;
}
- if (input_files.len == 0) {
- try stderr.writeAll("expected at least one source file argument\n");
+ if (input_files.items.len == 0) {
+ std.debug.warn("expected at least one source file argument\n", .{});
process.exit(1);
}
var fmt = Fmt{
- .allocator = allocator,
- .seen = event.Locked(Fmt.SeenMap).init(Fmt.SeenMap.init(allocator)),
+ .gpa = gpa,
+ .seen = Fmt.SeenMap.init(gpa),
.any_error = false,
.color = color,
};
- var group = event.Group(FmtError!void).init(allocator);
for (input_files.span()) |file_path| {
- try group.call(fmtPath, .{ &fmt, file_path, check_flag });
+ try fmtPath(&fmt, file_path, check_flag);
}
- try group.wait();
if (fmt.any_error) {
process.exit(1);
}
@@ -711,54 +711,45 @@ const FmtError = error{
ReadOnlyFileSystem,
LinkQuotaExceeded,
FileBusy,
- CurrentWorkingDirectoryUnlinked,
} || fs.File.OpenError;
-async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtError!void {
- const stderr_file = io.getStdErr();
- const stderr = stderr_file.outStream();
-
- const file_path = try std.mem.dupe(fmt.allocator, u8, file_path_ref);
- defer fmt.allocator.free(file_path);
-
- {
- const held = fmt.seen.acquire();
- defer held.release();
+fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool) FmtError!void {
+ // get the real path here to avoid Windows failing on relative file paths with . or .. in them
+ var real_path = fs.realpathAlloc(fmt.gpa, file_path) catch |err| {
+ std.debug.warn("unable to open '{}': {}\n", .{ file_path, err });
+ fmt.any_error = true;
+ return;
+ };
+ defer fmt.gpa.free(real_path);
- if (try held.value.put(file_path, {})) |_| return;
- }
+ if (fmt.seen.exists(real_path)) return;
+ try fmt.seen.put(real_path);
- const source_code = fs.cwd().readFileAlloc(
- fmt.allocator,
- file_path,
- max_src_size,
- ) catch |err| switch (err) {
+ const source_code = fs.cwd().readFileAlloc(fmt.gpa, real_path, max_src_size) catch |err| switch (err) {
error.IsDir, error.AccessDenied => {
var dir = try fs.cwd().openDir(file_path, .{ .iterate = true });
defer dir.close();
- var group = event.Group(FmtError!void).init(fmt.allocator);
- var it = dir.iterate();
- while (try it.next()) |entry| {
+ var dir_it = dir.iterate();
+
+ while (try dir_it.next()) |entry| {
if (entry.kind == .Directory or mem.endsWith(u8, entry.name, ".zig")) {
- const full_path = try fs.path.join(fmt.allocator, &[_][]const u8{ file_path, entry.name });
- @panic("TODO https://github.com/ziglang/zig/issues/3777");
- // try group.call(fmtPath, .{fmt, full_path, check_mode});
+ const full_path = try fs.path.join(fmt.gpa, &[_][]const u8{ file_path, entry.name });
+ try fmtPath(fmt, full_path, check_mode);
}
}
- return group.wait();
+ return;
},
else => {
- // TODO lock stderr printing
- try stderr.print("unable to open '{}': {}\n", .{ file_path, err });
+ std.debug.warn("unable to open '{}': {}\n", .{ file_path, err });
fmt.any_error = true;
return;
},
};
- defer fmt.allocator.free(source_code);
+ defer fmt.gpa.free(source_code);
- const tree = std.zig.parse(fmt.allocator, source_code) catch |err| {
- try stderr.print("error parsing file '{}': {}\n", .{ file_path, err });
+ const tree = std.zig.parse(fmt.gpa, source_code) catch |err| {
+ std.debug.warn("error parsing file '{}': {}\n", .{ file_path, err });
fmt.any_error = true;
return;
};
@@ -766,10 +757,7 @@ async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtErro
var error_it = tree.errors.iterator(0);
while (error_it.next()) |parse_error| {
- const msg = try errmsg.Msg.createFromParseError(fmt.allocator, parse_error, tree, file_path);
- defer fmt.allocator.destroy(msg);
-
- try msg.printToFile(stderr_file, fmt.color);
+ try printErrMsgToFile(fmt.gpa, parse_error, tree, file_path, std.io.getStdErr(), fmt.color);
}
if (tree.errors.len != 0) {
fmt.any_error = true;
@@ -777,32 +765,67 @@ async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtErro
}
if (check_mode) {
- const anything_changed = try std.zig.render(fmt.allocator, io.null_out_stream, tree);
+ const anything_changed = try std.zig.render(fmt.gpa, io.null_out_stream, tree);
if (anything_changed) {
- try stderr.print("{}\n", .{file_path});
+ std.debug.warn("{}\n", .{file_path});
fmt.any_error = true;
}
} else {
- // TODO make this evented
- const baf = try io.BufferedAtomicFile.create(fmt.allocator, file_path);
+ const baf = try io.BufferedAtomicFile.create(fmt.gpa, fs.cwd(), real_path, .{});
defer baf.destroy();
- const anything_changed = try std.zig.render(fmt.allocator, baf.stream(), tree);
+ const anything_changed = try std.zig.render(fmt.gpa, baf.stream(), tree);
if (anything_changed) {
- try stderr.print("{}\n", .{file_path});
+ std.debug.warn("{}\n", .{file_path});
try baf.finish();
}
}
}
-fn cmdVersion(allocator: *Allocator, args: []const []const u8) !void {
- const stdout = io.getStdOut().outStream();
- try stdout.print("{}\n", .{c.ZIG_VERSION_STRING});
-}
-
-fn cmdHelp(allocator: *Allocator, args: []const []const u8) !void {
- const stdout = io.getStdOut();
- try stdout.writeAll(usage);
+fn printErrMsgToFile(
+ gpa: *mem.Allocator,
+ parse_error: *const ast.Error,
+ tree: *ast.Tree,
+ path: []const u8,
+ file: fs.File,
+ color: Color,
+) !void {
+ const color_on = switch (color) {
+ .Auto => file.isTty(),
+ .On => true,
+ .Off => false,
+ };
+ const lok_token = parse_error.loc();
+ const span_first = lok_token;
+ const span_last = lok_token;
+
+ const first_token = tree.tokens.at(span_first);
+ const last_token = tree.tokens.at(span_last);
+ const start_loc = tree.tokenLocationPtr(0, first_token);
+ const end_loc = tree.tokenLocationPtr(first_token.end, last_token);
+
+ var text_buf = std.ArrayList(u8).init(gpa);
+ defer text_buf.deinit();
+ const out_stream = text_buf.outStream();
+ try parse_error.render(&tree.tokens, out_stream);
+ const text = text_buf.span();
+
+ const stream = file.outStream();
+ try stream.print("{}:{}:{}: error: {}\n", .{ path, start_loc.line + 1, start_loc.column + 1, text });
+
+ if (!color_on) return;
+
+ // Print \r and \t as one space each so that column counts line up
+ for (tree.source[start_loc.line_start..start_loc.line_end]) |byte| {
+ try stream.writeByte(switch (byte) {
+ '\r', '\t' => ' ',
+ else => byte,
+ });
+ }
+ try stream.writeByte('\n');
+ try stream.writeByteNTimes(' ', start_loc.column);
+ try stream.writeByteNTimes('~', last_token.end - first_token.start);
+ try stream.writeByte('\n');
}
pub const info_zen =
@@ -817,90 +840,8 @@ pub const info_zen =
\\ * Avoid local maximums.
\\ * Reduce the amount one must remember.
\\ * Minimize energy spent on coding style.
+ \\ * Resource deallocation must succeed.
\\ * Together we serve end users.
\\
\\
;
-
-fn cmdZen(allocator: *Allocator, args: []const []const u8) !void {
- try io.getStdOut().writeAll(info_zen);
-}
-
-const usage_internal =
- \\usage: zig internal [subcommand]
- \\
- \\Sub-Commands:
- \\ build-info Print static compiler build-info
- \\
- \\
-;
-
-fn cmdInternal(allocator: *Allocator, args: []const []const u8) !void {
- const stderr = io.getStdErr().outStream();
- if (args.len == 0) {
- try stderr.writeAll(usage_internal);
- process.exit(1);
- }
-
- const sub_commands = [_]Command{Command{
- .name = "build-info",
- .exec = cmdInternalBuildInfo,
- }};
-
- inline for (sub_commands) |sub_command| {
- if (mem.eql(u8, sub_command.name, args[0])) {
- var frame = try allocator.create(@Frame(sub_command.exec));
- defer allocator.destroy(frame);
- frame.* = async sub_command.exec(allocator, args[1..]);
- return await frame;
- }
- }
-
- try stderr.print("unknown sub command: {}\n\n", .{args[0]});
- try stderr.writeAll(usage_internal);
-}
-
-fn cmdInternalBuildInfo(allocator: *Allocator, args: []const []const u8) !void {
- const stdout = io.getStdOut().outStream();
- try stdout.print(
- \\ZIG_CMAKE_BINARY_DIR {}
- \\ZIG_CXX_COMPILER {}
- \\ZIG_LLD_INCLUDE_PATH {}
- \\ZIG_LLD_LIBRARIES {}
- \\ZIG_LLVM_CONFIG_EXE {}
- \\ZIG_DIA_GUIDS_LIB {}
- \\
- , .{
- c.ZIG_CMAKE_BINARY_DIR,
- c.ZIG_CXX_COMPILER,
- c.ZIG_LLD_INCLUDE_PATH,
- c.ZIG_LLD_LIBRARIES,
- c.ZIG_LLVM_CONFIG_EXE,
- c.ZIG_DIA_GUIDS_LIB,
- });
-}
-
-const CliPkg = struct {
- name: []const u8,
- path: []const u8,
- children: ArrayList(*CliPkg),
- parent: ?*CliPkg,
-
- pub fn init(allocator: *mem.Allocator, name: []const u8, path: []const u8, parent: ?*CliPkg) !*CliPkg {
- var pkg = try allocator.create(CliPkg);
- pkg.* = CliPkg{
- .name = name,
- .path = path,
- .children = ArrayList(*CliPkg).init(allocator),
- .parent = parent,
- };
- return pkg;
- }
-
- pub fn deinit(self: *CliPkg) void {
- for (self.children.span()) |child| {
- child.deinit();
- }
- self.children.deinit();
- }
-};
src-self-hosted/stage2.zig
@@ -12,7 +12,6 @@ const ArrayListSentineled = std.ArrayListSentineled;
const Target = std.Target;
const CrossTarget = std.zig.CrossTarget;
const self_hosted_main = @import("main.zig");
-const errmsg = @import("errmsg.zig");
const DepTokenizer = @import("dep_tokenizer.zig").Tokenizer;
const assert = std.debug.assert;
const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
@@ -168,8 +167,6 @@ export fn stage2_render_ast(tree: *ast.Tree, output_file: *FILE) Error {
return .None;
}
-// TODO: just use the actual self-hosted zig fmt. Until https://github.com/ziglang/zig/issues/2377,
-// we use a blocking implementation.
export fn stage2_fmt(argc: c_int, argv: [*]const [*:0]const u8) c_int {
if (std.debug.runtime_safety) {
fmtMain(argc, argv) catch unreachable;
@@ -191,258 +188,9 @@ fn fmtMain(argc: c_int, argv: [*]const [*:0]const u8) !void {
try args_list.append(mem.spanZ(argv[arg_i]));
}
- stdout = std.io.getStdOut().outStream();
- stderr_file = std.io.getStdErr();
- stderr = stderr_file.outStream();
-
const args = args_list.span()[2..];
- var color: errmsg.Color = .Auto;
- var stdin_flag: bool = false;
- var check_flag: bool = false;
- var input_files = ArrayList([]const u8).init(allocator);
-
- {
- var i: usize = 0;
- while (i < args.len) : (i += 1) {
- const arg = args[i];
- if (mem.startsWith(u8, arg, "-")) {
- if (mem.eql(u8, arg, "--help")) {
- try stdout.writeAll(self_hosted_main.usage_fmt);
- process.exit(0);
- } else if (mem.eql(u8, arg, "--color")) {
- if (i + 1 >= args.len) {
- try stderr.writeAll("expected [auto|on|off] after --color\n");
- process.exit(1);
- }
- i += 1;
- const next_arg = args[i];
- if (mem.eql(u8, next_arg, "auto")) {
- color = .Auto;
- } else if (mem.eql(u8, next_arg, "on")) {
- color = .On;
- } else if (mem.eql(u8, next_arg, "off")) {
- color = .Off;
- } else {
- try stderr.print("expected [auto|on|off] after --color, found '{}'\n", .{next_arg});
- process.exit(1);
- }
- } else if (mem.eql(u8, arg, "--stdin")) {
- stdin_flag = true;
- } else if (mem.eql(u8, arg, "--check")) {
- check_flag = true;
- } else {
- try stderr.print("unrecognized parameter: '{}'", .{arg});
- process.exit(1);
- }
- } else {
- try input_files.append(arg);
- }
- }
- }
-
- if (stdin_flag) {
- if (input_files.items.len != 0) {
- try stderr.writeAll("cannot use --stdin with positional arguments\n");
- process.exit(1);
- }
-
- const stdin_file = io.getStdIn();
- var stdin = stdin_file.inStream();
-
- const source_code = try stdin.readAllAlloc(allocator, self_hosted_main.max_src_size);
- defer allocator.free(source_code);
-
- const tree = std.zig.parse(allocator, source_code) catch |err| {
- try stderr.print("error parsing stdin: {}\n", .{err});
- process.exit(1);
- };
- defer tree.deinit();
-
- var error_it = tree.errors.iterator(0);
- while (error_it.next()) |parse_error| {
- try printErrMsgToFile(allocator, parse_error, tree, "<stdin>", stderr_file, color);
- }
- if (tree.errors.len != 0) {
- process.exit(1);
- }
- if (check_flag) {
- const anything_changed = try std.zig.render(allocator, io.null_out_stream, tree);
- const code = if (anything_changed) @as(u8, 1) else @as(u8, 0);
- process.exit(code);
- }
-
- _ = try std.zig.render(allocator, stdout, tree);
- return;
- }
-
- if (input_files.items.len == 0) {
- try stderr.writeAll("expected at least one source file argument\n");
- process.exit(1);
- }
-
- var fmt = Fmt{
- .seen = Fmt.SeenMap.init(allocator),
- .any_error = false,
- .color = color,
- .allocator = allocator,
- };
-
- for (input_files.span()) |file_path| {
- try fmtPath(&fmt, file_path, check_flag);
- }
- if (fmt.any_error) {
- process.exit(1);
- }
-}
-
-const FmtError = error{
- SystemResources,
- OperationAborted,
- IoPending,
- BrokenPipe,
- Unexpected,
- WouldBlock,
- FileClosed,
- DestinationAddressRequired,
- DiskQuota,
- FileTooBig,
- InputOutput,
- NoSpaceLeft,
- AccessDenied,
- OutOfMemory,
- RenameAcrossMountPoints,
- ReadOnlyFileSystem,
- LinkQuotaExceeded,
- FileBusy,
-} || fs.File.OpenError;
-
-fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool) FmtError!void {
- // get the real path here to avoid Windows failing on relative file paths with . or .. in them
- var real_path = fs.realpathAlloc(fmt.allocator, file_path) catch |err| {
- try stderr.print("unable to open '{}': {}\n", .{ file_path, err });
- fmt.any_error = true;
- return;
- };
- defer fmt.allocator.free(real_path);
-
- if (fmt.seen.exists(real_path)) return;
- try fmt.seen.put(real_path);
-
- const source_code = fs.cwd().readFileAlloc(fmt.allocator, real_path, self_hosted_main.max_src_size) catch |err| switch (err) {
- error.IsDir, error.AccessDenied => {
- // TODO make event based (and dir.next())
- var dir = try fs.cwd().openDir(file_path, .{ .iterate = true });
- defer dir.close();
-
- var dir_it = dir.iterate();
-
- while (try dir_it.next()) |entry| {
- if (entry.kind == .Directory or mem.endsWith(u8, entry.name, ".zig")) {
- const full_path = try fs.path.join(fmt.allocator, &[_][]const u8{ file_path, entry.name });
- try fmtPath(fmt, full_path, check_mode);
- }
- }
- return;
- },
- else => {
- // TODO lock stderr printing
- try stderr.print("unable to open '{}': {}\n", .{ file_path, err });
- fmt.any_error = true;
- return;
- },
- };
- defer fmt.allocator.free(source_code);
-
- const tree = std.zig.parse(fmt.allocator, source_code) catch |err| {
- try stderr.print("error parsing file '{}': {}\n", .{ file_path, err });
- fmt.any_error = true;
- return;
- };
- defer tree.deinit();
-
- var error_it = tree.errors.iterator(0);
- while (error_it.next()) |parse_error| {
- try printErrMsgToFile(fmt.allocator, parse_error, tree, file_path, stderr_file, fmt.color);
- }
- if (tree.errors.len != 0) {
- fmt.any_error = true;
- return;
- }
-
- if (check_mode) {
- const anything_changed = try std.zig.render(fmt.allocator, io.null_out_stream, tree);
- if (anything_changed) {
- try stderr.print("{}\n", .{file_path});
- fmt.any_error = true;
- }
- } else {
- const baf = try io.BufferedAtomicFile.create(fmt.allocator, fs.cwd(), real_path, .{});
- defer baf.destroy();
-
- const anything_changed = try std.zig.render(fmt.allocator, baf.stream(), tree);
- if (anything_changed) {
- try stderr.print("{}\n", .{file_path});
- try baf.finish();
- }
- }
-}
-
-const Fmt = struct {
- seen: SeenMap,
- any_error: bool,
- color: errmsg.Color,
- allocator: *mem.Allocator,
-
- const SeenMap = std.BufSet;
-};
-
-fn printErrMsgToFile(
- allocator: *mem.Allocator,
- parse_error: *const ast.Error,
- tree: *ast.Tree,
- path: []const u8,
- file: fs.File,
- color: errmsg.Color,
-) !void {
- const color_on = switch (color) {
- .Auto => file.isTty(),
- .On => true,
- .Off => false,
- };
- const lok_token = parse_error.loc();
- const span = errmsg.Span{
- .first = lok_token,
- .last = lok_token,
- };
-
- const first_token = tree.tokens.at(span.first);
- const last_token = tree.tokens.at(span.last);
- const start_loc = tree.tokenLocationPtr(0, first_token);
- const end_loc = tree.tokenLocationPtr(first_token.end, last_token);
-
- var text_buf = std.ArrayList(u8).init(allocator);
- defer text_buf.deinit();
- const out_stream = text_buf.outStream();
- try parse_error.render(&tree.tokens, out_stream);
- const text = text_buf.span();
-
- const stream = file.outStream();
- try stream.print("{}:{}:{}: error: {}\n", .{ path, start_loc.line + 1, start_loc.column + 1, text });
-
- if (!color_on) return;
-
- // Print \r and \t as one space each so that column counts line up
- for (tree.source[start_loc.line_start..start_loc.line_end]) |byte| {
- try stream.writeByte(switch (byte) {
- '\r', '\t' => ' ',
- else => byte,
- });
- }
- try stream.writeByte('\n');
- try stream.writeByteNTimes(' ', start_loc.column);
- try stream.writeByteNTimes('~', last_token.end - first_token.start);
- try stream.writeByte('\n');
+ return self_hosted_main.cmdFmt(allocator, args);
}
export fn stage2_DepTokenizer_init(input: [*]const u8, len: usize) stage2_DepTokenizer {
build.zig
@@ -51,6 +51,9 @@ pub fn build(b: *Builder) !void {
var exe = b.addExecutable("zig", "src-self-hosted/main.zig");
exe.setBuildMode(mode);
+ test_step.dependOn(&exe.step);
+ b.default_step.dependOn(&exe.step);
+ exe.install();
const skip_release = b.option(bool, "skip-release", "Main test suite skips release builds") orelse false;
const skip_release_small = b.option(bool, "skip-release-small", "Main test suite skips release-small builds") orelse skip_release;
@@ -58,21 +61,17 @@ pub fn build(b: *Builder) !void {
const skip_release_safe = b.option(bool, "skip-release-safe", "Main test suite skips release-safe builds") orelse skip_release;
const skip_non_native = b.option(bool, "skip-non-native", "Main test suite skips non-native builds") orelse false;
const skip_libc = b.option(bool, "skip-libc", "Main test suite skips tests that link libc") orelse false;
- const skip_self_hosted = (b.option(bool, "skip-self-hosted", "Main test suite skips building self hosted compiler") orelse false) or true; // TODO evented I/O good enough that this passes everywhere
- if (!skip_self_hosted) {
- test_step.dependOn(&exe.step);
- }
const only_install_lib_files = b.option(bool, "lib-files-only", "Only install library files") orelse false;
- if (!only_install_lib_files and !skip_self_hosted) {
+ const enable_llvm = b.option(bool, "enable-llvm", "Build self-hosted compiler with LLVM backend enabled") orelse false;
+ if (enable_llvm) {
var ctx = parseConfigH(b, config_h_text);
ctx.llvm = try findLLVM(b, ctx.llvm_config_exe);
try configureStage2(b, exe, ctx);
-
- b.default_step.dependOn(&exe.step);
- exe.install();
}
+ const link_libc = b.option(bool, "force-link-libc", "Force self-hosted compiler to link libc") orelse false;
+ if (link_libc) exe.linkLibC();
b.installDirectory(InstallDirectoryOptions{
.source_dir = "lib",