authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-05-15 15:20:42-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-05-15 15:20:42-04:00
loge1d4b59c5bd47c7bef3e41279c97cd36991676e2
tree2f441e0319bd85d74e8d69beac85810ed30f1ca0
parentebb81ebe59d56a2ccb104e100b9c96df82eedc97

self-hosted: update main.zig

After this commit there are no more bit rotted files. The testing program that was in ir.zig has been moved to main.zig Unsupported command line options have been deleted, or error messages added. The compiler repl is available from the build-exe, build-lib, build-obj commands with the --watch option. The main zig build script now builds the self-hosted compiler unconditionally. Linking against LLVM is behind a -Denable-llvm flag that defaults to off.

7 files changed, 443 insertions(+), 2598 deletions(-)

build.zig+7-8
......@@ -51,6 +51,9 @@ pub fn build(b: *Builder) !void {
5151
5252 var exe = b.addExecutable("zig", "src-self-hosted/main.zig");
5353 exe.setBuildMode(mode);
54 test_step.dependOn(&exe.step);
55 b.default_step.dependOn(&exe.step);
56 exe.install();
5457
5558 const skip_release = b.option(bool, "skip-release", "Main test suite skips release builds") orelse false;
5659 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 {
5861 const skip_release_safe = b.option(bool, "skip-release-safe", "Main test suite skips release-safe builds") orelse skip_release;
5962 const skip_non_native = b.option(bool, "skip-non-native", "Main test suite skips non-native builds") orelse false;
6063 const skip_libc = b.option(bool, "skip-libc", "Main test suite skips tests that link libc") orelse false;
61 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
62 if (!skip_self_hosted) {
63 test_step.dependOn(&exe.step);
64 }
6564
6665 const only_install_lib_files = b.option(bool, "lib-files-only", "Only install library files") orelse false;
67 if (!only_install_lib_files and !skip_self_hosted) {
66 const enable_llvm = b.option(bool, "enable-llvm", "Build self-hosted compiler with LLVM backend enabled") orelse false;
67 if (enable_llvm) {
6868 var ctx = parseConfigH(b, config_h_text);
6969 ctx.llvm = try findLLVM(b, ctx.llvm_config_exe);
7070
7171 try configureStage2(b, exe, ctx);
72
73 b.default_step.dependOn(&exe.step);
74 exe.install();
7572 }
73 const link_libc = b.option(bool, "force-link-libc", "Force self-hosted compiler to link libc") orelse false;
74 if (link_libc) exe.linkLibC();
7675
7776 b.installDirectory(InstallDirectoryOptions{
7877 .source_dir = "lib",
src-self-hosted/compilation.zig deleted-1457
......@@ -1,1457 +0,0 @@
1const std = @import("std");
2const io = std.io;
3const mem = std.mem;
4const Allocator = mem.Allocator;
5const ArrayListSentineled = std.ArrayListSentineled;
6const llvm = @import("llvm.zig");
7const c = @import("c.zig");
8const builtin = std.builtin;
9const Target = std.Target;
10const warn = std.debug.warn;
11const Token = std.zig.Token;
12const ArrayList = std.ArrayList;
13const errmsg = @import("errmsg.zig");
14const ast = std.zig.ast;
15const event = std.event;
16const assert = std.debug.assert;
17const AtomicRmwOp = builtin.AtomicRmwOp;
18const AtomicOrder = builtin.AtomicOrder;
19const Scope = @import("scope.zig").Scope;
20const Decl = @import("decl.zig").Decl;
21const ir = @import("ir.zig");
22const Value = @import("value.zig").Value;
23const Type = Value.Type;
24const Span = errmsg.Span;
25const Msg = errmsg.Msg;
26const codegen = @import("codegen.zig");
27const Package = @import("package.zig").Package;
28const link = @import("link.zig").link;
29const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
30const CInt = @import("c_int.zig").CInt;
31const fs = std.fs;
32
33pub const Visib = enum {
34 Private,
35 Pub,
36};
37
38const max_src_size = 2 * 1024 * 1024 * 1024; // 2 GiB
39
40/// Data that is local to the event loop.
41pub const ZigCompiler = struct {
42 llvm_handle_pool: std.atomic.Stack(*llvm.Context),
43 lld_lock: event.Lock,
44 allocator: *Allocator,
45
46 /// TODO pool these so that it doesn't have to lock
47 prng: event.Locked(std.rand.DefaultPrng),
48
49 native_libc: event.Future(LibCInstallation),
50
51 var lazy_init_targets = std.once(initializeAllTargets);
52
53 pub fn init(allocator: *Allocator) !ZigCompiler {
54 lazy_init_targets.call();
55
56 var seed_bytes: [@sizeOf(u64)]u8 = undefined;
57 try std.crypto.randomBytes(seed_bytes[0..]);
58 const seed = mem.readIntNative(u64, &seed_bytes);
59
60 return ZigCompiler{
61 .allocator = allocator,
62 .lld_lock = event.Lock.init(),
63 .llvm_handle_pool = std.atomic.Stack(*llvm.Context).init(),
64 .prng = event.Locked(std.rand.DefaultPrng).init(std.rand.DefaultPrng.init(seed)),
65 .native_libc = event.Future(LibCInstallation).init(),
66 };
67 }
68
69 /// Must be called only after EventLoop.run completes.
70 fn deinit(self: *ZigCompiler) void {
71 self.lld_lock.deinit();
72 while (self.llvm_handle_pool.pop()) |node| {
73 llvm.ContextDispose(node.data);
74 self.allocator.destroy(node);
75 }
76 }
77
78 /// Gets an exclusive handle on any LlvmContext.
79 /// Caller must release the handle when done.
80 pub fn getAnyLlvmContext(self: *ZigCompiler) !LlvmHandle {
81 if (self.llvm_handle_pool.pop()) |node| return LlvmHandle{ .node = node };
82
83 const context_ref = llvm.ContextCreate() orelse return error.OutOfMemory;
84 errdefer llvm.ContextDispose(context_ref);
85
86 const node = try self.allocator.create(std.atomic.Stack(*llvm.Context).Node);
87 node.* = std.atomic.Stack(*llvm.Context).Node{
88 .next = undefined,
89 .data = context_ref,
90 };
91 errdefer self.allocator.destroy(node);
92
93 return LlvmHandle{ .node = node };
94 }
95
96 pub fn getNativeLibC(self: *ZigCompiler) !*LibCInstallation {
97 if (self.native_libc.start()) |ptr| return ptr;
98 self.native_libc.data = try LibCInstallation.findNative(.{ .allocator = self.allocator });
99 self.native_libc.resolve();
100 return &self.native_libc.data;
101 }
102
103 /// Must be called only once, ever. Sets global state.
104 pub fn setLlvmArgv(allocator: *Allocator, llvm_argv: []const []const u8) !void {
105 if (llvm_argv.len != 0) {
106 var c_compatible_args = try std.cstr.NullTerminated2DArray.fromSlices(allocator, &[_][]const []const u8{
107 &[_][]const u8{"zig (LLVM option parsing)"},
108 llvm_argv,
109 });
110 defer c_compatible_args.deinit();
111 c.ZigLLVMParseCommandLineOptions(llvm_argv.len + 1, c_compatible_args.ptr);
112 }
113 }
114};
115
116pub const LlvmHandle = struct {
117 node: *std.atomic.Stack(*llvm.Context).Node,
118
119 pub fn release(self: LlvmHandle, zig_compiler: *ZigCompiler) void {
120 zig_compiler.llvm_handle_pool.push(self.node);
121 }
122};
123
124pub const Compilation = struct {
125 pub const FnLinkSet = std.TailQueue(?*Value.Fn);
126
127 zig_compiler: *ZigCompiler,
128 name: ArrayListSentineled(u8, 0),
129 llvm_triple: ArrayListSentineled(u8, 0),
130 root_src_path: ?[]const u8,
131 target: std.Target,
132 llvm_target: *llvm.Target,
133 build_mode: builtin.Mode,
134 zig_lib_dir: []const u8,
135 zig_std_dir: []const u8,
136
137 /// lazily created when we need it
138 tmp_dir: event.Future(BuildError![]u8) = event.Future(BuildError![]u8).init(),
139
140 version: builtin.Version = builtin.Version{ .major = 0, .minor = 0, .patch = 0 },
141
142 linker_script: ?[]const u8 = null,
143 out_h_path: ?[]const u8 = null,
144
145 is_test: bool = false,
146 strip: bool = false,
147 is_static: bool,
148 linker_rdynamic: bool = false,
149
150 clang_argv: []const []const u8 = &[_][]const u8{},
151 assembly_files: []const []const u8 = &[_][]const u8{},
152
153 /// paths that are explicitly provided by the user to link against
154 link_objects: []const []const u8 = &[_][]const u8{},
155
156 /// functions that have their own objects that we need to link
157 /// it uses an optional pointer so that tombstone removals are possible
158 fn_link_set: event.Locked(FnLinkSet) = event.Locked(FnLinkSet).init(FnLinkSet.init()),
159
160 link_libs_list: ArrayList(*LinkLib),
161 libc_link_lib: ?*LinkLib = null,
162
163 err_color: errmsg.Color = .Auto,
164
165 verbose_tokenize: bool = false,
166 verbose_ast_tree: bool = false,
167 verbose_ast_fmt: bool = false,
168 verbose_cimport: bool = false,
169 verbose_ir: bool = false,
170 verbose_llvm_ir: bool = false,
171 verbose_link: bool = false,
172
173 link_eh_frame_hdr: bool = false,
174
175 darwin_version_min: DarwinVersionMin = .None,
176
177 test_filters: []const []const u8 = &[_][]const u8{},
178 test_name_prefix: ?[]const u8 = null,
179
180 emit_bin: bool = true,
181 emit_asm: bool = false,
182 emit_llvm_ir: bool = false,
183 emit_h: bool = false,
184
185 kind: Kind,
186
187 events: *event.Channel(Event),
188
189 exported_symbol_names: event.Locked(Decl.Table),
190
191 /// Before code generation starts, must wait on this group to make sure
192 /// the build is complete.
193 prelink_group: event.Group(BuildError!void),
194
195 compile_errors: event.Locked(CompileErrList),
196
197 meta_type: *Type.MetaType,
198 void_type: *Type.Void,
199 bool_type: *Type.Bool,
200 noreturn_type: *Type.NoReturn,
201 comptime_int_type: *Type.ComptimeInt,
202 u8_type: *Type.Int,
203
204 void_value: *Value.Void,
205 true_value: *Value.Bool,
206 false_value: *Value.Bool,
207 noreturn_value: *Value.NoReturn,
208
209 target_machine: *llvm.TargetMachine,
210 target_data_ref: *llvm.TargetData,
211 target_layout_str: [*:0]u8,
212 target_ptr_bits: u32,
213
214 /// for allocating things which have the same lifetime as this Compilation
215 arena_allocator: std.heap.ArenaAllocator,
216
217 root_package: *Package,
218 std_package: *Package,
219
220 override_libc: ?*LibCInstallation = null,
221
222 /// need to wait on this group before deinitializing
223 deinit_group: event.Group(void),
224
225 destroy_frame: *@Frame(createAsync),
226 main_loop_frame: *@Frame(Compilation.mainLoop),
227 main_loop_future: event.Future(void) = event.Future(void).init(),
228
229 have_err_ret_tracing: bool = false,
230
231 /// not locked because it is read-only
232 primitive_type_table: TypeTable,
233
234 int_type_table: event.Locked(IntTypeTable),
235 array_type_table: event.Locked(ArrayTypeTable),
236 ptr_type_table: event.Locked(PtrTypeTable),
237 fn_type_table: event.Locked(FnTypeTable),
238
239 c_int_types: [CInt.list.len]*Type.Int,
240
241 fs_watch: *fs.Watch(*Scope.Root),
242
243 cancelled: bool = false,
244
245 const IntTypeTable = std.HashMap(*const Type.Int.Key, *Type.Int, Type.Int.Key.hash, Type.Int.Key.eql);
246 const ArrayTypeTable = std.HashMap(*const Type.Array.Key, *Type.Array, Type.Array.Key.hash, Type.Array.Key.eql);
247 const PtrTypeTable = std.HashMap(*const Type.Pointer.Key, *Type.Pointer, Type.Pointer.Key.hash, Type.Pointer.Key.eql);
248 const FnTypeTable = std.HashMap(*const Type.Fn.Key, *Type.Fn, Type.Fn.Key.hash, Type.Fn.Key.eql);
249 const TypeTable = std.StringHashMap(*Type);
250
251 const CompileErrList = std.ArrayList(*Msg);
252
253 // TODO handle some of these earlier and report them in a way other than error codes
254 pub const BuildError = error{
255 OutOfMemory,
256 EndOfStream,
257 IsDir,
258 Unexpected,
259 SystemResources,
260 SharingViolation,
261 PathAlreadyExists,
262 FileNotFound,
263 AccessDenied,
264 PipeBusy,
265 FileTooBig,
266 SymLinkLoop,
267 ProcessFdQuotaExceeded,
268 NameTooLong,
269 SystemFdQuotaExceeded,
270 NoDevice,
271 NoSpaceLeft,
272 NotDir,
273 FileSystem,
274 OperationAborted,
275 IoPending,
276 BrokenPipe,
277 WouldBlock,
278 FileClosed,
279 DestinationAddressRequired,
280 DiskQuota,
281 InputOutput,
282 NoStdHandles,
283 Overflow,
284 NotSupported,
285 BufferTooSmall,
286 Unimplemented, // TODO remove this one
287 SemanticAnalysisFailed, // TODO remove this one
288 ReadOnlyFileSystem,
289 LinkQuotaExceeded,
290 EnvironmentVariableNotFound,
291 AppDataDirUnavailable,
292 LinkFailed,
293 LibCRequiredButNotProvidedOrFound,
294 LibCMissingDynamicLinker,
295 InvalidDarwinVersionString,
296 UnsupportedLinkArchitecture,
297 UserResourceLimitReached,
298 InvalidUtf8,
299 BadPathName,
300 DeviceBusy,
301 CurrentWorkingDirectoryUnlinked,
302 };
303
304 pub const Event = union(enum) {
305 Ok,
306 Error: BuildError,
307 Fail: []*Msg,
308 };
309
310 pub const DarwinVersionMin = union(enum) {
311 None,
312 MacOS: []const u8,
313 Ios: []const u8,
314 };
315
316 pub const Kind = enum {
317 Exe,
318 Lib,
319 Obj,
320 };
321
322 pub const LinkLib = struct {
323 name: []const u8,
324 path: ?[]const u8,
325
326 /// the list of symbols we depend on from this lib
327 symbols: ArrayList([]u8),
328 provided_explicitly: bool,
329 };
330
331 pub const Emit = enum {
332 Binary,
333 Assembly,
334 LlvmIr,
335 };
336
337 pub fn create(
338 zig_compiler: *ZigCompiler,
339 name: []const u8,
340 root_src_path: ?[]const u8,
341 target: std.zig.CrossTarget,
342 kind: Kind,
343 build_mode: builtin.Mode,
344 is_static: bool,
345 zig_lib_dir: []const u8,
346 ) !*Compilation {
347 var optional_comp: ?*Compilation = null;
348 var frame = try zig_compiler.allocator.create(@Frame(createAsync));
349 errdefer zig_compiler.allocator.destroy(frame);
350 frame.* = async createAsync(
351 &optional_comp,
352 zig_compiler,
353 name,
354 root_src_path,
355 target,
356 kind,
357 build_mode,
358 is_static,
359 zig_lib_dir,
360 );
361 // TODO causes segfault
362 // return optional_comp orelse if (await frame) |_| unreachable else |err| err;
363 if (optional_comp) |comp| {
364 return comp;
365 } else if (await frame) |_| unreachable else |err| return err;
366 }
367 fn createAsync(
368 out_comp: *?*Compilation,
369 zig_compiler: *ZigCompiler,
370 name: []const u8,
371 root_src_path: ?[]const u8,
372 cross_target: std.zig.CrossTarget,
373 kind: Kind,
374 build_mode: builtin.Mode,
375 is_static: bool,
376 zig_lib_dir: []const u8,
377 ) callconv(.Async) !void {
378 const allocator = zig_compiler.allocator;
379
380 // TODO merge this line with stage2.zig crossTargetToTarget
381 const target_info = try std.zig.system.NativeTargetInfo.detect(std.heap.c_allocator, cross_target);
382 const target = target_info.target;
383
384 var comp = Compilation{
385 .arena_allocator = std.heap.ArenaAllocator.init(allocator),
386 .zig_compiler = zig_compiler,
387 .events = undefined,
388 .root_src_path = root_src_path,
389 .target = target,
390 .llvm_target = undefined,
391 .kind = kind,
392 .build_mode = build_mode,
393 .zig_lib_dir = zig_lib_dir,
394 .zig_std_dir = undefined,
395 .destroy_frame = @frame(),
396 .main_loop_frame = undefined,
397
398 .name = undefined,
399 .llvm_triple = undefined,
400 .is_static = is_static,
401 .link_libs_list = undefined,
402 .exported_symbol_names = event.Locked(Decl.Table).init(Decl.Table.init(allocator)),
403 .prelink_group = event.Group(BuildError!void).init(allocator),
404 .deinit_group = event.Group(void).init(allocator),
405 .compile_errors = event.Locked(CompileErrList).init(CompileErrList.init(allocator)),
406 .int_type_table = event.Locked(IntTypeTable).init(IntTypeTable.init(allocator)),
407 .array_type_table = event.Locked(ArrayTypeTable).init(ArrayTypeTable.init(allocator)),
408 .ptr_type_table = event.Locked(PtrTypeTable).init(PtrTypeTable.init(allocator)),
409 .fn_type_table = event.Locked(FnTypeTable).init(FnTypeTable.init(allocator)),
410 .c_int_types = undefined,
411
412 .meta_type = undefined,
413 .void_type = undefined,
414 .void_value = undefined,
415 .bool_type = undefined,
416 .true_value = undefined,
417 .false_value = undefined,
418 .noreturn_type = undefined,
419 .noreturn_value = undefined,
420 .comptime_int_type = undefined,
421 .u8_type = undefined,
422
423 .target_machine = undefined,
424 .target_data_ref = undefined,
425 .target_layout_str = undefined,
426 .target_ptr_bits = target.cpu.arch.ptrBitWidth(),
427
428 .root_package = undefined,
429 .std_package = undefined,
430
431 .primitive_type_table = undefined,
432
433 .fs_watch = undefined,
434 };
435 comp.link_libs_list = ArrayList(*LinkLib).init(comp.arena());
436 comp.primitive_type_table = TypeTable.init(comp.arena());
437
438 defer {
439 comp.int_type_table.private_data.deinit();
440 comp.array_type_table.private_data.deinit();
441 comp.ptr_type_table.private_data.deinit();
442 comp.fn_type_table.private_data.deinit();
443 comp.arena_allocator.deinit();
444 }
445
446 comp.name = try ArrayListSentineled(u8, 0).init(comp.arena(), name);
447 comp.llvm_triple = try getLLVMTriple(comp.arena(), target);
448 comp.llvm_target = try llvmTargetFromTriple(comp.llvm_triple);
449 comp.zig_std_dir = try fs.path.join(comp.arena(), &[_][]const u8{ zig_lib_dir, "std" });
450
451 const opt_level = switch (build_mode) {
452 .Debug => llvm.CodeGenLevelNone,
453 else => llvm.CodeGenLevelAggressive,
454 };
455
456 const reloc_mode = if (is_static) llvm.RelocStatic else llvm.RelocPIC;
457
458 var target_specific_cpu_args: ?[*:0]u8 = null;
459 var target_specific_cpu_features: ?[*:0]u8 = null;
460 defer llvm.DisposeMessage(target_specific_cpu_args);
461 defer llvm.DisposeMessage(target_specific_cpu_features);
462
463 // TODO detect native CPU & features here
464
465 comp.target_machine = llvm.CreateTargetMachine(
466 comp.llvm_target,
467 comp.llvm_triple.span(),
468 target_specific_cpu_args orelse "",
469 target_specific_cpu_features orelse "",
470 opt_level,
471 reloc_mode,
472 llvm.CodeModelDefault,
473 false, // TODO: add -ffunction-sections option
474 ) orelse return error.OutOfMemory;
475 defer llvm.DisposeTargetMachine(comp.target_machine);
476
477 comp.target_data_ref = llvm.CreateTargetDataLayout(comp.target_machine) orelse return error.OutOfMemory;
478 defer llvm.DisposeTargetData(comp.target_data_ref);
479
480 comp.target_layout_str = llvm.CopyStringRepOfTargetData(comp.target_data_ref) orelse return error.OutOfMemory;
481 defer llvm.DisposeMessage(comp.target_layout_str);
482
483 comp.events = try allocator.create(event.Channel(Event));
484 defer allocator.destroy(comp.events);
485
486 comp.events.init(&[0]Event{});
487 defer comp.events.deinit();
488
489 if (root_src_path) |root_src| {
490 const dirname = fs.path.dirname(root_src) orelse ".";
491 const basename = fs.path.basename(root_src);
492
493 comp.root_package = try Package.create(comp.arena(), dirname, basename);
494 comp.std_package = try Package.create(comp.arena(), comp.zig_std_dir, "std.zig");
495 try comp.root_package.add("std", comp.std_package);
496 } else {
497 comp.root_package = try Package.create(comp.arena(), ".", "");
498 }
499
500 comp.fs_watch = try fs.Watch(*Scope.Root).init(allocator, 16);
501 defer comp.fs_watch.deinit();
502
503 try comp.initTypes();
504 defer comp.primitive_type_table.deinit();
505
506 comp.main_loop_frame = try allocator.create(@Frame(mainLoop));
507 defer allocator.destroy(comp.main_loop_frame);
508
509 comp.main_loop_frame.* = async comp.mainLoop();
510 // Set this to indicate that initialization completed successfully.
511 // from here on out we must not return an error.
512 // This must occur before the first suspend/await.
513 out_comp.* = &comp;
514 // This suspend is resumed by destroy()
515 suspend;
516 // From here on is cleanup.
517
518 comp.deinit_group.wait();
519
520 if (comp.tmp_dir.getOrNull()) |tmp_dir_result|
521 if (tmp_dir_result.*) |tmp_dir| {
522 fs.cwd().deleteTree(tmp_dir) catch {};
523 } else |_| {};
524 }
525
526 /// it does ref the result because it could be an arbitrary integer size
527 pub fn getPrimitiveType(comp: *Compilation, name: []const u8) !?*Type {
528 if (name.len >= 2) {
529 switch (name[0]) {
530 'i', 'u' => blk: {
531 for (name[1..]) |byte|
532 switch (byte) {
533 '0'...'9' => {},
534 else => break :blk,
535 };
536 const is_signed = name[0] == 'i';
537 const bit_count = std.fmt.parseUnsigned(u32, name[1..], 10) catch |err| switch (err) {
538 error.Overflow => return error.Overflow,
539 error.InvalidCharacter => unreachable, // we just checked the characters above
540 };
541 const int_type = try Type.Int.get(comp, Type.Int.Key{
542 .bit_count = bit_count,
543 .is_signed = is_signed,
544 });
545 errdefer int_type.base.base.deref();
546 return &int_type.base;
547 },
548 else => {},
549 }
550 }
551
552 if (comp.primitive_type_table.get(name)) |entry| {
553 entry.value.base.ref();
554 return entry.value;
555 }
556
557 return null;
558 }
559
560 fn initTypes(comp: *Compilation) !void {
561 comp.meta_type = try comp.arena().create(Type.MetaType);
562 comp.meta_type.* = Type.MetaType{
563 .base = Type{
564 .name = "type",
565 .base = Value{
566 .id = .Type,
567 .typ = undefined,
568 .ref_count = std.atomic.Int(usize).init(3), // 3 because it references itself twice
569 },
570 .id = .Type,
571 .abi_alignment = Type.AbiAlignment.init(),
572 },
573 .value = undefined,
574 };
575 comp.meta_type.value = &comp.meta_type.base;
576 comp.meta_type.base.base.typ = &comp.meta_type.base;
577 assert((try comp.primitive_type_table.put(comp.meta_type.base.name, &comp.meta_type.base)) == null);
578
579 comp.void_type = try comp.arena().create(Type.Void);
580 comp.void_type.* = Type.Void{
581 .base = Type{
582 .name = "void",
583 .base = Value{
584 .id = .Type,
585 .typ = &Type.MetaType.get(comp).base,
586 .ref_count = std.atomic.Int(usize).init(1),
587 },
588 .id = .Void,
589 .abi_alignment = Type.AbiAlignment.init(),
590 },
591 };
592 assert((try comp.primitive_type_table.put(comp.void_type.base.name, &comp.void_type.base)) == null);
593
594 comp.noreturn_type = try comp.arena().create(Type.NoReturn);
595 comp.noreturn_type.* = Type.NoReturn{
596 .base = Type{
597 .name = "noreturn",
598 .base = Value{
599 .id = .Type,
600 .typ = &Type.MetaType.get(comp).base,
601 .ref_count = std.atomic.Int(usize).init(1),
602 },
603 .id = .NoReturn,
604 .abi_alignment = Type.AbiAlignment.init(),
605 },
606 };
607 assert((try comp.primitive_type_table.put(comp.noreturn_type.base.name, &comp.noreturn_type.base)) == null);
608
609 comp.comptime_int_type = try comp.arena().create(Type.ComptimeInt);
610 comp.comptime_int_type.* = Type.ComptimeInt{
611 .base = Type{
612 .name = "comptime_int",
613 .base = Value{
614 .id = .Type,
615 .typ = &Type.MetaType.get(comp).base,
616 .ref_count = std.atomic.Int(usize).init(1),
617 },
618 .id = .ComptimeInt,
619 .abi_alignment = Type.AbiAlignment.init(),
620 },
621 };
622 assert((try comp.primitive_type_table.put(comp.comptime_int_type.base.name, &comp.comptime_int_type.base)) == null);
623
624 comp.bool_type = try comp.arena().create(Type.Bool);
625 comp.bool_type.* = Type.Bool{
626 .base = Type{
627 .name = "bool",
628 .base = Value{
629 .id = .Type,
630 .typ = &Type.MetaType.get(comp).base,
631 .ref_count = std.atomic.Int(usize).init(1),
632 },
633 .id = .Bool,
634 .abi_alignment = Type.AbiAlignment.init(),
635 },
636 };
637 assert((try comp.primitive_type_table.put(comp.bool_type.base.name, &comp.bool_type.base)) == null);
638
639 comp.void_value = try comp.arena().create(Value.Void);
640 comp.void_value.* = Value.Void{
641 .base = Value{
642 .id = .Void,
643 .typ = &Type.Void.get(comp).base,
644 .ref_count = std.atomic.Int(usize).init(1),
645 },
646 };
647
648 comp.true_value = try comp.arena().create(Value.Bool);
649 comp.true_value.* = Value.Bool{
650 .base = Value{
651 .id = .Bool,
652 .typ = &Type.Bool.get(comp).base,
653 .ref_count = std.atomic.Int(usize).init(1),
654 },
655 .x = true,
656 };
657
658 comp.false_value = try comp.arena().create(Value.Bool);
659 comp.false_value.* = Value.Bool{
660 .base = Value{
661 .id = .Bool,
662 .typ = &Type.Bool.get(comp).base,
663 .ref_count = std.atomic.Int(usize).init(1),
664 },
665 .x = false,
666 };
667
668 comp.noreturn_value = try comp.arena().create(Value.NoReturn);
669 comp.noreturn_value.* = Value.NoReturn{
670 .base = Value{
671 .id = .NoReturn,
672 .typ = &Type.NoReturn.get(comp).base,
673 .ref_count = std.atomic.Int(usize).init(1),
674 },
675 };
676
677 for (CInt.list) |cint, i| {
678 const c_int_type = try comp.arena().create(Type.Int);
679 c_int_type.* = Type.Int{
680 .base = Type{
681 .name = cint.zig_name,
682 .base = Value{
683 .id = .Type,
684 .typ = &Type.MetaType.get(comp).base,
685 .ref_count = std.atomic.Int(usize).init(1),
686 },
687 .id = .Int,
688 .abi_alignment = Type.AbiAlignment.init(),
689 },
690 .key = Type.Int.Key{
691 .is_signed = cint.is_signed,
692 .bit_count = cint.sizeInBits(comp.target),
693 },
694 .garbage_node = undefined,
695 };
696 comp.c_int_types[i] = c_int_type;
697 assert((try comp.primitive_type_table.put(cint.zig_name, &c_int_type.base)) == null);
698 }
699 comp.u8_type = try comp.arena().create(Type.Int);
700 comp.u8_type.* = Type.Int{
701 .base = Type{
702 .name = "u8",
703 .base = Value{
704 .id = .Type,
705 .typ = &Type.MetaType.get(comp).base,
706 .ref_count = std.atomic.Int(usize).init(1),
707 },
708 .id = .Int,
709 .abi_alignment = Type.AbiAlignment.init(),
710 },
711 .key = Type.Int.Key{
712 .is_signed = false,
713 .bit_count = 8,
714 },
715 .garbage_node = undefined,
716 };
717 assert((try comp.primitive_type_table.put(comp.u8_type.base.name, &comp.u8_type.base)) == null);
718 }
719
720 pub fn destroy(self: *Compilation) void {
721 const allocator = self.gpa();
722 self.cancelled = true;
723 await self.main_loop_frame;
724 resume self.destroy_frame;
725 allocator.destroy(self.destroy_frame);
726 }
727
728 fn start(self: *Compilation) void {
729 self.main_loop_future.resolve();
730 }
731 fn mainLoop(self: *Compilation) callconv(.Async) void {
732 // wait until start() is called
733 _ = self.main_loop_future.get();
734
735 var build_result = self.initialCompile();
736
737 while (!self.cancelled) {
738 const link_result = if (build_result) blk: {
739 break :blk self.maybeLink();
740 } else |err| err;
741 // this makes a handy error return trace and stack trace in debug mode
742 if (std.debug.runtime_safety) {
743 link_result catch unreachable;
744 }
745
746 const compile_errors = blk: {
747 const held = self.compile_errors.acquire();
748 defer held.release();
749 break :blk held.value.toOwnedSlice();
750 };
751
752 if (link_result) |_| {
753 if (compile_errors.len == 0) {
754 self.events.put(Event.Ok);
755 } else {
756 self.events.put(Event{ .Fail = compile_errors });
757 }
758 } else |err| {
759 // if there's an error then the compile errors have dangling references
760 self.gpa().free(compile_errors);
761
762 self.events.put(Event{ .Error = err });
763 }
764
765 // First, get an item from the watch channel, waiting on the channel.
766 var group = event.Group(BuildError!void).init(self.gpa());
767 {
768 const ev = (self.fs_watch.channel.get()) catch |err| {
769 build_result = err;
770 continue;
771 };
772 const root_scope = ev.data;
773 group.call(rebuildFile, .{ self, root_scope }) catch |err| {
774 build_result = err;
775 continue;
776 };
777 }
778 // Next, get all the items from the channel that are buffered up.
779 while (self.fs_watch.channel.getOrNull()) |ev_or_err| {
780 if (ev_or_err) |ev| {
781 const root_scope = ev.data;
782 group.call(rebuildFile, .{ self, root_scope }) catch |err| {
783 build_result = err;
784 continue;
785 };
786 } else |err| {
787 build_result = err;
788 continue;
789 }
790 }
791 build_result = group.wait();
792 }
793 }
794 fn rebuildFile(self: *Compilation, root_scope: *Scope.Root) callconv(.Async) BuildError!void {
795 const tree_scope = blk: {
796 const source_code = fs.cwd().readFileAlloc(
797 self.gpa(),
798 root_scope.realpath,
799 max_src_size,
800 ) catch |err| {
801 try self.addCompileErrorCli(root_scope.realpath, "unable to open: {}", .{@errorName(err)});
802 return;
803 };
804 errdefer self.gpa().free(source_code);
805
806 const tree = try std.zig.parse(self.gpa(), source_code);
807 errdefer {
808 tree.deinit();
809 }
810
811 break :blk try Scope.AstTree.create(self, tree, root_scope);
812 };
813 defer tree_scope.base.deref(self);
814
815 var error_it = tree_scope.tree.errors.iterator(0);
816 while (error_it.next()) |parse_error| {
817 const msg = try Msg.createFromParseErrorAndScope(self, tree_scope, parse_error);
818 errdefer msg.destroy();
819
820 try self.addCompileErrorAsync(msg);
821 }
822 if (tree_scope.tree.errors.len != 0) {
823 return;
824 }
825
826 const locked_table = root_scope.decls.table.acquireWrite();
827 defer locked_table.release();
828
829 var decl_group = event.Group(BuildError!void).init(self.gpa());
830
831 try self.rebuildChangedDecls(
832 &decl_group,
833 locked_table.value,
834 root_scope.decls,
835 &tree_scope.tree.root_node.decls,
836 tree_scope,
837 );
838
839 try decl_group.wait();
840 }
841
842 fn rebuildChangedDecls(
843 self: *Compilation,
844 group: *event.Group(BuildError!void),
845 locked_table: *Decl.Table,
846 decl_scope: *Scope.Decls,
847 ast_decls: *ast.Node.Root.DeclList,
848 tree_scope: *Scope.AstTree,
849 ) !void {
850 var existing_decls = try locked_table.clone();
851 defer existing_decls.deinit();
852
853 var ast_it = ast_decls.iterator(0);
854 while (ast_it.next()) |decl_ptr| {
855 const decl = decl_ptr.*;
856 switch (decl.id) {
857 .Comptime => {
858 const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", decl);
859
860 // TODO connect existing comptime decls to updated source files
861
862 try self.prelink_group.call(addCompTimeBlock, .{ self, tree_scope, &decl_scope.base, comptime_node });
863 },
864 .VarDecl => @panic("TODO"),
865 .FnProto => {
866 const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);
867
868 const name = if (fn_proto.name_token) |name_token| tree_scope.tree.tokenSlice(name_token) else {
869 try self.addCompileError(tree_scope, Span{
870 .first = fn_proto.fn_token,
871 .last = fn_proto.fn_token + 1,
872 }, "missing function name", .{});
873 continue;
874 };
875
876 if (existing_decls.remove(name)) |entry| {
877 // compare new code to existing
878 if (entry.value.cast(Decl.Fn)) |existing_fn_decl| {
879 // Just compare the old bytes to the new bytes of the top level decl.
880 // Even if the AST is technically the same, we want error messages to display
881 // from the most recent source.
882 const old_decl_src = existing_fn_decl.base.tree_scope.tree.getNodeSource(
883 &existing_fn_decl.fn_proto.base,
884 );
885 const new_decl_src = tree_scope.tree.getNodeSource(&fn_proto.base);
886 if (mem.eql(u8, old_decl_src, new_decl_src)) {
887 // it's the same, we can skip this decl
888 continue;
889 } else {
890 @panic("TODO decl changed implementation");
891 // Add the new thing before dereferencing the old thing. This way we don't end
892 // up pointlessly re-creating things we end up using in the new thing.
893 }
894 } else {
895 @panic("TODO decl changed kind");
896 }
897 } else {
898 // add new decl
899 const fn_decl = try self.gpa().create(Decl.Fn);
900 fn_decl.* = Decl.Fn{
901 .base = Decl{
902 .id = Decl.Id.Fn,
903 .name = name,
904 .visib = parseVisibToken(tree_scope.tree, fn_proto.visib_token),
905 .resolution = event.Future(BuildError!void).init(),
906 .parent_scope = &decl_scope.base,
907 .tree_scope = tree_scope,
908 },
909 .value = .Unresolved,
910 .fn_proto = fn_proto,
911 };
912 tree_scope.base.ref();
913 errdefer self.gpa().destroy(fn_decl);
914
915 try group.call(addTopLevelDecl, .{ self, &fn_decl.base, locked_table });
916 }
917 },
918 .TestDecl => @panic("TODO"),
919 else => unreachable,
920 }
921 }
922
923 var existing_decl_it = existing_decls.iterator();
924 while (existing_decl_it.next()) |entry| {
925 // this decl was deleted
926 const existing_decl = entry.value;
927 @panic("TODO handle decl deletion");
928 }
929 }
930
931 fn initialCompile(self: *Compilation) !void {
932 if (self.root_src_path) |root_src_path| {
933 const root_scope = blk: {
934 // TODO async/await fs.realpath
935 const root_src_real_path = fs.realpathAlloc(self.gpa(), root_src_path) catch |err| {
936 try self.addCompileErrorCli(root_src_path, "unable to open: {}", .{@errorName(err)});
937 return;
938 };
939 errdefer self.gpa().free(root_src_real_path);
940
941 break :blk try Scope.Root.create(self, root_src_real_path);
942 };
943 defer root_scope.base.deref(self);
944
945 // assert((try self.fs_watch.addFile(root_scope.realpath, root_scope)) == null);
946 try self.rebuildFile(root_scope);
947 }
948 }
949
950 fn maybeLink(self: *Compilation) !void {
951 (self.prelink_group.wait()) catch |err| switch (err) {
952 error.SemanticAnalysisFailed => {},
953 else => return err,
954 };
955
956 const any_prelink_errors = blk: {
957 const compile_errors = self.compile_errors.acquire();
958 defer compile_errors.release();
959
960 break :blk compile_errors.value.len != 0;
961 };
962
963 if (!any_prelink_errors) {
964 try link(self);
965 }
966 }
967 /// caller takes ownership of resulting Code
968 fn genAndAnalyzeCode(
969 comp: *Compilation,
970 tree_scope: *Scope.AstTree,
971 scope: *Scope,
972 node: *ast.Node,
973 expected_type: ?*Type,
974 ) callconv(.Async) !*ir.Code {
975 const unanalyzed_code = try ir.gen(
976 comp,
977 node,
978 tree_scope,
979 scope,
980 );
981 defer unanalyzed_code.destroy(comp.gpa());
982
983 if (comp.verbose_ir) {
984 std.debug.warn("unanalyzed:\n", .{});
985 unanalyzed_code.dump();
986 }
987
988 const analyzed_code = try ir.analyze(
989 comp,
990 unanalyzed_code,
991 expected_type,
992 );
993 errdefer analyzed_code.destroy(comp.gpa());
994
995 if (comp.verbose_ir) {
996 std.debug.warn("analyzed:\n", .{});
997 analyzed_code.dump();
998 }
999
1000 return analyzed_code;
1001 }
1002 fn addCompTimeBlock(
1003 comp: *Compilation,
1004 tree_scope: *Scope.AstTree,
1005 scope: *Scope,
1006 comptime_node: *ast.Node.Comptime,
1007 ) callconv(.Async) BuildError!void {
1008 const void_type = Type.Void.get(comp);
1009 defer void_type.base.base.deref(comp);
1010
1011 const analyzed_code = genAndAnalyzeCode(
1012 comp,
1013 tree_scope,
1014 scope,
1015 comptime_node.expr,
1016 &void_type.base,
1017 ) catch |err| switch (err) {
1018 // This poison value should not cause the errdefers to run. It simply means
1019 // that comp.compile_errors is populated.
1020 error.SemanticAnalysisFailed => return {},
1021 else => return err,
1022 };
1023 analyzed_code.destroy(comp.gpa());
1024 }
1025 fn addTopLevelDecl(
1026 self: *Compilation,
1027 decl: *Decl,
1028 locked_table: *Decl.Table,
1029 ) callconv(.Async) BuildError!void {
1030 const is_export = decl.isExported(decl.tree_scope.tree);
1031
1032 if (is_export) {
1033 try self.prelink_group.call(verifyUniqueSymbol, .{ self, decl });
1034 try self.prelink_group.call(resolveDecl, .{ self, decl });
1035 }
1036
1037 const gop = try locked_table.getOrPut(decl.name);
1038 if (gop.found_existing) {
1039 try self.addCompileError(decl.tree_scope, decl.getSpan(), "redefinition of '{}'", .{decl.name});
1040 // TODO note: other definition here
1041 } else {
1042 gop.kv.value = decl;
1043 }
1044 }
1045
1046 fn addCompileError(self: *Compilation, tree_scope: *Scope.AstTree, span: Span, comptime fmt: []const u8, args: var) !void {
1047 const text = try std.fmt.allocPrint(self.gpa(), fmt, args);
1048 errdefer self.gpa().free(text);
1049
1050 const msg = try Msg.createFromScope(self, tree_scope, span, text);
1051 errdefer msg.destroy();
1052
1053 try self.prelink_group.call(addCompileErrorAsync, .{ self, msg });
1054 }
1055
1056 fn addCompileErrorCli(self: *Compilation, realpath: []const u8, comptime fmt: []const u8, args: var) !void {
1057 const text = try std.fmt.allocPrint(self.gpa(), fmt, args);
1058 errdefer self.gpa().free(text);
1059
1060 const msg = try Msg.createFromCli(self, realpath, text);
1061 errdefer msg.destroy();
1062
1063 try self.prelink_group.call(addCompileErrorAsync, .{ self, msg });
1064 }
1065 fn addCompileErrorAsync(
1066 self: *Compilation,
1067 msg: *Msg,
1068 ) callconv(.Async) BuildError!void {
1069 errdefer msg.destroy();
1070
1071 const compile_errors = self.compile_errors.acquire();
1072 defer compile_errors.release();
1073
1074 try compile_errors.value.append(msg);
1075 }
1076 fn verifyUniqueSymbol(self: *Compilation, decl: *Decl) callconv(.Async) BuildError!void {
1077 const exported_symbol_names = self.exported_symbol_names.acquire();
1078 defer exported_symbol_names.release();
1079
1080 if (try exported_symbol_names.value.put(decl.name, decl)) |other_decl| {
1081 try self.addCompileError(decl.tree_scope, decl.getSpan(), "exported symbol collision: '{}'", .{
1082 decl.name,
1083 });
1084 // TODO add error note showing location of other symbol
1085 }
1086 }
1087
1088 pub fn haveLibC(self: *Compilation) bool {
1089 return self.libc_link_lib != null;
1090 }
1091
1092 pub fn addLinkLib(self: *Compilation, name: []const u8, provided_explicitly: bool) !*LinkLib {
1093 const is_libc = mem.eql(u8, name, "c");
1094
1095 if (is_libc) {
1096 if (self.libc_link_lib) |libc_link_lib| {
1097 return libc_link_lib;
1098 }
1099 }
1100
1101 for (self.link_libs_list.span()) |existing_lib| {
1102 if (mem.eql(u8, name, existing_lib.name)) {
1103 return existing_lib;
1104 }
1105 }
1106
1107 const link_lib = try self.gpa().create(LinkLib);
1108 link_lib.* = LinkLib{
1109 .name = name,
1110 .path = null,
1111 .provided_explicitly = provided_explicitly,
1112 .symbols = ArrayList([]u8).init(self.gpa()),
1113 };
1114 try self.link_libs_list.append(link_lib);
1115 if (is_libc) {
1116 self.libc_link_lib = link_lib;
1117
1118 // get a head start on looking for the native libc
1119 // TODO this is missing a bunch of logic related to whether the target is native
1120 // and whether we can build libc
1121 if (self.override_libc == null) {
1122 try self.deinit_group.call(startFindingNativeLibC, .{self});
1123 }
1124 }
1125 return link_lib;
1126 }
1127 fn startFindingNativeLibC(self: *Compilation) callconv(.Async) void {
1128 event.Loop.startCpuBoundOperation();
1129 // we don't care if it fails, we're just trying to kick off the future resolution
1130 _ = self.zig_compiler.getNativeLibC() catch return;
1131 }
1132
1133 /// General Purpose Allocator. Must free when done.
1134 fn gpa(self: Compilation) *mem.Allocator {
1135 return self.zig_compiler.allocator;
1136 }
1137
1138 /// Arena Allocator. Automatically freed when the Compilation is destroyed.
1139 fn arena(self: *Compilation) *mem.Allocator {
1140 return &self.arena_allocator.allocator;
1141 }
1142
1143 /// If the temporary directory for this compilation has not been created, it creates it.
1144 /// Then it creates a random file name in that dir and returns it.
1145 pub fn createRandomOutputPath(self: *Compilation, suffix: []const u8) !ArrayListSentineled(u8, 0) {
1146 const tmp_dir = try self.getTmpDir();
1147 const file_prefix = self.getRandomFileName();
1148
1149 const file_name = try std.fmt.allocPrint(self.gpa(), "{}{}", .{ file_prefix[0..], suffix });
1150 defer self.gpa().free(file_name);
1151
1152 const full_path = try fs.path.join(self.gpa(), &[_][]const u8{ tmp_dir, file_name[0..] });
1153 errdefer self.gpa().free(full_path);
1154
1155 return ArrayListSentineled(u8, 0).fromOwnedSlice(self.gpa(), full_path);
1156 }
1157
1158 /// If the temporary directory for this Compilation has not been created, creates it.
1159 /// Then returns it. The directory is unique to this Compilation and cleaned up when
1160 /// the Compilation deinitializes.
1161 fn getTmpDir(self: *Compilation) ![]const u8 {
1162 if (self.tmp_dir.start()) |ptr| return ptr.*;
1163 self.tmp_dir.data = self.getTmpDirImpl();
1164 self.tmp_dir.resolve();
1165 return self.tmp_dir.data;
1166 }
1167
1168 fn getTmpDirImpl(self: *Compilation) ![]u8 {
1169 const comp_dir_name = self.getRandomFileName();
1170 const zig_dir_path = try getZigDir(self.gpa());
1171 defer self.gpa().free(zig_dir_path);
1172
1173 const tmp_dir = try fs.path.join(self.arena(), &[_][]const u8{ zig_dir_path, comp_dir_name[0..] });
1174 try fs.cwd().makePath(tmp_dir);
1175 return tmp_dir;
1176 }
1177
1178 fn getRandomFileName(self: *Compilation) [12]u8 {
1179 // here we replace the standard +/ with -_ so that it can be used in a file name
1180 const b64_fs_encoder = std.base64.Base64Encoder.init(
1181 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_",
1182 std.base64.standard_pad_char,
1183 );
1184
1185 var rand_bytes: [9]u8 = undefined;
1186
1187 {
1188 const held = self.zig_compiler.prng.acquire();
1189 defer held.release();
1190
1191 held.value.random.bytes(rand_bytes[0..]);
1192 }
1193
1194 var result: [12]u8 = undefined;
1195 b64_fs_encoder.encode(result[0..], &rand_bytes);
1196 return result;
1197 }
1198
1199 fn registerGarbage(comp: *Compilation, comptime T: type, node: *std.atomic.Stack(*T).Node) void {
1200 // TODO put the garbage somewhere
1201 }
1202
1203 /// Returns a value which has been ref()'d once
1204 fn analyzeConstValue(
1205 comp: *Compilation,
1206 tree_scope: *Scope.AstTree,
1207 scope: *Scope,
1208 node: *ast.Node,
1209 expected_type: *Type,
1210 ) !*Value {
1211 var frame = try comp.gpa().create(@Frame(genAndAnalyzeCode));
1212 defer comp.gpa().destroy(frame);
1213 frame.* = async comp.genAndAnalyzeCode(tree_scope, scope, node, expected_type);
1214 const analyzed_code = try await frame;
1215 defer analyzed_code.destroy(comp.gpa());
1216
1217 return analyzed_code.getCompTimeResult(comp);
1218 }
1219
1220 fn analyzeTypeExpr(comp: *Compilation, tree_scope: *Scope.AstTree, scope: *Scope, node: *ast.Node) !*Type {
1221 const meta_type = &Type.MetaType.get(comp).base;
1222 defer meta_type.base.deref(comp);
1223
1224 const result_val = try comp.analyzeConstValue(tree_scope, scope, node, meta_type);
1225 errdefer result_val.base.deref(comp);
1226
1227 return result_val.cast(Type).?;
1228 }
1229
1230 /// This declaration has been blessed as going into the final code generation.
1231 pub fn resolveDecl(comp: *Compilation, decl: *Decl) callconv(.Async) BuildError!void {
1232 if (decl.resolution.start()) |ptr| return ptr.*;
1233
1234 decl.resolution.data = try generateDecl(comp, decl);
1235 decl.resolution.resolve();
1236 return decl.resolution.data;
1237 }
1238};
1239
1240fn parseVisibToken(tree: *ast.Tree, optional_token_index: ?ast.TokenIndex) Visib {
1241 if (optional_token_index) |token_index| {
1242 const token = tree.tokens.at(token_index);
1243 assert(token.id == Token.Id.Keyword_pub);
1244 return Visib.Pub;
1245 } else {
1246 return Visib.Private;
1247 }
1248}
1249
1250/// The function that actually does the generation.
1251fn generateDecl(comp: *Compilation, decl: *Decl) !void {
1252 switch (decl.id) {
1253 .Var => @panic("TODO"),
1254 .Fn => {
1255 const fn_decl = @fieldParentPtr(Decl.Fn, "base", decl);
1256 return generateDeclFn(comp, fn_decl);
1257 },
1258 .CompTime => @panic("TODO"),
1259 }
1260}
1261
1262fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {
1263 const tree_scope = fn_decl.base.tree_scope;
1264
1265 const body_node = fn_decl.fn_proto.body_node orelse return generateDeclFnProto(comp, fn_decl);
1266
1267 const fndef_scope = try Scope.FnDef.create(comp, fn_decl.base.parent_scope);
1268 defer fndef_scope.base.deref(comp);
1269
1270 const fn_type = try analyzeFnType(comp, tree_scope, fn_decl.base.parent_scope, fn_decl.fn_proto);
1271 defer fn_type.base.base.deref(comp);
1272
1273 var symbol_name = try std.ArrayListSentineled(u8, 0).init(comp.gpa(), fn_decl.base.name);
1274 var symbol_name_consumed = false;
1275 errdefer if (!symbol_name_consumed) symbol_name.deinit();
1276
1277 // The Decl.Fn owns the initial 1 reference count
1278 const fn_val = try Value.Fn.create(comp, fn_type, fndef_scope, symbol_name);
1279 fn_decl.value = .{ .Fn = fn_val };
1280 symbol_name_consumed = true;
1281
1282 // Define local parameter variables
1283 for (fn_type.key.data.Normal.params) |param, i| {
1284 //AstNode *param_decl_node = get_param_decl_node(fn_table_entry, i);
1285 const param_decl = @fieldParentPtr(ast.Node.ParamDecl, "base", fn_decl.fn_proto.params.at(i).*);
1286 const name_token = param_decl.name_token orelse {
1287 try comp.addCompileError(tree_scope, Span{
1288 .first = param_decl.firstToken(),
1289 .last = param_decl.type_node.firstToken(),
1290 }, "missing parameter name", .{});
1291 return error.SemanticAnalysisFailed;
1292 };
1293 const param_name = tree_scope.tree.tokenSlice(name_token);
1294
1295 // if (is_noalias && get_codegen_ptr_type(param_type) == nullptr) {
1296 // add_node_error(g, param_decl_node, buf_sprintf("noalias on non-pointer parameter"));
1297 // }
1298
1299 // TODO check for shadowing
1300
1301 const var_scope = try Scope.Var.createParam(
1302 comp,
1303 fn_val.child_scope,
1304 param_name,
1305 &param_decl.base,
1306 i,
1307 param.typ,
1308 );
1309 fn_val.child_scope = &var_scope.base;
1310
1311 try fn_type.non_key.Normal.variable_list.append(var_scope);
1312 }
1313
1314 var frame = try comp.gpa().create(@Frame(Compilation.genAndAnalyzeCode));
1315 defer comp.gpa().destroy(frame);
1316 frame.* = async comp.genAndAnalyzeCode(
1317 tree_scope,
1318 fn_val.child_scope,
1319 body_node,
1320 fn_type.key.data.Normal.return_type,
1321 );
1322 const analyzed_code = try await frame;
1323 errdefer analyzed_code.destroy(comp.gpa());
1324
1325 assert(fn_val.block_scope != null);
1326
1327 // Kick off rendering to LLVM module, but it doesn't block the fn decl
1328 // analysis from being complete.
1329 try comp.prelink_group.call(codegen.renderToLlvm, .{ comp, fn_val, analyzed_code });
1330 try comp.prelink_group.call(addFnToLinkSet, .{ comp, fn_val });
1331}
1332fn addFnToLinkSet(comp: *Compilation, fn_val: *Value.Fn) callconv(.Async) Compilation.BuildError!void {
1333 fn_val.base.ref();
1334 defer fn_val.base.deref(comp);
1335
1336 fn_val.link_set_node.data = fn_val;
1337
1338 const held = comp.fn_link_set.acquire();
1339 defer held.release();
1340
1341 held.value.append(fn_val.link_set_node);
1342}
1343
1344fn getZigDir(allocator: *mem.Allocator) ![]u8 {
1345 return fs.getAppDataDir(allocator, "zig");
1346}
1347
1348fn analyzeFnType(
1349 comp: *Compilation,
1350 tree_scope: *Scope.AstTree,
1351 scope: *Scope,
1352 fn_proto: *ast.Node.FnProto,
1353) !*Type.Fn {
1354 const return_type_node = switch (fn_proto.return_type) {
1355 .Explicit => |n| n,
1356 .InferErrorSet => |n| n,
1357 };
1358 const return_type = try comp.analyzeTypeExpr(tree_scope, scope, return_type_node);
1359 return_type.base.deref(comp);
1360
1361 var params = ArrayList(Type.Fn.Param).init(comp.gpa());
1362 var params_consumed = false;
1363 defer if (!params_consumed) {
1364 for (params.span()) |param| {
1365 param.typ.base.deref(comp);
1366 }
1367 params.deinit();
1368 };
1369
1370 {
1371 var it = fn_proto.params.iterator(0);
1372 while (it.next()) |param_node_ptr| {
1373 const param_node = param_node_ptr.*.cast(ast.Node.ParamDecl).?;
1374 const param_type = try comp.analyzeTypeExpr(tree_scope, scope, param_node.type_node);
1375 errdefer param_type.base.deref(comp);
1376 try params.append(Type.Fn.Param{
1377 .typ = param_type,
1378 .is_noalias = param_node.noalias_token != null,
1379 });
1380 }
1381 }
1382
1383 const key = Type.Fn.Key{
1384 .alignment = null,
1385 .data = Type.Fn.Key.Data{
1386 .Normal = Type.Fn.Key.Normal{
1387 .return_type = return_type,
1388 .params = params.toOwnedSlice(),
1389 .is_var_args = false, // TODO
1390 .cc = .Unspecified, // TODO
1391 },
1392 },
1393 };
1394 params_consumed = true;
1395 var key_consumed = false;
1396 defer if (!key_consumed) {
1397 for (key.data.Normal.params) |param| {
1398 param.typ.base.deref(comp);
1399 }
1400 comp.gpa().free(key.data.Normal.params);
1401 };
1402
1403 const fn_type = try Type.Fn.get(comp, key);
1404 key_consumed = true;
1405 errdefer fn_type.base.base.deref(comp);
1406
1407 return fn_type;
1408}
1409
1410fn generateDeclFnProto(comp: *Compilation, fn_decl: *Decl.Fn) !void {
1411 const fn_type = try analyzeFnType(
1412 comp,
1413 fn_decl.base.tree_scope,
1414 fn_decl.base.parent_scope,
1415 fn_decl.fn_proto,
1416 );
1417 defer fn_type.base.base.deref(comp);
1418
1419 var symbol_name = try std.ArrayListSentineled(u8, 0).init(comp.gpa(), fn_decl.base.name);
1420 var symbol_name_consumed = false;
1421 defer if (!symbol_name_consumed) symbol_name.deinit();
1422
1423 // The Decl.Fn owns the initial 1 reference count
1424 const fn_proto_val = try Value.FnProto.create(comp, fn_type, symbol_name);
1425 fn_decl.value = .{ .FnProto = fn_proto_val };
1426 symbol_name_consumed = true;
1427}
1428
1429pub fn llvmTargetFromTriple(triple: [:0]const u8) !*llvm.Target {
1430 var result: *llvm.Target = undefined;
1431 var err_msg: [*:0]u8 = undefined;
1432 if (llvm.GetTargetFromTriple(triple, &result, &err_msg) != 0) {
1433 std.debug.warn("triple: {s} error: {s}\n", .{ triple, err_msg });
1434 return error.UnsupportedTarget;
1435 }
1436 return result;
1437}
1438
1439pub fn initializeAllTargets() void {
1440 llvm.InitializeAllTargets();
1441 llvm.InitializeAllTargetInfos();
1442 llvm.InitializeAllTargetMCs();
1443 llvm.InitializeAllAsmPrinters();
1444 llvm.InitializeAllAsmParsers();
1445}
1446
1447pub fn getLLVMTriple(allocator: *std.mem.Allocator, target: std.Target) ![:0]u8 {
1448 var result = try std.ArrayListSentineled(u8, 0).initSize(allocator, 0);
1449 defer result.deinit();
1450
1451 try result.outStream().print(
1452 "{}-unknown-{}-{}",
1453 .{ @tagName(target.cpu.arch), @tagName(target.os.tag), @tagName(target.abi) },
1454 );
1455
1456 return result.toOwnedSlice();
1457}
src-self-hosted/errmsg.zig deleted-284
......@@ -1,284 +0,0 @@
1const std = @import("std");
2const mem = std.mem;
3const fs = std.fs;
4const process = std.process;
5const Token = std.zig.Token;
6const ast = std.zig.ast;
7const TokenIndex = std.zig.ast.TokenIndex;
8const Compilation = @import("compilation.zig").Compilation;
9const Scope = @import("scope.zig").Scope;
10
11pub const Color = enum {
12 Auto,
13 Off,
14 On,
15};
16
17pub const Span = struct {
18 first: ast.TokenIndex,
19 last: ast.TokenIndex,
20
21 pub fn token(i: TokenIndex) Span {
22 return Span{
23 .first = i,
24 .last = i,
25 };
26 }
27
28 pub fn node(n: *ast.Node) Span {
29 return Span{
30 .first = n.firstToken(),
31 .last = n.lastToken(),
32 };
33 }
34};
35
36pub const Msg = struct {
37 text: []u8,
38 realpath: []u8,
39 data: Data,
40
41 const Data = union(enum) {
42 Cli: Cli,
43 PathAndTree: PathAndTree,
44 ScopeAndComp: ScopeAndComp,
45 };
46
47 const PathAndTree = struct {
48 span: Span,
49 tree: *ast.Tree,
50 allocator: *mem.Allocator,
51 };
52
53 const ScopeAndComp = struct {
54 span: Span,
55 tree_scope: *Scope.AstTree,
56 compilation: *Compilation,
57 };
58
59 const Cli = struct {
60 allocator: *mem.Allocator,
61 };
62
63 pub fn destroy(self: *Msg) void {
64 switch (self.data) {
65 .Cli => |cli| {
66 cli.allocator.free(self.text);
67 cli.allocator.free(self.realpath);
68 cli.allocator.destroy(self);
69 },
70 .PathAndTree => |path_and_tree| {
71 path_and_tree.allocator.free(self.text);
72 path_and_tree.allocator.free(self.realpath);
73 path_and_tree.allocator.destroy(self);
74 },
75 .ScopeAndComp => |scope_and_comp| {
76 scope_and_comp.tree_scope.base.deref(scope_and_comp.compilation);
77 scope_and_comp.compilation.gpa().free(self.text);
78 scope_and_comp.compilation.gpa().free(self.realpath);
79 scope_and_comp.compilation.gpa().destroy(self);
80 },
81 }
82 }
83
84 fn getAllocator(self: *const Msg) *mem.Allocator {
85 switch (self.data) {
86 .Cli => |cli| return cli.allocator,
87 .PathAndTree => |path_and_tree| {
88 return path_and_tree.allocator;
89 },
90 .ScopeAndComp => |scope_and_comp| {
91 return scope_and_comp.compilation.gpa();
92 },
93 }
94 }
95
96 pub fn getTree(self: *const Msg) *ast.Tree {
97 switch (self.data) {
98 .Cli => unreachable,
99 .PathAndTree => |path_and_tree| {
100 return path_and_tree.tree;
101 },
102 .ScopeAndComp => |scope_and_comp| {
103 return scope_and_comp.tree_scope.tree;
104 },
105 }
106 }
107
108 pub fn getSpan(self: *const Msg) Span {
109 return switch (self.data) {
110 .Cli => unreachable,
111 .PathAndTree => |path_and_tree| path_and_tree.span,
112 .ScopeAndComp => |scope_and_comp| scope_and_comp.span,
113 };
114 }
115
116 /// Takes ownership of text
117 /// References tree_scope, and derefs when the msg is freed
118 pub fn createFromScope(comp: *Compilation, tree_scope: *Scope.AstTree, span: Span, text: []u8) !*Msg {
119 const realpath = try mem.dupe(comp.gpa(), u8, tree_scope.root().realpath);
120 errdefer comp.gpa().free(realpath);
121
122 const msg = try comp.gpa().create(Msg);
123 msg.* = Msg{
124 .text = text,
125 .realpath = realpath,
126 .data = Data{
127 .ScopeAndComp = ScopeAndComp{
128 .tree_scope = tree_scope,
129 .compilation = comp,
130 .span = span,
131 },
132 },
133 };
134 tree_scope.base.ref();
135 return msg;
136 }
137
138 /// Caller owns returned Msg and must free with `allocator`
139 /// allocator will additionally be used for printing messages later.
140 pub fn createFromCli(comp: *Compilation, realpath: []const u8, text: []u8) !*Msg {
141 const realpath_copy = try mem.dupe(comp.gpa(), u8, realpath);
142 errdefer comp.gpa().free(realpath_copy);
143
144 const msg = try comp.gpa().create(Msg);
145 msg.* = Msg{
146 .text = text,
147 .realpath = realpath_copy,
148 .data = Data{
149 .Cli = Cli{ .allocator = comp.gpa() },
150 },
151 };
152 return msg;
153 }
154
155 pub fn createFromParseErrorAndScope(
156 comp: *Compilation,
157 tree_scope: *Scope.AstTree,
158 parse_error: *const ast.Error,
159 ) !*Msg {
160 const loc_token = parse_error.loc();
161 var text_buf = std.ArrayList(u8).init(comp.gpa());
162 defer text_buf.deinit();
163
164 const realpath_copy = try mem.dupe(comp.gpa(), u8, tree_scope.root().realpath);
165 errdefer comp.gpa().free(realpath_copy);
166
167 try parse_error.render(&tree_scope.tree.tokens, text_buf.outStream());
168
169 const msg = try comp.gpa().create(Msg);
170 msg.* = Msg{
171 .text = undefined,
172 .realpath = realpath_copy,
173 .data = Data{
174 .ScopeAndComp = ScopeAndComp{
175 .tree_scope = tree_scope,
176 .compilation = comp,
177 .span = Span{
178 .first = loc_token,
179 .last = loc_token,
180 },
181 },
182 },
183 };
184 tree_scope.base.ref();
185 msg.text = text_buf.toOwnedSlice();
186 return msg;
187 }
188
189 /// `realpath` must outlive the returned Msg
190 /// `tree` must outlive the returned Msg
191 /// Caller owns returned Msg and must free with `allocator`
192 /// allocator will additionally be used for printing messages later.
193 pub fn createFromParseError(
194 allocator: *mem.Allocator,
195 parse_error: *const ast.Error,
196 tree: *ast.Tree,
197 realpath: []const u8,
198 ) !*Msg {
199 const loc_token = parse_error.loc();
200 var text_buf = std.ArrayList(u8).init(allocator);
201 defer text_buf.deinit();
202
203 const realpath_copy = try mem.dupe(allocator, u8, realpath);
204 errdefer allocator.free(realpath_copy);
205
206 try parse_error.render(&tree.tokens, text_buf.outStream());
207
208 const msg = try allocator.create(Msg);
209 msg.* = Msg{
210 .text = undefined,
211 .realpath = realpath_copy,
212 .data = Data{
213 .PathAndTree = PathAndTree{
214 .allocator = allocator,
215 .tree = tree,
216 .span = Span{
217 .first = loc_token,
218 .last = loc_token,
219 },
220 },
221 },
222 };
223 msg.text = text_buf.toOwnedSlice();
224 errdefer allocator.destroy(msg);
225
226 return msg;
227 }
228
229 pub fn printToStream(msg: *const Msg, stream: var, color_on: bool) !void {
230 switch (msg.data) {
231 .Cli => {
232 try stream.print("{}:-:-: error: {}\n", .{ msg.realpath, msg.text });
233 return;
234 },
235 else => {},
236 }
237
238 const allocator = msg.getAllocator();
239 const tree = msg.getTree();
240
241 const cwd = try process.getCwdAlloc(allocator);
242 defer allocator.free(cwd);
243
244 const relpath = try fs.path.relative(allocator, cwd, msg.realpath);
245 defer allocator.free(relpath);
246
247 const path = if (relpath.len < msg.realpath.len) relpath else msg.realpath;
248 const span = msg.getSpan();
249
250 const first_token = tree.tokens.at(span.first);
251 const last_token = tree.tokens.at(span.last);
252 const start_loc = tree.tokenLocationPtr(0, first_token);
253 const end_loc = tree.tokenLocationPtr(first_token.end, last_token);
254 if (!color_on) {
255 try stream.print("{}:{}:{}: error: {}\n", .{
256 path,
257 start_loc.line + 1,
258 start_loc.column + 1,
259 msg.text,
260 });
261 return;
262 }
263
264 try stream.print("{}:{}:{}: error: {}\n{}\n", .{
265 path,
266 start_loc.line + 1,
267 start_loc.column + 1,
268 msg.text,
269 tree.source[start_loc.line_start..start_loc.line_end],
270 });
271 try stream.writeByteNTimes(' ', start_loc.column);
272 try stream.writeByteNTimes('~', last_token.end - first_token.start);
273 try stream.writeAll("\n");
274 }
275
276 pub fn printToFile(msg: *const Msg, file: fs.File, color: Color) !void {
277 const color_on = switch (color) {
278 .Auto => file.isTty(),
279 .On => true,
280 .Off => false,
281 };
282 return msg.printToStream(file.outStream(), color_on);
283 }
284};
src-self-hosted/ir.zig-99
......@@ -922,7 +922,6 @@ pub const Module = struct {
922922 if (self.decl_table.get(hash)) |kv| {
923923 return kv.value;
924924 } else {
925 std.debug.warn("creating new decl for {}\n", .{old_inst.name});
926925 const new_decl = blk: {
927926 try self.decl_table.ensureCapacity(self.decl_table.size + 1);
928927 const new_decl = try self.allocator.create(Decl);
......@@ -2161,101 +2160,3 @@ pub const ErrorMsg = struct {
21612160 self.* = undefined;
21622161 }
21632162};
2164
2165pub fn main() anyerror!void {
2166 var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
2167 defer arena.deinit();
2168 const allocator = if (std.builtin.link_libc) std.heap.c_allocator else &arena.allocator;
2169
2170 const args = try std.process.argsAlloc(allocator);
2171 defer std.process.argsFree(allocator, args);
2172
2173 const src_path = args[1];
2174 const bin_path = args[2];
2175 const debug_error_trace = false;
2176 const output_zir = false;
2177 const object_format: ?std.builtin.ObjectFormat = null;
2178
2179 const native_info = try std.zig.system.NativeTargetInfo.detect(allocator, .{});
2180
2181 var bin_file = try link.openBinFilePath(allocator, std.fs.cwd(), bin_path, .{
2182 .target = native_info.target,
2183 .output_mode = .Exe,
2184 .link_mode = .Static,
2185 .object_format = object_format orelse native_info.target.getObjectFormat(),
2186 });
2187 defer bin_file.deinit();
2188
2189 var module = blk: {
2190 const root_pkg = try Package.create(allocator, std.fs.cwd(), ".", src_path);
2191 errdefer root_pkg.destroy();
2192
2193 const root_scope = try allocator.create(Module.Scope.ZIRModule);
2194 errdefer allocator.destroy(root_scope);
2195 root_scope.* = .{
2196 .sub_file_path = root_pkg.root_src_path,
2197 .source = .{ .unloaded = {} },
2198 .contents = .{ .not_available = {} },
2199 .status = .never_loaded,
2200 };
2201
2202 break :blk Module{
2203 .allocator = allocator,
2204 .root_pkg = root_pkg,
2205 .root_scope = root_scope,
2206 .bin_file = &bin_file,
2207 .optimize_mode = .Debug,
2208 .decl_table = std.AutoHashMap(Module.Decl.Hash, *Module.Decl).init(allocator),
2209 .decl_exports = std.AutoHashMap(*Module.Decl, []*Module.Export).init(allocator),
2210 .export_owners = std.AutoHashMap(*Module.Decl, []*Module.Export).init(allocator),
2211 .failed_decls = std.AutoHashMap(*Module.Decl, *ErrorMsg).init(allocator),
2212 .failed_files = std.AutoHashMap(*Module.Scope.ZIRModule, *ErrorMsg).init(allocator),
2213 .failed_exports = std.AutoHashMap(*Module.Export, *ErrorMsg).init(allocator),
2214 .work_queue = std.fifo.LinearFifo(Module.WorkItem, .Dynamic).init(allocator),
2215 };
2216 };
2217 defer module.deinit();
2218
2219 const stdin = std.io.getStdIn().inStream();
2220 const stderr = std.io.getStdErr().outStream();
2221 var repl_buf: [1024]u8 = undefined;
2222
2223 while (true) {
2224 try module.update();
2225
2226 var errors = try module.getAllErrorsAlloc();
2227 defer errors.deinit(allocator);
2228
2229 if (errors.list.len != 0) {
2230 for (errors.list) |full_err_msg| {
2231 std.debug.warn("{}:{}:{}: error: {}\n", .{
2232 full_err_msg.src_path,
2233 full_err_msg.line + 1,
2234 full_err_msg.column + 1,
2235 full_err_msg.msg,
2236 });
2237 }
2238 if (debug_error_trace) return error.AnalysisFail;
2239 }
2240
2241 try stderr.print("🦎 ", .{});
2242 if (try stdin.readUntilDelimiterOrEof(&repl_buf, '\n')) |line| {
2243 if (mem.eql(u8, line, "update")) {
2244 continue;
2245 } else {
2246 try stderr.print("unknown command: {}\n", .{line});
2247 }
2248 } else {
2249 break;
2250 }
2251 }
2252
2253 if (output_zir) {
2254 var new_zir_module = try text.emit_zir(allocator, module);
2255 defer new_zir_module.deinit(allocator);
2256
2257 var bos = std.io.bufferedOutStream(std.io.getStdOut().outStream());
2258 try new_zir_module.writeToStream(allocator, bos.outStream());
2259 try bos.flush();
2260 }
2261}
src-self-hosted/ir/text.zig+1-4
......@@ -20,7 +20,7 @@ pub const Inst = struct {
2020 name: []const u8,
2121
2222 /// Slice into the source of the part after the = and before the next instruction.
23 contents: []const u8,
23 contents: []const u8 = &[0]u8{},
2424
2525 /// These names are used directly as the instruction names in the text format.
2626 pub const Tag = enum {
......@@ -825,7 +825,6 @@ const Parser = struct {
825825 .name = inst_name,
826826 .src = self.i,
827827 .tag = InstType.base_tag,
828 .contents = undefined,
829828 };
830829
831830 if (@hasField(InstType, "ty")) {
......@@ -960,7 +959,6 @@ const Parser = struct {
960959 .name = try self.generateName(),
961960 .src = src,
962961 .tag = Inst.Str.base_tag,
963 .contents = undefined,
964962 },
965963 .positionals = .{ .bytes = ident },
966964 .kw_args = .{},
......@@ -971,7 +969,6 @@ const Parser = struct {
971969 .name = try self.generateName(),
972970 .src = src,
973971 .tag = Inst.DeclRef.base_tag,
974 .contents = undefined,
975972 },
976973 .positionals = .{ .name = &name.base },
977974 .kw_args = .{},
src-self-hosted/main.zig+434-493
......@@ -1,29 +1,30 @@
11const std = @import("std");
2const builtin = @import("builtin");
3
4const event = std.event;
5const os = std.os;
62const io = std.io;
73const fs = std.fs;
84const mem = std.mem;
95const process = std.process;
106const Allocator = mem.Allocator;
117const ArrayList = std.ArrayList;
8const ast = std.zig.ast;
9const ir = @import("ir.zig");
10const link = @import("link.zig");
11const Package = @import("Package.zig");
1212
13const c = @import("c.zig");
14const introspect = @import("introspect.zig");
15const ZigCompiler = @import("compilation.zig").ZigCompiler;
16const Compilation = @import("compilation.zig").Compilation;
17const Target = std.Target;
18const errmsg = @import("errmsg.zig");
1913const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
2014
21pub const io_mode = .evented;
15// TODO Improve async I/O enough that we feel comfortable doing this.
16//pub const io_mode = .evented;
2217
2318pub const max_src_size = 2 * 1024 * 1024 * 1024; // 2 GiB
2419
20pub const Color = enum {
21 Auto,
22 Off,
23 On,
24};
25
2526const usage =
26 \\usage: zig [command] [options]
27 \\Usage: zig [command] [options]
2728 \\
2829 \\Commands:
2930 \\
......@@ -39,175 +40,154 @@ const usage =
3940 \\
4041;
4142
42const Command = struct {
43 name: []const u8,
44 exec: async fn (*Allocator, []const []const u8) anyerror!void,
45};
46
4743pub fn main() !void {
48 const allocator = std.heap.c_allocator;
49
50 const stderr = io.getStdErr().outStream();
44 // TODO general purpose allocator in the zig std lib
45 const gpa = if (std.builtin.link_libc) std.heap.c_allocator else std.heap.page_allocator;
46 var arena_instance = std.heap.ArenaAllocator.init(gpa);
47 defer arena_instance.deinit();
48 const arena = &arena_instance.allocator;
5149
52 const args = try process.argsAlloc(allocator);
53 defer process.argsFree(allocator, args);
50 const args = try process.argsAlloc(arena);
5451
5552 if (args.len <= 1) {
56 try stderr.writeAll("expected command argument\n\n");
57 try stderr.writeAll(usage);
53 std.debug.warn("expected command argument\n\n{}", .{usage});
5854 process.exit(1);
5955 }
6056
6157 const cmd = args[1];
6258 const cmd_args = args[2..];
6359 if (mem.eql(u8, cmd, "build-exe")) {
64 return buildOutputType(allocator, cmd_args, .Exe);
60 return buildOutputType(gpa, arena, cmd_args, .Exe);
6561 } else if (mem.eql(u8, cmd, "build-lib")) {
66 return buildOutputType(allocator, cmd_args, .Lib);
62 return buildOutputType(gpa, arena, cmd_args, .Lib);
6763 } else if (mem.eql(u8, cmd, "build-obj")) {
68 return buildOutputType(allocator, cmd_args, .Obj);
64 return buildOutputType(gpa, arena, cmd_args, .Obj);
6965 } else if (mem.eql(u8, cmd, "fmt")) {
70 return cmdFmt(allocator, cmd_args);
66 return cmdFmt(gpa, cmd_args);
7167 } else if (mem.eql(u8, cmd, "libc")) {
72 return cmdLibC(allocator, cmd_args);
68 return cmdLibC(gpa, cmd_args);
7369 } else if (mem.eql(u8, cmd, "targets")) {
74 const info = try std.zig.system.NativeTargetInfo.detect(allocator, .{});
70 const info = try std.zig.system.NativeTargetInfo.detect(arena, .{});
7571 const stdout = io.getStdOut().outStream();
76 return @import("print_targets.zig").cmdTargets(allocator, cmd_args, stdout, info.target);
72 return @import("print_targets.zig").cmdTargets(arena, cmd_args, stdout, info.target);
7773 } else if (mem.eql(u8, cmd, "version")) {
78 return cmdVersion(allocator, cmd_args);
74 // Need to set up the build script to give the version as a comptime value.
75 std.debug.warn("TODO version command not implemented yet\n", .{});
76 return error.Unimplemented;
7977 } else if (mem.eql(u8, cmd, "zen")) {
80 return cmdZen(allocator, cmd_args);
78 try io.getStdOut().writeAll(info_zen);
8179 } else if (mem.eql(u8, cmd, "help")) {
82 return cmdHelp(allocator, cmd_args);
83 } else if (mem.eql(u8, cmd, "internal")) {
84 return cmdInternal(allocator, cmd_args);
80 try io.getStdOut().writeAll(usage);
8581 } else {
86 try stderr.print("unknown command: {}\n\n", .{args[1]});
87 try stderr.writeAll(usage);
82 std.debug.warn("unknown command: {}\n\n{}", .{ args[1], usage });
8883 process.exit(1);
8984 }
9085}
9186
9287const usage_build_generic =
93 \\usage: zig build-exe <options> [file]
94 \\ zig build-lib <options> [file]
95 \\ zig build-obj <options> [file]
88 \\Usage: zig build-exe <options> [files]
89 \\ zig build-lib <options> [files]
90 \\ zig build-obj <options> [files]
91 \\
92 \\Supported file types:
93 \\ (planned) .zig Zig source code
94 \\ .zir Zig Intermediate Representation code
95 \\ (planned) .o ELF object file
96 \\ (planned) .o MACH-O (macOS) object file
97 \\ (planned) .obj COFF (Windows) object file
98 \\ (planned) .lib COFF (Windows) static library
99 \\ (planned) .a ELF static library
100 \\ (planned) .so ELF shared object (dynamic link)
101 \\ (planned) .dll Windows Dynamic Link Library
102 \\ (planned) .dylib MACH-O (macOS) dynamic library
103 \\ (planned) .s Target-specific assembly source code
104 \\ (planned) .S Assembly with C preprocessor (requires LLVM extensions)
105 \\ (planned) .c C source code (requires LLVM extensions)
106 \\ (planned) .cpp C++ source code (requires LLVM extensions)
107 \\ Other C++ extensions: .C .cc .cxx
96108 \\
97109 \\General Options:
98 \\ --help Print this help and exit
99 \\ --color [auto|off|on] Enable or disable colored error messages
110 \\ -h, --help Print this help and exit
111 \\ --watch Enable compiler REPL
112 \\ --color [auto|off|on] Enable or disable colored error messages
113 \\ -femit-bin[=path] (default) output machine code
114 \\ -fno-emit-bin Do not output machine code
100115 \\
101116 \\Compile Options:
102 \\ --libc [file] Provide a file which specifies libc paths
103 \\ --assembly [source] Add assembly file to build
104 \\ --emit [filetype] Emit a specific file format as compilation output
105 \\ --enable-timing-info Print timing diagnostics
106 \\ --name [name] Override output name
107 \\ --output [file] Override destination path
108 \\ --output-h [file] Override generated header file path
109 \\ --pkg-begin [name] [path] Make package available to import and push current pkg
110 \\ --pkg-end Pop current pkg
111 \\ --mode [mode] Set the build mode
112 \\ debug (default) optimizations off, safety on
113 \\ release-fast optimizations on, safety off
114 \\ release-safe optimizations on, safety on
115 \\ release-small optimize for small binary, safety off
116 \\ --static Output will be statically linked
117 \\ --strip Exclude debug symbols
118 \\ -target [name] <arch><sub>-<os>-<abi> see the targets command
119 \\ --eh-frame-hdr enable C++ exception handling by passing --eh-frame-hdr to linker
120 \\ --verbose-tokenize Turn on compiler debug output for tokenization
121 \\ --verbose-ast-tree Turn on compiler debug output for parsing into an AST (tree view)
122 \\ --verbose-ast-fmt Turn on compiler debug output for parsing into an AST (render source)
123 \\ --verbose-link Turn on compiler debug output for linking
124 \\ --verbose-ir Turn on compiler debug output for Zig IR
125 \\ --verbose-llvm-ir Turn on compiler debug output for LLVM IR
126 \\ --verbose-cimport Turn on compiler debug output for C imports
127 \\ -dirafter [dir] Same as -isystem but do it last
128 \\ -isystem [dir] Add additional search path for other .h files
129 \\ -mllvm [arg] Additional arguments to forward to LLVM's option processing
117 \\ -target [name] <arch><sub>-<os>-<abi> see the targets command
118 \\ -mcpu [cpu] Specify target CPU and feature set
119 \\ --name [name] Override output name
120 \\ --mode [mode] Set the build mode
121 \\ Debug (default) optimizations off, safety on
122 \\ ReleaseFast optimizations on, safety off
123 \\ ReleaseSafe optimizations on, safety on
124 \\ ReleaseSmall optimize for small binary, safety off
125 \\ --dynamic Force output to be dynamically linked
126 \\ --strip Exclude debug symbols
130127 \\
131128 \\Link Options:
132 \\ --ar-path [path] Set the path to ar
133 \\ --each-lib-rpath Add rpath for each used dynamic library
134 \\ --library [lib] Link against lib
135 \\ --forbid-library [lib] Make it an error to link against lib
136 \\ --library-path [dir] Add a directory to the library search path
137 \\ --linker-script [path] Use a custom linker script
138 \\ --object [obj] Add object file to build
139 \\ -rdynamic Add all symbols to the dynamic symbol table
140 \\ -rpath [path] Add directory to the runtime library search path
141 \\ -framework [name] (darwin) link against framework
142 \\ -mios-version-min [ver] (darwin) set iOS deployment target
143 \\ -mmacosx-version-min [ver] (darwin) set Mac OS X deployment target
144 \\ --ver-major [ver] Dynamic library semver major version
145 \\ --ver-minor [ver] Dynamic library semver minor version
146 \\ --ver-patch [ver] Dynamic library semver patch version
129 \\ -l[lib], --library [lib] Link against system library
130 \\ --dynamic-linker [path] Set the dynamic interpreter path (usually ld.so)
131 \\ --version [ver] Dynamic library semver
147132 \\
133 \\Debug Options (Zig Compiler Development):
134 \\ -ftime-report Print timing diagnostics
135 \\ --debug-tokenize verbose tokenization
136 \\ --debug-ast-tree verbose parsing into an AST (tree view)
137 \\ --debug-ast-fmt verbose parsing into an AST (render source)
138 \\ --debug-ir verbose Zig IR
139 \\ --debug-link verbose linking
140 \\ --debug-codegen verbose machine code generation
148141 \\
149142;
150143
151fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Compilation.Kind) !void {
152 const stderr = io.getStdErr().outStream();
144const Emit = union(enum) {
145 no,
146 yes_default_path,
147 yes: []const u8,
148};
153149
154 var color: errmsg.Color = .Auto;
150fn buildOutputType(
151 gpa: *Allocator,
152 arena: *Allocator,
153 args: []const []const u8,
154 output_mode: std.builtin.OutputMode,
155) !void {
156 var color: Color = .Auto;
155157 var build_mode: std.builtin.Mode = .Debug;
156 var emit_bin = true;
157 var emit_asm = false;
158 var emit_llvm_ir = false;
159 var emit_h = false;
160158 var provided_name: ?[]const u8 = null;
161159 var is_dynamic = false;
162160 var root_src_file: ?[]const u8 = null;
163 var libc_arg: ?[]const u8 = null;
164161 var version: std.builtin.Version = .{ .major = 0, .minor = 0, .patch = 0 };
165 var linker_script: ?[]const u8 = null;
166162 var strip = false;
167 var verbose_tokenize = false;
168 var verbose_ast_tree = false;
169 var verbose_ast_fmt = false;
170 var verbose_link = false;
171 var verbose_ir = false;
172 var verbose_llvm_ir = false;
173 var verbose_cimport = false;
174 var linker_rdynamic = false;
175 var link_eh_frame_hdr = false;
176 var macosx_version_min: ?[]const u8 = null;
177 var ios_version_min: ?[]const u8 = null;
178
179 var assembly_files = ArrayList([]const u8).init(allocator);
180 defer assembly_files.deinit();
181
182 var link_objects = ArrayList([]const u8).init(allocator);
183 defer link_objects.deinit();
184
185 var clang_argv_buf = ArrayList([]const u8).init(allocator);
186 defer clang_argv_buf.deinit();
187
188 var mllvm_flags = ArrayList([]const u8).init(allocator);
189 defer mllvm_flags.deinit();
190
191 var cur_pkg = try CliPkg.init(allocator, "", "", null);
192 defer cur_pkg.deinit();
193
194 var system_libs = ArrayList([]const u8).init(allocator);
163 var watch = false;
164 var debug_tokenize = false;
165 var debug_ast_tree = false;
166 var debug_ast_fmt = false;
167 var debug_link = false;
168 var debug_ir = false;
169 var debug_codegen = false;
170 var time_report = false;
171 var emit_bin: Emit = .yes_default_path;
172 var emit_zir: Emit = .no;
173 var target_arch_os_abi: []const u8 = "native";
174 var target_mcpu: ?[]const u8 = null;
175 var target_dynamic_linker: ?[]const u8 = null;
176
177 var system_libs = std.ArrayList([]const u8).init(gpa);
195178 defer system_libs.deinit();
196179
197 var c_src_files = ArrayList([]const u8).init(allocator);
198 defer c_src_files.deinit();
199
200180 {
201181 var i: usize = 0;
202182 while (i < args.len) : (i += 1) {
203183 const arg = args[i];
204184 if (mem.startsWith(u8, arg, "-")) {
205 if (mem.eql(u8, arg, "--help")) {
185 if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
206186 try io.getStdOut().writeAll(usage_build_generic);
207187 process.exit(0);
208188 } else if (mem.eql(u8, arg, "--color")) {
209189 if (i + 1 >= args.len) {
210 try stderr.writeAll("expected [auto|on|off] after --color\n");
190 std.debug.warn("expected [auto|on|off] after --color\n", .{});
211191 process.exit(1);
212192 }
213193 i += 1;
......@@ -219,12 +199,12 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
219199 } else if (mem.eql(u8, next_arg, "off")) {
220200 color = .Off;
221201 } else {
222 try stderr.print("expected [auto|on|off] after --color, found '{}'\n", .{next_arg});
202 std.debug.warn("expected [auto|on|off] after --color, found '{}'\n", .{next_arg});
223203 process.exit(1);
224204 }
225205 } else if (mem.eql(u8, arg, "--mode")) {
226206 if (i + 1 >= args.len) {
227 try stderr.writeAll("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode\n");
207 std.debug.warn("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode\n", .{});
228208 process.exit(1);
229209 }
230210 i += 1;
......@@ -238,289 +218,317 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
238218 } else if (mem.eql(u8, next_arg, "ReleaseSmall")) {
239219 build_mode = .ReleaseSmall;
240220 } else {
241 try stderr.print("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode, found '{}'\n", .{next_arg});
221 std.debug.warn("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode, found '{}'\n", .{next_arg});
242222 process.exit(1);
243223 }
244224 } else if (mem.eql(u8, arg, "--name")) {
245225 if (i + 1 >= args.len) {
246 try stderr.writeAll("expected parameter after --name\n");
226 std.debug.warn("expected parameter after --name\n", .{});
247227 process.exit(1);
248228 }
249229 i += 1;
250230 provided_name = args[i];
251 } else if (mem.eql(u8, arg, "--ver-major")) {
252 if (i + 1 >= args.len) {
253 try stderr.writeAll("expected parameter after --ver-major\n");
254 process.exit(1);
255 }
256 i += 1;
257 version.major = try std.fmt.parseInt(u32, args[i], 10);
258 } else if (mem.eql(u8, arg, "--ver-minor")) {
231 } else if (mem.eql(u8, arg, "--library")) {
259232 if (i + 1 >= args.len) {
260 try stderr.writeAll("expected parameter after --ver-minor\n");
233 std.debug.warn("expected parameter after --library\n", .{});
261234 process.exit(1);
262235 }
263236 i += 1;
264 version.minor = try std.fmt.parseInt(u32, args[i], 10);
265 } else if (mem.eql(u8, arg, "--ver-patch")) {
237 try system_libs.append(args[i]);
238 } else if (mem.eql(u8, arg, "--version")) {
266239 if (i + 1 >= args.len) {
267 try stderr.writeAll("expected parameter after --ver-patch\n");
240 std.debug.warn("expected parameter after --version\n", .{});
268241 process.exit(1);
269242 }
270243 i += 1;
271 version.patch = try std.fmt.parseInt(u32, args[i], 10);
272 } else if (mem.eql(u8, arg, "--linker-script")) {
273 if (i + 1 >= args.len) {
274 try stderr.writeAll("expected parameter after --linker-script\n");
275 process.exit(1);
276 }
277 i += 1;
278 linker_script = args[i];
279 } else if (mem.eql(u8, arg, "--libc")) {
280 if (i + 1 >= args.len) {
281 try stderr.writeAll("expected parameter after --libc\n");
244 version = std.builtin.Version.parse(args[i]) catch |err| {
245 std.debug.warn("unable to parse --version '{}': {}\n", .{ args[i], @errorName(err) });
282246 process.exit(1);
283 }
284 i += 1;
285 libc_arg = args[i];
286 } else if (mem.eql(u8, arg, "-mllvm")) {
247 };
248 } else if (mem.eql(u8, arg, "-target")) {
287249 if (i + 1 >= args.len) {
288 try stderr.writeAll("expected parameter after -mllvm\n");
250 std.debug.warn("expected parameter after -target\n", .{});
289251 process.exit(1);
290252 }
291253 i += 1;
292 try clang_argv_buf.append("-mllvm");
293 try clang_argv_buf.append(args[i]);
294
295 try mllvm_flags.append(args[i]);
296 } else if (mem.eql(u8, arg, "-mmacosx-version-min")) {
254 target_arch_os_abi = args[i];
255 } else if (mem.eql(u8, arg, "-mcpu")) {
297256 if (i + 1 >= args.len) {
298 try stderr.writeAll("expected parameter after -mmacosx-version-min\n");
257 std.debug.warn("expected parameter after -mcpu\n", .{});
299258 process.exit(1);
300259 }
301260 i += 1;
302 macosx_version_min = args[i];
303 } else if (mem.eql(u8, arg, "-mios-version-min")) {
261 target_mcpu = args[i];
262 } else if (mem.startsWith(u8, arg, "-mcpu=")) {
263 target_mcpu = arg["-mcpu=".len..];
264 } else if (mem.eql(u8, arg, "--dynamic-linker")) {
304265 if (i + 1 >= args.len) {
305 try stderr.writeAll("expected parameter after -mios-version-min\n");
266 std.debug.warn("expected parameter after --dynamic-linker\n", .{});
306267 process.exit(1);
307268 }
308269 i += 1;
309 ios_version_min = args[i];
270 target_dynamic_linker = args[i];
271 } else if (mem.eql(u8, arg, "--watch")) {
272 watch = true;
273 } else if (mem.eql(u8, arg, "-ftime-report")) {
274 time_report = true;
310275 } else if (mem.eql(u8, arg, "-femit-bin")) {
311 emit_bin = true;
276 emit_bin = .yes_default_path;
277 } else if (mem.startsWith(u8, arg, "-femit-bin=")) {
278 emit_bin = .{ .yes = arg["-femit-bin=".len..] };
312279 } else if (mem.eql(u8, arg, "-fno-emit-bin")) {
313 emit_bin = false;
314 } else if (mem.eql(u8, arg, "-femit-asm")) {
315 emit_asm = true;
316 } else if (mem.eql(u8, arg, "-fno-emit-asm")) {
317 emit_asm = false;
318 } else if (mem.eql(u8, arg, "-femit-llvm-ir")) {
319 emit_llvm_ir = true;
320 } else if (mem.eql(u8, arg, "-fno-emit-llvm-ir")) {
321 emit_llvm_ir = false;
280 emit_bin = .no;
281 } else if (mem.eql(u8, arg, "-femit-zir")) {
282 emit_zir = .yes_default_path;
283 } else if (mem.startsWith(u8, arg, "-femit-zir=")) {
284 emit_zir = .{ .yes = arg["-femit-zir=".len..] };
285 } else if (mem.eql(u8, arg, "-fno-emit-zir")) {
286 emit_zir = .no;
322287 } else if (mem.eql(u8, arg, "-dynamic")) {
323288 is_dynamic = true;
324289 } else if (mem.eql(u8, arg, "--strip")) {
325290 strip = true;
326 } else if (mem.eql(u8, arg, "--verbose-tokenize")) {
327 verbose_tokenize = true;
328 } else if (mem.eql(u8, arg, "--verbose-ast-tree")) {
329 verbose_ast_tree = true;
330 } else if (mem.eql(u8, arg, "--verbose-ast-fmt")) {
331 verbose_ast_fmt = true;
332 } else if (mem.eql(u8, arg, "--verbose-link")) {
333 verbose_link = true;
334 } else if (mem.eql(u8, arg, "--verbose-ir")) {
335 verbose_ir = true;
336 } else if (mem.eql(u8, arg, "--verbose-llvm-ir")) {
337 verbose_llvm_ir = true;
338 } else if (mem.eql(u8, arg, "--eh-frame-hdr")) {
339 link_eh_frame_hdr = true;
340 } else if (mem.eql(u8, arg, "--verbose-cimport")) {
341 verbose_cimport = true;
342 } else if (mem.eql(u8, arg, "-rdynamic")) {
343 linker_rdynamic = true;
344 } else if (mem.eql(u8, arg, "--pkg-begin")) {
345 if (i + 2 >= args.len) {
346 try stderr.writeAll("expected [name] [path] after --pkg-begin\n");
347 process.exit(1);
348 }
349 i += 1;
350 const new_pkg_name = args[i];
351 i += 1;
352 const new_pkg_path = args[i];
353
354 var new_cur_pkg = try CliPkg.init(allocator, new_pkg_name, new_pkg_path, cur_pkg);
355 try cur_pkg.children.append(new_cur_pkg);
356 cur_pkg = new_cur_pkg;
357 } else if (mem.eql(u8, arg, "--pkg-end")) {
358 if (cur_pkg.parent) |parent| {
359 cur_pkg = parent;
360 } else {
361 try stderr.writeAll("encountered --pkg-end with no matching --pkg-begin\n");
362 process.exit(1);
363 }
291 } else if (mem.eql(u8, arg, "--debug-tokenize")) {
292 debug_tokenize = true;
293 } else if (mem.eql(u8, arg, "--debug-ast-tree")) {
294 debug_ast_tree = true;
295 } else if (mem.eql(u8, arg, "--debug-ast-fmt")) {
296 debug_ast_fmt = true;
297 } else if (mem.eql(u8, arg, "--debug-link")) {
298 debug_link = true;
299 } else if (mem.eql(u8, arg, "--debug-ir")) {
300 debug_ir = true;
301 } else if (mem.eql(u8, arg, "--debug-codegen")) {
302 debug_codegen = true;
364303 } else if (mem.startsWith(u8, arg, "-l")) {
365304 try system_libs.append(arg[2..]);
366305 } else {
367 try stderr.print("unrecognized parameter: '{}'", .{arg});
306 std.debug.warn("unrecognized parameter: '{}'", .{arg});
368307 process.exit(1);
369308 }
370 } else if (mem.endsWith(u8, arg, ".s")) {
371 try assembly_files.append(arg);
309 } else if (mem.endsWith(u8, arg, ".s") or mem.endsWith(u8, arg, ".S")) {
310 std.debug.warn("assembly files not supported yet", .{});
311 process.exit(1);
372312 } else if (mem.endsWith(u8, arg, ".o") or
373313 mem.endsWith(u8, arg, ".obj") or
374314 mem.endsWith(u8, arg, ".a") or
375315 mem.endsWith(u8, arg, ".lib"))
376316 {
377 try link_objects.append(arg);
317 std.debug.warn("object files and static libraries not supported yet", .{});
318 process.exit(1);
378319 } else if (mem.endsWith(u8, arg, ".c") or
379320 mem.endsWith(u8, arg, ".cpp"))
380321 {
381 try c_src_files.append(arg);
382 } else if (mem.endsWith(u8, arg, ".zig")) {
322 std.debug.warn("compilation of C and C++ source code requires LLVM extensions which are not implemented yet", .{});
323 process.exit(1);
324 } else if (mem.endsWith(u8, arg, ".so") or
325 mem.endsWith(u8, arg, ".dylib") or
326 mem.endsWith(u8, arg, ".dll"))
327 {
328 std.debug.warn("linking against dynamic libraries not yet supported", .{});
329 process.exit(1);
330 } else if (mem.endsWith(u8, arg, ".zig") or mem.endsWith(u8, arg, ".zir")) {
383331 if (root_src_file) |other| {
384 try stderr.print("found another zig file '{}' after root source file '{}'", .{
385 arg,
386 other,
387 });
332 std.debug.warn("found another zig file '{}' after root source file '{}'", .{ arg, other });
388333 process.exit(1);
389334 } else {
390335 root_src_file = arg;
391336 }
392337 } else {
393 try stderr.print("unrecognized file extension of parameter '{}'", .{arg});
338 std.debug.warn("unrecognized file extension of parameter '{}'", .{arg});
394339 }
395340 }
396341 }
397342
398 if (cur_pkg.parent != null) {
399 try stderr.print("unmatched --pkg-begin\n", .{});
400 process.exit(1);
401 }
402
403343 const root_name = if (provided_name) |n| n else blk: {
404344 if (root_src_file) |file| {
405345 const basename = fs.path.basename(file);
406346 var it = mem.split(basename, ".");
407347 break :blk it.next() orelse basename;
408348 } else {
409 try stderr.writeAll("--name [name] not provided and unable to infer\n");
349 std.debug.warn("--name [name] not provided and unable to infer\n", .{});
410350 process.exit(1);
411351 }
412352 };
413353
414 if (root_src_file == null and link_objects.len == 0 and assembly_files.len == 0) {
415 try stderr.writeAll("Expected source file argument or at least one --object or --assembly argument\n");
354 if (system_libs.items.len != 0) {
355 std.debug.warn("linking against system libraries not yet supported", .{});
416356 process.exit(1);
417357 }
418358
419 if (out_type == Compilation.Kind.Obj and link_objects.len != 0) {
420 try stderr.writeAll("When building an object file, --object arguments are invalid\n");
359 var diags: std.zig.CrossTarget.ParseOptions.Diagnostics = .{};
360 const cross_target = std.zig.CrossTarget.parse(.{
361 .arch_os_abi = target_arch_os_abi,
362 .cpu_features = target_mcpu,
363 .dynamic_linker = target_dynamic_linker,
364 .diagnostics = &diags,
365 }) catch |err| switch (err) {
366 error.UnknownCpuModel => {
367 std.debug.warn("Unknown CPU: '{}'\nAvailable CPUs for architecture '{}':\n", .{
368 diags.cpu_name.?,
369 @tagName(diags.arch.?),
370 });
371 for (diags.arch.?.allCpuModels()) |cpu| {
372 std.debug.warn(" {}\n", .{cpu.name});
373 }
374 process.exit(1);
375 },
376 error.UnknownCpuFeature => {
377 std.debug.warn(
378 \\Unknown CPU feature: '{}'
379 \\Available CPU features for architecture '{}':
380 \\
381 , .{
382 diags.unknown_feature_name,
383 @tagName(diags.arch.?),
384 });
385 for (diags.arch.?.allFeaturesList()) |feature| {
386 std.debug.warn(" {}: {}\n", .{ feature.name, feature.description });
387 }
388 process.exit(1);
389 },
390 else => |e| return e,
391 };
392
393 const object_format: ?std.builtin.ObjectFormat = null;
394 var target_info = try std.zig.system.NativeTargetInfo.detect(gpa, cross_target);
395 if (target_info.cpu_detection_unimplemented) {
396 // TODO We want to just use detected_info.target but implementing
397 // CPU model & feature detection is todo so here we rely on LLVM.
398 std.debug.warn("CPU features detection is not yet available for this system without LLVM extensions\n", .{});
421399 process.exit(1);
422400 }
423401
424 try ZigCompiler.setLlvmArgv(allocator, mllvm_flags.span());
425
426 const zig_lib_dir = introspect.resolveZigLibDir(allocator) catch process.exit(1);
427 defer allocator.free(zig_lib_dir);
428
429 var override_libc: LibCInstallation = undefined;
402 const src_path = root_src_file orelse {
403 std.debug.warn("expected at least one file argument", .{});
404 process.exit(1);
405 };
430406
431 var zig_compiler = try ZigCompiler.init(allocator);
432 defer zig_compiler.deinit();
407 const bin_path = switch (emit_bin) {
408 .no => {
409 std.debug.warn("-fno-emit-bin not supported yet", .{});
410 process.exit(1);
411 },
412 .yes_default_path => try std.fmt.allocPrint(arena, "{}{}", .{ root_name, target_info.target.exeFileExt() }),
413 .yes => |p| p,
414 };
433415
434 var comp = try Compilation.create(
435 &zig_compiler,
436 root_name,
437 root_src_file,
438 .{},
439 out_type,
440 build_mode,
441 !is_dynamic,
442 zig_lib_dir,
443 );
444 defer comp.destroy();
416 const zir_out_path: ?[]const u8 = switch (emit_zir) {
417 .no => null,
418 .yes_default_path => blk: {
419 if (root_src_file) |rsf| {
420 if (mem.endsWith(u8, rsf, ".zir")) {
421 break :blk try std.fmt.allocPrint(arena, "{}.out.zir", .{root_name});
422 }
423 }
424 break :blk try std.fmt.allocPrint(arena, "{}.zir", .{root_name});
425 },
426 .yes => |p| p,
427 };
445428
446 if (libc_arg) |libc_path| {
447 parseLibcPaths(allocator, &override_libc, libc_path);
448 comp.override_libc = &override_libc;
449 }
429 var bin_file = try link.openBinFilePath(gpa, fs.cwd(), bin_path, .{
430 .target = target_info.target,
431 .output_mode = output_mode,
432 .link_mode = if (is_dynamic) .Dynamic else .Static,
433 .object_format = object_format orelse target_info.target.getObjectFormat(),
434 });
435 defer bin_file.deinit();
436
437 var module = blk: {
438 const root_pkg = try Package.create(gpa, fs.cwd(), ".", src_path);
439 errdefer root_pkg.destroy();
440
441 const root_scope = try gpa.create(ir.Module.Scope.ZIRModule);
442 errdefer gpa.destroy(root_scope);
443 root_scope.* = .{
444 .sub_file_path = root_pkg.root_src_path,
445 .source = .{ .unloaded = {} },
446 .contents = .{ .not_available = {} },
447 .status = .never_loaded,
448 };
450449
451 for (system_libs.span()) |lib| {
452 _ = try comp.addLinkLib(lib, true);
450 break :blk ir.Module{
451 .allocator = gpa,
452 .root_pkg = root_pkg,
453 .root_scope = root_scope,
454 .bin_file = &bin_file,
455 .optimize_mode = .Debug,
456 .decl_table = std.AutoHashMap(ir.Module.Decl.Hash, *ir.Module.Decl).init(gpa),
457 .decl_exports = std.AutoHashMap(*ir.Module.Decl, []*ir.Module.Export).init(gpa),
458 .export_owners = std.AutoHashMap(*ir.Module.Decl, []*ir.Module.Export).init(gpa),
459 .failed_decls = std.AutoHashMap(*ir.Module.Decl, *ir.ErrorMsg).init(gpa),
460 .failed_files = std.AutoHashMap(*ir.Module.Scope.ZIRModule, *ir.ErrorMsg).init(gpa),
461 .failed_exports = std.AutoHashMap(*ir.Module.Export, *ir.ErrorMsg).init(gpa),
462 .work_queue = std.fifo.LinearFifo(ir.Module.WorkItem, .Dynamic).init(gpa),
463 };
464 };
465 defer module.deinit();
466
467 const stdin = std.io.getStdIn().inStream();
468 const stderr = std.io.getStdErr().outStream();
469 var repl_buf: [1024]u8 = undefined;
470
471 try updateModule(gpa, &module, zir_out_path);
472
473 while (watch) {
474 try stderr.print("🦎 ", .{});
475 if (stdin.readUntilDelimiterOrEof(&repl_buf, '\n') catch |err| {
476 try stderr.print("\nUnable to parse command: {}\n", .{@errorName(err)});
477 continue;
478 }) |line| {
479 if (mem.eql(u8, line, "update")) {
480 try updateModule(gpa, &module, zir_out_path);
481 } else if (mem.eql(u8, line, "exit")) {
482 break;
483 } else if (mem.eql(u8, line, "help")) {
484 try stderr.writeAll(repl_help);
485 } else {
486 try stderr.print("unknown command: {}\n", .{line});
487 }
488 } else {
489 break;
490 }
453491 }
492}
454493
455 comp.version = version;
456 comp.is_test = false;
457 comp.linker_script = linker_script;
458 comp.clang_argv = clang_argv_buf.span();
459 comp.strip = strip;
460
461 comp.verbose_tokenize = verbose_tokenize;
462 comp.verbose_ast_tree = verbose_ast_tree;
463 comp.verbose_ast_fmt = verbose_ast_fmt;
464 comp.verbose_link = verbose_link;
465 comp.verbose_ir = verbose_ir;
466 comp.verbose_llvm_ir = verbose_llvm_ir;
467 comp.verbose_cimport = verbose_cimport;
468
469 comp.link_eh_frame_hdr = link_eh_frame_hdr;
470
471 comp.err_color = color;
494fn updateModule(gpa: *Allocator, module: *ir.Module, zir_out_path: ?[]const u8) !void {
495 try module.update();
472496
473 comp.linker_rdynamic = linker_rdynamic;
497 var errors = try module.getAllErrorsAlloc();
498 defer errors.deinit(module.allocator);
474499
475 if (macosx_version_min != null and ios_version_min != null) {
476 try stderr.writeAll("-mmacosx-version-min and -mios-version-min options not allowed together\n");
477 process.exit(1);
500 if (errors.list.len != 0) {
501 for (errors.list) |full_err_msg| {
502 std.debug.warn("{}:{}:{}: error: {}\n", .{
503 full_err_msg.src_path,
504 full_err_msg.line + 1,
505 full_err_msg.column + 1,
506 full_err_msg.msg,
507 });
508 }
478509 }
479510
480 if (macosx_version_min) |ver| {
481 comp.darwin_version_min = Compilation.DarwinVersionMin{ .MacOS = ver };
482 }
483 if (ios_version_min) |ver| {
484 comp.darwin_version_min = Compilation.DarwinVersionMin{ .Ios = ver };
485 }
511 if (zir_out_path) |zop| {
512 var new_zir_module = try ir.text.emit_zir(gpa, module.*);
513 defer new_zir_module.deinit(gpa);
486514
487 comp.emit_bin = emit_bin;
488 comp.emit_asm = emit_asm;
489 comp.emit_llvm_ir = emit_llvm_ir;
490 comp.emit_h = emit_h;
491 comp.assembly_files = assembly_files.span();
492 comp.link_objects = link_objects.span();
515 const baf = try io.BufferedAtomicFile.create(gpa, fs.cwd(), zop, .{});
516 defer baf.destroy();
493517
494 comp.start();
495 processBuildEvents(comp, color);
496}
518 try new_zir_module.writeToStream(gpa, baf.stream());
497519
498fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void {
499 const stderr_file = io.getStdErr();
500 const stderr = stderr_file.outStream();
501 var count: usize = 0;
502 while (!comp.cancelled) {
503 const build_event = comp.events.get();
504 count += 1;
505
506 switch (build_event) {
507 .Ok => {
508 stderr.print("Build {} succeeded\n", .{count}) catch process.exit(1);
509 },
510 .Error => |err| {
511 stderr.print("Build {} failed: {}\n", .{ count, @errorName(err) }) catch process.exit(1);
512 },
513 .Fail => |msgs| {
514 stderr.print("Build {} compile errors:\n", .{count}) catch process.exit(1);
515 for (msgs) |msg| {
516 defer msg.destroy();
517 msg.printToFile(stderr_file, color) catch process.exit(1);
518 }
519 },
520 }
520 try baf.finish();
521521 }
522522}
523523
524const repl_help =
525 \\Commands:
526 \\ update Detect changes to source files and update output files.
527 \\ help Print this text
528 \\ exit Quit this repl
529 \\
530;
531
524532pub const usage_fmt =
525533 \\usage: zig fmt [file]...
526534 \\
......@@ -539,17 +547,17 @@ pub const usage_fmt =
539547;
540548
541549const Fmt = struct {
542 seen: event.Locked(SeenMap),
550 seen: SeenMap,
543551 any_error: bool,
544 color: errmsg.Color,
545 allocator: *Allocator,
552 color: Color,
553 gpa: *Allocator,
546554
547 const SeenMap = std.StringHashMap(void);
555 const SeenMap = std.BufSet;
548556};
549557
550fn parseLibcPaths(allocator: *Allocator, libc: *LibCInstallation, libc_paths_file: []const u8) void {
558fn parseLibcPaths(gpa: *Allocator, libc: *LibCInstallation, libc_paths_file: []const u8) void {
551559 const stderr = io.getStdErr().outStream();
552 libc.* = LibCInstallation.parse(allocator, libc_paths_file, stderr) catch |err| {
560 libc.* = LibCInstallation.parse(gpa, libc_paths_file, stderr) catch |err| {
553561 stderr.print("Unable to parse libc path file '{}': {}.\n" ++
554562 "Try running `zig libc` to see an example for the native target.\n", .{
555563 libc_paths_file,
......@@ -559,13 +567,13 @@ fn parseLibcPaths(allocator: *Allocator, libc: *LibCInstallation, libc_paths_fil
559567 };
560568}
561569
562fn cmdLibC(allocator: *Allocator, args: []const []const u8) !void {
570fn cmdLibC(gpa: *Allocator, args: []const []const u8) !void {
563571 const stderr = io.getStdErr().outStream();
564572 switch (args.len) {
565573 0 => {},
566574 1 => {
567575 var libc_installation: LibCInstallation = undefined;
568 parseLibcPaths(allocator, &libc_installation, args[0]);
576 parseLibcPaths(gpa, &libc_installation, args[0]);
569577 return;
570578 },
571579 else => {
......@@ -574,23 +582,20 @@ fn cmdLibC(allocator: *Allocator, args: []const []const u8) !void {
574582 },
575583 }
576584
577 var zig_compiler = try ZigCompiler.init(allocator);
578 defer zig_compiler.deinit();
579
580 const libc = zig_compiler.getNativeLibC() catch |err| {
585 const libc = LibCInstallation.findNative(.{ .allocator = gpa }) catch |err| {
581586 stderr.print("unable to find libc: {}\n", .{@errorName(err)}) catch {};
582587 process.exit(1);
583588 };
589
584590 libc.render(io.getStdOut().outStream()) catch process.exit(1);
585591}
586592
587fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
593pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
588594 const stderr_file = io.getStdErr();
589 const stderr = stderr_file.outStream();
590 var color: errmsg.Color = .Auto;
595 var color: Color = .Auto;
591596 var stdin_flag: bool = false;
592597 var check_flag: bool = false;
593 var input_files = ArrayList([]const u8).init(allocator);
598 var input_files = ArrayList([]const u8).init(gpa);
594599
595600 {
596601 var i: usize = 0;
......@@ -603,7 +608,7 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
603608 process.exit(0);
604609 } else if (mem.eql(u8, arg, "--color")) {
605610 if (i + 1 >= args.len) {
606 try stderr.writeAll("expected [auto|on|off] after --color\n");
611 std.debug.warn("expected [auto|on|off] after --color\n", .{});
607612 process.exit(1);
608613 }
609614 i += 1;
......@@ -615,7 +620,7 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
615620 } else if (mem.eql(u8, next_arg, "off")) {
616621 color = .Off;
617622 } else {
618 try stderr.print("expected [auto|on|off] after --color, found '{}'\n", .{next_arg});
623 std.debug.warn("expected [auto|on|off] after --color, found '{}'\n", .{next_arg});
619624 process.exit(1);
620625 }
621626 } else if (mem.eql(u8, arg, "--stdin")) {
......@@ -623,7 +628,7 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
623628 } else if (mem.eql(u8, arg, "--check")) {
624629 check_flag = true;
625630 } else {
626 try stderr.print("unrecognized parameter: '{}'", .{arg});
631 std.debug.warn("unrecognized parameter: '{}'", .{arg});
627632 process.exit(1);
628633 }
629634 } else {
......@@ -633,60 +638,55 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
633638 }
634639
635640 if (stdin_flag) {
636 if (input_files.len != 0) {
637 try stderr.writeAll("cannot use --stdin with positional arguments\n");
641 if (input_files.items.len != 0) {
642 std.debug.warn("cannot use --stdin with positional arguments\n", .{});
638643 process.exit(1);
639644 }
640645
641646 const stdin = io.getStdIn().inStream();
642647
643 const source_code = try stdin.readAllAlloc(allocator, max_src_size);
644 defer allocator.free(source_code);
648 const source_code = try stdin.readAllAlloc(gpa, max_src_size);
649 defer gpa.free(source_code);
645650
646 const tree = std.zig.parse(allocator, source_code) catch |err| {
647 try stderr.print("error parsing stdin: {}\n", .{err});
651 const tree = std.zig.parse(gpa, source_code) catch |err| {
652 std.debug.warn("error parsing stdin: {}\n", .{err});
648653 process.exit(1);
649654 };
650655 defer tree.deinit();
651656
652657 var error_it = tree.errors.iterator(0);
653658 while (error_it.next()) |parse_error| {
654 const msg = try errmsg.Msg.createFromParseError(allocator, parse_error, tree, "<stdin>");
655 defer msg.destroy();
656
657 try msg.printToFile(io.getStdErr(), color);
659 try printErrMsgToFile(gpa, parse_error, tree, "<stdin>", stderr_file, color);
658660 }
659661 if (tree.errors.len != 0) {
660662 process.exit(1);
661663 }
662664 if (check_flag) {
663 const anything_changed = try std.zig.render(allocator, io.null_out_stream, tree);
664 const code: u8 = if (anything_changed) 1 else 0;
665 const anything_changed = try std.zig.render(gpa, io.null_out_stream, tree);
666 const code = if (anything_changed) @as(u8, 1) else @as(u8, 0);
665667 process.exit(code);
666668 }
667669
668670 const stdout = io.getStdOut().outStream();
669 _ = try std.zig.render(allocator, stdout, tree);
671 _ = try std.zig.render(gpa, stdout, tree);
670672 return;
671673 }
672674
673 if (input_files.len == 0) {
674 try stderr.writeAll("expected at least one source file argument\n");
675 if (input_files.items.len == 0) {
676 std.debug.warn("expected at least one source file argument\n", .{});
675677 process.exit(1);
676678 }
677679
678680 var fmt = Fmt{
679 .allocator = allocator,
680 .seen = event.Locked(Fmt.SeenMap).init(Fmt.SeenMap.init(allocator)),
681 .gpa = gpa,
682 .seen = Fmt.SeenMap.init(gpa),
681683 .any_error = false,
682684 .color = color,
683685 };
684686
685 var group = event.Group(FmtError!void).init(allocator);
686687 for (input_files.span()) |file_path| {
687 try group.call(fmtPath, .{ &fmt, file_path, check_flag });
688 try fmtPath(&fmt, file_path, check_flag);
688689 }
689 try group.wait();
690690 if (fmt.any_error) {
691691 process.exit(1);
692692 }
......@@ -711,54 +711,45 @@ const FmtError = error{
711711 ReadOnlyFileSystem,
712712 LinkQuotaExceeded,
713713 FileBusy,
714 CurrentWorkingDirectoryUnlinked,
715714} || fs.File.OpenError;
716715
717async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtError!void {
718 const stderr_file = io.getStdErr();
719 const stderr = stderr_file.outStream();
720
721 const file_path = try std.mem.dupe(fmt.allocator, u8, file_path_ref);
722 defer fmt.allocator.free(file_path);
723
724 {
725 const held = fmt.seen.acquire();
726 defer held.release();
716fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool) FmtError!void {
717 // get the real path here to avoid Windows failing on relative file paths with . or .. in them
718 var real_path = fs.realpathAlloc(fmt.gpa, file_path) catch |err| {
719 std.debug.warn("unable to open '{}': {}\n", .{ file_path, err });
720 fmt.any_error = true;
721 return;
722 };
723 defer fmt.gpa.free(real_path);
727724
728 if (try held.value.put(file_path, {})) |_| return;
729 }
725 if (fmt.seen.exists(real_path)) return;
726 try fmt.seen.put(real_path);
730727
731 const source_code = fs.cwd().readFileAlloc(
732 fmt.allocator,
733 file_path,
734 max_src_size,
735 ) catch |err| switch (err) {
728 const source_code = fs.cwd().readFileAlloc(fmt.gpa, real_path, max_src_size) catch |err| switch (err) {
736729 error.IsDir, error.AccessDenied => {
737730 var dir = try fs.cwd().openDir(file_path, .{ .iterate = true });
738731 defer dir.close();
739732
740 var group = event.Group(FmtError!void).init(fmt.allocator);
741 var it = dir.iterate();
742 while (try it.next()) |entry| {
733 var dir_it = dir.iterate();
734
735 while (try dir_it.next()) |entry| {
743736 if (entry.kind == .Directory or mem.endsWith(u8, entry.name, ".zig")) {
744 const full_path = try fs.path.join(fmt.allocator, &[_][]const u8{ file_path, entry.name });
745 @panic("TODO https://github.com/ziglang/zig/issues/3777");
746 // try group.call(fmtPath, .{fmt, full_path, check_mode});
737 const full_path = try fs.path.join(fmt.gpa, &[_][]const u8{ file_path, entry.name });
738 try fmtPath(fmt, full_path, check_mode);
747739 }
748740 }
749 return group.wait();
741 return;
750742 },
751743 else => {
752 // TODO lock stderr printing
753 try stderr.print("unable to open '{}': {}\n", .{ file_path, err });
744 std.debug.warn("unable to open '{}': {}\n", .{ file_path, err });
754745 fmt.any_error = true;
755746 return;
756747 },
757748 };
758 defer fmt.allocator.free(source_code);
749 defer fmt.gpa.free(source_code);
759750
760 const tree = std.zig.parse(fmt.allocator, source_code) catch |err| {
761 try stderr.print("error parsing file '{}': {}\n", .{ file_path, err });
751 const tree = std.zig.parse(fmt.gpa, source_code) catch |err| {
752 std.debug.warn("error parsing file '{}': {}\n", .{ file_path, err });
762753 fmt.any_error = true;
763754 return;
764755 };
......@@ -766,10 +757,7 @@ async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtErro
766757
767758 var error_it = tree.errors.iterator(0);
768759 while (error_it.next()) |parse_error| {
769 const msg = try errmsg.Msg.createFromParseError(fmt.allocator, parse_error, tree, file_path);
770 defer fmt.allocator.destroy(msg);
771
772 try msg.printToFile(stderr_file, fmt.color);
760 try printErrMsgToFile(fmt.gpa, parse_error, tree, file_path, std.io.getStdErr(), fmt.color);
773761 }
774762 if (tree.errors.len != 0) {
775763 fmt.any_error = true;
......@@ -777,32 +765,67 @@ async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtErro
777765 }
778766
779767 if (check_mode) {
780 const anything_changed = try std.zig.render(fmt.allocator, io.null_out_stream, tree);
768 const anything_changed = try std.zig.render(fmt.gpa, io.null_out_stream, tree);
781769 if (anything_changed) {
782 try stderr.print("{}\n", .{file_path});
770 std.debug.warn("{}\n", .{file_path});
783771 fmt.any_error = true;
784772 }
785773 } else {
786 // TODO make this evented
787 const baf = try io.BufferedAtomicFile.create(fmt.allocator, file_path);
774 const baf = try io.BufferedAtomicFile.create(fmt.gpa, fs.cwd(), real_path, .{});
788775 defer baf.destroy();
789776
790 const anything_changed = try std.zig.render(fmt.allocator, baf.stream(), tree);
777 const anything_changed = try std.zig.render(fmt.gpa, baf.stream(), tree);
791778 if (anything_changed) {
792 try stderr.print("{}\n", .{file_path});
779 std.debug.warn("{}\n", .{file_path});
793780 try baf.finish();
794781 }
795782 }
796783}
797784
798fn cmdVersion(allocator: *Allocator, args: []const []const u8) !void {
799 const stdout = io.getStdOut().outStream();
800 try stdout.print("{}\n", .{c.ZIG_VERSION_STRING});
801}
802
803fn cmdHelp(allocator: *Allocator, args: []const []const u8) !void {
804 const stdout = io.getStdOut();
805 try stdout.writeAll(usage);
785fn printErrMsgToFile(
786 gpa: *mem.Allocator,
787 parse_error: *const ast.Error,
788 tree: *ast.Tree,
789 path: []const u8,
790 file: fs.File,
791 color: Color,
792) !void {
793 const color_on = switch (color) {
794 .Auto => file.isTty(),
795 .On => true,
796 .Off => false,
797 };
798 const lok_token = parse_error.loc();
799 const span_first = lok_token;
800 const span_last = lok_token;
801
802 const first_token = tree.tokens.at(span_first);
803 const last_token = tree.tokens.at(span_last);
804 const start_loc = tree.tokenLocationPtr(0, first_token);
805 const end_loc = tree.tokenLocationPtr(first_token.end, last_token);
806
807 var text_buf = std.ArrayList(u8).init(gpa);
808 defer text_buf.deinit();
809 const out_stream = text_buf.outStream();
810 try parse_error.render(&tree.tokens, out_stream);
811 const text = text_buf.span();
812
813 const stream = file.outStream();
814 try stream.print("{}:{}:{}: error: {}\n", .{ path, start_loc.line + 1, start_loc.column + 1, text });
815
816 if (!color_on) return;
817
818 // Print \r and \t as one space each so that column counts line up
819 for (tree.source[start_loc.line_start..start_loc.line_end]) |byte| {
820 try stream.writeByte(switch (byte) {
821 '\r', '\t' => ' ',
822 else => byte,
823 });
824 }
825 try stream.writeByte('\n');
826 try stream.writeByteNTimes(' ', start_loc.column);
827 try stream.writeByteNTimes('~', last_token.end - first_token.start);
828 try stream.writeByte('\n');
806829}
807830
808831pub const info_zen =
......@@ -817,90 +840,8 @@ pub const info_zen =
817840 \\ * Avoid local maximums.
818841 \\ * Reduce the amount one must remember.
819842 \\ * Minimize energy spent on coding style.
843 \\ * Resource deallocation must succeed.
820844 \\ * Together we serve end users.
821845 \\
822846 \\
823847;
824
825fn cmdZen(allocator: *Allocator, args: []const []const u8) !void {
826 try io.getStdOut().writeAll(info_zen);
827}
828
829const usage_internal =
830 \\usage: zig internal [subcommand]
831 \\
832 \\Sub-Commands:
833 \\ build-info Print static compiler build-info
834 \\
835 \\
836;
837
838fn cmdInternal(allocator: *Allocator, args: []const []const u8) !void {
839 const stderr = io.getStdErr().outStream();
840 if (args.len == 0) {
841 try stderr.writeAll(usage_internal);
842 process.exit(1);
843 }
844
845 const sub_commands = [_]Command{Command{
846 .name = "build-info",
847 .exec = cmdInternalBuildInfo,
848 }};
849
850 inline for (sub_commands) |sub_command| {
851 if (mem.eql(u8, sub_command.name, args[0])) {
852 var frame = try allocator.create(@Frame(sub_command.exec));
853 defer allocator.destroy(frame);
854 frame.* = async sub_command.exec(allocator, args[1..]);
855 return await frame;
856 }
857 }
858
859 try stderr.print("unknown sub command: {}\n\n", .{args[0]});
860 try stderr.writeAll(usage_internal);
861}
862
863fn cmdInternalBuildInfo(allocator: *Allocator, args: []const []const u8) !void {
864 const stdout = io.getStdOut().outStream();
865 try stdout.print(
866 \\ZIG_CMAKE_BINARY_DIR {}
867 \\ZIG_CXX_COMPILER {}
868 \\ZIG_LLD_INCLUDE_PATH {}
869 \\ZIG_LLD_LIBRARIES {}
870 \\ZIG_LLVM_CONFIG_EXE {}
871 \\ZIG_DIA_GUIDS_LIB {}
872 \\
873 , .{
874 c.ZIG_CMAKE_BINARY_DIR,
875 c.ZIG_CXX_COMPILER,
876 c.ZIG_LLD_INCLUDE_PATH,
877 c.ZIG_LLD_LIBRARIES,
878 c.ZIG_LLVM_CONFIG_EXE,
879 c.ZIG_DIA_GUIDS_LIB,
880 });
881}
882
883const CliPkg = struct {
884 name: []const u8,
885 path: []const u8,
886 children: ArrayList(*CliPkg),
887 parent: ?*CliPkg,
888
889 pub fn init(allocator: *mem.Allocator, name: []const u8, path: []const u8, parent: ?*CliPkg) !*CliPkg {
890 var pkg = try allocator.create(CliPkg);
891 pkg.* = CliPkg{
892 .name = name,
893 .path = path,
894 .children = ArrayList(*CliPkg).init(allocator),
895 .parent = parent,
896 };
897 return pkg;
898 }
899
900 pub fn deinit(self: *CliPkg) void {
901 for (self.children.span()) |child| {
902 child.deinit();
903 }
904 self.children.deinit();
905 }
906};
src-self-hosted/stage2.zig+1-253
......@@ -12,7 +12,6 @@ const ArrayListSentineled = std.ArrayListSentineled;
1212const Target = std.Target;
1313const CrossTarget = std.zig.CrossTarget;
1414const self_hosted_main = @import("main.zig");
15const errmsg = @import("errmsg.zig");
1615const DepTokenizer = @import("dep_tokenizer.zig").Tokenizer;
1716const assert = std.debug.assert;
1817const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
......@@ -168,8 +167,6 @@ export fn stage2_render_ast(tree: *ast.Tree, output_file: *FILE) Error {
168167 return .None;
169168}
170169
171// TODO: just use the actual self-hosted zig fmt. Until https://github.com/ziglang/zig/issues/2377,
172// we use a blocking implementation.
173170export fn stage2_fmt(argc: c_int, argv: [*]const [*:0]const u8) c_int {
174171 if (std.debug.runtime_safety) {
175172 fmtMain(argc, argv) catch unreachable;
......@@ -191,258 +188,9 @@ fn fmtMain(argc: c_int, argv: [*]const [*:0]const u8) !void {
191188 try args_list.append(mem.spanZ(argv[arg_i]));
192189 }
193190
194 stdout = std.io.getStdOut().outStream();
195 stderr_file = std.io.getStdErr();
196 stderr = stderr_file.outStream();
197
198191 const args = args_list.span()[2..];
199192
200 var color: errmsg.Color = .Auto;
201 var stdin_flag: bool = false;
202 var check_flag: bool = false;
203 var input_files = ArrayList([]const u8).init(allocator);
204
205 {
206 var i: usize = 0;
207 while (i < args.len) : (i += 1) {
208 const arg = args[i];
209 if (mem.startsWith(u8, arg, "-")) {
210 if (mem.eql(u8, arg, "--help")) {
211 try stdout.writeAll(self_hosted_main.usage_fmt);
212 process.exit(0);
213 } else if (mem.eql(u8, arg, "--color")) {
214 if (i + 1 >= args.len) {
215 try stderr.writeAll("expected [auto|on|off] after --color\n");
216 process.exit(1);
217 }
218 i += 1;
219 const next_arg = args[i];
220 if (mem.eql(u8, next_arg, "auto")) {
221 color = .Auto;
222 } else if (mem.eql(u8, next_arg, "on")) {
223 color = .On;
224 } else if (mem.eql(u8, next_arg, "off")) {
225 color = .Off;
226 } else {
227 try stderr.print("expected [auto|on|off] after --color, found '{}'\n", .{next_arg});
228 process.exit(1);
229 }
230 } else if (mem.eql(u8, arg, "--stdin")) {
231 stdin_flag = true;
232 } else if (mem.eql(u8, arg, "--check")) {
233 check_flag = true;
234 } else {
235 try stderr.print("unrecognized parameter: '{}'", .{arg});
236 process.exit(1);
237 }
238 } else {
239 try input_files.append(arg);
240 }
241 }
242 }
243
244 if (stdin_flag) {
245 if (input_files.items.len != 0) {
246 try stderr.writeAll("cannot use --stdin with positional arguments\n");
247 process.exit(1);
248 }
249
250 const stdin_file = io.getStdIn();
251 var stdin = stdin_file.inStream();
252
253 const source_code = try stdin.readAllAlloc(allocator, self_hosted_main.max_src_size);
254 defer allocator.free(source_code);
255
256 const tree = std.zig.parse(allocator, source_code) catch |err| {
257 try stderr.print("error parsing stdin: {}\n", .{err});
258 process.exit(1);
259 };
260 defer tree.deinit();
261
262 var error_it = tree.errors.iterator(0);
263 while (error_it.next()) |parse_error| {
264 try printErrMsgToFile(allocator, parse_error, tree, "<stdin>", stderr_file, color);
265 }
266 if (tree.errors.len != 0) {
267 process.exit(1);
268 }
269 if (check_flag) {
270 const anything_changed = try std.zig.render(allocator, io.null_out_stream, tree);
271 const code = if (anything_changed) @as(u8, 1) else @as(u8, 0);
272 process.exit(code);
273 }
274
275 _ = try std.zig.render(allocator, stdout, tree);
276 return;
277 }
278
279 if (input_files.items.len == 0) {
280 try stderr.writeAll("expected at least one source file argument\n");
281 process.exit(1);
282 }
283
284 var fmt = Fmt{
285 .seen = Fmt.SeenMap.init(allocator),
286 .any_error = false,
287 .color = color,
288 .allocator = allocator,
289 };
290
291 for (input_files.span()) |file_path| {
292 try fmtPath(&fmt, file_path, check_flag);
293 }
294 if (fmt.any_error) {
295 process.exit(1);
296 }
297}
298
299const FmtError = error{
300 SystemResources,
301 OperationAborted,
302 IoPending,
303 BrokenPipe,
304 Unexpected,
305 WouldBlock,
306 FileClosed,
307 DestinationAddressRequired,
308 DiskQuota,
309 FileTooBig,
310 InputOutput,
311 NoSpaceLeft,
312 AccessDenied,
313 OutOfMemory,
314 RenameAcrossMountPoints,
315 ReadOnlyFileSystem,
316 LinkQuotaExceeded,
317 FileBusy,
318} || fs.File.OpenError;
319
320fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool) FmtError!void {
321 // get the real path here to avoid Windows failing on relative file paths with . or .. in them
322 var real_path = fs.realpathAlloc(fmt.allocator, file_path) catch |err| {
323 try stderr.print("unable to open '{}': {}\n", .{ file_path, err });
324 fmt.any_error = true;
325 return;
326 };
327 defer fmt.allocator.free(real_path);
328
329 if (fmt.seen.exists(real_path)) return;
330 try fmt.seen.put(real_path);
331
332 const source_code = fs.cwd().readFileAlloc(fmt.allocator, real_path, self_hosted_main.max_src_size) catch |err| switch (err) {
333 error.IsDir, error.AccessDenied => {
334 // TODO make event based (and dir.next())
335 var dir = try fs.cwd().openDir(file_path, .{ .iterate = true });
336 defer dir.close();
337
338 var dir_it = dir.iterate();
339
340 while (try dir_it.next()) |entry| {
341 if (entry.kind == .Directory or mem.endsWith(u8, entry.name, ".zig")) {
342 const full_path = try fs.path.join(fmt.allocator, &[_][]const u8{ file_path, entry.name });
343 try fmtPath(fmt, full_path, check_mode);
344 }
345 }
346 return;
347 },
348 else => {
349 // TODO lock stderr printing
350 try stderr.print("unable to open '{}': {}\n", .{ file_path, err });
351 fmt.any_error = true;
352 return;
353 },
354 };
355 defer fmt.allocator.free(source_code);
356
357 const tree = std.zig.parse(fmt.allocator, source_code) catch |err| {
358 try stderr.print("error parsing file '{}': {}\n", .{ file_path, err });
359 fmt.any_error = true;
360 return;
361 };
362 defer tree.deinit();
363
364 var error_it = tree.errors.iterator(0);
365 while (error_it.next()) |parse_error| {
366 try printErrMsgToFile(fmt.allocator, parse_error, tree, file_path, stderr_file, fmt.color);
367 }
368 if (tree.errors.len != 0) {
369 fmt.any_error = true;
370 return;
371 }
372
373 if (check_mode) {
374 const anything_changed = try std.zig.render(fmt.allocator, io.null_out_stream, tree);
375 if (anything_changed) {
376 try stderr.print("{}\n", .{file_path});
377 fmt.any_error = true;
378 }
379 } else {
380 const baf = try io.BufferedAtomicFile.create(fmt.allocator, fs.cwd(), real_path, .{});
381 defer baf.destroy();
382
383 const anything_changed = try std.zig.render(fmt.allocator, baf.stream(), tree);
384 if (anything_changed) {
385 try stderr.print("{}\n", .{file_path});
386 try baf.finish();
387 }
388 }
389}
390
391const Fmt = struct {
392 seen: SeenMap,
393 any_error: bool,
394 color: errmsg.Color,
395 allocator: *mem.Allocator,
396
397 const SeenMap = std.BufSet;
398};
399
400fn printErrMsgToFile(
401 allocator: *mem.Allocator,
402 parse_error: *const ast.Error,
403 tree: *ast.Tree,
404 path: []const u8,
405 file: fs.File,
406 color: errmsg.Color,
407) !void {
408 const color_on = switch (color) {
409 .Auto => file.isTty(),
410 .On => true,
411 .Off => false,
412 };
413 const lok_token = parse_error.loc();
414 const span = errmsg.Span{
415 .first = lok_token,
416 .last = lok_token,
417 };
418
419 const first_token = tree.tokens.at(span.first);
420 const last_token = tree.tokens.at(span.last);
421 const start_loc = tree.tokenLocationPtr(0, first_token);
422 const end_loc = tree.tokenLocationPtr(first_token.end, last_token);
423
424 var text_buf = std.ArrayList(u8).init(allocator);
425 defer text_buf.deinit();
426 const out_stream = text_buf.outStream();
427 try parse_error.render(&tree.tokens, out_stream);
428 const text = text_buf.span();
429
430 const stream = file.outStream();
431 try stream.print("{}:{}:{}: error: {}\n", .{ path, start_loc.line + 1, start_loc.column + 1, text });
432
433 if (!color_on) return;
434
435 // Print \r and \t as one space each so that column counts line up
436 for (tree.source[start_loc.line_start..start_loc.line_end]) |byte| {
437 try stream.writeByte(switch (byte) {
438 '\r', '\t' => ' ',
439 else => byte,
440 });
441 }
442 try stream.writeByte('\n');
443 try stream.writeByteNTimes(' ', start_loc.column);
444 try stream.writeByteNTimes('~', last_token.end - first_token.start);
445 try stream.writeByte('\n');
193 return self_hosted_main.cmdFmt(allocator, args);
446194}
447195
448196export fn stage2_DepTokenizer_init(input: [*]const u8, len: usize) stage2_DepTokenizer {