| author | |
| committer | |
| log | 7b31e360a1c000e1e050a00c9b9bff290c7f3d97 |
| tree | fc5064dc235062ec7e31a0d0511cce087716e9e9 |
| parent | 9dd8ae35364e735ec507ce0f4cfa8468adef830f |
| parent | 82e8ed66ecaa3fc88267bfcb2850c50fe9a4b955 |
Reviewed-on: https://codeberg.org/ziglang/zig/pulls/3558116 files changed, 186 insertions(+), 148 deletions(-)
src/Compilation.zig+17-6| ... | @@ -3344,15 +3344,15 @@ fn flush(comp: *Compilation, arena: Allocator, tid: Zcu.PerThread.Id) (Io.Cancel | ... | @@ -3344,15 +3344,15 @@ fn flush(comp: *Compilation, arena: Allocator, tid: Zcu.PerThread.Id) (Io.Cancel |
| 3344 | comp.time_report.?.stats.real_ns_llvm_emit = ns; | 3344 | comp.time_report.?.stats.real_ns_llvm_emit = ns; |
| 3345 | }; | 3345 | }; |
| 3346 | 3346 | ||
| 3347 | const zcu_obj_path: ?Cache.Path = if (comp.bin_file != null) p: { | ||
| 3348 | break :p try comp.resolveEmitPathFlush(arena, .temp, llvm_object.out_bin_basename); | ||
| 3349 | } else null; | ||
| 3350 | |||
| 3347 | llvm_object.emit(pt, .{ | 3351 | llvm_object.emit(pt, .{ |
| 3348 | .pre_ir_path = comp.verbose_llvm_ir, | 3352 | .pre_ir_path = comp.verbose_llvm_ir, |
| 3349 | .pre_bc_path = comp.verbose_llvm_bc, | 3353 | .pre_bc_path = comp.verbose_llvm_bc, |
| 3350 | 3354 | ||
| 3351 | .bin_path = p: { | 3355 | .bin_path = if (zcu_obj_path) |p| try p.toStringZ(arena) else null, |
| 3352 | const lf = comp.bin_file orelse break :p null; | ||
| 3353 | const p = try comp.resolveEmitPathFlush(arena, .temp, lf.zcu_object_basename.?); | ||
| 3354 | break :p try p.toStringZ(arena); | ||
| 3355 | }, | ||
| 3356 | .asm_path = p: { | 3356 | .asm_path = p: { |
| 3357 | const raw = comp.emit_asm orelse break :p null; | 3357 | const raw = comp.emit_asm orelse break :p null; |
| 3358 | const p = try comp.resolveEmitPathFlush(arena, .artifact, raw); | 3358 | const p = try comp.resolveEmitPathFlush(arena, .artifact, raw); |
| ... | @@ -3379,6 +3379,17 @@ fn flush(comp: *Compilation, arena: Allocator, tid: Zcu.PerThread.Id) (Io.Cancel | ... | @@ -3379,6 +3379,17 @@ fn flush(comp: *Compilation, arena: Allocator, tid: Zcu.PerThread.Id) (Io.Cancel |
| 3379 | error.AlreadyReported => {}, | 3379 | error.AlreadyReported => {}, |
| 3380 | error.OutOfMemory => |e| return e, | 3380 | error.OutOfMemory => |e| return e, |
| 3381 | }; | 3381 | }; |
| 3382 | |||
| 3383 | if (zcu_obj_path) |path| { | ||
| 3384 | // Tell the linker backend about the ZCU object emitted by LLVM. | ||
| 3385 | link.doPrelinkTask(comp, .{ .load_object = path }); | ||
| 3386 | // `link.Queue` has not called `prelink` because it knew we would want to send that | ||
| 3387 | // final link input. It is *our* responsibility to call `prelink` now we're done. | ||
| 3388 | comp.bin_file.?.prelink() catch |err| switch (err) { | ||
| 3389 | error.AlreadyReported => return, | ||
| 3390 | else => |e| return e, | ||
| 3391 | }; | ||
| 3392 | } | ||
| 3382 | } | 3393 | } |
| 3383 | } | 3394 | } |
| 3384 | if (comp.bin_file) |lf| { | 3395 | if (comp.bin_file) |lf| { |
| ... | @@ -3390,7 +3401,7 @@ fn flush(comp: *Compilation, arena: Allocator, tid: Zcu.PerThread.Id) (Io.Cancel | ... | @@ -3390,7 +3401,7 @@ fn flush(comp: *Compilation, arena: Allocator, tid: Zcu.PerThread.Id) (Io.Cancel |
| 3390 | }; | 3401 | }; |
| 3391 | // This is needed before reading the error flags. | 3402 | // This is needed before reading the error flags. |
| 3392 | lf.flush(arena, tid, comp.link_prog_node) catch |err| switch (err) { | 3403 | lf.flush(arena, tid, comp.link_prog_node) catch |err| switch (err) { |
| 3393 | error.AlreadyReported => {}, | 3404 | error.AlreadyReported => return, |
| 3394 | error.OutOfMemory, error.Canceled => |e| return e, | 3405 | error.OutOfMemory, error.Canceled => |e| return e, |
| 3395 | }; | 3406 | }; |
| 3396 | } | 3407 | } |
src/Compilation/Config.zig+10-6| ... | @@ -130,7 +130,7 @@ pub const ResolveError = error{ | ... | @@ -130,7 +130,7 @@ pub const ResolveError = error{ |
| 130 | ZigLacksTargetSupport, | 130 | ZigLacksTargetSupport, |
| 131 | EmittingBinaryRequiresLlvmLibrary, | 131 | EmittingBinaryRequiresLlvmLibrary, |
| 132 | LldIncompatibleObjectFormat, | 132 | LldIncompatibleObjectFormat, |
| 133 | LldCannotIncrementallyLink, | 133 | LldIncompatibleWithSelfHostedBackend, |
| 134 | LtoRequiresLld, | 134 | LtoRequiresLld, |
| 135 | SanitizeThreadRequiresLibCpp, | 135 | SanitizeThreadRequiresLibCpp, |
| 136 | LibCRequiresLibUnwind, | 136 | LibCRequiresLibUnwind, |
| ... | @@ -400,15 +400,19 @@ pub fn resolve(options: Options) ResolveError!Config { | ... | @@ -400,15 +400,19 @@ pub fn resolve(options: Options) ResolveError!Config { |
| 400 | break :b true; | 400 | break :b true; |
| 401 | } | 401 | } |
| 402 | 402 | ||
| 403 | // If there's no ZCU we aren't using the LLVM backend but | 403 | // If we have Zig code (i.e. a ZCU) and are compiling with a self-hosted backend, then we |
| 404 | // it shouldn't influence which linker we pick | 404 | // also need to use a self-hosted linker. |
| 405 | if (!use_llvm and options.have_zcu) { | 405 | if (options.have_zcu and !use_llvm) { |
| 406 | if (options.use_lld == true) return error.LldCannotIncrementallyLink; | 406 | if (options.use_lld == true) return error.LldIncompatibleWithSelfHostedBackend; |
| 407 | break :b false; | 407 | break :b false; |
| 408 | } | 408 | } |
| 409 | 409 | ||
| 410 | if (options.use_lld) |x| break :b x; | 410 | if (options.use_lld) |x| break :b x; |
| 411 | 411 | ||
| 412 | // If the user didn't specify whether to use LLD but did specify to use the new linker, | ||
| 413 | // assume no LLD. | ||
| 414 | if (options.use_new_linker == true) break :b false; | ||
| 415 | |||
| 412 | // If we have no zig code to compile, no need for the self-hosted linker. | 416 | // If we have no zig code to compile, no need for the self-hosted linker. |
| 413 | if (!options.have_zcu) break :b true; | 417 | if (!options.have_zcu) break :b true; |
| 414 | 418 | ||
| ... | @@ -437,7 +441,7 @@ pub fn resolve(options: Options) ResolveError!Config { | ... | @@ -437,7 +441,7 @@ pub fn resolve(options: Options) ResolveError!Config { |
| 437 | break :b false; | 441 | break :b false; |
| 438 | } | 442 | } |
| 439 | 443 | ||
| 440 | if (!target_util.hasNewLinkerSupport(target.ofmt, backend)) { | 444 | if (!target_util.hasNewLinker(target.ofmt)) { |
| 441 | if (options.use_new_linker == true) return error.NewLinkerIncompatibleObjectFormat; | 445 | if (options.use_new_linker == true) return error.NewLinkerIncompatibleObjectFormat; |
| 442 | break :b false; | 446 | break :b false; |
| 443 | } | 447 | } |
src/codegen/llvm.zig+14| ... | @@ -518,6 +518,12 @@ pub const Object = struct { | ... | @@ -518,6 +518,12 @@ pub const Object = struct { |
| 518 | gpa: Allocator, | 518 | gpa: Allocator, |
| 519 | builder: Builder, | 519 | builder: Builder, |
| 520 | 520 | ||
| 521 | /// The basename of the object file which will emitted by LLVM for the ZCU. Once it it emitted, | ||
| 522 | /// this object file is passed to the active linker implementation as an ordinary link input. | ||
| 523 | /// | ||
| 524 | /// For the full path, use `Compilation.resolveEmitPath` with `kind == .temp`. | ||
| 525 | out_bin_basename: []const u8, | ||
| 526 | |||
| 521 | /// This pool contains only types (and not `@as(type, undefined)`). It has two purposes: | 527 | /// This pool contains only types (and not `@as(type, undefined)`). It has two purposes: |
| 522 | /// | 528 | /// |
| 523 | /// * Lazily tracking ABI alignment of types, so that `@"align"` attributes can be set to a | 529 | /// * Lazily tracking ABI alignment of types, so that `@"align"` attributes can be set to a |
| ... | @@ -657,6 +663,14 @@ pub const Object = struct { | ... | @@ -657,6 +663,14 @@ pub const Object = struct { |
| 657 | obj.* = .{ | 663 | obj.* = .{ |
| 658 | .gpa = gpa, | 664 | .gpa = gpa, |
| 659 | .builder = builder, | 665 | .builder = builder, |
| 666 | .out_bin_basename = try std.zig.binNameAlloc(arena, .{ | ||
| 667 | .root_name = try std.fmt.allocPrint(arena, "{s}_zcu", .{comp.root_name}), | ||
| 668 | .cpu_arch = target.cpu.arch, | ||
| 669 | .os_tag = target.os.tag, | ||
| 670 | .ofmt = target.ofmt, | ||
| 671 | .abi = target.abi, | ||
| 672 | .output_mode = .Obj, | ||
| 673 | }), | ||
| 660 | .type_pool = .empty, | 674 | .type_pool = .empty, |
| 661 | .lazy_abi_aligns = .empty, | 675 | .lazy_abi_aligns = .empty, |
| 662 | .debug_compile_unit = debug_compile_unit, | 676 | .debug_compile_unit = debug_compile_unit, |
src/link.zig+15-13| ... | @@ -402,11 +402,6 @@ pub const File = struct { | ... | @@ -402,11 +402,6 @@ pub const File = struct { |
| 402 | emit: Path, | 402 | emit: Path, |
| 403 | 403 | ||
| 404 | file: ?Io.File, | 404 | file: ?Io.File, |
| 405 | /// When using the LLVM backend, the emitted object is written to a file with this name. This | ||
| 406 | /// object file then becomes a normal link input to LLD or a self-hosted linker. | ||
| 407 | /// | ||
| 408 | /// To convert this to an actual path, see `Compilation.resolveEmitPath` (with `kind == .temp`). | ||
| 409 | zcu_object_basename: ?[]const u8 = null, | ||
| 410 | gc_sections: bool, | 405 | gc_sections: bool, |
| 411 | print_gc_sections: bool, | 406 | print_gc_sections: bool, |
| 412 | build_id: std.zig.BuildId, | 407 | build_id: std.zig.BuildId, |
| ... | @@ -1196,20 +1191,22 @@ pub const File = struct { | ... | @@ -1196,20 +1191,22 @@ pub const File = struct { |
| 1196 | /// Called when all linker inputs have been sent via `loadInput`. After | 1191 | /// Called when all linker inputs have been sent via `loadInput`. After |
| 1197 | /// this, `loadInput` will not be called anymore. | 1192 | /// this, `loadInput` will not be called anymore. |
| 1198 | pub fn prelink(base: *File) Error!void { | 1193 | pub fn prelink(base: *File) Error!void { |
| 1199 | assert(!base.post_prelink); | 1194 | // The guard on this assertion is a temporary hack to make the LLVM backend with LLD work with |
| 1200 | 1195 | // `-fincremental`. This works only because `File.Lld` does nothing in prelink. | |
| 1201 | // In this case, an object file is created by the LLVM backend, so | 1196 | // Related: https://codeberg.org/ziglang/zig/issues/32081 |
| 1202 | // there is no prelink phase. The Zig code is linked as a standard | 1197 | if (base.tag != .lld) { |
| 1203 | // object along with the others. | 1198 | assert(!base.post_prelink); |
| 1204 | if (base.zcu_object_basename != null) return; | 1199 | } |
| 1205 | 1200 | ||
| 1206 | switch (base.tag) { | 1201 | switch (base.tag) { |
| 1207 | inline .elf2, .coff2, .wasm => |tag| { | 1202 | inline .elf2, .coff2, .wasm => |tag| { |
| 1208 | dev.check(tag.devFeature()); | 1203 | dev.check(tag.devFeature()); |
| 1209 | return @as(*tag.Type(), @fieldParentPtr("base", base)).prelink(base.comp.link_prog_node); | 1204 | try @as(*tag.Type(), @fieldParentPtr("base", base)).prelink(base.comp.link_prog_node); |
| 1210 | }, | 1205 | }, |
| 1211 | else => {}, | 1206 | else => {}, |
| 1212 | } | 1207 | } |
| 1208 | |||
| 1209 | base.post_prelink = true; | ||
| 1213 | } | 1210 | } |
| 1214 | 1211 | ||
| 1215 | /// Legacy function for old linker code | 1212 | /// Legacy function for old linker code |
| ... | @@ -1407,7 +1404,12 @@ pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void { | ... | @@ -1407,7 +1404,12 @@ pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void { |
| 1407 | return; | 1404 | return; |
| 1408 | }; | 1405 | }; |
| 1409 | 1406 | ||
| 1410 | assert(!base.post_prelink); | 1407 | // The guard on this assertion is a temporary hack to make the LLVM backend with LLD work with |
| 1408 | // `-fincremental`. This works only because `File.Lld` does nothing in prelink. | ||
| 1409 | // Related: https://codeberg.org/ziglang/zig/issues/32081 | ||
| 1410 | if (base.tag != .lld) { | ||
| 1411 | assert(!base.post_prelink); | ||
| 1412 | } | ||
| 1411 | 1413 | ||
| 1412 | var timer = comp.startTimer(); | 1414 | var timer = comp.startTimer(); |
| 1413 | defer if (timer.finish(io)) |ns| { | 1415 | defer if (timer.finish(io)) |ns| { |
src/link/Coff.zig+1-1| ... | @@ -1524,7 +1524,7 @@ pub fn addReloc( | ... | @@ -1524,7 +1524,7 @@ pub fn addReloc( |
| 1524 | target.target_relocs = ri; | 1524 | target.target_relocs = ri; |
| 1525 | } | 1525 | } |
| 1526 | 1526 | ||
| 1527 | pub fn prelink(coff: *Coff, prog_node: std.Progress.Node) void { | 1527 | pub fn prelink(coff: *Coff, prog_node: std.Progress.Node) link.Error!void { |
| 1528 | _ = coff; | 1528 | _ = coff; |
| 1529 | _ = prog_node; | 1529 | _ = prog_node; |
| 1530 | } | 1530 | } |
src/link/Elf.zig+2-31| ... | @@ -260,10 +260,6 @@ pub fn createEmpty( | ... | @@ -260,10 +260,6 @@ pub fn createEmpty( |
| 260 | .tag = .elf, | 260 | .tag = .elf, |
| 261 | .comp = comp, | 261 | .comp = comp, |
| 262 | .emit = emit, | 262 | .emit = emit, |
| 263 | .zcu_object_basename = if (use_llvm) | ||
| 264 | try std.fmt.allocPrint(arena, "{s}_zcu.o", .{fs.path.stem(emit.sub_path)}) | ||
| 265 | else | ||
| 266 | null, | ||
| 267 | .gc_sections = options.gc_sections orelse (optimize_mode != .Debug and output_mode != .Obj), | 263 | .gc_sections = options.gc_sections orelse (optimize_mode != .Debug and output_mode != .Obj), |
| 268 | .print_gc_sections = options.print_gc_sections, | 264 | .print_gc_sections = options.print_gc_sections, |
| 269 | .stack_size = options.stack_size orelse 16777216, | 265 | .stack_size = options.stack_size orelse 16777216, |
| ... | @@ -762,18 +758,14 @@ pub fn flush(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std | ... | @@ -762,18 +758,14 @@ pub fn flush(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std |
| 762 | } | 758 | } |
| 763 | 759 | ||
| 764 | fn flushInner(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id) !void { | 760 | fn flushInner(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id) !void { |
| 761 | _ = arena; | ||
| 762 | |||
| 765 | const comp = self.base.comp; | 763 | const comp = self.base.comp; |
| 766 | const gpa = comp.gpa; | 764 | const gpa = comp.gpa; |
| 767 | const diags = &comp.link_diags; | 765 | const diags = &comp.link_diags; |
| 768 | 766 | ||
| 769 | const zcu_obj_path: ?Path = if (self.base.zcu_object_basename) |raw| p: { | ||
| 770 | break :p try comp.resolveEmitPathFlush(arena, .temp, raw); | ||
| 771 | } else null; | ||
| 772 | |||
| 773 | if (self.zigObjectPtr()) |zig_object| try zig_object.flush(self, tid); | 767 | if (self.zigObjectPtr()) |zig_object| try zig_object.flush(self, tid); |
| 774 | 768 | ||
| 775 | if (zcu_obj_path) |path| openParseObjectReportingFailure(self, path); | ||
| 776 | |||
| 777 | switch (comp.config.output_mode) { | 769 | switch (comp.config.output_mode) { |
| 778 | .Obj => return relocatable.flushObject(self, comp), | 770 | .Obj => return relocatable.flushObject(self, comp), |
| 779 | .Lib => switch (comp.config.link_mode) { | 771 | .Lib => switch (comp.config.link_mode) { |
| ... | @@ -1046,27 +1038,6 @@ fn dumpArgvInit(self: *Elf, arena: Allocator) !void { | ... | @@ -1046,27 +1038,6 @@ fn dumpArgvInit(self: *Elf, arena: Allocator) !void { |
| 1046 | } | 1038 | } |
| 1047 | } | 1039 | } |
| 1048 | 1040 | ||
| 1049 | pub fn openParseObjectReportingFailure(self: *Elf, path: Path) void { | ||
| 1050 | const comp = self.base.comp; | ||
| 1051 | const io = comp.io; | ||
| 1052 | const diags = &comp.link_diags; | ||
| 1053 | const obj = link.openObject(io, path, false, false) catch |err| { | ||
| 1054 | switch (diags.failParse(path, "failed to open object: {t}", .{err})) { | ||
| 1055 | error.AlreadyReported => return, | ||
| 1056 | } | ||
| 1057 | }; | ||
| 1058 | self.parseObjectReportingFailure(obj); | ||
| 1059 | } | ||
| 1060 | |||
| 1061 | fn parseObjectReportingFailure(self: *Elf, obj: link.Input.Object) void { | ||
| 1062 | const comp = self.base.comp; | ||
| 1063 | const diags = &comp.link_diags; | ||
| 1064 | self.parseObject(obj) catch |err| switch (err) { | ||
| 1065 | error.AlreadyReported => return, // already reported | ||
| 1066 | else => |e| diags.addParseError(obj.path, "failed to parse object: {t}", .{e}), | ||
| 1067 | }; | ||
| 1068 | } | ||
| 1069 | |||
| 1070 | fn parseObject(self: *Elf, obj: link.Input.Object) !void { | 1041 | fn parseObject(self: *Elf, obj: link.Input.Object) !void { |
| 1071 | const tracy = trace(@src()); | 1042 | const tracy = trace(@src()); |
| 1072 | defer tracy.end(); | 1043 | defer tracy.end(); |
src/link/Elf2.zig+24-20| ... | @@ -4825,25 +4825,29 @@ pub fn prelink(elf: *Elf, prog_node: std.Progress.Node) link.Error!void { | ... | @@ -4825,25 +4825,29 @@ pub fn prelink(elf: *Elf, prog_node: std.Progress.Node) link.Error!void { |
| 4825 | fn prelinkInner(elf: *Elf) Error!void { | 4825 | fn prelinkInner(elf: *Elf) Error!void { |
| 4826 | const comp = elf.base.comp; | 4826 | const comp = elf.base.comp; |
| 4827 | const gpa = comp.gpa; | 4827 | const gpa = comp.gpa; |
| 4828 | try elf.ensureUnusedSymbolCapacity(1, .all_local); | 4828 | |
| 4829 | try elf.inputs.ensureUnusedCapacity(gpa, 1); | 4829 | if (comp.zcu != null and !comp.config.use_llvm) { |
| 4830 | const zcu_name = try std.fmt.allocPrint(gpa, "{s}_zcu", .{ | 4830 | // We're use self-hosted codegen---add an input representing the Zig "object". |
| 4831 | std.fs.path.stem(elf.base.emit.sub_path), | 4831 | try elf.ensureUnusedSymbolCapacity(1, .all_local); |
| 4832 | }); | 4832 | try elf.inputs.ensureUnusedCapacity(gpa, 1); |
| 4833 | defer gpa.free(zcu_name); | 4833 | const zcu_name = try std.fmt.allocPrint(gpa, "{s}_zcu", .{ |
| 4834 | const zcu_file_symbol = elf.addLocalSymbolAssumeCapacity(.{ | 4834 | std.fs.path.stem(elf.base.emit.sub_path), |
| 4835 | .node = .none, | 4835 | }); |
| 4836 | .name = try elf.string(.strtab, zcu_name), | 4836 | defer gpa.free(zcu_name); |
| 4837 | .value = 0, | 4837 | const zcu_file_symbol = elf.addLocalSymbolAssumeCapacity(.{ |
| 4838 | .size = 0, | 4838 | .node = .none, |
| 4839 | .type = .FILE, | 4839 | .name = try elf.string(.strtab, zcu_name), |
| 4840 | .shndx = .ABS, | 4840 | .value = 0, |
| 4841 | }); | 4841 | .size = 0, |
| 4842 | elf.inputs.addOneAssumeCapacity().* = .{ | 4842 | .type = .FILE, |
| 4843 | .path = elf.base.emit, | 4843 | .shndx = .ABS, |
| 4844 | .member = null, | 4844 | }); |
| 4845 | .file_symbol = zcu_file_symbol, | 4845 | elf.inputs.addOneAssumeCapacity().* = .{ |
| 4846 | }; | 4846 | .path = elf.base.emit, |
| 4847 | .member = null, | ||
| 4848 | .file_symbol = zcu_file_symbol, | ||
| 4849 | }; | ||
| 4850 | } | ||
| 4847 | 4851 | ||
| 4848 | if (elf.shndx.dynamic != .UNDEF) switch (elf.identClass()) { | 4852 | if (elf.shndx.dynamic != .UNDEF) switch (elf.identClass()) { |
| 4849 | .NONE, _ => unreachable, | 4853 | .NONE, _ => unreachable, |
| ... | @@ -5859,8 +5863,8 @@ pub fn flush( | ... | @@ -5859,8 +5863,8 @@ pub fn flush( |
| 5859 | ) link.Error!void { | 5863 | ) link.Error!void { |
| 5860 | const comp = elf.base.comp; | 5864 | const comp = elf.base.comp; |
| 5861 | const diags = &comp.link_diags; | 5865 | const diags = &comp.link_diags; |
| 5862 | _ = arena; | ||
| 5863 | _ = prog_node; | 5866 | _ = prog_node; |
| 5867 | _ = arena; | ||
| 5864 | 5868 | ||
| 5865 | if (comp.config.output_mode == .Exe) { | 5869 | if (comp.config.output_mode == .Exe) { |
| 5866 | var any_undef = false; | 5870 | var any_undef = false; |
src/link/Lld.zig+8-14| ... | @@ -207,11 +207,6 @@ pub fn createEmpty( | ... | @@ -207,11 +207,6 @@ pub fn createEmpty( |
| 207 | const output_mode = comp.config.output_mode; | 207 | const output_mode = comp.config.output_mode; |
| 208 | const optimize_mode = comp.root_mod.optimize_mode; | 208 | const optimize_mode = comp.root_mod.optimize_mode; |
| 209 | 209 | ||
| 210 | const obj_file_ext: []const u8 = switch (target.ofmt) { | ||
| 211 | .coff => "obj", | ||
| 212 | .elf, .wasm => "o", | ||
| 213 | else => unreachable, | ||
| 214 | }; | ||
| 215 | const gc_sections: bool = options.gc_sections orelse switch (target.ofmt) { | 210 | const gc_sections: bool = options.gc_sections orelse switch (target.ofmt) { |
| 216 | .coff => optimize_mode != .Debug, | 211 | .coff => optimize_mode != .Debug, |
| 217 | .elf => optimize_mode != .Debug and output_mode != .Obj, | 212 | .elf => optimize_mode != .Debug and output_mode != .Obj, |
| ... | @@ -230,7 +225,6 @@ pub fn createEmpty( | ... | @@ -230,7 +225,6 @@ pub fn createEmpty( |
| 230 | .tag = .lld, | 225 | .tag = .lld, |
| 231 | .comp = comp, | 226 | .comp = comp, |
| 232 | .emit = emit, | 227 | .emit = emit, |
| 233 | .zcu_object_basename = try allocPrint(arena, "{s}_zcu.{s}", .{ fs.path.stem(emit.sub_path), obj_file_ext }), | ||
| 234 | .gc_sections = gc_sections, | 228 | .gc_sections = gc_sections, |
| 235 | .print_gc_sections = options.print_gc_sections, | 229 | .print_gc_sections = options.print_gc_sections, |
| 236 | .stack_size = stack_size, | 230 | .stack_size = stack_size, |
| ... | @@ -290,8 +284,8 @@ fn linkAsArchive(lld: *Lld, arena: Allocator) !void { | ... | @@ -290,8 +284,8 @@ fn linkAsArchive(lld: *Lld, arena: Allocator) !void { |
| 290 | const full_out_path_z = try arena.dupeSentinel(u8, full_out_path, 0); | 284 | const full_out_path_z = try arena.dupeSentinel(u8, full_out_path, 0); |
| 291 | const opt_zcu = comp.zcu; | 285 | const opt_zcu = comp.zcu; |
| 292 | 286 | ||
| 293 | const zcu_obj_path: ?Cache.Path = if (opt_zcu != null) p: { | 287 | const zcu_obj_path: ?Cache.Path = if (opt_zcu) |zcu| p: { |
| 294 | break :p try comp.resolveEmitPathFlush(arena, .temp, base.zcu_object_basename.?); | 288 | break :p try comp.resolveEmitPathFlush(arena, .temp, zcu.llvm_object.?.out_bin_basename); |
| 295 | } else null; | 289 | } else null; |
| 296 | 290 | ||
| 297 | log.debug("zcu_obj_path={?f}", .{zcu_obj_path}); | 291 | log.debug("zcu_obj_path={?f}", .{zcu_obj_path}); |
| ... | @@ -376,8 +370,8 @@ fn coffLink(lld: *Lld, arena: Allocator) !void { | ... | @@ -376,8 +370,8 @@ fn coffLink(lld: *Lld, arena: Allocator) !void { |
| 376 | const directory = base.emit.root_dir; // Just an alias to make it shorter to type. | 370 | const directory = base.emit.root_dir; // Just an alias to make it shorter to type. |
| 377 | const full_out_path = try directory.join(arena, &[_][]const u8{base.emit.sub_path}); | 371 | const full_out_path = try directory.join(arena, &[_][]const u8{base.emit.sub_path}); |
| 378 | 372 | ||
| 379 | const zcu_obj_path: ?Cache.Path = if (comp.zcu != null) p: { | 373 | const zcu_obj_path: ?Cache.Path = if (comp.zcu) |zcu| p: { |
| 380 | break :p try comp.resolveEmitPathFlush(arena, .temp, base.zcu_object_basename.?); | 374 | break :p try comp.resolveEmitPathFlush(arena, .temp, zcu.llvm_object.?.out_bin_basename); |
| 381 | } else null; | 375 | } else null; |
| 382 | 376 | ||
| 383 | const is_lib = comp.config.output_mode == .Lib; | 377 | const is_lib = comp.config.output_mode == .Lib; |
| ... | @@ -766,8 +760,8 @@ fn elfLink(lld: *Lld, arena: Allocator) !void { | ... | @@ -766,8 +760,8 @@ fn elfLink(lld: *Lld, arena: Allocator) !void { |
| 766 | const directory = base.emit.root_dir; // Just an alias to make it shorter to type. | 760 | const directory = base.emit.root_dir; // Just an alias to make it shorter to type. |
| 767 | const full_out_path = try directory.join(arena, &[_][]const u8{base.emit.sub_path}); | 761 | const full_out_path = try directory.join(arena, &[_][]const u8{base.emit.sub_path}); |
| 768 | 762 | ||
| 769 | const zcu_obj_path: ?Cache.Path = if (comp.zcu != null) p: { | 763 | const zcu_obj_path: ?Cache.Path = if (comp.zcu) |zcu| p: { |
| 770 | break :p try comp.resolveEmitPathFlush(arena, .temp, base.zcu_object_basename.?); | 764 | break :p try comp.resolveEmitPathFlush(arena, .temp, zcu.llvm_object.?.out_bin_basename); |
| 771 | } else null; | 765 | } else null; |
| 772 | 766 | ||
| 773 | const output_mode = comp.config.output_mode; | 767 | const output_mode = comp.config.output_mode; |
| ... | @@ -1364,8 +1358,8 @@ fn wasmLink(lld: *Lld, arena: Allocator) !void { | ... | @@ -1364,8 +1358,8 @@ fn wasmLink(lld: *Lld, arena: Allocator) !void { |
| 1364 | const directory = base.emit.root_dir; // Just an alias to make it shorter to type. | 1358 | const directory = base.emit.root_dir; // Just an alias to make it shorter to type. |
| 1365 | const full_out_path = try directory.join(arena, &[_][]const u8{base.emit.sub_path}); | 1359 | const full_out_path = try directory.join(arena, &[_][]const u8{base.emit.sub_path}); |
| 1366 | 1360 | ||
| 1367 | const zcu_obj_path: ?Cache.Path = if (comp.zcu != null) p: { | 1361 | const zcu_obj_path: ?Cache.Path = if (comp.zcu) |zcu| p: { |
| 1368 | break :p try comp.resolveEmitPathFlush(arena, .temp, base.zcu_object_basename.?); | 1362 | break :p try comp.resolveEmitPathFlush(arena, .temp, zcu.llvm_object.?.out_bin_basename); |
| 1369 | } else null; | 1363 | } else null; |
| 1370 | 1364 | ||
| 1371 | const is_obj = comp.config.output_mode == .Obj; | 1365 | const is_obj = comp.config.output_mode == .Obj; |
src/link/MachO.zig+10-10| ... | @@ -181,10 +181,6 @@ pub fn createEmpty( | ... | @@ -181,10 +181,6 @@ pub fn createEmpty( |
| 181 | .tag = .macho, | 181 | .tag = .macho, |
| 182 | .comp = comp, | 182 | .comp = comp, |
| 183 | .emit = emit, | 183 | .emit = emit, |
| 184 | .zcu_object_basename = if (use_llvm) | ||
| 185 | try std.fmt.allocPrint(arena, "{s}_zcu.o", .{fs.path.stem(emit.sub_path)}) | ||
| 186 | else | ||
| 187 | null, | ||
| 188 | .gc_sections = options.gc_sections orelse (optimize_mode != .Debug), | 184 | .gc_sections = options.gc_sections orelse (optimize_mode != .Debug), |
| 189 | .print_gc_sections = options.print_gc_sections, | 185 | .print_gc_sections = options.print_gc_sections, |
| 190 | .stack_size = options.stack_size orelse 16777216, | 186 | .stack_size = options.stack_size orelse 16777216, |
| ... | @@ -353,9 +349,11 @@ pub fn flush( | ... | @@ -353,9 +349,11 @@ pub fn flush( |
| 353 | const sub_prog_node = prog_node.start("MachO Flush", 0); | 349 | const sub_prog_node = prog_node.start("MachO Flush", 0); |
| 354 | defer sub_prog_node.end(); | 350 | defer sub_prog_node.end(); |
| 355 | 351 | ||
| 356 | const zcu_obj_path: ?Path = if (self.base.zcu_object_basename) |raw| p: { | 352 | const zcu_obj_path: ?Path = p: { |
| 357 | break :p try comp.resolveEmitPathFlush(arena, .temp, raw); | 353 | const zcu = comp.zcu orelse break :p null; |
| 358 | } else null; | 354 | const llvm_object = zcu.llvm_object orelse break :p null; |
| 355 | break :p try comp.resolveEmitPathFlush(arena, .temp, llvm_object.out_bin_basename); | ||
| 356 | }; | ||
| 359 | 357 | ||
| 360 | // --verbose-link | 358 | // --verbose-link |
| 361 | if (comp.verbose_link) try self.dumpArgv(comp); | 359 | if (comp.verbose_link) try self.dumpArgv(comp); |
| ... | @@ -630,10 +628,12 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void { | ... | @@ -630,10 +628,12 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void { |
| 630 | 628 | ||
| 631 | const directory = self.base.emit.root_dir; | 629 | const directory = self.base.emit.root_dir; |
| 632 | const full_out_path = try directory.join(arena, &[_][]const u8{self.base.emit.sub_path}); | 630 | const full_out_path = try directory.join(arena, &[_][]const u8{self.base.emit.sub_path}); |
| 633 | const zcu_obj_path: ?[]const u8 = if (self.base.zcu_object_basename) |raw| p: { | 631 | const zcu_obj_path: ?[]const u8 = p: { |
| 634 | const p = try comp.resolveEmitPathFlush(arena, .temp, raw); | 632 | const zcu = comp.zcu orelse break :p null; |
| 633 | const llvm_object = zcu.llvm_object orelse break :p null; | ||
| 634 | const p = try comp.resolveEmitPathFlush(arena, .temp, llvm_object.out_bin_basename); | ||
| 635 | break :p try p.toString(arena); | 635 | break :p try p.toString(arena); |
| 636 | } else null; | 636 | }; |
| 637 | 637 | ||
| 638 | var argv = std.array_list.Managed([]const u8).init(arena); | 638 | var argv = std.array_list.Managed([]const u8).init(arena); |
| 639 | 639 |
src/link/Queue.zig+19-13| ... | @@ -130,14 +130,17 @@ pub fn finishPrelinkQueue(q: *Queue, comp: *Compilation) Io.Cancelable!void { | ... | @@ -130,14 +130,17 @@ pub fn finishPrelinkQueue(q: *Queue, comp: *Compilation) Io.Cancelable!void { |
| 130 | prelink: { | 130 | prelink: { |
| 131 | const lf = comp.bin_file orelse break :prelink; | 131 | const lf = comp.bin_file orelse break :prelink; |
| 132 | if (lf.post_prelink) break :prelink; | 132 | if (lf.post_prelink) break :prelink; |
| 133 | if (comp.zcu != null and comp.zcu.?.llvm_object != null) { | ||
| 134 | // Don't call `prelink` just yet. It will be the frontend's responsibility instead, | ||
| 135 | // after it sends the ZCU object emitted by LLVM as the final link input. | ||
| 136 | break :prelink; | ||
| 137 | } | ||
| 133 | 138 | ||
| 134 | if (lf.prelink()) |_| { | 139 | lf.prelink() catch |err| switch (err) { |
| 135 | lf.post_prelink = true; | ||
| 136 | } else |err| switch (err) { | ||
| 137 | error.OutOfMemory => comp.link_diags.setAllocFailure(), | 140 | error.OutOfMemory => comp.link_diags.setAllocFailure(), |
| 138 | error.AlreadyReported => {}, | 141 | error.AlreadyReported => {}, |
| 139 | error.Canceled => |e| return e, | 142 | error.Canceled => |e| return e, |
| 140 | } | 143 | }; |
| 141 | } | 144 | } |
| 142 | } | 145 | } |
| 143 | 146 | ||
| ... | @@ -171,16 +174,19 @@ fn runLinkTasks(q: *Queue, comp: *Compilation) void { | ... | @@ -171,16 +174,19 @@ fn runLinkTasks(q: *Queue, comp: *Compilation) void { |
| 171 | } | 174 | } |
| 172 | 175 | ||
| 173 | // We've finished the prelink tasks, so run prelink if necessary. | 176 | // We've finished the prelink tasks, so run prelink if necessary. |
| 174 | if (comp.bin_file) |lf| { | 177 | prelink: { |
| 175 | if (!lf.post_prelink) { | 178 | const lf = comp.bin_file orelse break :prelink; |
| 176 | if (lf.prelink()) |_| { | 179 | if (lf.post_prelink) break :prelink; |
| 177 | lf.post_prelink = true; | 180 | if (comp.zcu != null and comp.zcu.?.llvm_object != null) { |
| 178 | } else |err| switch (err) { | 181 | // Don't call `prelink` just yet. It will be the frontend's responsibility instead, |
| 179 | error.OutOfMemory => comp.link_diags.setAllocFailure(), | 182 | // after it sends the ZCU object emitted by LLVM as the final link input. |
| 180 | error.Canceled => @panic("TODO"), | 183 | break :prelink; |
| 181 | error.AlreadyReported => {}, | ||
| 182 | } | ||
| 183 | } | 184 | } |
| 185 | lf.prelink() catch |err| switch (err) { | ||
| 186 | error.OutOfMemory => comp.link_diags.setAllocFailure(), | ||
| 187 | error.Canceled => @panic("TODO"), | ||
| 188 | error.AlreadyReported => {}, | ||
| 189 | }; | ||
| 184 | } | 190 | } |
| 185 | 191 | ||
| 186 | zcu_tasks: while (true) { | 192 | zcu_tasks: while (true) { |
src/link/Wasm.zig+1-27| ... | @@ -2944,7 +2944,6 @@ pub fn createEmpty( | ... | @@ -2944,7 +2944,6 @@ pub fn createEmpty( |
| 2944 | const target = &comp.root_mod.resolved_target.result; | 2944 | const target = &comp.root_mod.resolved_target.result; |
| 2945 | assert(target.ofmt == .wasm); | 2945 | assert(target.ofmt == .wasm); |
| 2946 | 2946 | ||
| 2947 | const use_llvm = comp.config.use_llvm; | ||
| 2948 | const output_mode = comp.config.output_mode; | 2947 | const output_mode = comp.config.output_mode; |
| 2949 | const wasi_exec_model = comp.config.wasi_exec_model; | 2948 | const wasi_exec_model = comp.config.wasi_exec_model; |
| 2950 | 2949 | ||
| ... | @@ -2954,10 +2953,6 @@ pub fn createEmpty( | ... | @@ -2954,10 +2953,6 @@ pub fn createEmpty( |
| 2954 | .tag = .wasm, | 2953 | .tag = .wasm, |
| 2955 | .comp = comp, | 2954 | .comp = comp, |
| 2956 | .emit = emit, | 2955 | .emit = emit, |
| 2957 | .zcu_object_basename = if (use_llvm) | ||
| 2958 | try std.fmt.allocPrint(arena, "{s}_zcu.o", .{fs.path.stem(emit.sub_path)}) | ||
| 2959 | else | ||
| 2960 | null, | ||
| 2961 | // Garbage collection is so crucial to WebAssembly that we design | 2956 | // Garbage collection is so crucial to WebAssembly that we design |
| 2962 | // the linker around the assumption that it will be on in the vast | 2957 | // the linker around the assumption that it will be on in the vast |
| 2963 | // majority of cases, and therefore express "no garbage collection" | 2958 | // majority of cases, and therefore express "no garbage collection" |
| ... | @@ -3021,22 +3016,6 @@ pub fn createEmpty( | ... | @@ -3021,22 +3016,6 @@ pub fn createEmpty( |
| 3021 | return wasm; | 3016 | return wasm; |
| 3022 | } | 3017 | } |
| 3023 | 3018 | ||
| 3024 | fn openParseObjectReportingFailure(wasm: *Wasm, path: Path) void { | ||
| 3025 | const comp = wasm.base.comp; | ||
| 3026 | const io = comp.io; | ||
| 3027 | const diags = &comp.link_diags; | ||
| 3028 | const obj = link.openObject(io, path, false, false) catch |err| { | ||
| 3029 | switch (diags.failParse(path, "failed to open object: {t}", .{err})) { | ||
| 3030 | error.AlreadyReported => return, | ||
| 3031 | } | ||
| 3032 | }; | ||
| 3033 | wasm.parseObject(obj) catch |err| { | ||
| 3034 | switch (diags.failParse(path, "failed to parse object: {t}", .{err})) { | ||
| 3035 | error.AlreadyReported => return, | ||
| 3036 | } | ||
| 3037 | }; | ||
| 3038 | } | ||
| 3039 | |||
| 3040 | fn parseObject(wasm: *Wasm, obj: link.Input.Object) !void { | 3019 | fn parseObject(wasm: *Wasm, obj: link.Input.Object) !void { |
| 3041 | log.debug("parseObject {f}", .{obj.path}); | 3020 | log.debug("parseObject {f}", .{obj.path}); |
| 3042 | const gpa = wasm.base.comp.gpa; | 3021 | const gpa = wasm.base.comp.gpa; |
| ... | @@ -3822,6 +3801,7 @@ pub fn flush( | ... | @@ -3822,6 +3801,7 @@ pub fn flush( |
| 3822 | tid: Zcu.PerThread.Id, | 3801 | tid: Zcu.PerThread.Id, |
| 3823 | prog_node: std.Progress.Node, | 3802 | prog_node: std.Progress.Node, |
| 3824 | ) link.Error!void { | 3803 | ) link.Error!void { |
| 3804 | _ = arena; | ||
| 3825 | // The goal is to never use this because it's only needed if we need to | 3805 | // The goal is to never use this because it's only needed if we need to |
| 3826 | // write to InternPool, but flush is too late to be writing to the | 3806 | // write to InternPool, but flush is too late to be writing to the |
| 3827 | // InternPool. | 3807 | // InternPool. |
| ... | @@ -3833,12 +3813,6 @@ pub fn flush( | ... | @@ -3833,12 +3813,6 @@ pub fn flush( |
| 3833 | 3813 | ||
| 3834 | if (comp.verbose_link) try Compilation.dumpArgv(io, wasm.dump_argv_list.items); | 3814 | if (comp.verbose_link) try Compilation.dumpArgv(io, wasm.dump_argv_list.items); |
| 3835 | 3815 | ||
| 3836 | if (wasm.base.zcu_object_basename) |raw| { | ||
| 3837 | const zcu_obj_path: Path = try comp.resolveEmitPathFlush(arena, .temp, raw); | ||
| 3838 | openParseObjectReportingFailure(wasm, zcu_obj_path); | ||
| 3839 | try prelink(wasm, prog_node); | ||
| 3840 | } | ||
| 3841 | |||
| 3842 | const tracy = trace(@src()); | 3816 | const tracy = trace(@src()); |
| 3843 | defer tracy.end(); | 3817 | defer tracy.end(); |
| 3844 | 3818 |
src/main.zig+2-2| ... | @@ -4190,7 +4190,7 @@ fn createModule( | ... | @@ -4190,7 +4190,7 @@ fn createModule( |
| 4190 | error.ZigLacksTargetSupport => fatal("compiler backend unavailable for the specified target", .{}), | 4190 | error.ZigLacksTargetSupport => fatal("compiler backend unavailable for the specified target", .{}), |
| 4191 | error.EmittingBinaryRequiresLlvmLibrary => fatal("producing machine code via LLVM requires using the LLVM library", .{}), | 4191 | error.EmittingBinaryRequiresLlvmLibrary => fatal("producing machine code via LLVM requires using the LLVM library", .{}), |
| 4192 | error.LldIncompatibleObjectFormat => fatal("using LLD to link {s} files is unsupported", .{@tagName(target.ofmt)}), | 4192 | error.LldIncompatibleObjectFormat => fatal("using LLD to link {s} files is unsupported", .{@tagName(target.ofmt)}), |
| 4193 | error.LldCannotIncrementallyLink => fatal("self-hosted backends do not support linking with LLD", .{}), | 4193 | error.LldIncompatibleWithSelfHostedBackend => fatal("self-hosted backends do not support linking with LLD", .{}), |
| 4194 | error.LtoRequiresLld => fatal("LTO requires using LLD", .{}), | 4194 | error.LtoRequiresLld => fatal("LTO requires using LLD", .{}), |
| 4195 | error.SanitizeThreadRequiresLibCpp => fatal("thread sanitization is (for now) implemented in C++, so it requires linking libc++", .{}), | 4195 | error.SanitizeThreadRequiresLibCpp => fatal("thread sanitization is (for now) implemented in C++, so it requires linking libc++", .{}), |
| 4196 | error.LibCRequiresLibUnwind => fatal("libc of the specified target requires linking libunwind", .{}), | 4196 | error.LibCRequiresLibUnwind => fatal("libc of the specified target requires linking libunwind", .{}), |
| ... | @@ -4212,8 +4212,8 @@ fn createModule( | ... | @@ -4212,8 +4212,8 @@ fn createModule( |
| 4212 | error.LldUnavailable => fatal("zig was compiled without LLD libraries", .{}), | 4212 | error.LldUnavailable => fatal("zig was compiled without LLD libraries", .{}), |
| 4213 | error.ClangUnavailable => fatal("zig was compiled without Clang libraries", .{}), | 4213 | error.ClangUnavailable => fatal("zig was compiled without Clang libraries", .{}), |
| 4214 | error.DllExportFnsRequiresWindows => fatal("only Windows OS targets support DLLs", .{}), | 4214 | error.DllExportFnsRequiresWindows => fatal("only Windows OS targets support DLLs", .{}), |
| 4215 | error.NewLinkerIncompatibleObjectFormat => fatal("using the new linker to link {s} files is unsupported", .{@tagName(target.ofmt)}), | ||
| 4216 | error.NewLinkerIncompatibleWithLld => fatal("using the new linker is incompatible with using lld", .{}), | 4215 | error.NewLinkerIncompatibleWithLld => fatal("using the new linker is incompatible with using lld", .{}), |
| 4216 | error.NewLinkerIncompatibleObjectFormat => fatal("no new linker available for '{t}' files", .{target.ofmt}), | ||
| 4217 | }; | 4217 | }; |
| 4218 | } | 4218 | } |
| 4219 | 4219 |
src/target.zig+3-5| ... | @@ -275,12 +275,10 @@ pub fn hasLldSupport(ofmt: std.Target.ObjectFormat) bool { | ... | @@ -275,12 +275,10 @@ pub fn hasLldSupport(ofmt: std.Target.ObjectFormat) bool { |
| 275 | }; | 275 | }; |
| 276 | } | 276 | } |
| 277 | 277 | ||
| 278 | pub fn hasNewLinkerSupport(ofmt: std.Target.ObjectFormat, backend: std.lang.CompilerBackend) bool { | 278 | /// Returns `true` if `ofmt` has two linker implementations, so `-fnew-linker` is meaningful. |
| 279 | pub fn hasNewLinker(ofmt: std.Target.ObjectFormat) bool { | ||
| 279 | return switch (ofmt) { | 280 | return switch (ofmt) { |
| 280 | .elf, .coff => switch (backend) { | 281 | .elf => true, |
| 281 | .stage2_x86_64 => true, | ||
| 282 | else => false, | ||
| 283 | }, | ||
| 284 | else => false, | 282 | else => false, |
| 285 | }; | 283 | }; |
| 286 | } | 284 | } |
test/standalone/build.zig.zon+3| ... | @@ -193,6 +193,9 @@ | ... | @@ -193,6 +193,9 @@ |
| 193 | .debug_io_color = .{ | 193 | .debug_io_color = .{ |
| 194 | .path = "debug_io_color", | 194 | .path = "debug_io_color", |
| 195 | }, | 195 | }, |
| 196 | .elf2 = .{ | ||
| 197 | .path = "elf2", | ||
| 198 | }, | ||
| 196 | }, | 199 | }, |
| 197 | .paths = .{ | 200 | .paths = .{ |
| 198 | "build.zig", | 201 | "build.zig", |
test/standalone/elf2/build.zig created+51| ... | @@ -0,0 +1,51 @@ | ||
| 1 | pub fn build(b: *Build) void { | ||
| 2 | const test_step = b.step("test", "Test the new ELF linker"); | ||
| 3 | b.default_step = test_step; | ||
| 4 | |||
| 5 | if (b.graph.host.result.cpu.arch == .x86_64 and b.graph.host.result.os.tag == .linux) { | ||
| 6 | addOne(b, test_step, b.graph.host, false, .static, "elf2-hello-native-selfhosted-static"); | ||
| 7 | addOne(b, test_step, b.graph.host, false, .dynamic, "elf2-hello-native-selfhosted-dynamic"); | ||
| 8 | addOne(b, test_step, b.graph.host, true, .static, "elf2-hello-native-llvm-static"); | ||
| 9 | addOne(b, test_step, b.graph.host, true, .dynamic, "elf2-hello-native-llvm-dynamic"); | ||
| 10 | } | ||
| 11 | |||
| 12 | const x86_64_linux_target: Build.ResolvedTarget = b.resolveTargetQuery(.{ | ||
| 13 | .cpu_arch = .x86_64, | ||
| 14 | .os_tag = .linux, | ||
| 15 | }); | ||
| 16 | addOne(b, test_step, x86_64_linux_target, false, .static, "elf2-hello-selfhosted-static"); | ||
| 17 | addOne(b, test_step, x86_64_linux_target, true, .static, "elf2-hello-llvm-static"); | ||
| 18 | } | ||
| 19 | |||
| 20 | fn addOne( | ||
| 21 | b: *Build, | ||
| 22 | test_step: *Build.Step, | ||
| 23 | target: Build.ResolvedTarget, | ||
| 24 | use_llvm: bool, | ||
| 25 | link_mode: std.lang.LinkMode, | ||
| 26 | name: []const u8, | ||
| 27 | ) void { | ||
| 28 | const mod = b.createModule(.{ | ||
| 29 | .root_source_file = b.path("hello.zig"), | ||
| 30 | .target = target, | ||
| 31 | .optimize = .Debug, | ||
| 32 | .link_libc = link_mode == .dynamic, | ||
| 33 | }); | ||
| 34 | const exe = b.addExecutable(.{ | ||
| 35 | .name = name, | ||
| 36 | .root_module = mod, | ||
| 37 | .linkage = link_mode, | ||
| 38 | }); | ||
| 39 | exe.use_new_linker = true; | ||
| 40 | exe.use_llvm = use_llvm; | ||
| 41 | |||
| 42 | const run = b.addRunArtifact(exe); | ||
| 43 | run.expectExitCode(0); | ||
| 44 | run.expectStdOutEqual("Hello, World!\n"); | ||
| 45 | run.skip_foreign_checks = true; | ||
| 46 | |||
| 47 | test_step.dependOn(&run.step); | ||
| 48 | } | ||
| 49 | |||
| 50 | const std = @import("std"); | ||
| 51 | const Build = std.Build; | ||
test/standalone/elf2/hello.zig created+6| ... | @@ -0,0 +1,6 @@ | ||
| 1 | pub fn main(init: std.process.Init) !void { | ||
| 2 | const stdout: std.Io.File = .stdout(); | ||
| 3 | try stdout.writeStreamingAll(init.io, "Hello, World!\n"); | ||
| 4 | } | ||
| 5 | |||
| 6 | const std = @import("std"); | ||