authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-09-26 12:42:07-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-09-26 12:42:07-07:00
log26f2f9bf1c774caf246317b3fc032449e982a150
tree43bc4d4b19cea9a245d87f105841d075ab75f5ab
parent9b83112a1f6af758a1a995ea8838a2319d2fbc1e

stage2: implement -fno-emit-bin

we are now passing the cli tests

12 files changed, 137 insertions(+), 102 deletions(-)

BRANCH_TODO-1
...@@ -1,4 +1,3 @@...@@ -1,4 +1,3 @@
1 * support -fno-emit-bin for godbolt
2 * restore the legacy -femit-h feature using the stage1 backend1 * restore the legacy -femit-h feature using the stage1 backend
3 * figure out why test-translate-c is failing2 * figure out why test-translate-c is failing
4 * tests passing with -Dskip-non-native3 * tests passing with -Dskip-non-native
src/Compilation.zig+73-56
...@@ -500,8 +500,6 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {...@@ -500,8 +500,6 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
500 break :pic explicit;500 break :pic explicit;
501 } else must_pic;501 } else must_pic;
502502
503 const emit_bin = options.emit_bin orelse fatal("-fno-emit-bin not supported yet", .{}); // TODO
504
505 // Make a decision on whether to use Clang for translate-c and compiling C files.503 // Make a decision on whether to use Clang for translate-c and compiling C files.
506 const use_clang = if (options.use_clang) |explicit| explicit else blk: {504 const use_clang = if (options.use_clang) |explicit| explicit else blk: {
507 if (build_options.have_llvm) {505 if (build_options.have_llvm) {
...@@ -667,13 +665,29 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {...@@ -667,13 +665,29 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
667 } else null;665 } else null;
668 errdefer if (module) |zm| zm.deinit();666 errdefer if (module) |zm| zm.deinit();
669667
668 const error_return_tracing = !options.strip and switch (options.optimize_mode) {
669 .Debug, .ReleaseSafe => true,
670 .ReleaseFast, .ReleaseSmall => false,
671 };
672
670 // For resource management purposes.673 // For resource management purposes.
671 var owned_link_dir: ?std.fs.Dir = null;674 var owned_link_dir: ?std.fs.Dir = null;
672 errdefer if (owned_link_dir) |*dir| dir.close();675 errdefer if (owned_link_dir) |*dir| dir.close();
673676
674 const bin_directory = emit_bin.directory orelse blk: {677 const bin_file_emit: ?link.Emit = blk: {
675 if (module) |zm| break :blk zm.zig_cache_artifact_directory;678 const emit_bin = options.emit_bin orelse break :blk null;
676679 if (emit_bin.directory) |directory| {
680 break :blk link.Emit{
681 .directory = directory,
682 .sub_path = emit_bin.basename,
683 };
684 }
685 if (module) |zm| {
686 break :blk link.Emit{
687 .directory = zm.zig_cache_artifact_directory,
688 .sub_path = emit_bin.basename,
689 };
690 }
677 // We could use the cache hash as is no problem, however, we increase691 // We could use the cache hash as is no problem, however, we increase
678 // the likelihood of cache hits by adding the first C source file692 // the likelihood of cache hits by adding the first C source file
679 // path name (not contents) to the hash. This way if the user is compiling693 // path name (not contents) to the hash. This way if the user is compiling
...@@ -694,17 +708,14 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {...@@ -694,17 +708,14 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
694 .handle = artifact_dir,708 .handle = artifact_dir,
695 .path = try options.local_cache_directory.join(arena, &[_][]const u8{artifact_sub_dir}),709 .path = try options.local_cache_directory.join(arena, &[_][]const u8{artifact_sub_dir}),
696 };710 };
697 break :blk link_artifact_directory;711 break :blk link.Emit{
698 };712 .directory = link_artifact_directory,
699713 .sub_path = emit_bin.basename,
700 const error_return_tracing = !options.strip and switch (options.optimize_mode) {714 };
701 .Debug, .ReleaseSafe => true,
702 .ReleaseFast, .ReleaseSmall => false,
703 };715 };
704716
705 const bin_file = try link.File.openPath(gpa, .{717 const bin_file = try link.File.openPath(gpa, .{
706 .directory = bin_directory,718 .emit = bin_file_emit,
707 .sub_path = emit_bin.basename,
708 .root_name = root_name,719 .root_name = root_name,
709 .module = module,720 .module = module,
710 .target = options.target,721 .target = options.target,
...@@ -815,43 +826,46 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {...@@ -815,43 +826,46 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
815 comp.c_object_table.putAssumeCapacityNoClobber(c_object, {});826 comp.c_object_table.putAssumeCapacityNoClobber(c_object, {});
816 }827 }
817828
818 // If we need to build glibc for the target, add work items for it.829 if (comp.bin_file.options.emit != null) {
819 // We go through the work queue so that building can be done in parallel.830 // If we need to build glibc for the target, add work items for it.
820 if (comp.wantBuildGLibCFromSource()) {831 // We go through the work queue so that building can be done in parallel.
821 try comp.addBuildingGLibCJobs();832 if (comp.wantBuildGLibCFromSource()) {
822 }833 try comp.addBuildingGLibCJobs();
823 if (comp.wantBuildMuslFromSource()) {834 }
824 try comp.work_queue.write(&[_]Job{835 if (comp.wantBuildMuslFromSource()) {
825 .{ .musl_crt_file = .crti_o },836 try comp.work_queue.write(&[_]Job{
826 .{ .musl_crt_file = .crtn_o },837 .{ .musl_crt_file = .crti_o },
827 .{ .musl_crt_file = .crt1_o },838 .{ .musl_crt_file = .crtn_o },
828 .{ .musl_crt_file = .scrt1_o },839 .{ .musl_crt_file = .crt1_o },
829 .{ .musl_crt_file = .libc_a },840 .{ .musl_crt_file = .scrt1_o },
830 });841 .{ .musl_crt_file = .libc_a },
831 }842 });
832 if (comp.wantBuildMinGWW64FromSource()) {843 }
833 @panic("TODO");844 if (comp.wantBuildMinGWW64FromSource()) {
834 }845 @panic("TODO");
835 if (comp.wantBuildLibUnwindFromSource()) {846 }
836 try comp.work_queue.writeItem(.{ .libunwind = {} });847 if (comp.wantBuildLibUnwindFromSource()) {
837 }848 try comp.work_queue.writeItem(.{ .libunwind = {} });
838 if (build_options.have_llvm and comp.bin_file.options.output_mode != .Obj and849 }
839 comp.bin_file.options.link_libcpp)850 if (build_options.have_llvm and comp.bin_file.options.output_mode != .Obj and
840 {851 comp.bin_file.options.link_libcpp)
841 try comp.work_queue.writeItem(.libcxx);852 {
842 try comp.work_queue.writeItem(.libcxxabi);853 try comp.work_queue.writeItem(.libcxx);
854 try comp.work_queue.writeItem(.libcxxabi);
855 }
856 if (is_exe_or_dyn_lib and !comp.bin_file.options.is_compiler_rt_or_libc and
857 build_options.is_stage1)
858 {
859 try comp.work_queue.writeItem(.{ .libcompiler_rt = {} });
860 if (!comp.bin_file.options.link_libc) {
861 try comp.work_queue.writeItem(.{ .zig_libc = {} });
862 }
863 }
843 }864 }
865
844 if (build_options.is_stage1 and comp.bin_file.options.use_llvm) {866 if (build_options.is_stage1 and comp.bin_file.options.use_llvm) {
845 try comp.work_queue.writeItem(.{ .stage1_module = {} });867 try comp.work_queue.writeItem(.{ .stage1_module = {} });
846 }868 }
847 if (is_exe_or_dyn_lib and !comp.bin_file.options.is_compiler_rt_or_libc and
848 build_options.is_stage1)
849 {
850 try comp.work_queue.writeItem(.{ .libcompiler_rt = {} });
851 if (!comp.bin_file.options.link_libc) {
852 try comp.work_queue.writeItem(.{ .zig_libc = {} });
853 }
854 }
855869
856 return comp;870 return comp;
857}871}
...@@ -2408,8 +2422,8 @@ fn buildStaticLibFromZig(comp: *Compilation, src_basename: []const u8, out: *?CR...@@ -2408,8 +2422,8 @@ fn buildStaticLibFromZig(comp: *Compilation, src_basename: []const u8, out: *?CR
24082422
2409 assert(out.* == null);2423 assert(out.* == null);
2410 out.* = Compilation.CRTFile{2424 out.* = Compilation.CRTFile{
2411 .full_object_path = try sub_compilation.bin_file.options.directory.join(comp.gpa, &[_][]const u8{2425 .full_object_path = try sub_compilation.bin_file.options.emit.?.directory.join(comp.gpa, &[_][]const u8{
2412 sub_compilation.bin_file.options.sub_path,2426 sub_compilation.bin_file.options.emit.?.sub_path,
2413 }),2427 }),
2414 .lock = sub_compilation.bin_file.toOwnedLock(),2428 .lock = sub_compilation.bin_file.toOwnedLock(),
2415 };2429 };
...@@ -2452,6 +2466,7 @@ fn updateStage1Module(comp: *Compilation) !void {...@@ -2452,6 +2466,7 @@ fn updateStage1Module(comp: *Compilation) !void {
2452 man.hash.add(comp.bin_file.options.dll_export_fns);2466 man.hash.add(comp.bin_file.options.dll_export_fns);
2453 man.hash.add(comp.bin_file.options.function_sections);2467 man.hash.add(comp.bin_file.options.function_sections);
2454 man.hash.add(comp.is_test);2468 man.hash.add(comp.is_test);
2469 man.hash.add(comp.bin_file.options.emit != null);
2455 man.hash.add(comp.emit_h != null);2470 man.hash.add(comp.emit_h != null);
2456 man.hash.add(comp.emit_asm != null);2471 man.hash.add(comp.emit_asm != null);
2457 man.hash.add(comp.emit_llvm_ir != null);2472 man.hash.add(comp.emit_llvm_ir != null);
...@@ -2517,12 +2532,14 @@ fn updateStage1Module(comp: *Compilation) !void {...@@ -2517,12 +2532,14 @@ fn updateStage1Module(comp: *Compilation) !void {
2517 comp.is_test,2532 comp.is_test,
2518 ) orelse return error.OutOfMemory;2533 ) orelse return error.OutOfMemory;
25192534
2520 const bin_basename = try std.zig.binNameAlloc(arena, .{2535 const emit_bin_path = if (comp.bin_file.options.emit != null) blk: {
2521 .root_name = comp.bin_file.options.root_name,2536 const bin_basename = try std.zig.binNameAlloc(arena, .{
2522 .target = target,2537 .root_name = comp.bin_file.options.root_name,
2523 .output_mode = .Obj,2538 .target = target,
2524 });2539 .output_mode = .Obj,
2525 const emit_bin_path = try directory.join(arena, &[_][]const u8{bin_basename});2540 });
2541 break :blk try directory.join(arena, &[_][]const u8{bin_basename});
2542 } else "";
2526 const emit_h_path = try stage1LocPath(arena, comp.emit_h, directory);2543 const emit_h_path = try stage1LocPath(arena, comp.emit_h, directory);
2527 const emit_asm_path = try stage1LocPath(arena, comp.emit_asm, directory);2544 const emit_asm_path = try stage1LocPath(arena, comp.emit_asm, directory);
2528 const emit_llvm_ir_path = try stage1LocPath(arena, comp.emit_llvm_ir, directory);2545 const emit_llvm_ir_path = try stage1LocPath(arena, comp.emit_llvm_ir, directory);
...@@ -2697,8 +2714,8 @@ pub fn build_crt_file(...@@ -2697,8 +2714,8 @@ pub fn build_crt_file(
2697 try comp.crt_files.ensureCapacity(comp.gpa, comp.crt_files.count() + 1);2714 try comp.crt_files.ensureCapacity(comp.gpa, comp.crt_files.count() + 1);
26982715
2699 comp.crt_files.putAssumeCapacityNoClobber(basename, .{2716 comp.crt_files.putAssumeCapacityNoClobber(basename, .{
2700 .full_object_path = try sub_compilation.bin_file.options.directory.join(comp.gpa, &[_][]const u8{2717 .full_object_path = try sub_compilation.bin_file.options.emit.?.directory.join(comp.gpa, &[_][]const u8{
2701 sub_compilation.bin_file.options.sub_path,2718 sub_compilation.bin_file.options.emit.?.sub_path,
2702 }),2719 }),
2703 .lock = sub_compilation.bin_file.toOwnedLock(),2720 .lock = sub_compilation.bin_file.toOwnedLock(),
2704 });2721 });
src/libcxx.zig+8-2
...@@ -189,7 +189,10 @@ pub fn buildLibCXX(comp: *Compilation) !void {...@@ -189,7 +189,10 @@ pub fn buildLibCXX(comp: *Compilation) !void {
189189
190 assert(comp.libcxx_static_lib == null);190 assert(comp.libcxx_static_lib == null);
191 comp.libcxx_static_lib = Compilation.CRTFile{191 comp.libcxx_static_lib = Compilation.CRTFile{
192 .full_object_path = try sub_compilation.bin_file.options.directory.join(comp.gpa, &[_][]const u8{basename}),192 .full_object_path = try sub_compilation.bin_file.options.emit.?.directory.join(
193 comp.gpa,
194 &[_][]const u8{basename},
195 ),
193 .lock = sub_compilation.bin_file.toOwnedLock(),196 .lock = sub_compilation.bin_file.toOwnedLock(),
194 };197 };
195}198}
...@@ -303,7 +306,10 @@ pub fn buildLibCXXABI(comp: *Compilation) !void {...@@ -303,7 +306,10 @@ pub fn buildLibCXXABI(comp: *Compilation) !void {
303306
304 assert(comp.libcxxabi_static_lib == null);307 assert(comp.libcxxabi_static_lib == null);
305 comp.libcxxabi_static_lib = Compilation.CRTFile{308 comp.libcxxabi_static_lib = Compilation.CRTFile{
306 .full_object_path = try sub_compilation.bin_file.options.directory.join(comp.gpa, &[_][]const u8{basename}),309 .full_object_path = try sub_compilation.bin_file.options.emit.?.directory.join(
310 comp.gpa,
311 &[_][]const u8{basename},
312 ),
307 .lock = sub_compilation.bin_file.toOwnedLock(),313 .lock = sub_compilation.bin_file.toOwnedLock(),
308 };314 };
309}315}
src/libunwind.zig+4-1
...@@ -126,7 +126,10 @@ pub fn buildStaticLib(comp: *Compilation) !void {...@@ -126,7 +126,10 @@ pub fn buildStaticLib(comp: *Compilation) !void {
126126
127 assert(comp.libunwind_static_lib == null);127 assert(comp.libunwind_static_lib == null);
128 comp.libunwind_static_lib = Compilation.CRTFile{128 comp.libunwind_static_lib = Compilation.CRTFile{
129 .full_object_path = try sub_compilation.bin_file.options.directory.join(comp.gpa, &[_][]const u8{basename}),129 .full_object_path = try sub_compilation.bin_file.options.emit.?.directory.join(
130 comp.gpa,
131 &[_][]const u8{basename},
132 ),
130 .lock = sub_compilation.bin_file.toOwnedLock(),133 .lock = sub_compilation.bin_file.toOwnedLock(),
131 };134 };
132}135}
src/link.zig+18-13
...@@ -16,11 +16,17 @@ const LibCInstallation = @import("libc_installation.zig").LibCInstallation;...@@ -16,11 +16,17 @@ const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
1616
17pub const producer_string = if (std.builtin.is_test) "zig test" else "zig " ++ build_options.version;17pub const producer_string = if (std.builtin.is_test) "zig test" else "zig " ++ build_options.version;
1818
19pub const Options = struct {19pub const Emit = struct {
20 /// Where the output will go.20 /// Where the output will go.
21 directory: Compilation.Directory,21 directory: Compilation.Directory,
22 /// Path to the output file, relative to `directory`.22 /// Path to the output file, relative to `directory`.
23 sub_path: []const u8,23 sub_path: []const u8,
24};
25
26pub const Options = struct {
27 /// This is `null` when -fno-emit-bin is used. When `openPath` or `flush` is called,
28 /// it will have already been null-checked.
29 emit: ?Emit,
24 target: std.Target,30 target: std.Target,
25 output_mode: std.builtin.OutputMode,31 output_mode: std.builtin.OutputMode,
26 link_mode: std.builtin.LinkMode,32 link_mode: std.builtin.LinkMode,
...@@ -141,7 +147,7 @@ pub const File = struct {...@@ -141,7 +147,7 @@ pub const File = struct {
141 /// and does not cause Illegal Behavior. This operation is not atomic.147 /// and does not cause Illegal Behavior. This operation is not atomic.
142 pub fn openPath(allocator: *Allocator, options: Options) !*File {148 pub fn openPath(allocator: *Allocator, options: Options) !*File {
143 const use_stage1 = build_options.is_stage1 and options.use_llvm;149 const use_stage1 = build_options.is_stage1 and options.use_llvm;
144 if (use_stage1) {150 if (use_stage1 or options.emit == null) {
145 return switch (options.object_format) {151 return switch (options.object_format) {
146 .coff, .pe => &(try Coff.createEmpty(allocator, options)).base,152 .coff, .pe => &(try Coff.createEmpty(allocator, options)).base,
147 .elf => &(try Elf.createEmpty(allocator, options)).base,153 .elf => &(try Elf.createEmpty(allocator, options)).base,
...@@ -152,6 +158,7 @@ pub const File = struct {...@@ -152,6 +158,7 @@ pub const File = struct {
152 .raw => return error.RawObjectFormatUnimplemented,158 .raw => return error.RawObjectFormatUnimplemented,
153 };159 };
154 }160 }
161 const emit = options.emit.?;
155 const use_lld = build_options.have_llvm and options.use_lld; // comptime known false when !have_llvm162 const use_lld = build_options.have_llvm and options.use_lld; // comptime known false when !have_llvm
156 const sub_path = if (use_lld) blk: {163 const sub_path = if (use_lld) blk: {
157 if (options.module == null) {164 if (options.module == null) {
...@@ -167,8 +174,8 @@ pub const File = struct {...@@ -167,8 +174,8 @@ pub const File = struct {
167 };174 };
168 }175 }
169 // Open a temporary object file, not the final output file because we want to link with LLD.176 // Open a temporary object file, not the final output file because we want to link with LLD.
170 break :blk try std.fmt.allocPrint(allocator, "{}{}", .{ options.sub_path, options.target.oFileExt() });177 break :blk try std.fmt.allocPrint(allocator, "{}{}", .{ emit.sub_path, options.target.oFileExt() });
171 } else options.sub_path;178 } else emit.sub_path;
172 errdefer if (use_lld) allocator.free(sub_path);179 errdefer if (use_lld) allocator.free(sub_path);
173180
174 const file: *File = switch (options.object_format) {181 const file: *File = switch (options.object_format) {
...@@ -199,7 +206,8 @@ pub const File = struct {...@@ -199,7 +206,8 @@ pub const File = struct {
199 switch (base.tag) {206 switch (base.tag) {
200 .coff, .elf, .macho => {207 .coff, .elf, .macho => {
201 if (base.file != null) return;208 if (base.file != null) return;
202 base.file = try base.options.directory.handle.createFile(base.options.sub_path, .{209 const emit = base.options.emit orelse return;
210 base.file = try emit.directory.handle.createFile(emit.sub_path, .{
203 .truncate = false,211 .truncate = false,
204 .read = true,212 .read = true,
205 .mode = determineMode(base.options),213 .mode = determineMode(base.options),
...@@ -305,14 +313,14 @@ pub const File = struct {...@@ -305,14 +313,14 @@ pub const File = struct {
305 /// Commit pending changes and write headers. Takes into account final output mode313 /// Commit pending changes and write headers. Takes into account final output mode
306 /// and `use_lld`, not only `effectiveOutputMode`.314 /// and `use_lld`, not only `effectiveOutputMode`.
307 pub fn flush(base: *File, comp: *Compilation) !void {315 pub fn flush(base: *File, comp: *Compilation) !void {
316 const emit = base.options.emit orelse return; // -fno-emit-bin
317
308 if (comp.clang_preprocessor_mode == .yes) {318 if (comp.clang_preprocessor_mode == .yes) {
309 // TODO: avoid extra link step when it's just 1 object file (the `zig cc -c` case)319 // TODO: avoid extra link step when it's just 1 object file (the `zig cc -c` case)
310 // Until then, we do `lld -r -o output.o input.o` even though the output is the same320 // Until then, we do `lld -r -o output.o input.o` even though the output is the same
311 // as the input. For the preprocessing case (`zig cc -E -o foo`) we copy the file321 // as the input. For the preprocessing case (`zig cc -E -o foo`) we copy the file
312 // to the final location.322 // to the final location.
313 const full_out_path = try base.options.directory.join(comp.gpa, &[_][]const u8{323 const full_out_path = try emit.directory.join(comp.gpa, &[_][]const u8{emit.sub_path});
314 base.options.sub_path,
315 });
316 defer comp.gpa.free(full_out_path);324 defer comp.gpa.free(full_out_path);
317 assert(comp.c_object_table.count() == 1);325 assert(comp.c_object_table.count() == 1);
318 const the_entry = comp.c_object_table.items()[0];326 const the_entry = comp.c_object_table.items()[0];
...@@ -402,7 +410,7 @@ pub const File = struct {...@@ -402,7 +410,7 @@ pub const File = struct {
402 defer arena_allocator.deinit();410 defer arena_allocator.deinit();
403 const arena = &arena_allocator.allocator;411 const arena = &arena_allocator.allocator;
404412
405 const directory = base.options.directory; // Just an alias to make it shorter to type.413 const directory = base.options.emit.?.directory; // Just an alias to make it shorter to type.
406414
407 // If there is no Zig code to compile, then we should skip flushing the output file because it415 // If there is no Zig code to compile, then we should skip flushing the output file because it
408 // will not be part of the linker line anyway.416 // will not be part of the linker line anyway.
...@@ -471,10 +479,7 @@ pub const File = struct {...@@ -471,10 +479,7 @@ pub const File = struct {
471 object_files.appendAssumeCapacity(try arena.dupeZ(u8, p));479 object_files.appendAssumeCapacity(try arena.dupeZ(u8, p));
472 }480 }
473481
474 const full_out_path = if (directory.path) |dir_path|482 const full_out_path = try directory.join(arena, &[_][]const u8{base.options.emit.?.sub_path});
475 try std.fs.path.join(arena, &[_][]const u8{ dir_path, base.options.sub_path })
476 else
477 base.options.sub_path;
478 const full_out_path_z = try arena.dupeZ(u8, full_out_path);483 const full_out_path_z = try arena.dupeZ(u8, full_out_path);
479484
480 if (base.options.verbose_link) {485 if (base.options.verbose_link) {
src/link/C.zig+1-1
...@@ -30,7 +30,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio...@@ -30,7 +30,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
30 if (options.use_llvm) return error.LLVMHasNoCBackend;30 if (options.use_llvm) return error.LLVMHasNoCBackend;
31 if (options.use_lld) return error.LLDHasNoCBackend;31 if (options.use_lld) return error.LLDHasNoCBackend;
3232
33 const file = try options.directory.handle.createFile(sub_path, .{ .truncate = true, .read = true, .mode = link.determineMode(options) });33 const file = try options.emit.?.directory.handle.createFile(sub_path, .{ .truncate = true, .read = true, .mode = link.determineMode(options) });
34 errdefer file.close();34 errdefer file.close();
3535
36 var c_file = try allocator.create(C);36 var c_file = try allocator.create(C);
src/link/Coff.zig+1-1
...@@ -120,7 +120,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio...@@ -120,7 +120,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
120 if (options.use_llvm) return error.LLVM_BackendIsTODO_ForCoff; // TODO120 if (options.use_llvm) return error.LLVM_BackendIsTODO_ForCoff; // TODO
121 if (options.use_lld) return error.LLD_LinkingIsTODO_ForCoff; // TODO121 if (options.use_lld) return error.LLD_LinkingIsTODO_ForCoff; // TODO
122122
123 const file = try options.directory.handle.createFile(sub_path, .{123 const file = try options.emit.?.directory.handle.createFile(sub_path, .{
124 .truncate = false,124 .truncate = false,
125 .read = true,125 .read = true,
126 .mode = link.determineMode(options),126 .mode = link.determineMode(options),
src/link/Elf.zig+3-3
...@@ -229,7 +229,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio...@@ -229,7 +229,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
229229
230 if (options.use_llvm) return error.LLVMBackendUnimplementedForELF; // TODO230 if (options.use_llvm) return error.LLVMBackendUnimplementedForELF; // TODO
231231
232 const file = try options.directory.handle.createFile(sub_path, .{232 const file = try options.emit.?.directory.handle.createFile(sub_path, .{
233 .truncate = false,233 .truncate = false,
234 .read = true,234 .read = true,
235 .mode = link.determineMode(options),235 .mode = link.determineMode(options),
...@@ -1218,7 +1218,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {...@@ -1218,7 +1218,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
1218 defer arena_allocator.deinit();1218 defer arena_allocator.deinit();
1219 const arena = &arena_allocator.allocator;1219 const arena = &arena_allocator.allocator;
12201220
1221 const directory = self.base.options.directory; // Just an alias to make it shorter to type.1221 const directory = self.base.options.emit.?.directory; // Just an alias to make it shorter to type.
12221222
1223 // If there is no Zig code to compile, then we should skip flushing the output file because it1223 // If there is no Zig code to compile, then we should skip flushing the output file because it
1224 // will not be part of the linker line anyway.1224 // will not be part of the linker line anyway.
...@@ -1401,7 +1401,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {...@@ -1401,7 +1401,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
1401 try argv.append("-pie");1401 try argv.append("-pie");
1402 }1402 }
14031403
1404 const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.sub_path});1404 const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path});
1405 try argv.append("-o");1405 try argv.append("-o");
1406 try argv.append(full_out_path);1406 try argv.append(full_out_path);
14071407
src/link/MachO.zig+1-1
...@@ -142,7 +142,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio...@@ -142,7 +142,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
142 if (options.use_llvm) return error.LLVM_BackendIsTODO_ForMachO; // TODO142 if (options.use_llvm) return error.LLVM_BackendIsTODO_ForMachO; // TODO
143 if (options.use_lld) return error.LLD_LinkingIsTODO_ForMachO; // TODO143 if (options.use_lld) return error.LLD_LinkingIsTODO_ForMachO; // TODO
144144
145 const file = try options.directory.handle.createFile(sub_path, .{145 const file = try options.emit.?.directory.handle.createFile(sub_path, .{
146 .truncate = false,146 .truncate = false,
147 .read = true,147 .read = true,
148 .mode = link.determineMode(options),148 .mode = link.determineMode(options),
src/link/Wasm.zig+1-1
...@@ -59,7 +59,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio...@@ -59,7 +59,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
59 if (options.use_lld) return error.LLD_LinkingIsTODO_ForWasm; // TODO59 if (options.use_lld) return error.LLD_LinkingIsTODO_ForWasm; // TODO
6060
61 // TODO: read the file and keep vaild parts instead of truncating61 // TODO: read the file and keep vaild parts instead of truncating
62 const file = try options.directory.handle.createFile(sub_path, .{ .truncate = true, .read = true });62 const file = try options.emit.?.directory.handle.createFile(sub_path, .{ .truncate = true, .read = true });
63 errdefer file.close();63 errdefer file.close();
6464
65 const wasm = try createEmpty(allocator, options);65 const wasm = try createEmpty(allocator, options);
src/main.zig+11-6
...@@ -1307,7 +1307,9 @@ fn buildOutputType(...@@ -1307,7 +1307,9 @@ fn buildOutputType(
1307 };1307 };
1308 }1308 }
1309 if (fs.path.dirname(full_path)) |dirname| {1309 if (fs.path.dirname(full_path)) |dirname| {
1310 const handle = try fs.cwd().openDir(dirname, .{});1310 const handle = fs.cwd().openDir(dirname, .{}) catch |err| {
1311 fatal("unable to open output directory '{}': {}", .{ dirname, @errorName(err) });
1312 };
1311 cleanup_emit_bin_dir = handle;1313 cleanup_emit_bin_dir = handle;
1312 break :b Compilation.EmitLoc{1314 break :b Compilation.EmitLoc{
1313 .basename = basename,1315 .basename = basename,
...@@ -1545,7 +1547,7 @@ fn buildOutputType(...@@ -1545,7 +1547,7 @@ fn buildOutputType(
1545 switch (emit_bin) {1547 switch (emit_bin) {
1546 .no => break :blk .none,1548 .no => break :blk .none,
1547 .yes_default_path => break :blk .{1549 .yes_default_path => break :blk .{
1548 .print = comp.bin_file.options.directory.path orelse ".",1550 .print = comp.bin_file.options.emit.?.directory.path orelse ".",
1549 },1551 },
1550 .yes => |full_path| break :blk .{ .update = full_path },1552 .yes => |full_path| break :blk .{ .update = full_path },
1551 }1553 }
...@@ -1560,7 +1562,7 @@ fn buildOutputType(...@@ -1560,7 +1562,7 @@ fn buildOutputType(
1560 switch (arg_mode) {1562 switch (arg_mode) {
1561 .run, .zig_test => run: {1563 .run, .zig_test => run: {
1562 const exe_loc = emit_bin_loc orelse break :run;1564 const exe_loc = emit_bin_loc orelse break :run;
1563 const exe_directory = exe_loc.directory orelse comp.bin_file.options.directory;1565 const exe_directory = exe_loc.directory orelse comp.bin_file.options.emit.?.directory;
1564 const exe_path = try fs.path.join(arena, &[_][]const u8{1566 const exe_path = try fs.path.join(arena, &[_][]const u8{
1565 exe_directory.path orelse ".", exe_loc.basename,1567 exe_directory.path orelse ".", exe_loc.basename,
1566 });1568 });
...@@ -1676,8 +1678,8 @@ fn updateModule(gpa: *Allocator, comp: *Compilation, zir_out_path: ?[]const u8,...@@ -1676,8 +1678,8 @@ fn updateModule(gpa: *Allocator, comp: *Compilation, zir_out_path: ?[]const u8,
1676 } else switch (hook) {1678 } else switch (hook) {
1677 .none => {},1679 .none => {},
1678 .print => |bin_path| try io.getStdOut().writer().print("{s}\n", .{bin_path}),1680 .print => |bin_path| try io.getStdOut().writer().print("{s}\n", .{bin_path}),
1679 .update => |full_path| _ = try comp.bin_file.options.directory.handle.updateFile(1681 .update => |full_path| _ = try comp.bin_file.options.emit.?.directory.handle.updateFile(
1680 comp.bin_file.options.sub_path,1682 comp.bin_file.options.emit.?.sub_path,
1681 fs.cwd(),1683 fs.cwd(),
1682 full_path,1684 full_path,
1683 .{},1685 .{},
...@@ -2106,7 +2108,10 @@ pub fn cmdBuild(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v...@@ -2106,7 +2108,10 @@ pub fn cmdBuild(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v
21062108
2107 try updateModule(gpa, comp, null, .none);2109 try updateModule(gpa, comp, null, .none);
21082110
2109 child_argv.items[argv_index_exe] = try comp.bin_file.options.directory.join(arena, &[_][]const u8{exe_basename});2111 child_argv.items[argv_index_exe] = try comp.bin_file.options.emit.?.directory.join(
2112 arena,
2113 &[_][]const u8{exe_basename},
2114 );
21102115
2111 break :lock_and_argv .{2116 break :lock_and_argv .{
2112 .child_argv = child_argv.items,2117 .child_argv = child_argv.items,
test/cli.zig+16-16
...@@ -58,7 +58,7 @@ fn printCmd(cwd: []const u8, argv: []const []const u8) void {...@@ -58,7 +58,7 @@ fn printCmd(cwd: []const u8, argv: []const []const u8) void {
58 std.debug.warn("\n", .{});58 std.debug.warn("\n", .{});
59}59}
6060
61fn exec(cwd: []const u8, argv: []const []const u8) !ChildProcess.ExecResult {61fn exec(cwd: []const u8, expect_0: bool, argv: []const []const u8) !ChildProcess.ExecResult {
62 const max_output_size = 100 * 1024;62 const max_output_size = 100 * 1024;
63 const result = ChildProcess.exec(.{63 const result = ChildProcess.exec(.{
64 .allocator = a,64 .allocator = a,
...@@ -72,7 +72,7 @@ fn exec(cwd: []const u8, argv: []const []const u8) !ChildProcess.ExecResult {...@@ -72,7 +72,7 @@ fn exec(cwd: []const u8, argv: []const []const u8) !ChildProcess.ExecResult {
72 };72 };
73 switch (result.term) {73 switch (result.term) {
74 .Exited => |code| {74 .Exited => |code| {
75 if (code != 0) {75 if ((code != 0) == expect_0) {
76 std.debug.warn("The following command exited with error code {}:\n", .{code});76 std.debug.warn("The following command exited with error code {}:\n", .{code});
77 printCmd(cwd, argv);77 printCmd(cwd, argv);
78 std.debug.warn("stderr:\n{}\n", .{result.stderr});78 std.debug.warn("stderr:\n{}\n", .{result.stderr});
...@@ -90,14 +90,14 @@ fn exec(cwd: []const u8, argv: []const []const u8) !ChildProcess.ExecResult {...@@ -90,14 +90,14 @@ fn exec(cwd: []const u8, argv: []const []const u8) !ChildProcess.ExecResult {
90}90}
9191
92fn testZigInitLib(zig_exe: []const u8, dir_path: []const u8) !void {92fn testZigInitLib(zig_exe: []const u8, dir_path: []const u8) !void {
93 _ = try exec(dir_path, &[_][]const u8{ zig_exe, "init-lib" });93 _ = try exec(dir_path, true, &[_][]const u8{ zig_exe, "init-lib" });
94 const test_result = try exec(dir_path, &[_][]const u8{ zig_exe, "build", "test" });94 const test_result = try exec(dir_path, true, &[_][]const u8{ zig_exe, "build", "test" });
95 testing.expect(std.mem.endsWith(u8, test_result.stderr, "All 1 tests passed.\n"));95 testing.expect(std.mem.endsWith(u8, test_result.stderr, "All 1 tests passed.\n"));
96}96}
9797
98fn testZigInitExe(zig_exe: []const u8, dir_path: []const u8) !void {98fn testZigInitExe(zig_exe: []const u8, dir_path: []const u8) !void {
99 _ = try exec(dir_path, &[_][]const u8{ zig_exe, "init-exe" });99 _ = try exec(dir_path, true, &[_][]const u8{ zig_exe, "init-exe" });
100 const run_result = try exec(dir_path, &[_][]const u8{ zig_exe, "build", "run" });100 const run_result = try exec(dir_path, true, &[_][]const u8{ zig_exe, "build", "run" });
101 testing.expect(std.mem.eql(u8, run_result.stderr, "info: All your codebase are belong to us.\n"));101 testing.expect(std.mem.eql(u8, run_result.stderr, "info: All your codebase are belong to us.\n"));
102}102}
103103
...@@ -131,7 +131,7 @@ fn testGodboltApi(zig_exe: []const u8, dir_path: []const u8) anyerror!void {...@@ -131,7 +131,7 @@ fn testGodboltApi(zig_exe: []const u8, dir_path: []const u8) anyerror!void {
131 const emit_asm_arg = try std.fmt.allocPrint(a, "-femit-asm={s}", .{example_s_path});131 const emit_asm_arg = try std.fmt.allocPrint(a, "-femit-asm={s}", .{example_s_path});
132 try args.append(emit_asm_arg);132 try args.append(emit_asm_arg);
133133
134 _ = try exec(dir_path, args.items);134 _ = try exec(dir_path, true, args.items);
135135
136 const out_asm = try std.fs.cwd().readFileAlloc(a, example_s_path, std.math.maxInt(usize));136 const out_asm = try std.fs.cwd().readFileAlloc(a, example_s_path, std.math.maxInt(usize));
137 testing.expect(std.mem.indexOf(u8, out_asm, "square:") != null);137 testing.expect(std.mem.indexOf(u8, out_asm, "square:") != null);
...@@ -140,23 +140,23 @@ fn testGodboltApi(zig_exe: []const u8, dir_path: []const u8) anyerror!void {...@@ -140,23 +140,23 @@ fn testGodboltApi(zig_exe: []const u8, dir_path: []const u8) anyerror!void {
140}140}
141141
142fn testMissingOutputPath(zig_exe: []const u8, dir_path: []const u8) !void {142fn testMissingOutputPath(zig_exe: []const u8, dir_path: []const u8) !void {
143 _ = try exec(dir_path, &[_][]const u8{ zig_exe, "init-exe" });143 _ = try exec(dir_path, true, &[_][]const u8{ zig_exe, "init-exe" });
144 const output_path = try fs.path.join(a, &[_][]const u8{ "does", "not", "exist" });144 const output_path = try fs.path.join(a, &[_][]const u8{ "does", "not", "exist", "foo.exe" });
145 const output_arg = try std.fmt.allocPrint(a, "-femit-bin={s}", .{output_path});
145 const source_path = try fs.path.join(a, &[_][]const u8{ "src", "main.zig" });146 const source_path = try fs.path.join(a, &[_][]const u8{ "src", "main.zig" });
146 _ = try exec(dir_path, &[_][]const u8{147 const result = try exec(dir_path, false, &[_][]const u8{ zig_exe, "build-exe", source_path, output_arg });
147 zig_exe, "build-exe", source_path, "--output-dir", output_path,148 testing.expect(std.mem.eql(u8, result.stderr, "error: unable to open output directory 'does/not/exist': FileNotFound\n"));
148 });
149}149}
150150
151fn testZigFmt(zig_exe: []const u8, dir_path: []const u8) !void {151fn testZigFmt(zig_exe: []const u8, dir_path: []const u8) !void {
152 _ = try exec(dir_path, &[_][]const u8{ zig_exe, "init-exe" });152 _ = try exec(dir_path, true, &[_][]const u8{ zig_exe, "init-exe" });
153153
154 const unformatted_code = " // no reason for indent";154 const unformatted_code = " // no reason for indent";
155155
156 const fmt1_zig_path = try fs.path.join(a, &[_][]const u8{ dir_path, "fmt1.zig" });156 const fmt1_zig_path = try fs.path.join(a, &[_][]const u8{ dir_path, "fmt1.zig" });
157 try fs.cwd().writeFile(fmt1_zig_path, unformatted_code);157 try fs.cwd().writeFile(fmt1_zig_path, unformatted_code);
158158
159 const run_result1 = try exec(dir_path, &[_][]const u8{ zig_exe, "fmt", fmt1_zig_path });159 const run_result1 = try exec(dir_path, true, &[_][]const u8{ zig_exe, "fmt", fmt1_zig_path });
160 // stderr should be file path + \n160 // stderr should be file path + \n
161 testing.expect(std.mem.startsWith(u8, run_result1.stderr, fmt1_zig_path));161 testing.expect(std.mem.startsWith(u8, run_result1.stderr, fmt1_zig_path));
162 testing.expect(run_result1.stderr.len == fmt1_zig_path.len + 1 and run_result1.stderr[run_result1.stderr.len - 1] == '\n');162 testing.expect(run_result1.stderr.len == fmt1_zig_path.len + 1 and run_result1.stderr[run_result1.stderr.len - 1] == '\n');
...@@ -164,12 +164,12 @@ fn testZigFmt(zig_exe: []const u8, dir_path: []const u8) !void {...@@ -164,12 +164,12 @@ fn testZigFmt(zig_exe: []const u8, dir_path: []const u8) !void {
164 const fmt2_zig_path = try fs.path.join(a, &[_][]const u8{ dir_path, "fmt2.zig" });164 const fmt2_zig_path = try fs.path.join(a, &[_][]const u8{ dir_path, "fmt2.zig" });
165 try fs.cwd().writeFile(fmt2_zig_path, unformatted_code);165 try fs.cwd().writeFile(fmt2_zig_path, unformatted_code);
166166
167 const run_result2 = try exec(dir_path, &[_][]const u8{ zig_exe, "fmt", dir_path });167 const run_result2 = try exec(dir_path, true, &[_][]const u8{ zig_exe, "fmt", dir_path });
168 // running it on the dir, only the new file should be changed168 // running it on the dir, only the new file should be changed
169 testing.expect(std.mem.startsWith(u8, run_result2.stderr, fmt2_zig_path));169 testing.expect(std.mem.startsWith(u8, run_result2.stderr, fmt2_zig_path));
170 testing.expect(run_result2.stderr.len == fmt2_zig_path.len + 1 and run_result2.stderr[run_result2.stderr.len - 1] == '\n');170 testing.expect(run_result2.stderr.len == fmt2_zig_path.len + 1 and run_result2.stderr[run_result2.stderr.len - 1] == '\n');
171171
172 const run_result3 = try exec(dir_path, &[_][]const u8{ zig_exe, "fmt", dir_path });172 const run_result3 = try exec(dir_path, true, &[_][]const u8{ zig_exe, "fmt", dir_path });
173 // both files have been formatted, nothing should change now173 // both files have been formatted, nothing should change now
174 testing.expect(run_result3.stderr.len == 0);174 testing.expect(run_result3.stderr.len == 0);
175}175}