diff --git a/lib/std/Build/Cache.zig b/lib/std/Build/Cache.zig index 93908807eb..53f1dcff29 100644 --- a/lib/std/Build/Cache.zig +++ b/lib/std/Build/Cache.zig @@ -398,12 +398,19 @@ pub const Manifest = struct { return gop.index; } + /// Deprecated, use `addOptionalFilePath`. pub fn addOptionalFile(self: *Manifest, optional_file_path: ?[]const u8) !void { self.hash.add(optional_file_path != null); const file_path = optional_file_path orelse return; _ = try self.addFile(file_path, null); } + pub fn addOptionalFilePath(self: *Manifest, optional_file_path: ?Path) !void { + self.hash.add(optional_file_path != null); + const file_path = optional_file_path orelse return; + _ = try self.addFilePath(file_path, null); + } + pub fn addListOfFiles(self: *Manifest, list_of_files: []const []const u8) !void { self.hash.add(list_of_files.len); for (list_of_files) |file_path| { diff --git a/lib/std/Build/Cache/Path.zig b/lib/std/Build/Cache/Path.zig index ee0666b70a..edd306d06d 100644 --- a/lib/std/Build/Cache/Path.zig +++ b/lib/std/Build/Cache/Path.zig @@ -11,7 +11,11 @@ pub fn clone(p: Path, arena: Allocator) Allocator.Error!Path { } pub fn cwd() Path { - return .{ .root_dir = Cache.Directory.cwd() }; + return initCwd(""); +} + +pub fn initCwd(sub_path: []const u8) Path { + return .{ .root_dir = Cache.Directory.cwd(), .sub_path = sub_path }; } pub fn join(p: Path, arena: Allocator, sub_path: []const u8) Allocator.Error!Path { @@ -126,6 +130,14 @@ pub fn makePath(p: Path, sub_path: []const u8) !void { return p.root_dir.handle.makePath(joined_path); } +pub fn toString(p: Path, allocator: Allocator) Allocator.Error![]u8 { + return std.fmt.allocPrint(allocator, "{}", .{p}); +} + +pub fn toStringZ(p: Path, allocator: Allocator) Allocator.Error![:0]u8 { + return std.fmt.allocPrintZ(allocator, "{}", .{p}); +} + pub fn format( self: Path, comptime fmt_string: []const u8, @@ -182,6 +194,14 @@ pub fn subPathOrDot(self: Path) []const u8 { return if (self.sub_path.len == 0) "." else self.sub_path; } +pub fn stem(p: Path) []const u8 { + return fs.path.stem(p.sub_path); +} + +pub fn basename(p: Path) []const u8 { + return fs.path.basename(p.sub_path); +} + /// Useful to make `Path` a key in `std.ArrayHashMap`. pub const TableAdapter = struct { pub const Hash = std.hash.Wyhash; diff --git a/lib/std/zig/LibCInstallation.zig b/lib/std/zig/LibCInstallation.zig index 44cd029f60..3d163532d1 100644 --- a/lib/std/zig/LibCInstallation.zig +++ b/lib/std/zig/LibCInstallation.zig @@ -690,12 +690,340 @@ fn appendCcExe(args: *std.ArrayList([]const u8), skip_cc_env_var: bool) !void { } } +/// These are basenames. This data is produced with a pure function. See also +/// `CsuPaths`. +pub const CrtBasenames = struct { + crt0: ?[]const u8 = null, + crti: ?[]const u8 = null, + crtbegin: ?[]const u8 = null, + crtend: ?[]const u8 = null, + crtn: ?[]const u8 = null, + + pub const GetArgs = struct { + target: std.Target, + link_libc: bool, + output_mode: std.builtin.OutputMode, + link_mode: std.builtin.LinkMode, + pie: bool, + }; + + /// Determine file system path names of C runtime startup objects for supported + /// link modes. + pub fn get(args: GetArgs) CrtBasenames { + // crt objects are only required for libc. + if (!args.link_libc) return .{}; + + // Flatten crt cases. + const mode: enum { + dynamic_lib, + dynamic_exe, + dynamic_pie, + static_exe, + static_pie, + } = switch (args.output_mode) { + .Obj => return .{}, + .Lib => switch (args.link_mode) { + .dynamic => .dynamic_lib, + .static => return .{}, + }, + .Exe => switch (args.link_mode) { + .dynamic => if (args.pie) .dynamic_pie else .dynamic_exe, + .static => if (args.pie) .static_pie else .static_exe, + }, + }; + + const target = args.target; + + if (target.isAndroid()) return switch (mode) { + .dynamic_lib => .{ + .crtbegin = "crtbegin_so.o", + .crtend = "crtend_so.o", + }, + .dynamic_exe, .dynamic_pie => .{ + .crtbegin = "crtbegin_dynamic.o", + .crtend = "crtend_android.o", + }, + .static_exe, .static_pie => .{ + .crtbegin = "crtbegin_static.o", + .crtend = "crtend_android.o", + }, + }; + + return switch (target.os.tag) { + .linux => switch (mode) { + .dynamic_lib => .{ + .crti = "crti.o", + .crtn = "crtn.o", + }, + .dynamic_exe => .{ + .crt0 = "crt1.o", + .crti = "crti.o", + .crtn = "crtn.o", + }, + .dynamic_pie => .{ + .crt0 = "Scrt1.o", + .crti = "crti.o", + .crtn = "crtn.o", + }, + .static_exe => .{ + .crt0 = "crt1.o", + .crti = "crti.o", + .crtn = "crtn.o", + }, + .static_pie => .{ + .crt0 = "rcrt1.o", + .crti = "crti.o", + .crtn = "crtn.o", + }, + }, + .dragonfly => switch (mode) { + .dynamic_lib => .{ + .crti = "crti.o", + .crtbegin = "crtbeginS.o", + .crtend = "crtendS.o", + .crtn = "crtn.o", + }, + .dynamic_exe => .{ + .crt0 = "crt1.o", + .crti = "crti.o", + .crtbegin = "crtbegin.o", + .crtend = "crtend.o", + .crtn = "crtn.o", + }, + .dynamic_pie => .{ + .crt0 = "Scrt1.o", + .crti = "crti.o", + .crtbegin = "crtbeginS.o", + .crtend = "crtendS.o", + .crtn = "crtn.o", + }, + .static_exe => .{ + .crt0 = "crt1.o", + .crti = "crti.o", + .crtbegin = "crtbegin.o", + .crtend = "crtend.o", + .crtn = "crtn.o", + }, + .static_pie => .{ + .crt0 = "Scrt1.o", + .crti = "crti.o", + .crtbegin = "crtbeginS.o", + .crtend = "crtendS.o", + .crtn = "crtn.o", + }, + }, + .freebsd => switch (mode) { + .dynamic_lib => .{ + .crti = "crti.o", + .crtbegin = "crtbeginS.o", + .crtend = "crtendS.o", + .crtn = "crtn.o", + }, + .dynamic_exe => .{ + .crt0 = "crt1.o", + .crti = "crti.o", + .crtbegin = "crtbegin.o", + .crtend = "crtend.o", + .crtn = "crtn.o", + }, + .dynamic_pie => .{ + .crt0 = "Scrt1.o", + .crti = "crti.o", + .crtbegin = "crtbeginS.o", + .crtend = "crtendS.o", + .crtn = "crtn.o", + }, + .static_exe => .{ + .crt0 = "crt1.o", + .crti = "crti.o", + .crtbegin = "crtbeginT.o", + .crtend = "crtend.o", + .crtn = "crtn.o", + }, + .static_pie => .{ + .crt0 = "Scrt1.o", + .crti = "crti.o", + .crtbegin = "crtbeginS.o", + .crtend = "crtendS.o", + .crtn = "crtn.o", + }, + }, + .netbsd => switch (mode) { + .dynamic_lib => .{ + .crti = "crti.o", + .crtbegin = "crtbeginS.o", + .crtend = "crtendS.o", + .crtn = "crtn.o", + }, + .dynamic_exe => .{ + .crt0 = "crt0.o", + .crti = "crti.o", + .crtbegin = "crtbegin.o", + .crtend = "crtend.o", + .crtn = "crtn.o", + }, + .dynamic_pie => .{ + .crt0 = "crt0.o", + .crti = "crti.o", + .crtbegin = "crtbeginS.o", + .crtend = "crtendS.o", + .crtn = "crtn.o", + }, + .static_exe => .{ + .crt0 = "crt0.o", + .crti = "crti.o", + .crtbegin = "crtbeginT.o", + .crtend = "crtend.o", + .crtn = "crtn.o", + }, + .static_pie => .{ + .crt0 = "crt0.o", + .crti = "crti.o", + .crtbegin = "crtbeginT.o", + .crtend = "crtendS.o", + .crtn = "crtn.o", + }, + }, + .openbsd => switch (mode) { + .dynamic_lib => .{ + .crtbegin = "crtbeginS.o", + .crtend = "crtendS.o", + }, + .dynamic_exe, .dynamic_pie => .{ + .crt0 = "crt0.o", + .crtbegin = "crtbegin.o", + .crtend = "crtend.o", + }, + .static_exe, .static_pie => .{ + .crt0 = "rcrt0.o", + .crtbegin = "crtbegin.o", + .crtend = "crtend.o", + }, + }, + .haiku => switch (mode) { + .dynamic_lib => .{ + .crti = "crti.o", + .crtbegin = "crtbeginS.o", + .crtend = "crtendS.o", + .crtn = "crtn.o", + }, + .dynamic_exe => .{ + .crt0 = "start_dyn.o", + .crti = "crti.o", + .crtbegin = "crtbegin.o", + .crtend = "crtend.o", + .crtn = "crtn.o", + }, + .dynamic_pie => .{ + .crt0 = "start_dyn.o", + .crti = "crti.o", + .crtbegin = "crtbeginS.o", + .crtend = "crtendS.o", + .crtn = "crtn.o", + }, + .static_exe => .{ + .crt0 = "start_dyn.o", + .crti = "crti.o", + .crtbegin = "crtbegin.o", + .crtend = "crtend.o", + .crtn = "crtn.o", + }, + .static_pie => .{ + .crt0 = "start_dyn.o", + .crti = "crti.o", + .crtbegin = "crtbeginS.o", + .crtend = "crtendS.o", + .crtn = "crtn.o", + }, + }, + .solaris, .illumos => switch (mode) { + .dynamic_lib => .{ + .crti = "crti.o", + .crtn = "crtn.o", + }, + .dynamic_exe, .dynamic_pie => .{ + .crt0 = "crt1.o", + .crti = "crti.o", + .crtn = "crtn.o", + }, + .static_exe, .static_pie => .{}, + }, + else => .{}, + }; + } +}; + +pub const CrtPaths = struct { + crt0: ?Path = null, + crti: ?Path = null, + crtbegin: ?Path = null, + crtend: ?Path = null, + crtn: ?Path = null, +}; + +pub fn resolveCrtPaths( + lci: LibCInstallation, + arena: Allocator, + crt_basenames: CrtBasenames, + target: std.Target, +) error{ OutOfMemory, LibCInstallationMissingCrtDir }!CrtPaths { + const crt_dir_path: Path = .{ + .root_dir = std.Build.Cache.Directory.cwd(), + .sub_path = lci.crt_dir orelse return error.LibCInstallationMissingCrtDir, + }; + switch (target.os.tag) { + .dragonfly => { + const gccv: []const u8 = if (target.os.version_range.semver.isAtLeast(.{ + .major = 5, + .minor = 4, + .patch = 0, + }) orelse true) "gcc80" else "gcc54"; + return .{ + .crt0 = if (crt_basenames.crt0) |basename| try crt_dir_path.join(arena, basename) else null, + .crti = if (crt_basenames.crti) |basename| try crt_dir_path.join(arena, basename) else null, + .crtbegin = if (crt_basenames.crtbegin) |basename| .{ + .root_dir = crt_dir_path.root_dir, + .sub_path = try fs.path.join(arena, &.{ crt_dir_path.sub_path, gccv, basename }), + } else null, + .crtend = if (crt_basenames.crtend) |basename| .{ + .root_dir = crt_dir_path.root_dir, + .sub_path = try fs.path.join(arena, &.{ crt_dir_path.sub_path, gccv, basename }), + } else null, + .crtn = if (crt_basenames.crtn) |basename| try crt_dir_path.join(arena, basename) else null, + }; + }, + .haiku => { + const gcc_dir_path: Path = .{ + .root_dir = std.Build.Cache.Directory.cwd(), + .sub_path = lci.gcc_dir orelse return error.LibCInstallationMissingCrtDir, + }; + return .{ + .crt0 = if (crt_basenames.crt0) |basename| try crt_dir_path.join(arena, basename) else null, + .crti = if (crt_basenames.crti) |basename| try crt_dir_path.join(arena, basename) else null, + .crtbegin = if (crt_basenames.crtbegin) |basename| try gcc_dir_path.join(arena, basename) else null, + .crtend = if (crt_basenames.crtend) |basename| try gcc_dir_path.join(arena, basename) else null, + .crtn = if (crt_basenames.crtn) |basename| try crt_dir_path.join(arena, basename) else null, + }; + }, + else => { + return .{ + .crt0 = if (crt_basenames.crt0) |basename| try crt_dir_path.join(arena, basename) else null, + .crti = if (crt_basenames.crti) |basename| try crt_dir_path.join(arena, basename) else null, + .crtbegin = if (crt_basenames.crtbegin) |basename| try crt_dir_path.join(arena, basename) else null, + .crtend = if (crt_basenames.crtend) |basename| try crt_dir_path.join(arena, basename) else null, + .crtn = if (crt_basenames.crtn) |basename| try crt_dir_path.join(arena, basename) else null, + }; + }, + } +} + const LibCInstallation = @This(); const std = @import("std"); const builtin = @import("builtin"); const Target = std.Target; const fs = std.fs; const Allocator = std.mem.Allocator; +const Path = std.Build.Cache.Path; const is_darwin = builtin.target.isDarwin(); const is_windows = builtin.target.os.tag == .windows; diff --git a/src/Compilation.zig b/src/Compilation.zig index 589c69fa97..6ba09d5c6d 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -217,37 +217,37 @@ thread_pool: *ThreadPool, /// Populated when we build the libc++ static library. A Job to build this is placed in the queue /// and resolved before calling linker.flush(). -libcxx_static_lib: ?CRTFile = null, +libcxx_static_lib: ?CrtFile = null, /// Populated when we build the libc++abi static library. A Job to build this is placed in the queue /// and resolved before calling linker.flush(). -libcxxabi_static_lib: ?CRTFile = null, +libcxxabi_static_lib: ?CrtFile = null, /// Populated when we build the libunwind static library. A Job to build this is placed in the queue /// and resolved before calling linker.flush(). -libunwind_static_lib: ?CRTFile = null, +libunwind_static_lib: ?CrtFile = null, /// Populated when we build the TSAN library. A Job to build this is placed in the queue /// and resolved before calling linker.flush(). -tsan_lib: ?CRTFile = null, +tsan_lib: ?CrtFile = null, /// Populated when we build the libc static library. A Job to build this is placed in the queue /// and resolved before calling linker.flush(). -libc_static_lib: ?CRTFile = null, +libc_static_lib: ?CrtFile = null, /// Populated when we build the libcompiler_rt static library. A Job to build this is indicated /// by setting `job_queued_compiler_rt_lib` and resolved before calling linker.flush(). -compiler_rt_lib: ?CRTFile = null, +compiler_rt_lib: ?CrtFile = null, /// Populated when we build the compiler_rt_obj object. A Job to build this is indicated /// by setting `job_queued_compiler_rt_obj` and resolved before calling linker.flush(). -compiler_rt_obj: ?CRTFile = null, +compiler_rt_obj: ?CrtFile = null, /// Populated when we build the libfuzzer static library. A Job to build this /// is indicated by setting `job_queued_fuzzer_lib` and resolved before /// calling linker.flush(). -fuzzer_lib: ?CRTFile = null, +fuzzer_lib: ?CrtFile = null, glibc_so_files: ?glibc.BuiltSharedObjects = null, -wasi_emulated_libs: []const wasi_libc.CRTFile, +wasi_emulated_libs: []const wasi_libc.CrtFile, /// For example `Scrt1.o` and `libc_nonshared.a`. These are populated after building libc from source, /// The set of needed CRT (C runtime) files differs depending on the target and compilation settings. /// The key is the basename, and the value is the absolute path to the completed build artifact. -crt_files: std.StringHashMapUnmanaged(CRTFile) = .empty, +crt_files: std.StringHashMapUnmanaged(CrtFile) = .empty, /// How many lines of reference trace should be included per compile error. /// Null means only show snippet on first error. @@ -276,20 +276,20 @@ digest: ?[Cache.bin_digest_len]u8 = null, pub const default_stack_protector_buffer_size = target_util.default_stack_protector_buffer_size; pub const SemaError = Zcu.SemaError; -pub const CRTFile = struct { +pub const CrtFile = struct { lock: Cache.Lock, - full_object_path: []const u8, + full_object_path: Path, - pub fn isObject(cf: CRTFile) bool { - return switch (classifyFileExt(cf.full_object_path)) { + pub fn isObject(cf: CrtFile) bool { + return switch (classifyFileExt(cf.full_object_path.sub_path)) { .object => true, else => false, }; } - pub fn deinit(self: *CRTFile, gpa: Allocator) void { + pub fn deinit(self: *CrtFile, gpa: Allocator) void { self.lock.release(); - gpa.free(self.full_object_path); + gpa.free(self.full_object_path.sub_path); self.* = undefined; } }; @@ -369,13 +369,13 @@ const Job = union(enum) { resolve_type_fully: InternPool.Index, /// one of the glibc static objects - glibc_crt_file: glibc.CRTFile, + glibc_crt_file: glibc.CrtFile, /// all of the glibc shared objects glibc_shared_objects, /// one of the musl static objects - musl_crt_file: musl.CRTFile, + musl_crt_file: musl.CrtFile, /// one of the mingw-w64 static objects - mingw_crt_file: mingw.CRTFile, + mingw_crt_file: mingw.CrtFile, /// libunwind.a, usually needed when linking libc libunwind: void, libcxx: void, @@ -385,7 +385,7 @@ const Job = union(enum) { /// calls to, for example, memcpy and memset. zig_libc: void, /// one of WASI libc static objects - wasi_libc_crt_file: wasi_libc.CRTFile, + wasi_libc_crt_file: wasi_libc.CrtFile, /// The value is the index into `system_libs`. windows_import_lib: usize, @@ -422,8 +422,8 @@ pub const CObject = struct { status: union(enum) { new, success: struct { - /// The outputted result. Owned by gpa. - object_path: []u8, + /// The outputted result. `sub_path` owned by gpa. + object_path: Path, /// This is a file system lock on the cache hash manifest representing this /// object. It prevents other invocations of the Zig compiler from interfering /// with this object until released. @@ -719,7 +719,7 @@ pub const CObject = struct { return true; }, .success => |*success| { - gpa.free(success.object_path); + gpa.free(success.object_path.sub_path); success.lock.release(); self.status = .new; return false; @@ -1018,7 +1018,7 @@ const CacheUse = union(CacheMode) { }; pub const LinkObject = struct { - path: []const u8, + path: Path, must_link: bool = false, // When the library is passed via a positional argument, it will be // added as a full path. If it's `-l`, then just the basename. @@ -1027,7 +1027,7 @@ pub const LinkObject = struct { loption: bool = false, pub fn isObject(lo: LinkObject) bool { - return switch (classifyFileExt(lo.path)) { + return switch (classifyFileExt(lo.path.sub_path)) { .object => true, else => false, }; @@ -1095,7 +1095,7 @@ pub const CreateOptions = struct { /// * getpid /// * mman /// * signal - wasi_emulated_libs: []const wasi_libc.CRTFile = &.{}, + wasi_emulated_libs: []const wasi_libc.CrtFile = &.{}, /// This means that if the output mode is an executable it will be a /// Position Independent Executable. If the output mode is not an /// executable this field is ignored. @@ -2578,7 +2578,7 @@ fn addNonIncrementalStuffToCacheManifest( } for (comp.objects) |obj| { - _ = try man.addFile(obj.path, null); + _ = try man.addFilePath(obj.path, null); man.hash.add(obj.must_link); man.hash.add(obj.loption); } @@ -2703,9 +2703,8 @@ fn emitOthers(comp: *Compilation) void { return; } const obj_path = comp.c_object_table.keys()[0].status.success.object_path; - const cwd = std.fs.cwd(); - const ext = std.fs.path.extension(obj_path); - const basename = obj_path[0 .. obj_path.len - ext.len]; + const ext = std.fs.path.extension(obj_path.sub_path); + const dirname = obj_path.sub_path[0 .. obj_path.sub_path.len - ext.len]; // This obj path always ends with the object file extension, but if we change the // extension to .ll, .bc, or .s, then it will be the path to those things. const outs = [_]struct { @@ -2720,13 +2719,13 @@ fn emitOthers(comp: *Compilation) void { if (out.emit) |loc| { if (loc.directory) |directory| { const src_path = std.fmt.allocPrint(comp.gpa, "{s}{s}", .{ - basename, out.ext, + dirname, out.ext, }) catch |err| { - log.err("unable to copy {s}{s}: {s}", .{ basename, out.ext, @errorName(err) }); + log.err("unable to copy {s}{s}: {s}", .{ dirname, out.ext, @errorName(err) }); continue; }; defer comp.gpa.free(src_path); - cwd.copyFile(src_path, directory.handle, loc.basename, .{}) catch |err| { + obj_path.root_dir.handle.copyFile(src_path, directory.handle, loc.basename, .{}) catch |err| { log.err("unable to copy {s}: {s}", .{ src_path, @errorName(err) }); }; } @@ -3774,7 +3773,7 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job, prog_node: std.Progre const named_frame = tracy.namedFrame("glibc_crt_file"); defer named_frame.end(); - glibc.buildCRTFile(comp, crt_file, prog_node) catch |err| { + glibc.buildCrtFile(comp, crt_file, prog_node) catch |err| { // TODO Surface more error details. comp.lockAndSetMiscFailure(.glibc_crt_file, "unable to build glibc CRT file: {s}", .{ @errorName(err), @@ -3798,7 +3797,7 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job, prog_node: std.Progre const named_frame = tracy.namedFrame("musl_crt_file"); defer named_frame.end(); - musl.buildCRTFile(comp, crt_file, prog_node) catch |err| { + musl.buildCrtFile(comp, crt_file, prog_node) catch |err| { // TODO Surface more error details. comp.lockAndSetMiscFailure( .musl_crt_file, @@ -3811,7 +3810,7 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job, prog_node: std.Progre const named_frame = tracy.namedFrame("mingw_crt_file"); defer named_frame.end(); - mingw.buildCRTFile(comp, crt_file, prog_node) catch |err| { + mingw.buildCrtFile(comp, crt_file, prog_node) catch |err| { // TODO Surface more error details. comp.lockAndSetMiscFailure( .mingw_crt_file, @@ -3894,7 +3893,7 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job, prog_node: std.Progre const named_frame = tracy.namedFrame("wasi_libc_crt_file"); defer named_frame.end(); - wasi_libc.buildCRTFile(comp, crt_file, prog_node) catch |err| { + wasi_libc.buildCrtFile(comp, crt_file, prog_node) catch |err| { // TODO Surface more error details. comp.lockAndSetMiscFailure( .wasi_libc_crt_file, @@ -4602,7 +4601,7 @@ fn buildRt( root_source_name: []const u8, misc_task: MiscTask, output_mode: std.builtin.OutputMode, - out: *?CRTFile, + out: *?CrtFile, prog_node: std.Progress.Node, ) void { comp.buildOutputFromZig( @@ -4703,7 +4702,9 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr log.debug("updating C object: {s}", .{c_object.src.src_path}); - if (c_object.clearStatus(comp.gpa)) { + const gpa = comp.gpa; + + if (c_object.clearStatus(gpa)) { // There was previous failure. comp.mutex.lock(); defer comp.mutex.unlock(); @@ -4722,7 +4723,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr try cache_helpers.hashCSource(&man, c_object.src); - var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa); + var arena_allocator = std.heap.ArenaAllocator.init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); @@ -4744,7 +4745,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr const target = comp.getTarget(); const o_ext = target.ofmt.fileExt(target.cpu.arch); const digest = if (!comp.disable_c_depfile and try man.hit()) man.final() else blk: { - var argv = std.ArrayList([]const u8).init(comp.gpa); + var argv = std.ArrayList([]const u8).init(gpa); defer argv.deinit(); // In case we are doing passthrough mode, we need to detect -S and -emit-llvm. @@ -4908,7 +4909,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr switch (term) { .Exited => |code| if (code != 0) if (out_diag_path) |diag_file_path| { - const bundle = CObject.Diag.Bundle.parse(comp.gpa, diag_file_path) catch |err| { + const bundle = CObject.Diag.Bundle.parse(gpa, diag_file_path) catch |err| { log.err("{}: failed to parse clang diagnostics: {s}", .{ err, stderr }); return comp.failCObj(c_object, "clang exited with code {d}", .{code}); }; @@ -4982,9 +4983,10 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr c_object.status = .{ .success = .{ - .object_path = try comp.local_cache_directory.join(comp.gpa, &[_][]const u8{ - "o", &digest, o_basename, - }), + .object_path = .{ + .root_dir = comp.local_cache_directory, + .sub_path = try std.fs.path.join(gpa, &.{ "o", &digest, o_basename }), + }, .lock = man.toOwnedLock(), }, }; @@ -6092,18 +6094,23 @@ test "classifyFileExt" { try std.testing.expectEqual(FileExt.zig, classifyFileExt("foo.zig")); } -pub fn get_libc_crt_file(comp: *Compilation, arena: Allocator, basename: []const u8) ![]const u8 { - if (comp.wantBuildGLibCFromSource() or - comp.wantBuildMuslFromSource() or - comp.wantBuildMinGWFromSource() or - comp.wantBuildWasiLibcFromSource()) - { - return comp.crt_files.get(basename).?.full_object_path; - } - const lci = comp.libc_installation orelse return error.LibCInstallationNotAvailable; - const crt_dir_path = lci.crt_dir orelse return error.LibCInstallationMissingCRTDir; - const full_path = try std.fs.path.join(arena, &[_][]const u8{ crt_dir_path, basename }); - return full_path; +pub fn get_libc_crt_file(comp: *Compilation, arena: Allocator, basename: []const u8) !Path { + return (try crtFilePath(comp, basename)) orelse { + const lci = comp.libc_installation orelse return error.LibCInstallationNotAvailable; + const crt_dir_path = lci.crt_dir orelse return error.LibCInstallationMissingCrtDir; + const full_path = try std.fs.path.join(arena, &[_][]const u8{ crt_dir_path, basename }); + return Path.initCwd(full_path); + }; +} + +pub fn crtFileAsString(comp: *Compilation, arena: Allocator, basename: []const u8) ![]const u8 { + const path = try get_libc_crt_file(comp, arena, basename); + return path.toString(arena); +} + +pub fn crtFilePath(comp: *Compilation, basename: []const u8) Allocator.Error!?Path { + const crt_file = comp.crt_files.get(basename) orelse return null; + return crt_file.full_object_path; } fn wantBuildLibCFromSource(comp: Compilation) bool { @@ -6314,7 +6321,7 @@ fn buildOutputFromZig( comp: *Compilation, src_basename: []const u8, output_mode: std.builtin.OutputMode, - out: *?CRTFile, + out: *?CrtFile, misc_task_tag: MiscTask, prog_node: std.Progress.Node, ) !void { @@ -6542,15 +6549,39 @@ pub fn build_crt_file( comp.crt_files.putAssumeCapacityNoClobber(basename, try sub_compilation.toCrtFile()); } -pub fn toCrtFile(comp: *Compilation) Allocator.Error!CRTFile { +pub fn toCrtFile(comp: *Compilation) Allocator.Error!CrtFile { return .{ - .full_object_path = try comp.local_cache_directory.join(comp.gpa, &.{ - comp.cache_use.whole.bin_sub_path.?, - }), + .full_object_path = .{ + .root_dir = comp.local_cache_directory, + .sub_path = try comp.gpa.dupe(u8, comp.cache_use.whole.bin_sub_path.?), + }, .lock = comp.cache_use.whole.moveLock(), }; } +pub fn getCrtPaths( + comp: *Compilation, + arena: Allocator, +) error{ OutOfMemory, LibCInstallationMissingCrtDir }!LibCInstallation.CrtPaths { + const target = comp.root_mod.resolved_target.result; + const basenames = LibCInstallation.CrtBasenames.get(.{ + .target = target, + .link_libc = comp.config.link_libc, + .output_mode = comp.config.output_mode, + .link_mode = comp.config.link_mode, + .pie = comp.config.pie, + }); + if (comp.libc_installation) |lci| return lci.resolveCrtPaths(arena, basenames, target); + + return .{ + .crt0 = if (basenames.crt0) |basename| try comp.crtFilePath(basename) else null, + .crti = if (basenames.crti) |basename| try comp.crtFilePath(basename) else null, + .crtbegin = if (basenames.crtbegin) |basename| try comp.crtFilePath(basename) else null, + .crtend = if (basenames.crtend) |basename| try comp.crtFilePath(basename) else null, + .crtn = if (basenames.crtn) |basename| try comp.crtFilePath(basename) else null, + }; +} + pub fn addLinkLib(comp: *Compilation, lib_name: []const u8) !void { // Avoid deadlocking on building import libs such as kernel32.lib // This can happen when the user uses `build-exe foo.obj -lkernel32` and diff --git a/src/glibc.zig b/src/glibc.zig index 7afa4f7432..f01e867843 100644 --- a/src/glibc.zig +++ b/src/glibc.zig @@ -169,14 +169,14 @@ fn useElfInitFini(target: std.Target) bool { }; } -pub const CRTFile = enum { +pub const CrtFile = enum { crti_o, crtn_o, scrt1_o, libc_nonshared_a, }; -pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: std.Progress.Node) !void { +pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progress.Node) !void { if (!build_options.have_llvm) { return error.ZigCompilerNotBuiltWithLLVMExtensions; } @@ -292,7 +292,8 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: std.Progre .owner = undefined, }; var files = [_]Compilation.CSourceFile{ start_o, abi_note_o, init_o }; - return comp.build_crt_file("Scrt1", .Obj, .@"glibc Scrt1.o", prog_node, &files); + const basename = if (comp.config.output_mode == .Exe and !comp.config.pie) "crt1" else "Scrt1"; + return comp.build_crt_file(basename, .Obj, .@"glibc Scrt1.o", prog_node, &files); }, .libc_nonshared_a => { const s = path.sep_str; diff --git a/src/link.zig b/src/link.zig index 879fca9a84..64115ab3ee 100644 --- a/src/link.zig +++ b/src/link.zig @@ -11,7 +11,7 @@ const wasi_libc = @import("wasi_libc.zig"); const Air = @import("Air.zig"); const Allocator = std.mem.Allocator; const Cache = std.Build.Cache; -const Path = Cache.Path; +const Path = std.Build.Cache.Path; const Compilation = @import("Compilation.zig"); const LibCInstallation = std.zig.LibCInstallation; const Liveness = @import("Liveness.zig"); @@ -34,7 +34,7 @@ pub const SystemLib = struct { /// 1. Windows DLLs that zig ships such as advapi32. /// 2. extern "foo" fn declarations where we find out about libraries too late /// TODO: make this non-optional and resolve those two cases somehow. - path: ?[]const u8, + path: ?Path, }; pub fn hashAddSystemLibs( @@ -46,7 +46,7 @@ pub fn hashAddSystemLibs( for (hm.values()) |value| { man.hash.add(value.needed); man.hash.add(value.weak); - if (value.path) |p| _ = try man.addFile(p, null); + if (value.path) |p| _ = try man.addFilePath(p, null); } } @@ -551,7 +551,7 @@ pub const File = struct { LLDCrashed, LLDReportedFailure, LLD_LinkingIsTODO_ForSpirV, - LibCInstallationMissingCRTDir, + LibCInstallationMissingCrtDir, LibCInstallationNotAvailable, LinkingWithoutZigSourceUnimplemented, MalformedArchive, @@ -606,18 +606,15 @@ pub const File = struct { const comp = base.comp; if (comp.clang_preprocessor_mode == .yes or comp.clang_preprocessor_mode == .pch) { dev.check(.clang_command); - const gpa = comp.gpa; const emit = base.emit; // TODO: avoid extra link step when it's just 1 object file (the `zig cc -c` case) // Until then, we do `lld -r -o output.o input.o` even though the output is the same // as the input. For the preprocessing case (`zig cc -E -o foo`) we copy the file // to the final location. See also the corresponding TODO in Coff linking. - const full_out_path = try emit.root_dir.join(gpa, &[_][]const u8{emit.sub_path}); - defer gpa.free(full_out_path); assert(comp.c_object_table.count() == 1); const the_key = comp.c_object_table.keys()[0]; const cached_pp_file_path = the_key.status.success.object_path; - try fs.cwd().copyFile(cached_pp_file_path, fs.cwd(), full_out_path, .{}); + try cached_pp_file_path.root_dir.handle.copyFile(cached_pp_file_path.sub_path, emit.root_dir.handle, emit.sub_path, .{}); return; } @@ -781,7 +778,7 @@ pub const File = struct { log.debug("zcu_obj_path={s}", .{if (zcu_obj_path) |s| s else "(null)"}); - const compiler_rt_path: ?[]const u8 = if (comp.include_compiler_rt) + const compiler_rt_path: ?Path = if (comp.include_compiler_rt) comp.compiler_rt_obj.?.full_object_path else null; @@ -806,18 +803,18 @@ pub const File = struct { base.releaseLock(); for (objects) |obj| { - _ = try man.addFile(obj.path, null); + _ = try man.addFilePath(obj.path, null); man.hash.add(obj.must_link); man.hash.add(obj.loption); } for (comp.c_object_table.keys()) |key| { - _ = try man.addFile(key.status.success.object_path, null); + _ = try man.addFilePath(key.status.success.object_path, null); } for (comp.win32_resource_table.keys()) |key| { _ = try man.addFile(key.status.success.res_path, null); } try man.addOptionalFile(zcu_obj_path); - try man.addOptionalFile(compiler_rt_path); + try man.addOptionalFilePath(compiler_rt_path); // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock. _ = try man.hit(); @@ -851,10 +848,10 @@ pub const File = struct { defer object_files.deinit(); for (objects) |obj| { - object_files.appendAssumeCapacity(try arena.dupeZ(u8, obj.path)); + object_files.appendAssumeCapacity(try obj.path.toStringZ(arena)); } for (comp.c_object_table.keys()) |key| { - object_files.appendAssumeCapacity(try arena.dupeZ(u8, key.status.success.object_path)); + object_files.appendAssumeCapacity(try key.status.success.object_path.toStringZ(arena)); } for (comp.win32_resource_table.keys()) |key| { object_files.appendAssumeCapacity(try arena.dupeZ(u8, key.status.success.res_path)); @@ -863,7 +860,7 @@ pub const File = struct { object_files.appendAssumeCapacity(try arena.dupeZ(u8, p)); } if (compiler_rt_path) |p| { - object_files.appendAssumeCapacity(try arena.dupeZ(u8, p)); + object_files.appendAssumeCapacity(try p.toStringZ(arena)); } if (comp.verbose_link) { diff --git a/src/link/Coff/lld.zig b/src/link/Coff/lld.zig index 651c76c7f1..cf09174e88 100644 --- a/src/link/Coff/lld.zig +++ b/src/link/Coff/lld.zig @@ -7,6 +7,7 @@ const fs = std.fs; const log = std.log.scoped(.link); const mem = std.mem; const Cache = std.Build.Cache; +const Path = std.Build.Cache.Path; const mingw = @import("../../mingw.zig"); const link = @import("../../link.zig"); @@ -74,11 +75,11 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no comptime assert(Compilation.link_hash_implementation_version == 14); for (comp.objects) |obj| { - _ = try man.addFile(obj.path, null); + _ = try man.addFilePath(obj.path, null); man.hash.add(obj.must_link); } for (comp.c_object_table.keys()) |key| { - _ = try man.addFile(key.status.success.object_path, null); + _ = try man.addFilePath(key.status.success.object_path, null); } for (comp.win32_resource_table.keys()) |key| { _ = try man.addFile(key.status.success.res_path, null); @@ -154,17 +155,19 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no break :blk comp.c_object_table.keys()[0].status.success.object_path; if (module_obj_path) |p| - break :blk p; + break :blk Path.initCwd(p); // TODO I think this is unreachable. Audit this situation when solving the above TODO // regarding eliding redundant object -> object transformations. return error.NoObjectsToLink; }; - // This can happen when using --enable-cache and using the stage1 backend. In this case - // we can skip the file copy. - if (!mem.eql(u8, the_object_path, full_out_path)) { - try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{}); - } + try std.fs.Dir.copyFile( + the_object_path.root_dir.handle, + the_object_path.sub_path, + directory.handle, + self.base.emit.sub_path, + .{}, + ); } else { // Create an LLD command line and invoke it. var argv = std.ArrayList([]const u8).init(gpa); @@ -270,14 +273,14 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no try argv.ensureUnusedCapacity(comp.objects.len); for (comp.objects) |obj| { if (obj.must_link) { - argv.appendAssumeCapacity(try allocPrint(arena, "-WHOLEARCHIVE:{s}", .{obj.path})); + argv.appendAssumeCapacity(try allocPrint(arena, "-WHOLEARCHIVE:{}", .{@as(Path, obj.path)})); } else { - argv.appendAssumeCapacity(obj.path); + argv.appendAssumeCapacity(try obj.path.toString(arena)); } } for (comp.c_object_table.keys()) |key| { - try argv.append(key.status.success.object_path); + try argv.append(try key.status.success.object_path.toString(arena)); } for (comp.win32_resource_table.keys()) |key| { @@ -401,17 +404,17 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no } if (is_dyn_lib) { - try argv.append(try comp.get_libc_crt_file(arena, "dllcrt2.obj")); + try argv.append(try comp.crtFileAsString(arena, "dllcrt2.obj")); if (target.cpu.arch == .x86) { try argv.append("-ALTERNATENAME:__DllMainCRTStartup@12=_DllMainCRTStartup@12"); } else { try argv.append("-ALTERNATENAME:_DllMainCRTStartup=DllMainCRTStartup"); } } else { - try argv.append(try comp.get_libc_crt_file(arena, "crt2.obj")); + try argv.append(try comp.crtFileAsString(arena, "crt2.obj")); } - try argv.append(try comp.get_libc_crt_file(arena, "mingw32.lib")); + try argv.append(try comp.crtFileAsString(arena, "mingw32.lib")); } else { const lib_str = switch (comp.config.link_mode) { .dynamic => "", @@ -456,36 +459,36 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no // libc++ dep if (comp.config.link_libcpp) { - try argv.append(comp.libcxxabi_static_lib.?.full_object_path); - try argv.append(comp.libcxx_static_lib.?.full_object_path); + try argv.append(try comp.libcxxabi_static_lib.?.full_object_path.toString(arena)); + try argv.append(try comp.libcxx_static_lib.?.full_object_path.toString(arena)); } // libunwind dep if (comp.config.link_libunwind) { - try argv.append(comp.libunwind_static_lib.?.full_object_path); + try argv.append(try comp.libunwind_static_lib.?.full_object_path.toString(arena)); } if (comp.config.any_fuzz) { - try argv.append(comp.fuzzer_lib.?.full_object_path); + try argv.append(try comp.fuzzer_lib.?.full_object_path.toString(arena)); } if (is_exe_or_dyn_lib and !comp.skip_linker_dependencies) { if (!comp.config.link_libc) { if (comp.libc_static_lib) |lib| { - try argv.append(lib.full_object_path); + try argv.append(try lib.full_object_path.toString(arena)); } } // MSVC compiler_rt is missing some stuff, so we build it unconditionally but // and rely on weak linkage to allow MSVC compiler_rt functions to override ours. - if (comp.compiler_rt_obj) |obj| try argv.append(obj.full_object_path); - if (comp.compiler_rt_lib) |lib| try argv.append(lib.full_object_path); + if (comp.compiler_rt_obj) |obj| try argv.append(try obj.full_object_path.toString(arena)); + if (comp.compiler_rt_lib) |lib| try argv.append(try lib.full_object_path.toString(arena)); } try argv.ensureUnusedCapacity(comp.system_libs.count()); for (comp.system_libs.keys()) |key| { const lib_basename = try allocPrint(arena, "{s}.lib", .{key}); if (comp.crt_files.get(lib_basename)) |crt_file| { - argv.appendAssumeCapacity(crt_file.full_object_path); + argv.appendAssumeCapacity(try crt_file.full_object_path.toString(arena)); continue; } if (try findLib(arena, lib_basename, self.lib_dirs)) |full_path| { diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 62bf6be63a..5f6a073842 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -383,9 +383,9 @@ pub fn createEmpty( const index: File.Index = @intCast(try self.files.addOne(gpa)); self.files.set(index, .{ .zig_object = .{ .index = index, - .path = try std.fmt.allocPrint(arena, "{s}.o", .{fs.path.stem( - zcu.main_mod.root_src_path, - )}), + .basename = try std.fmt.allocPrint(arena, "{s}.o", .{ + fs.path.stem(zcu.main_mod.root_src_path), + }), } }); self.zig_object_index = index; try self.zigObjectPtr().?.init(self, .{ @@ -742,13 +742,12 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod const target = self.getTarget(); const link_mode = comp.config.link_mode; const directory = self.base.emit.root_dir; // Just an alias to make it shorter to type. - const module_obj_path: ?[]const u8 = if (self.base.zcu_object_sub_path) |path| blk: { - const full_out_path = try directory.join(arena, &[_][]const u8{self.base.emit.sub_path}); - if (fs.path.dirname(full_out_path)) |dirname| { - break :blk try fs.path.join(arena, &.{ dirname, path }); - } else { - break :blk path; - } + const module_obj_path: ?Path = if (self.base.zcu_object_sub_path) |path| .{ + .root_dir = directory, + .sub_path = if (fs.path.dirname(self.base.emit.sub_path)) |dirname| + try fs.path.join(arena, &.{ dirname, path }) + else + path, } else null; // --verbose-link @@ -758,7 +757,7 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod if (self.base.isStaticLib()) return relocatable.flushStaticLib(self, comp, module_obj_path); if (self.base.isObject()) return relocatable.flushObject(self, comp, module_obj_path); - const csu = try CsuObjects.init(arena, comp); + const csu = try comp.getCrtPaths(arena); // csu prelude if (csu.crt0) |path| try parseObjectReportingFailure(self, path); @@ -790,23 +789,22 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod if (comp.libc_static_lib) |lib| try parseCrtFileReportingFailure(self, lib); } - var system_libs = std.ArrayList(SystemLib).init(arena); - - try system_libs.ensureUnusedCapacity(comp.system_libs.values().len); for (comp.system_libs.values()) |lib_info| { - system_libs.appendAssumeCapacity(.{ .needed = lib_info.needed, .path = lib_info.path.? }); + try self.parseLibraryReportingFailure(.{ + .needed = lib_info.needed, + .path = lib_info.path.?, + }, false); } // libc++ dep if (comp.config.link_libcpp) { - try system_libs.ensureUnusedCapacity(2); - system_libs.appendAssumeCapacity(.{ .path = comp.libcxxabi_static_lib.?.full_object_path }); - system_libs.appendAssumeCapacity(.{ .path = comp.libcxx_static_lib.?.full_object_path }); + try self.parseLibraryReportingFailure(.{ .path = comp.libcxxabi_static_lib.?.full_object_path }, false); + try self.parseLibraryReportingFailure(.{ .path = comp.libcxx_static_lib.?.full_object_path }, false); } // libunwind dep if (comp.config.link_libunwind) { - try system_libs.append(.{ .path = comp.libunwind_static_lib.?.full_object_path }); + try self.parseLibraryReportingFailure(.{ .path = comp.libunwind_static_lib.?.full_object_path }, false); } // libc dep @@ -814,7 +812,6 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod if (comp.config.link_libc) { if (comp.libc_installation) |lc| { const flags = target_util.libcFullLinkFlags(target); - try system_libs.ensureUnusedCapacity(flags.len); var test_path = std.ArrayList(u8).init(arena); var checked_paths = std.ArrayList([]const u8).init(arena); @@ -840,39 +837,34 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod continue; } - const resolved_path = try arena.dupe(u8, test_path.items); - system_libs.appendAssumeCapacity(.{ .path = resolved_path }); + const resolved_path = Path.initCwd(try arena.dupe(u8, test_path.items)); + try self.parseLibraryReportingFailure(.{ .path = resolved_path }, false); } } else if (target.isGnuLibC()) { - try system_libs.ensureUnusedCapacity(glibc.libs.len + 1); for (glibc.libs) |lib| { if (lib.removed_in) |rem_in| { if (target.os.version_range.linux.glibc.order(rem_in) != .lt) continue; } - const lib_path = try std.fmt.allocPrint(arena, "{s}{c}lib{s}.so.{d}", .{ + const lib_path = Path.initCwd(try std.fmt.allocPrint(arena, "{s}{c}lib{s}.so.{d}", .{ comp.glibc_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover, - }); - system_libs.appendAssumeCapacity(.{ .path = lib_path }); + })); + try self.parseLibraryReportingFailure(.{ .path = lib_path }, false); } - system_libs.appendAssumeCapacity(.{ + try self.parseLibraryReportingFailure(.{ .path = try comp.get_libc_crt_file(arena, "libc_nonshared.a"), - }); + }, false); } else if (target.isMusl()) { const path = try comp.get_libc_crt_file(arena, switch (link_mode) { .static => "libc.a", .dynamic => "libc.so", }); - try system_libs.append(.{ .path = path }); + try self.parseLibraryReportingFailure(.{ .path = path }, false); } else { comp.link_error_flags.missing_libc = true; } } - for (system_libs.items) |lib| { - try self.parseLibraryReportingFailure(lib, false); - } - // Finally, as the last input objects we add compiler_rt and CSU postlude (if any). // compiler-rt. Since compiler_rt exports symbols like `memset`, it needs @@ -1066,10 +1058,10 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void { } } else null; - const csu = try CsuObjects.init(arena, comp); + const csu = try comp.getCrtPaths(arena); const compiler_rt_path: ?[]const u8 = blk: { - if (comp.compiler_rt_lib) |x| break :blk x.full_object_path; - if (comp.compiler_rt_obj) |x| break :blk x.full_object_path; + if (comp.compiler_rt_lib) |x| break :blk try x.full_object_path.toString(arena); + if (comp.compiler_rt_obj) |x| break :blk try x.full_object_path.toString(arena); break :blk null; }; @@ -1092,11 +1084,11 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void { if (self.base.isRelocatable()) { for (comp.objects) |obj| { - try argv.append(obj.path); + try argv.append(try obj.path.toString(arena)); } for (comp.c_object_table.keys()) |key| { - try argv.append(key.status.success.object_path); + try argv.append(try key.status.success.object_path.toString(arena)); } if (module_obj_path) |p| { @@ -1178,9 +1170,9 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void { } // csu prelude - if (csu.crt0) |v| try argv.append(v); - if (csu.crti) |v| try argv.append(v); - if (csu.crtbegin) |v| try argv.append(v); + if (csu.crt0) |path| try argv.append(try path.toString(arena)); + if (csu.crti) |path| try argv.append(try path.toString(arena)); + if (csu.crtbegin) |path| try argv.append(try path.toString(arena)); for (self.lib_dirs) |lib_dir| { try argv.append("-L"); @@ -1205,10 +1197,9 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void { } if (obj.loption) { - assert(obj.path[0] == ':'); try argv.append("-l"); } - try argv.append(obj.path); + try argv.append(try obj.path.toString(arena)); } if (whole_archive) { try argv.append("-no-whole-archive"); @@ -1216,7 +1207,7 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void { } for (comp.c_object_table.keys()) |key| { - try argv.append(key.status.success.object_path); + try argv.append(try key.status.success.object_path.toString(arena)); } if (module_obj_path) |p| { @@ -1224,17 +1215,17 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void { } if (comp.config.any_sanitize_thread) { - try argv.append(comp.tsan_lib.?.full_object_path); + try argv.append(try comp.tsan_lib.?.full_object_path.toString(arena)); } if (comp.config.any_fuzz) { - try argv.append(comp.fuzzer_lib.?.full_object_path); + try argv.append(try comp.fuzzer_lib.?.full_object_path.toString(arena)); } // libc if (!comp.skip_linker_dependencies and !comp.config.link_libc) { if (comp.libc_static_lib) |lib| { - try argv.append(lib.full_object_path); + try argv.append(try lib.full_object_path.toString(arena)); } } @@ -1258,7 +1249,7 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void { as_needed = true; }, } - argv.appendAssumeCapacity(lib_info.path.?); + argv.appendAssumeCapacity(try lib_info.path.?.toString(arena)); } if (!as_needed) { @@ -1268,13 +1259,13 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void { // libc++ dep if (comp.config.link_libcpp) { - try argv.append(comp.libcxxabi_static_lib.?.full_object_path); - try argv.append(comp.libcxx_static_lib.?.full_object_path); + try argv.append(try comp.libcxxabi_static_lib.?.full_object_path.toString(arena)); + try argv.append(try comp.libcxx_static_lib.?.full_object_path.toString(arena)); } // libunwind dep if (comp.config.link_libunwind) { - try argv.append(comp.libunwind_static_lib.?.full_object_path); + try argv.append(try comp.libunwind_static_lib.?.full_object_path.toString(arena)); } // libc dep @@ -1295,9 +1286,9 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void { }); try argv.append(lib_path); } - try argv.append(try comp.get_libc_crt_file(arena, "libc_nonshared.a")); + try argv.append(try comp.crtFileAsString(arena, "libc_nonshared.a")); } else if (target.isMusl()) { - try argv.append(try comp.get_libc_crt_file(arena, switch (link_mode) { + try argv.append(try comp.crtFileAsString(arena, switch (link_mode) { .static => "libc.a", .dynamic => "libc.so", })); @@ -1310,8 +1301,8 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void { } // crt postlude - if (csu.crtend) |v| try argv.append(v); - if (csu.crtn) |v| try argv.append(v); + if (csu.crtend) |path| try argv.append(try path.toString(arena)); + if (csu.crtn) |path| try argv.append(try path.toString(arena)); } Compilation.dump_argv(argv.items); @@ -1331,7 +1322,7 @@ pub const ParseError = error{ UnknownFileType, } || LdScript.Error || fs.Dir.AccessError || fs.File.SeekError || fs.File.OpenError || fs.File.ReadError; -fn parseCrtFileReportingFailure(self: *Elf, crt_file: Compilation.CRTFile) error{OutOfMemory}!void { +fn parseCrtFileReportingFailure(self: *Elf, crt_file: Compilation.CrtFile) error{OutOfMemory}!void { if (crt_file.isObject()) { try parseObjectReportingFailure(self, crt_file.full_object_path); } else { @@ -1339,7 +1330,7 @@ fn parseCrtFileReportingFailure(self: *Elf, crt_file: Compilation.CRTFile) error } } -pub fn parseObjectReportingFailure(self: *Elf, path: []const u8) error{OutOfMemory}!void { +pub fn parseObjectReportingFailure(self: *Elf, path: Path) error{OutOfMemory}!void { self.parseObject(path) catch |err| switch (err) { error.LinkFailure => return, // already reported error.OutOfMemory => return error.OutOfMemory, @@ -1367,17 +1358,20 @@ fn parseLibrary(self: *Elf, lib: SystemLib, must_link: bool) ParseError!void { } } -fn parseObject(self: *Elf, path: []const u8) ParseError!void { +fn parseObject(self: *Elf, path: Path) ParseError!void { const tracy = trace(@src()); defer tracy.end(); const gpa = self.base.comp.gpa; - const handle = try fs.cwd().openFile(path, .{}); + const handle = try path.root_dir.handle.openFile(path.sub_path, .{}); const fh = try self.addFileHandle(handle); - const index = @as(File.Index, @intCast(try self.files.addOne(gpa))); + const index: File.Index = @intCast(try self.files.addOne(gpa)); self.files.set(index, .{ .object = .{ - .path = try gpa.dupe(u8, path), + .path = .{ + .root_dir = path.root_dir, + .sub_path = try gpa.dupe(u8, path.sub_path), + }, .file_handle = fh, .index = index, } }); @@ -1387,15 +1381,15 @@ fn parseObject(self: *Elf, path: []const u8) ParseError!void { try object.parse(self); } -fn parseArchive(self: *Elf, path: []const u8, must_link: bool) ParseError!void { +fn parseArchive(self: *Elf, path: Path, must_link: bool) ParseError!void { const tracy = trace(@src()); defer tracy.end(); const gpa = self.base.comp.gpa; - const handle = try fs.cwd().openFile(path, .{}); + const handle = try path.root_dir.handle.openFile(path.sub_path, .{}); const fh = try self.addFileHandle(handle); - var archive = Archive{}; + var archive: Archive = .{}; defer archive.deinit(gpa); try archive.parse(self, path, fh); @@ -1403,7 +1397,7 @@ fn parseArchive(self: *Elf, path: []const u8, must_link: bool) ParseError!void { defer gpa.free(objects); for (objects) |extracted| { - const index = @as(File.Index, @intCast(try self.files.addOne(gpa))); + const index: File.Index = @intCast(try self.files.addOne(gpa)); self.files.set(index, .{ .object = extracted }); const object = &self.files.items(.data)[index].object; object.index = index; @@ -1418,12 +1412,15 @@ fn parseSharedObject(self: *Elf, lib: SystemLib) ParseError!void { defer tracy.end(); const gpa = self.base.comp.gpa; - const handle = try fs.cwd().openFile(lib.path, .{}); + const handle = try lib.path.root_dir.handle.openFile(lib.path.sub_path, .{}); defer handle.close(); const index = @as(File.Index, @intCast(try self.files.addOne(gpa))); self.files.set(index, .{ .shared_object = .{ - .path = try gpa.dupe(u8, lib.path), + .path = .{ + .root_dir = lib.path.root_dir, + .sub_path = try gpa.dupe(u8, lib.path.sub_path), + }, .index = index, .needed = lib.needed, .alive = lib.needed, @@ -1439,12 +1436,12 @@ fn parseLdScript(self: *Elf, lib: SystemLib) ParseError!void { defer tracy.end(); const gpa = self.base.comp.gpa; - const in_file = try fs.cwd().openFile(lib.path, .{}); + const in_file = try lib.path.root_dir.handle.openFile(lib.path.sub_path, .{}); defer in_file.close(); const data = try in_file.readToEndAlloc(gpa, std.math.maxInt(u32)); defer gpa.free(data); - var script = LdScript{ .path = lib.path }; + var script: LdScript = .{ .path = lib.path }; defer script.deinit(gpa); try script.parse(data, self); @@ -1455,12 +1452,12 @@ fn parseLdScript(self: *Elf, lib: SystemLib) ParseError!void { var test_path = std.ArrayList(u8).init(arena); var checked_paths = std.ArrayList([]const u8).init(arena); - for (script.args.items) |scr_obj| { + for (script.args.items) |script_arg| { checked_paths.clearRetainingCapacity(); success: { - if (mem.startsWith(u8, scr_obj.path, "-l")) { - const lib_name = scr_obj.path["-l".len..]; + if (mem.startsWith(u8, script_arg.path, "-l")) { + const lib_name = script_arg.path["-l".len..]; // TODO I think technically we should re-use the mechanism used by the frontend here. // Maybe we should hoist search-strategy all the way here? @@ -1474,33 +1471,30 @@ fn parseLdScript(self: *Elf, lib: SystemLib) ParseError!void { } } else { var buffer: [fs.max_path_bytes]u8 = undefined; - if (fs.realpath(scr_obj.path, &buffer)) |path| { + if (fs.realpath(script_arg.path, &buffer)) |path| { test_path.clearRetainingCapacity(); try test_path.writer().writeAll(path); break :success; } else |_| {} - try checked_paths.append(try arena.dupe(u8, scr_obj.path)); + try checked_paths.append(try arena.dupe(u8, script_arg.path)); for (self.lib_dirs) |lib_dir| { - if (try self.accessLibPath(arena, &test_path, &checked_paths, lib_dir, scr_obj.path, null)) + if (try self.accessLibPath(arena, &test_path, &checked_paths, lib_dir, script_arg.path, null)) break :success; } } try self.reportMissingLibraryError( checked_paths.items, - "missing library dependency: GNU ld script '{s}' requires '{s}', but file not found", - .{ - lib.path, - scr_obj.path, - }, + "missing library dependency: GNU ld script '{}' requires '{s}', but file not found", + .{ @as(Path, lib.path), script_arg.path }, ); continue; } - const full_path = test_path.items; + const full_path = Path.initCwd(test_path.items); self.parseLibrary(.{ - .needed = scr_obj.needed, + .needed = script_arg.needed, .path = full_path, }, false) catch |err| switch (err) { error.LinkFailure => continue, // already reported @@ -1841,7 +1835,7 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s const have_dynamic_linker = comp.config.link_libc and link_mode == .dynamic and is_exe_or_dyn_lib; const target = self.getTarget(); - const compiler_rt_path: ?[]const u8 = blk: { + const compiler_rt_path: ?Path = blk: { if (comp.compiler_rt_lib) |x| break :blk x.full_object_path; if (comp.compiler_rt_obj) |x| break :blk x.full_object_path; break :blk null; @@ -1875,17 +1869,17 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s man.hash.add(self.allow_undefined_version); man.hash.addOptional(self.enable_new_dtags); for (comp.objects) |obj| { - _ = try man.addFile(obj.path, null); + _ = try man.addFilePath(obj.path, null); man.hash.add(obj.must_link); man.hash.add(obj.loption); } for (comp.c_object_table.keys()) |key| { - _ = try man.addFile(key.status.success.object_path, null); + _ = try man.addFilePath(key.status.success.object_path, null); } try man.addOptionalFile(module_obj_path); - try man.addOptionalFile(compiler_rt_path); - try man.addOptionalFile(if (comp.tsan_lib) |l| l.full_object_path else null); - try man.addOptionalFile(if (comp.fuzzer_lib) |l| l.full_object_path else null); + try man.addOptionalFilePath(compiler_rt_path); + try man.addOptionalFilePath(if (comp.tsan_lib) |l| l.full_object_path else null); + try man.addOptionalFilePath(if (comp.fuzzer_lib) |l| l.full_object_path else null); // We can skip hashing libc and libc++ components that we are in charge of building from Zig // installation sources because they are always a product of the compiler version + target information. @@ -1982,17 +1976,19 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s break :blk comp.c_object_table.keys()[0].status.success.object_path; if (module_obj_path) |p| - break :blk p; + break :blk Path.initCwd(p); // TODO I think this is unreachable. Audit this situation when solving the above TODO // regarding eliding redundant object -> object transformations. return error.NoObjectsToLink; }; - // This can happen when using --enable-cache and using the stage1 backend. In this case - // we can skip the file copy. - if (!mem.eql(u8, the_object_path, full_out_path)) { - try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{}); - } + try std.fs.Dir.copyFile( + the_object_path.root_dir.handle, + the_object_path.sub_path, + directory.handle, + self.base.emit.sub_path, + .{}, + ); } else { // Create an LLD command line and invoke it. var argv = std.ArrayList([]const u8).init(gpa); @@ -2177,10 +2173,10 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s try argv.append(full_out_path); // csu prelude - const csu = try CsuObjects.init(arena, comp); - if (csu.crt0) |v| try argv.append(v); - if (csu.crti) |v| try argv.append(v); - if (csu.crtbegin) |v| try argv.append(v); + const csu = try comp.getCrtPaths(arena); + if (csu.crt0) |p| try argv.append(try p.toString(arena)); + if (csu.crti) |p| try argv.append(try p.toString(arena)); + if (csu.crtbegin) |p| try argv.append(try p.toString(arena)); for (self.rpath_table.keys()) |rpath| { try argv.appendSlice(&.{ "-rpath", rpath }); @@ -2244,10 +2240,10 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s } if (obj.loption) { - assert(obj.path[0] == ':'); + assert(obj.path.sub_path[0] == ':'); try argv.append("-l"); } - try argv.append(obj.path); + try argv.append(try obj.path.toString(arena)); } if (whole_archive) { try argv.append("-no-whole-archive"); @@ -2255,7 +2251,7 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s } for (comp.c_object_table.keys()) |key| { - try argv.append(key.status.success.object_path); + try argv.append(try key.status.success.object_path.toString(arena)); } if (module_obj_path) |p| { @@ -2264,12 +2260,12 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s if (comp.tsan_lib) |lib| { assert(comp.config.any_sanitize_thread); - try argv.append(lib.full_object_path); + try argv.append(try lib.full_object_path.toString(arena)); } if (comp.fuzzer_lib) |lib| { assert(comp.config.any_fuzz); - try argv.append(lib.full_object_path); + try argv.append(try lib.full_object_path.toString(arena)); } // libc @@ -2278,7 +2274,7 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s !comp.config.link_libc) { if (comp.libc_static_lib) |lib| { - try argv.append(lib.full_object_path); + try argv.append(try lib.full_object_path.toString(arena)); } } @@ -2311,7 +2307,7 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s // libraries and not static libraries (the check for that needs to be earlier), // but they could be full paths to .so files, in which case we // want to avoid prepending "-l". - argv.appendAssumeCapacity(lib_info.path.?); + argv.appendAssumeCapacity(try lib_info.path.?.toString(arena)); } if (!as_needed) { @@ -2321,13 +2317,13 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s // libc++ dep if (comp.config.link_libcpp) { - try argv.append(comp.libcxxabi_static_lib.?.full_object_path); - try argv.append(comp.libcxx_static_lib.?.full_object_path); + try argv.append(try comp.libcxxabi_static_lib.?.full_object_path.toString(arena)); + try argv.append(try comp.libcxx_static_lib.?.full_object_path.toString(arena)); } // libunwind dep if (comp.config.link_libunwind) { - try argv.append(comp.libunwind_static_lib.?.full_object_path); + try argv.append(try comp.libunwind_static_lib.?.full_object_path.toString(arena)); } // libc dep @@ -2349,9 +2345,9 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s }); try argv.append(lib_path); } - try argv.append(try comp.get_libc_crt_file(arena, "libc_nonshared.a")); + try argv.append(try comp.crtFileAsString(arena, "libc_nonshared.a")); } else if (target.isMusl()) { - try argv.append(try comp.get_libc_crt_file(arena, switch (link_mode) { + try argv.append(try comp.crtFileAsString(arena, switch (link_mode) { .static => "libc.a", .dynamic => "libc.so", })); @@ -2365,12 +2361,12 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s // to be after the shared libraries, so they are picked up from the shared // libraries, not libcompiler_rt. if (compiler_rt_path) |p| { - try argv.append(p); + try argv.append(try p.toString(arena)); } // crt postlude - if (csu.crtend) |v| try argv.append(v); - if (csu.crtn) |v| try argv.append(v); + if (csu.crtend) |p| try argv.append(try p.toString(arena)); + if (csu.crtn) |p| try argv.append(try p.toString(arena)); if (self.base.allow_shlib_undefined) { try argv.append("--allow-shlib-undefined"); @@ -3183,8 +3179,9 @@ fn sortInitFini(self: *Elf) !void { const object = atom_ptr.file(self).?.object; const priority = blk: { if (is_ctor_dtor) { - if (mem.indexOf(u8, object.path, "crtbegin") != null) break :blk std.math.minInt(i32); - if (mem.indexOf(u8, object.path, "crtend") != null) break :blk std.math.maxInt(i32); + const basename = object.path.basename(); + if (mem.eql(u8, basename, "crtbegin.o")) break :blk std.math.minInt(i32); + if (mem.eql(u8, basename, "crtend.o")) break :blk std.math.maxInt(i32); } const default: i32 = if (is_ctor_dtor) -1 else std.math.maxInt(i32); const name = atom_ptr.name(self); @@ -4472,210 +4469,6 @@ pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) { return actual_size +| (actual_size / ideal_factor); } -// Provide a blueprint of csu (c-runtime startup) objects for supported -// link modes. -// -// This is for cross-mode targets only. For host-mode targets the system -// compiler can be probed to produce a robust blueprint. -// -// Targets requiring a libc for which zig does not bundle a libc are -// host-mode targets. Unfortunately, host-mode probes are not yet -// implemented. For now the data is hard-coded here. Such targets are -// { freebsd, netbsd, openbsd, dragonfly }. -const CsuObjects = struct { - crt0: ?[]const u8 = null, - crti: ?[]const u8 = null, - crtbegin: ?[]const u8 = null, - crtend: ?[]const u8 = null, - crtn: ?[]const u8 = null, - - const InitArgs = struct {}; - - fn init(arena: Allocator, comp: *const Compilation) !CsuObjects { - // crt objects are only required for libc. - if (!comp.config.link_libc) return .{}; - - var result: CsuObjects = .{}; - - // Flatten crt cases. - const mode: enum { - dynamic_lib, - dynamic_exe, - dynamic_pie, - static_exe, - static_pie, - } = switch (comp.config.output_mode) { - .Obj => return CsuObjects{}, - .Lib => switch (comp.config.link_mode) { - .dynamic => .dynamic_lib, - .static => return CsuObjects{}, - }, - .Exe => switch (comp.config.link_mode) { - .dynamic => if (comp.config.pie) .dynamic_pie else .dynamic_exe, - .static => if (comp.config.pie) .static_pie else .static_exe, - }, - }; - - const target = comp.root_mod.resolved_target.result; - - if (target.isAndroid()) { - switch (mode) { - // zig fmt: off - .dynamic_lib => result.set( null, null, "crtbegin_so.o", "crtend_so.o", null ), - .dynamic_exe, - .dynamic_pie => result.set( null, null, "crtbegin_dynamic.o", "crtend_android.o", null ), - .static_exe, - .static_pie => result.set( null, null, "crtbegin_static.o", "crtend_android.o", null ), - // zig fmt: on - } - } else { - switch (target.os.tag) { - .linux => { - switch (mode) { - // zig fmt: off - .dynamic_lib => result.set( null, "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ), - .dynamic_exe => result.set( "crt1.o", "crti.o", "crtbegin.o", "crtend.o", "crtn.o" ), - .dynamic_pie => result.set( "Scrt1.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ), - .static_exe => result.set( "crt1.o", "crti.o", "crtbeginT.o", "crtend.o", "crtn.o" ), - .static_pie => result.set( "rcrt1.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ), - // zig fmt: on - } - if (comp.libc_installation) |_| { - // hosted-glibc provides crtbegin/end objects in platform/compiler-specific dirs - // and they are not known at comptime. For now null-out crtbegin/end objects; - // there is no feature loss, zig has never linked those objects in before. - result.crtbegin = null; - result.crtend = null; - } else { - // Bundled glibc only has Scrt1.o . - if (result.crt0 != null and target.isGnuLibC()) result.crt0 = "Scrt1.o"; - } - }, - .dragonfly => switch (mode) { - // zig fmt: off - .dynamic_lib => result.set( null, "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ), - .dynamic_exe => result.set( "crt1.o", "crti.o", "crtbegin.o", "crtend.o", "crtn.o" ), - .dynamic_pie => result.set( "Scrt1.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ), - .static_exe => result.set( "crt1.o", "crti.o", "crtbegin.o", "crtend.o", "crtn.o" ), - .static_pie => result.set( "Scrt1.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ), - // zig fmt: on - }, - .freebsd => switch (mode) { - // zig fmt: off - .dynamic_lib => result.set( null, "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ), - .dynamic_exe => result.set( "crt1.o", "crti.o", "crtbegin.o", "crtend.o", "crtn.o" ), - .dynamic_pie => result.set( "Scrt1.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ), - .static_exe => result.set( "crt1.o", "crti.o", "crtbeginT.o", "crtend.o", "crtn.o" ), - .static_pie => result.set( "Scrt1.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ), - // zig fmt: on - }, - .netbsd => switch (mode) { - // zig fmt: off - .dynamic_lib => result.set( null, "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ), - .dynamic_exe => result.set( "crt0.o", "crti.o", "crtbegin.o", "crtend.o", "crtn.o" ), - .dynamic_pie => result.set( "crt0.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ), - .static_exe => result.set( "crt0.o", "crti.o", "crtbeginT.o", "crtend.o", "crtn.o" ), - .static_pie => result.set( "crt0.o", "crti.o", "crtbeginT.o", "crtendS.o", "crtn.o" ), - // zig fmt: on - }, - .openbsd => switch (mode) { - // zig fmt: off - .dynamic_lib => result.set( null, null, "crtbeginS.o", "crtendS.o", null ), - .dynamic_exe, - .dynamic_pie => result.set( "crt0.o", null, "crtbegin.o", "crtend.o", null ), - .static_exe, - .static_pie => result.set( "rcrt0.o", null, "crtbegin.o", "crtend.o", null ), - // zig fmt: on - }, - .haiku => switch (mode) { - // zig fmt: off - .dynamic_lib => result.set( null, "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ), - .dynamic_exe => result.set( "start_dyn.o", "crti.o", "crtbegin.o", "crtend.o", "crtn.o" ), - .dynamic_pie => result.set( "start_dyn.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ), - .static_exe => result.set( "start_dyn.o", "crti.o", "crtbegin.o", "crtend.o", "crtn.o" ), - .static_pie => result.set( "start_dyn.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ), - // zig fmt: on - }, - .solaris, .illumos => switch (mode) { - // zig fmt: off - .dynamic_lib => result.set( null, "crti.o", null, null, "crtn.o" ), - .dynamic_exe, - .dynamic_pie => result.set( "crt1.o", "crti.o", null, null, "crtn.o" ), - .static_exe, - .static_pie => result.set( null, null, null, null, null ), - // zig fmt: on - }, - else => {}, - } - } - - // Convert each object to a full pathname. - if (comp.libc_installation) |lci| { - const crt_dir_path = lci.crt_dir orelse return error.LibCInstallationMissingCRTDir; - switch (target.os.tag) { - .dragonfly => { - if (result.crt0) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, obj.* }); - if (result.crti) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, obj.* }); - if (result.crtn) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, obj.* }); - - var gccv: []const u8 = undefined; - if (target.os.version_range.semver.isAtLeast(.{ .major = 5, .minor = 4, .patch = 0 }) orelse true) { - gccv = "gcc80"; - } else { - gccv = "gcc54"; - } - - if (result.crtbegin) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, gccv, obj.* }); - if (result.crtend) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, gccv, obj.* }); - }, - .haiku => { - const gcc_dir_path = lci.gcc_dir orelse return error.LibCInstallationMissingCRTDir; - if (result.crt0) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, obj.* }); - if (result.crti) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, obj.* }); - if (result.crtn) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, obj.* }); - - if (result.crtbegin) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ gcc_dir_path, obj.* }); - if (result.crtend) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ gcc_dir_path, obj.* }); - }, - else => { - inline for (std.meta.fields(@TypeOf(result))) |f| { - if (@field(result, f.name)) |*obj| { - obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, obj.* }); - } - } - }, - } - } else { - inline for (std.meta.fields(@TypeOf(result))) |f| { - if (@field(result, f.name)) |*obj| { - if (comp.crt_files.get(obj.*)) |crtf| { - obj.* = crtf.full_object_path; - } else { - @field(result, f.name) = null; - } - } - } - } - - return result; - } - - fn set( - self: *CsuObjects, - crt0: ?[]const u8, - crti: ?[]const u8, - crtbegin: ?[]const u8, - crtend: ?[]const u8, - crtn: ?[]const u8, - ) void { - self.crt0 = crt0; - self.crti = crti; - self.crtbegin = crtbegin; - self.crtend = crtend; - self.crtn = crtn; - } -}; - /// If a target compiles other output modes as dynamic libraries, /// this function returns true for those too. pub fn isEffectivelyDynLib(self: Elf) bool { @@ -5089,13 +4882,13 @@ fn reportUnsupportedCpuArch(self: *Elf) error{OutOfMemory}!void { pub fn addParseError( self: *Elf, - path: []const u8, + path: Path, comptime format: []const u8, args: anytype, ) error{OutOfMemory}!void { var err = try self.base.addErrorWithNotes(1); try err.addMsg(format, args); - try err.addNote("while parsing {s}", .{path}); + try err.addNote("while parsing {}", .{path}); } pub fn addFileError( @@ -5121,7 +4914,7 @@ pub fn failFile( pub fn failParse( self: *Elf, - path: []const u8, + path: Path, comptime format: []const u8, args: anytype, ) error{ OutOfMemory, LinkFailure } { @@ -5274,7 +5067,7 @@ fn fmtDumpState( _ = options; if (self.zigObjectPtr()) |zig_object| { - try writer.print("zig_object({d}) : {s}\n", .{ zig_object.index, zig_object.path }); + try writer.print("zig_object({d}) : {s}\n", .{ zig_object.index, zig_object.basename }); try writer.print("{}{}", .{ zig_object.fmtAtoms(self), zig_object.fmtSymtab(self), @@ -5299,7 +5092,7 @@ fn fmtDumpState( for (self.shared_objects.items) |index| { const shared_object = self.file(index).?.shared_object; try writer.print("shared_object({d}) : ", .{index}); - try writer.print("{s}", .{shared_object.path}); + try writer.print("{}", .{shared_object.path}); try writer.print(" : needed({})", .{shared_object.needed}); if (!shared_object.alive) try writer.writeAll(" : [*]"); try writer.writeByte('\n'); @@ -5482,7 +5275,7 @@ pub const null_shdr = elf.Elf64_Shdr{ pub const SystemLib = struct { needed: bool = false, - path: []const u8, + path: Path, }; pub const Ref = struct { diff --git a/src/link/Elf/Archive.zig b/src/link/Elf/Archive.zig index 1314328d5b..7cc454f10c 100644 --- a/src/link/Elf/Archive.zig +++ b/src/link/Elf/Archive.zig @@ -1,8 +1,8 @@ objects: std.ArrayListUnmanaged(Object) = .empty, strtab: std.ArrayListUnmanaged(u8) = .empty, -pub fn isArchive(path: []const u8) !bool { - const file = try std.fs.cwd().openFile(path, .{}); +pub fn isArchive(path: Path) !bool { + const file = try path.root_dir.handle.openFile(path.sub_path, .{}); defer file.close(); const reader = file.reader(); const magic = reader.readBytesNoEof(elf.ARMAG.len) catch return false; @@ -15,7 +15,7 @@ pub fn deinit(self: *Archive, allocator: Allocator) void { self.strtab.deinit(allocator); } -pub fn parse(self: *Archive, elf_file: *Elf, path: []const u8, handle_index: File.HandleIndex) !void { +pub fn parse(self: *Archive, elf_file: *Elf, path: Path, handle_index: File.HandleIndex) !void { const comp = elf_file.base.comp; const gpa = comp.gpa; const handle = elf_file.fileHandle(handle_index); @@ -59,19 +59,24 @@ pub fn parse(self: *Archive, elf_file: *Elf, path: []const u8, handle_index: Fil else unreachable; - const object = Object{ + const object: Object = .{ .archive = .{ - .path = try gpa.dupe(u8, path), + .path = .{ + .root_dir = path.root_dir, + .sub_path = try gpa.dupe(u8, path.sub_path), + }, .offset = pos, .size = obj_size, }, - .path = try gpa.dupe(u8, name), + .path = Path.initCwd(try gpa.dupe(u8, name)), .file_handle = handle_index, .index = undefined, .alive = false, }; - log.debug("extracting object '{s}' from archive '{s}'", .{ object.path, path }); + log.debug("extracting object '{}' from archive '{}'", .{ + @as(Path, object.path), @as(Path, path), + }); try self.objects.append(gpa, object); } @@ -292,6 +297,7 @@ const elf = std.elf; const fs = std.fs; const log = std.log.scoped(.link); const mem = std.mem; +const Path = std.Build.Cache.Path; const Allocator = mem.Allocator; const Archive = @This(); diff --git a/src/link/Elf/LdScript.zig b/src/link/Elf/LdScript.zig index 6735430a42..7bb1f62104 100644 --- a/src/link/Elf/LdScript.zig +++ b/src/link/Elf/LdScript.zig @@ -1,6 +1,11 @@ -path: []const u8, +path: Path, cpu_arch: ?std.Target.Cpu.Arch = null, -args: std.ArrayListUnmanaged(Elf.SystemLib) = .empty, +args: std.ArrayListUnmanaged(Arg) = .empty, + +pub const Arg = struct { + needed: bool = false, + path: []const u8, +}; pub fn deinit(scr: *LdScript, allocator: Allocator) void { scr.args.deinit(allocator); @@ -47,7 +52,7 @@ pub fn parse(scr: *LdScript, data: []const u8, elf_file: *Elf) Error!void { var it = TokenIterator{ .tokens = tokens.items }; var parser = Parser{ .source = data, .it = &it }; - var args = std.ArrayList(Elf.SystemLib).init(gpa); + var args = std.ArrayList(Arg).init(gpa); scr.doParse(.{ .parser = &parser, .args = &args, @@ -70,7 +75,7 @@ pub fn parse(scr: *LdScript, data: []const u8, elf_file: *Elf) Error!void { fn doParse(scr: *LdScript, ctx: struct { parser: *Parser, - args: *std.ArrayList(Elf.SystemLib), + args: *std.ArrayList(Arg), }) !void { while (true) { ctx.parser.skipAny(&.{ .comment, .new_line }); @@ -142,7 +147,7 @@ const Parser = struct { return error.UnknownCpuArch; } - fn group(p: *Parser, args: *std.ArrayList(Elf.SystemLib)) !void { + fn group(p: *Parser, args: *std.ArrayList(Arg)) !void { if (!p.skip(&.{.lparen})) return error.UnexpectedToken; while (true) { @@ -162,7 +167,7 @@ const Parser = struct { _ = try p.require(.rparen); } - fn asNeeded(p: *Parser, args: *std.ArrayList(Elf.SystemLib)) !void { + fn asNeeded(p: *Parser, args: *std.ArrayList(Arg)) !void { if (!p.skip(&.{.lparen})) return error.UnexpectedToken; while (p.maybe(.literal)) |tok_id| { @@ -239,7 +244,7 @@ const Token = struct { const Index = usize; - inline fn get(tok: Token, source: []const u8) []const u8 { + fn get(tok: Token, source: []const u8) []const u8 { return source[tok.start..tok.end]; } }; @@ -399,11 +404,11 @@ const TokenIterator = struct { return it.tokens[it.pos]; } - inline fn reset(it: *TokenIterator) void { + fn reset(it: *TokenIterator) void { it.pos = 0; } - inline fn seekTo(it: *TokenIterator, pos: Token.Index) void { + fn seekTo(it: *TokenIterator, pos: Token.Index) void { it.pos = pos; } @@ -416,7 +421,7 @@ const TokenIterator = struct { } } - inline fn get(it: *TokenIterator, pos: Token.Index) Token { + fn get(it: *TokenIterator, pos: Token.Index) Token { assert(pos < it.tokens.len); return it.tokens[pos]; } @@ -426,6 +431,7 @@ const LdScript = @This(); const std = @import("std"); const assert = std.debug.assert; +const Path = std.Build.Cache.Path; const Allocator = std.mem.Allocator; const Elf = @import("../Elf.zig"); diff --git a/src/link/Elf/Object.zig b/src/link/Elf/Object.zig index 96178ec6c5..23216020a6 100644 --- a/src/link/Elf/Object.zig +++ b/src/link/Elf/Object.zig @@ -1,5 +1,7 @@ archive: ?InArchive = null, -path: []const u8, +/// Archive files cannot contain subdirectories, so only the basename is needed +/// for output. However, the full path is kept for error reporting. +path: Path, file_handle: File.HandleIndex, index: File.Index, @@ -36,8 +38,8 @@ output_symtab_ctx: Elf.SymtabCtx = .{}, output_ar_state: Archive.ArState = .{}, pub fn deinit(self: *Object, allocator: Allocator) void { - if (self.archive) |*ar| allocator.free(ar.path); - allocator.free(self.path); + if (self.archive) |*ar| allocator.free(ar.path.sub_path); + allocator.free(self.path.sub_path); self.shdrs.deinit(allocator); self.symtab.deinit(allocator); self.strtab.deinit(allocator); @@ -474,8 +476,7 @@ pub fn scanRelocs(self: *Object, elf_file: *Elf, undefs: anytype) !void { if (sym.type(elf_file) != elf.STT_FUNC) // TODO convert into an error log.debug("{s}: {s}: CIE referencing external data reference", .{ - self.fmtPath(), - sym.name(elf_file), + self.fmtPath(), sym.name(elf_file), }); sym.flags.needs_plt = true; } @@ -996,7 +997,7 @@ pub fn updateArSize(self: *Object, elf_file: *Elf) !void { pub fn writeAr(self: Object, elf_file: *Elf, writer: anytype) !void { const size = std.math.cast(usize, self.output_ar_state.size) orelse return error.Overflow; const offset: u64 = if (self.archive) |ar| ar.offset else 0; - const name = self.path; + const name = std.fs.path.basename(self.path.sub_path); const hdr = Archive.setArHdr(.{ .name = if (name.len <= Archive.max_member_name_len) .{ .name = name } @@ -1489,15 +1490,14 @@ fn formatPath( _ = unused_fmt_string; _ = options; if (object.archive) |ar| { - try writer.writeAll(ar.path); - try writer.writeByte('('); - try writer.writeAll(object.path); - try writer.writeByte(')'); - } else try writer.writeAll(object.path); + try writer.print("{}({})", .{ ar.path, object.path }); + } else { + try writer.print("{}", .{object.path}); + } } const InArchive = struct { - path: []const u8, + path: Path, offset: u64, size: u32, }; @@ -1512,8 +1512,9 @@ const fs = std.fs; const log = std.log.scoped(.link); const math = std.math; const mem = std.mem; - +const Path = std.Build.Cache.Path; const Allocator = mem.Allocator; + const Archive = @import("Archive.zig"); const Atom = @import("Atom.zig"); const AtomList = @import("AtomList.zig"); diff --git a/src/link/Elf/SharedObject.zig b/src/link/Elf/SharedObject.zig index 581fba9560..7e6aabea38 100644 --- a/src/link/Elf/SharedObject.zig +++ b/src/link/Elf/SharedObject.zig @@ -1,4 +1,4 @@ -path: []const u8, +path: Path, index: File.Index, header: ?elf.Elf64_Ehdr = null, @@ -22,8 +22,8 @@ alive: bool, output_symtab_ctx: Elf.SymtabCtx = .{}, -pub fn isSharedObject(path: []const u8) !bool { - const file = try std.fs.cwd().openFile(path, .{}); +pub fn isSharedObject(path: Path) !bool { + const file = try path.root_dir.handle.openFile(path.sub_path, .{}); defer file.close(); const reader = file.reader(); const header = reader.readStruct(elf.Elf64_Ehdr) catch return false; @@ -34,7 +34,7 @@ pub fn isSharedObject(path: []const u8) !bool { } pub fn deinit(self: *SharedObject, allocator: Allocator) void { - allocator.free(self.path); + allocator.free(self.path.sub_path); self.shdrs.deinit(allocator); self.symtab.deinit(allocator); self.strtab.deinit(allocator); @@ -319,7 +319,7 @@ pub fn asFile(self: *SharedObject) File { fn verdefNum(self: *SharedObject) u32 { for (self.dynamic_table.items) |entry| switch (entry.d_tag) { - elf.DT_VERDEFNUM => return @as(u32, @intCast(entry.d_val)), + elf.DT_VERDEFNUM => return @intCast(entry.d_val), else => {}, }; return 0; @@ -327,10 +327,10 @@ fn verdefNum(self: *SharedObject) u32 { pub fn soname(self: *SharedObject) []const u8 { for (self.dynamic_table.items) |entry| switch (entry.d_tag) { - elf.DT_SONAME => return self.getString(@as(u32, @intCast(entry.d_val))), + elf.DT_SONAME => return self.getString(@intCast(entry.d_val)), else => {}, }; - return std.fs.path.basename(self.path); + return std.fs.path.basename(self.path.sub_path); } pub fn initSymbolAliases(self: *SharedObject, elf_file: *Elf) !void { @@ -508,6 +508,7 @@ const assert = std.debug.assert; const elf = std.elf; const log = std.log.scoped(.elf); const mem = std.mem; +const Path = std.Build.Cache.Path; const Allocator = mem.Allocator; const Elf = @import("../Elf.zig"); diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index 846bcac15c..54d15297d5 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -5,7 +5,7 @@ data: std.ArrayListUnmanaged(u8) = .empty, /// Externally owned memory. -path: []const u8, +basename: []const u8, index: File.Index, symtab: std.MultiArrayList(ElfSym) = .{}, @@ -88,7 +88,7 @@ pub fn init(self: *ZigObject, elf_file: *Elf, options: InitOptions) !void { try self.strtab.buffer.append(gpa, 0); { - const name_off = try self.strtab.insert(gpa, self.path); + const name_off = try self.strtab.insert(gpa, self.basename); const symbol_index = try self.newLocalSymbol(gpa, name_off); const sym = self.symbol(symbol_index); const esym = &self.symtab.items(.elf_sym)[sym.esym_index]; @@ -774,7 +774,7 @@ pub fn updateArSize(self: *ZigObject) void { } pub fn writeAr(self: ZigObject, writer: anytype) !void { - const name = self.path; + const name = self.basename; const hdr = Archive.setArHdr(.{ .name = if (name.len <= Archive.max_member_name_len) .{ .name = name } @@ -2384,9 +2384,9 @@ const relocation = @import("relocation.zig"); const target_util = @import("../../target.zig"); const trace = @import("../../tracy.zig").trace; const std = @import("std"); +const Allocator = std.mem.Allocator; const Air = @import("../../Air.zig"); -const Allocator = std.mem.Allocator; const Archive = @import("Archive.zig"); const Atom = @import("Atom.zig"); const Dwarf = @import("../Dwarf.zig"); diff --git a/src/link/Elf/file.zig b/src/link/Elf/file.zig index 740987feb2..6eb4c2201f 100644 --- a/src/link/Elf/file.zig +++ b/src/link/Elf/file.zig @@ -23,10 +23,10 @@ pub const File = union(enum) { _ = unused_fmt_string; _ = options; switch (file) { - .zig_object => |x| try writer.print("{s}", .{x.path}), + .zig_object => |zo| try writer.writeAll(zo.basename), .linker_defined => try writer.writeAll("(linker defined)"), .object => |x| try writer.print("{}", .{x.fmtPath()}), - .shared_object => |x| try writer.writeAll(x.path), + .shared_object => |x| try writer.print("{}", .{@as(Path, x.path)}), } } @@ -240,30 +240,31 @@ pub const File = union(enum) { return switch (file) { .zig_object => |x| x.updateArSymtab(ar_symtab, elf_file), .object => |x| x.updateArSymtab(ar_symtab, elf_file), - inline else => unreachable, + else => unreachable, }; } pub fn updateArStrtab(file: File, allocator: Allocator, ar_strtab: *Archive.ArStrtab) !void { - const path = switch (file) { - .zig_object => |x| x.path, - .object => |x| x.path, - inline else => unreachable, - }; - const state = switch (file) { - .zig_object => |x| &x.output_ar_state, - .object => |x| &x.output_ar_state, - inline else => unreachable, - }; - if (path.len <= Archive.max_member_name_len) return; - state.name_off = try ar_strtab.insert(allocator, path); + switch (file) { + .zig_object => |zo| { + const basename = zo.basename; + if (basename.len <= Archive.max_member_name_len) return; + zo.output_ar_state.name_off = try ar_strtab.insert(allocator, basename); + }, + .object => |o| { + const basename = std.fs.path.basename(o.path.sub_path); + if (basename.len <= Archive.max_member_name_len) return; + o.output_ar_state.name_off = try ar_strtab.insert(allocator, basename); + }, + else => unreachable, + } } pub fn updateArSize(file: File, elf_file: *Elf) !void { return switch (file) { .zig_object => |x| x.updateArSize(), .object => |x| x.updateArSize(elf_file), - inline else => unreachable, + else => unreachable, }; } @@ -271,7 +272,7 @@ pub const File = union(enum) { return switch (file) { .zig_object => |x| x.writeAr(writer), .object => |x| x.writeAr(elf_file, writer), - inline else => unreachable, + else => unreachable, }; } @@ -292,8 +293,9 @@ pub const File = union(enum) { const std = @import("std"); const elf = std.elf; const log = std.log.scoped(.link); - +const Path = std.Build.Cache.Path; const Allocator = std.mem.Allocator; + const Archive = @import("Archive.zig"); const Atom = @import("Atom.zig"); const Cie = @import("eh_frame.zig").Cie; diff --git a/src/link/Elf/relocatable.zig b/src/link/Elf/relocatable.zig index 8768c1d754..bdf0d378ff 100644 --- a/src/link/Elf/relocatable.zig +++ b/src/link/Elf/relocatable.zig @@ -1,8 +1,8 @@ -pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void { +pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation, module_obj_path: ?Path) link.File.FlushError!void { const gpa = comp.gpa; for (comp.objects) |obj| { - switch (Compilation.classifyFileExt(obj.path)) { + switch (Compilation.classifyFileExt(obj.path.sub_path)) { .object => try parseObjectStaticLibReportingFailure(elf_file, obj.path), .static_library => try parseArchiveStaticLibReportingFailure(elf_file, obj.path), else => try elf_file.addParseError(obj.path, "unrecognized file extension", .{}), @@ -140,7 +140,7 @@ pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]co if (elf_file.base.hasErrors()) return error.FlushFailure; } -pub fn flushObject(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void { +pub fn flushObject(elf_file: *Elf, comp: *Compilation, module_obj_path: ?Path) link.File.FlushError!void { for (comp.objects) |obj| { if (obj.isObject()) { try elf_file.parseObjectReportingFailure(obj.path); @@ -198,7 +198,7 @@ pub fn flushObject(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]const if (elf_file.base.hasErrors()) return error.FlushFailure; } -fn parseObjectStaticLibReportingFailure(elf_file: *Elf, path: []const u8) error{OutOfMemory}!void { +fn parseObjectStaticLibReportingFailure(elf_file: *Elf, path: Path) error{OutOfMemory}!void { parseObjectStaticLib(elf_file, path) catch |err| switch (err) { error.LinkFailure => return, error.OutOfMemory => return error.OutOfMemory, @@ -206,7 +206,7 @@ fn parseObjectStaticLibReportingFailure(elf_file: *Elf, path: []const u8) error{ }; } -fn parseArchiveStaticLibReportingFailure(elf_file: *Elf, path: []const u8) error{OutOfMemory}!void { +fn parseArchiveStaticLibReportingFailure(elf_file: *Elf, path: Path) error{OutOfMemory}!void { parseArchiveStaticLib(elf_file, path) catch |err| switch (err) { error.LinkFailure => return, error.OutOfMemory => return error.OutOfMemory, @@ -214,14 +214,17 @@ fn parseArchiveStaticLibReportingFailure(elf_file: *Elf, path: []const u8) error }; } -fn parseObjectStaticLib(elf_file: *Elf, path: []const u8) Elf.ParseError!void { +fn parseObjectStaticLib(elf_file: *Elf, path: Path) Elf.ParseError!void { const gpa = elf_file.base.comp.gpa; - const handle = try std.fs.cwd().openFile(path, .{}); + const handle = try path.root_dir.handle.openFile(path.sub_path, .{}); const fh = try elf_file.addFileHandle(handle); - const index = @as(File.Index, @intCast(try elf_file.files.addOne(gpa))); + const index: File.Index = @intCast(try elf_file.files.addOne(gpa)); elf_file.files.set(index, .{ .object = .{ - .path = try gpa.dupe(u8, path), + .path = .{ + .root_dir = path.root_dir, + .sub_path = try gpa.dupe(u8, path.sub_path), + }, .file_handle = fh, .index = index, } }); @@ -231,9 +234,9 @@ fn parseObjectStaticLib(elf_file: *Elf, path: []const u8) Elf.ParseError!void { try object.parseAr(elf_file); } -fn parseArchiveStaticLib(elf_file: *Elf, path: []const u8) Elf.ParseError!void { +fn parseArchiveStaticLib(elf_file: *Elf, path: Path) Elf.ParseError!void { const gpa = elf_file.base.comp.gpa; - const handle = try std.fs.cwd().openFile(path, .{}); + const handle = try path.root_dir.handle.openFile(path.sub_path, .{}); const fh = try elf_file.addFileHandle(handle); var archive = Archive{}; @@ -531,6 +534,7 @@ const log = std.log.scoped(.link); const math = std.math; const mem = std.mem; const state_log = std.log.scoped(.link_state); +const Path = std.Build.Cache.Path; const std = @import("std"); const Archive = @import("Archive.zig"); diff --git a/src/link/MachO.zig b/src/link/MachO.zig index ec766a69ac..38684e4d6a 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -144,14 +144,14 @@ hot_state: if (is_hot_update_compatible) HotUpdateState else struct {} = .{}, pub const Framework = struct { needed: bool = false, weak: bool = false, - path: []const u8, + path: Path, }; pub fn hashAddFrameworks(man: *Cache.Manifest, hm: []const Framework) !void { for (hm) |value| { man.hash.add(value.needed); man.hash.add(value.weak); - _ = try man.addFile(value.path, null); + _ = try man.addFilePath(value.path, null); } } @@ -239,9 +239,9 @@ pub fn createEmpty( const index: File.Index = @intCast(try self.files.addOne(gpa)); self.files.set(index, .{ .zig_object = .{ .index = index, - .path = try std.fmt.allocPrint(arena, "{s}.o", .{fs.path.stem( - zcu.main_mod.root_src_path, - )}), + .basename = try std.fmt.allocPrint(arena, "{s}.o", .{ + fs.path.stem(zcu.main_mod.root_src_path), + }), } }); self.zig_object = index; const zo = self.getZigObject().?; @@ -356,13 +356,12 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n defer sub_prog_node.end(); const directory = self.base.emit.root_dir; - const full_out_path = try directory.join(arena, &[_][]const u8{self.base.emit.sub_path}); - const module_obj_path: ?[]const u8 = if (self.base.zcu_object_sub_path) |path| blk: { - if (fs.path.dirname(full_out_path)) |dirname| { - break :blk try fs.path.join(arena, &.{ dirname, path }); - } else { - break :blk path; - } + const module_obj_path: ?Path = if (self.base.zcu_object_sub_path) |path| .{ + .root_dir = directory, + .sub_path = if (fs.path.dirname(self.base.emit.sub_path)) |dirname| + try fs.path.join(arena, &.{ dirname, path }) + else + path, } else null; // --verbose-link @@ -455,7 +454,7 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n } // Finally, link against compiler_rt. - const compiler_rt_path: ?[]const u8 = blk: { + const compiler_rt_path: ?Path = blk: { if (comp.compiler_rt_lib) |x| break :blk x.full_object_path; if (comp.compiler_rt_obj) |x| break :blk x.full_object_path; break :blk null; @@ -567,7 +566,7 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n // The most important here is to have the correct vm and filesize of the __LINKEDIT segment // where the code signature goes into. var codesig = CodeSignature.init(self.getPageSize()); - codesig.code_directory.ident = fs.path.basename(full_out_path); + codesig.code_directory.ident = fs.path.basename(self.base.emit.sub_path); if (self.entitlements) |path| try codesig.addEntitlements(gpa, path); try self.writeCodeSignaturePadding(&codesig); break :blk codesig; @@ -625,11 +624,11 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void { if (self.base.isRelocatable()) { for (comp.objects) |obj| { - try argv.append(obj.path); + try argv.append(try obj.path.toString(arena)); } for (comp.c_object_table.keys()) |key| { - try argv.append(key.status.success.object_path); + try argv.append(try key.status.success.object_path.toString(arena)); } if (module_obj_path) |p| { @@ -711,11 +710,11 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void { if (obj.must_link) { try argv.append("-force_load"); } - try argv.append(obj.path); + try argv.append(try obj.path.toString(arena)); } for (comp.c_object_table.keys()) |key| { - try argv.append(key.status.success.object_path); + try argv.append(try key.status.success.object_path.toString(arena)); } if (module_obj_path) |p| { @@ -723,13 +722,12 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void { } if (comp.config.any_sanitize_thread) { - const path = comp.tsan_lib.?.full_object_path; - try argv.append(path); - try argv.appendSlice(&.{ "-rpath", std.fs.path.dirname(path) orelse "." }); + const path = try comp.tsan_lib.?.full_object_path.toString(arena); + try argv.appendSlice(&.{ path, "-rpath", std.fs.path.dirname(path) orelse "." }); } if (comp.config.any_fuzz) { - try argv.append(comp.fuzzer_lib.?.full_object_path); + try argv.append(try comp.fuzzer_lib.?.full_object_path.toString(arena)); } for (self.lib_dirs) |lib_dir| { @@ -754,7 +752,7 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void { } for (self.frameworks) |framework| { - const name = fs.path.stem(framework.path); + const name = framework.path.stem(); const arg = if (framework.needed) try std.fmt.allocPrint(arena, "-needed_framework {s}", .{name}) else if (framework.weak) @@ -765,14 +763,16 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void { } if (comp.config.link_libcpp) { - try argv.append(comp.libcxxabi_static_lib.?.full_object_path); - try argv.append(comp.libcxx_static_lib.?.full_object_path); + try argv.appendSlice(&.{ + try comp.libcxxabi_static_lib.?.full_object_path.toString(arena), + try comp.libcxx_static_lib.?.full_object_path.toString(arena), + }); } try argv.append("-lSystem"); - if (comp.compiler_rt_lib) |lib| try argv.append(lib.full_object_path); - if (comp.compiler_rt_obj) |obj| try argv.append(obj.full_object_path); + if (comp.compiler_rt_lib) |lib| try argv.append(try lib.full_object_path.toString(arena)); + if (comp.compiler_rt_obj) |obj| try argv.append(try obj.full_object_path.toString(arena)); } Compilation.dump_argv(argv.items); @@ -807,20 +807,20 @@ pub fn resolveLibSystem( return error.MissingLibSystem; } - const libsystem_path = try arena.dupe(u8, test_path.items); + const libsystem_path = Path.initCwd(try arena.dupe(u8, test_path.items)); try out_libs.append(.{ .needed = true, .path = libsystem_path, }); } -pub fn classifyInputFile(self: *MachO, path: []const u8, lib: SystemLib, must_link: bool) !void { +pub fn classifyInputFile(self: *MachO, path: Path, lib: SystemLib, must_link: bool) !void { const tracy = trace(@src()); defer tracy.end(); - log.debug("classifying input file {s}", .{path}); + log.debug("classifying input file {}", .{path}); - const file = try std.fs.cwd().openFile(path, .{}); + const file = try path.root_dir.handle.openFile(path.sub_path, .{}); const fh = try self.addFileHandle(file); var buffer: [Archive.SARMAG]u8 = undefined; @@ -844,7 +844,7 @@ pub fn classifyInputFile(self: *MachO, path: []const u8, lib: SystemLib, must_li _ = try self.addTbd(lib, true, fh); } -fn parseFatFile(self: *MachO, file: std.fs.File, path: []const u8) !?fat.Arch { +fn parseFatFile(self: *MachO, file: std.fs.File, path: Path) !?fat.Arch { const fat_h = fat.readFatHeader(file) catch return null; if (fat_h.magic != macho.FAT_MAGIC and fat_h.magic != macho.FAT_MAGIC_64) return null; var fat_archs_buffer: [2]fat.Arch = undefined; @@ -873,7 +873,7 @@ pub fn readArMagic(file: std.fs.File, offset: usize, buffer: *[Archive.SARMAG]u8 return buffer[0..Archive.SARMAG]; } -fn addObject(self: *MachO, path: []const u8, handle: File.HandleIndex, offset: u64) !void { +fn addObject(self: *MachO, path: Path, handle: File.HandleIndex, offset: u64) !void { const tracy = trace(@src()); defer tracy.end(); @@ -886,7 +886,10 @@ fn addObject(self: *MachO, path: []const u8, handle: File.HandleIndex, offset: u const index = @as(File.Index, @intCast(try self.files.addOne(gpa))); self.files.set(index, .{ .object = .{ .offset = offset, - .path = try gpa.dupe(u8, path), + .path = .{ + .root_dir = path.root_dir, + .sub_path = try gpa.dupe(u8, path.sub_path), + }, .file_handle = handle, .mtime = mtime, .index = index, @@ -937,7 +940,7 @@ fn addArchive(self: *MachO, lib: SystemLib, must_link: bool, handle: File.Handle const gpa = self.base.comp.gpa; - var archive = Archive{}; + var archive: Archive = .{}; defer archive.deinit(gpa); try archive.unpack(self, lib.path, handle, fat_arch); @@ -963,7 +966,10 @@ fn addDylib(self: *MachO, lib: SystemLib, explicit: bool, handle: File.HandleInd .offset = offset, .file_handle = handle, .tag = .dylib, - .path = try gpa.dupe(u8, lib.path), + .path = .{ + .root_dir = lib.path.root_dir, + .sub_path = try gpa.dupe(u8, lib.path.sub_path), + }, .index = index, .needed = lib.needed, .weak = lib.weak, @@ -986,7 +992,10 @@ fn addTbd(self: *MachO, lib: SystemLib, explicit: bool, handle: File.HandleIndex .offset = 0, .file_handle = handle, .tag = .tbd, - .path = try gpa.dupe(u8, lib.path), + .path = .{ + .root_dir = lib.path.root_dir, + .sub_path = try gpa.dupe(u8, lib.path.sub_path), + }, .index = index, .needed = lib.needed, .weak = lib.weak, @@ -1175,11 +1184,11 @@ fn parseDependentDylibs(self: *MachO) !void { continue; } }; - const lib = SystemLib{ - .path = full_path, + const lib: SystemLib = .{ + .path = Path.initCwd(full_path), .weak = is_weak, }; - const file = try std.fs.cwd().openFile(lib.path, .{}); + const file = try lib.path.root_dir.handle.openFile(lib.path.sub_path, .{}); const fh = try self.addFileHandle(file); const fat_arch = try self.parseFatFile(file, lib.path); const offset = if (fat_arch) |fa| fa.offset else 0; @@ -2865,7 +2874,8 @@ fn writeLoadCommands(self: *MachO) !struct { usize, usize, u64 } { ncmds += 1; } if (comp.config.any_sanitize_thread) { - const path = comp.tsan_lib.?.full_object_path; + const path = try comp.tsan_lib.?.full_object_path.toString(gpa); + defer gpa.free(path); const rpath = std.fs.path.dirname(path) orelse "."; try load_commands.writeRpathLC(rpath, writer); ncmds += 1; @@ -3758,13 +3768,13 @@ pub fn eatPrefix(path: []const u8, prefix: []const u8) ?[]const u8 { pub fn reportParseError( self: *MachO, - path: []const u8, + path: Path, comptime format: []const u8, args: anytype, ) error{OutOfMemory}!void { var err = try self.base.addErrorWithNotes(1); try err.addMsg(format, args); - try err.addNote("while parsing {s}", .{path}); + try err.addNote("while parsing {}", .{path}); } pub fn reportParseError2( @@ -3913,7 +3923,7 @@ fn fmtDumpState( _ = options; _ = unused_fmt_string; if (self.getZigObject()) |zo| { - try writer.print("zig_object({d}) : {s}\n", .{ zo.index, zo.path }); + try writer.print("zig_object({d}) : {s}\n", .{ zo.index, zo.basename }); try writer.print("{}{}\n", .{ zo.fmtAtoms(self), zo.fmtSymtab(self), @@ -3938,9 +3948,9 @@ fn fmtDumpState( } for (self.dylibs.items) |index| { const dylib = self.getFile(index).?.dylib; - try writer.print("dylib({d}) : {s} : needed({}) : weak({})", .{ + try writer.print("dylib({d}) : {} : needed({}) : weak({})", .{ index, - dylib.path, + @as(Path, dylib.path), dylib.needed, dylib.weak, }); @@ -4442,7 +4452,7 @@ pub const default_pagezero_size: u64 = 0x100000000; pub const default_headerpad_size: u32 = 0x1000; const SystemLib = struct { - path: []const u8, + path: Path, needed: bool = false, weak: bool = false, hidden: bool = false, diff --git a/src/link/MachO/Archive.zig b/src/link/MachO/Archive.zig index bb589dfb66..ba3c66689b 100644 --- a/src/link/MachO/Archive.zig +++ b/src/link/MachO/Archive.zig @@ -4,7 +4,7 @@ pub fn deinit(self: *Archive, allocator: Allocator) void { self.objects.deinit(allocator); } -pub fn unpack(self: *Archive, macho_file: *MachO, path: []const u8, handle_index: File.HandleIndex, fat_arch: ?fat.Arch) !void { +pub fn unpack(self: *Archive, macho_file: *MachO, path: Path, handle_index: File.HandleIndex, fat_arch: ?fat.Arch) !void { const gpa = macho_file.base.comp.gpa; var arena = std.heap.ArenaAllocator.init(gpa); @@ -55,20 +55,23 @@ pub fn unpack(self: *Archive, macho_file: *MachO, path: []const u8, handle_index mem.eql(u8, name, SYMDEF_SORTED) or mem.eql(u8, name, SYMDEF64_SORTED)) continue; - const object = Object{ + const object: Object = .{ .offset = pos, .in_archive = .{ - .path = try gpa.dupe(u8, path), + .path = .{ + .root_dir = path.root_dir, + .sub_path = try gpa.dupe(u8, path.sub_path), + }, .size = hdr_size, }, - .path = try gpa.dupe(u8, name), + .path = Path.initCwd(try gpa.dupe(u8, name)), .file_handle = handle_index, .index = undefined, .alive = false, .mtime = hdr.date() catch 0, }; - log.debug("extracting object '{s}' from archive '{s}'", .{ object.path, path }); + log.debug("extracting object '{}' from archive '{}'", .{ object.path, path }); try self.objects.append(gpa, object); } @@ -301,8 +304,9 @@ const log = std.log.scoped(.link); const macho = std.macho; const mem = std.mem; const std = @import("std"); - const Allocator = mem.Allocator; +const Path = std.Build.Cache.Path; + const Archive = @This(); const File = @import("file.zig").File; const MachO = @import("../MachO.zig"); diff --git a/src/link/MachO/Dylib.zig b/src/link/MachO/Dylib.zig index 9852cfb234..01dd25fa8c 100644 --- a/src/link/MachO/Dylib.zig +++ b/src/link/MachO/Dylib.zig @@ -1,6 +1,6 @@ /// Non-zero for fat dylibs offset: u64, -path: []const u8, +path: Path, index: File.Index, file_handle: File.HandleIndex, tag: enum { dylib, tbd }, @@ -28,7 +28,7 @@ referenced: bool = false, output_symtab_ctx: MachO.SymtabCtx = .{}, pub fn deinit(self: *Dylib, allocator: Allocator) void { - allocator.free(self.path); + allocator.free(self.path.sub_path); self.exports.deinit(allocator); self.strtab.deinit(allocator); if (self.id) |*id| id.deinit(allocator); @@ -61,7 +61,7 @@ fn parseBinary(self: *Dylib, macho_file: *MachO) !void { const file = macho_file.getFileHandle(self.file_handle); const offset = self.offset; - log.debug("parsing dylib from binary: {s}", .{self.path}); + log.debug("parsing dylib from binary: {}", .{@as(Path, self.path)}); var header_buffer: [@sizeOf(macho.mach_header_64)]u8 = undefined; { @@ -267,7 +267,7 @@ fn parseTbd(self: *Dylib, macho_file: *MachO) !void { const gpa = macho_file.base.comp.gpa; - log.debug("parsing dylib from stub: {s}", .{self.path}); + log.debug("parsing dylib from stub: {}", .{self.path}); const file = macho_file.getFileHandle(self.file_handle); var lib_stub = LibStub.loadFromFile(gpa, file) catch |err| { @@ -959,8 +959,9 @@ const mem = std.mem; const tapi = @import("../tapi.zig"); const trace = @import("../../tracy.zig").trace; const std = @import("std"); - const Allocator = mem.Allocator; +const Path = std.Build.Cache.Path; + const Dylib = @This(); const File = @import("file.zig").File; const LibStub = tapi.LibStub; diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index 81f28de65a..9181112f2a 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -1,6 +1,8 @@ /// Non-zero for fat object files or archives offset: u64, -path: []const u8, +/// Archive files cannot contain subdirectories, so only the basename is needed +/// for output. However, the full path is kept for error reporting. +path: Path, file_handle: File.HandleIndex, mtime: u64, index: File.Index, @@ -39,8 +41,8 @@ output_symtab_ctx: MachO.SymtabCtx = .{}, output_ar_state: Archive.ArState = .{}, pub fn deinit(self: *Object, allocator: Allocator) void { - if (self.in_archive) |*ar| allocator.free(ar.path); - allocator.free(self.path); + if (self.in_archive) |*ar| allocator.free(ar.path.sub_path); + allocator.free(self.path.sub_path); for (self.sections.items(.relocs), self.sections.items(.subsections)) |*relocs, *sub| { relocs.deinit(allocator); sub.deinit(allocator); @@ -1723,7 +1725,8 @@ pub fn updateArSize(self: *Object, macho_file: *MachO) !void { pub fn writeAr(self: Object, ar_format: Archive.Format, macho_file: *MachO, writer: anytype) !void { // Header const size = std.math.cast(usize, self.output_ar_state.size) orelse return error.Overflow; - try Archive.writeHeader(self.path, size, ar_format, writer); + const basename = std.fs.path.basename(self.path.sub_path); + try Archive.writeHeader(basename, size, ar_format, writer); // Data const file = macho_file.getFileHandle(self.file_handle); // TODO try using copyRangeAll @@ -1774,6 +1777,11 @@ pub fn calcSymtabSize(self: *Object, macho_file: *MachO) void { self.calcStabsSize(macho_file); } +fn pathLen(path: Path) usize { + // +1 for the path separator + return (if (path.root_dir.path) |p| p.len + @intFromBool(path.sub_path.len != 0) else 0) + path.sub_path.len; +} + pub fn calcStabsSize(self: *Object, macho_file: *MachO) void { if (self.compile_unit) |cu| { const comp_dir = cu.getCompDir(self.*); @@ -1784,9 +1792,9 @@ pub fn calcStabsSize(self: *Object, macho_file: *MachO) void { self.output_symtab_ctx.strsize += @as(u32, @intCast(tu_name.len + 1)); // tu_name if (self.in_archive) |ar| { - self.output_symtab_ctx.strsize += @as(u32, @intCast(ar.path.len + 1 + self.path.len + 1 + 1)); + self.output_symtab_ctx.strsize += @intCast(pathLen(ar.path) + 1 + self.path.basename().len + 1 + 1); } else { - self.output_symtab_ctx.strsize += @as(u32, @intCast(self.path.len + 1)); + self.output_symtab_ctx.strsize += @intCast(pathLen(self.path) + 1); } for (self.symbols.items, 0..) |sym, i| { @@ -2118,19 +2126,36 @@ pub fn writeStabs(self: Object, stroff: u32, macho_file: *MachO, ctx: anytype) v }; index += 1; if (self.in_archive) |ar| { - @memcpy(ctx.strtab.items[n_strx..][0..ar.path.len], ar.path); - n_strx += @intCast(ar.path.len); + if (ar.path.root_dir.path) |p| { + @memcpy(ctx.strtab.items[n_strx..][0..p.len], p); + n_strx += @intCast(p.len); + if (ar.path.sub_path.len != 0) { + ctx.strtab.items[n_strx] = '/'; + n_strx += 1; + } + } + @memcpy(ctx.strtab.items[n_strx..][0..ar.path.sub_path.len], ar.path.sub_path); + n_strx += @intCast(ar.path.sub_path.len); ctx.strtab.items[n_strx] = '('; n_strx += 1; - @memcpy(ctx.strtab.items[n_strx..][0..self.path.len], self.path); - n_strx += @intCast(self.path.len); + const basename = self.path.basename(); + @memcpy(ctx.strtab.items[n_strx..][0..basename.len], basename); + n_strx += @intCast(basename.len); ctx.strtab.items[n_strx] = ')'; n_strx += 1; ctx.strtab.items[n_strx] = 0; n_strx += 1; } else { - @memcpy(ctx.strtab.items[n_strx..][0..self.path.len], self.path); - n_strx += @intCast(self.path.len); + if (self.path.root_dir.path) |p| { + @memcpy(ctx.strtab.items[n_strx..][0..p.len], p); + n_strx += @intCast(p.len); + if (self.path.sub_path.len != 0) { + ctx.strtab.items[n_strx] = '/'; + n_strx += 1; + } + } + @memcpy(ctx.strtab.items[n_strx..][0..self.path.sub_path.len], self.path.sub_path); + n_strx += @intCast(self.path.sub_path.len); ctx.strtab.items[n_strx] = 0; n_strx += 1; } @@ -2666,11 +2691,12 @@ fn formatPath( _ = unused_fmt_string; _ = options; if (object.in_archive) |ar| { - try writer.writeAll(ar.path); - try writer.writeByte('('); - try writer.writeAll(object.path); - try writer.writeByte(')'); - } else try writer.writeAll(object.path); + try writer.print("{}({s})", .{ + @as(Path, ar.path), object.path.basename(), + }); + } else { + try writer.print("{}", .{@as(Path, object.path)}); + } } const Section = struct { @@ -2777,7 +2803,7 @@ const CompileUnit = struct { }; const InArchive = struct { - path: []const u8, + path: Path, size: u32, }; @@ -3170,6 +3196,7 @@ const math = std.math; const mem = std.mem; const trace = @import("../../tracy.zig").trace; const std = @import("std"); +const Path = std.Build.Cache.Path; const Allocator = mem.Allocator; const Archive = @import("Archive.zig"); diff --git a/src/link/MachO/ZigObject.zig b/src/link/MachO/ZigObject.zig index a2d578845c..2a89e5da56 100644 --- a/src/link/MachO/ZigObject.zig +++ b/src/link/MachO/ZigObject.zig @@ -1,6 +1,6 @@ data: std.ArrayListUnmanaged(u8) = .empty, /// Externally owned memory. -path: []const u8, +basename: []const u8, index: File.Index, symtab: std.MultiArrayList(Nlist) = .{}, @@ -317,7 +317,7 @@ pub fn updateArSize(self: *ZigObject) void { pub fn writeAr(self: ZigObject, ar_format: Archive.Format, writer: anytype) !void { // Header const size = std.math.cast(usize, self.output_ar_state.size) orelse return error.Overflow; - try Archive.writeHeader(self.path, size, ar_format, writer); + try Archive.writeHeader(self.basename, size, ar_format, writer); // Data try writer.writeAll(self.data.items); } diff --git a/src/link/MachO/file.zig b/src/link/MachO/file.zig index 26b5957058..554a8b1966 100644 --- a/src/link/MachO/file.zig +++ b/src/link/MachO/file.zig @@ -23,10 +23,10 @@ pub const File = union(enum) { _ = unused_fmt_string; _ = options; switch (file) { - .zig_object => |x| try writer.writeAll(x.path), + .zig_object => |zo| try writer.writeAll(zo.basename), .internal => try writer.writeAll("internal"), .object => |x| try writer.print("{}", .{x.fmtPath()}), - .dylib => |x| try writer.writeAll(x.path), + .dylib => |dl| try writer.print("{}", .{@as(Path, dl.path)}), } } @@ -373,13 +373,14 @@ pub const File = union(enum) { pub const HandleIndex = Index; }; +const std = @import("std"); const assert = std.debug.assert; const log = std.log.scoped(.link); const macho = std.macho; -const std = @import("std"); -const trace = @import("../../tracy.zig").trace; - const Allocator = std.mem.Allocator; +const Path = std.Build.Cache.Path; + +const trace = @import("../../tracy.zig").trace; const Archive = @import("Archive.zig"); const Atom = @import("Atom.zig"); const InternalObject = @import("InternalObject.zig"); diff --git a/src/link/MachO/load_commands.zig b/src/link/MachO/load_commands.zig index b90b090864..08ab11e3f9 100644 --- a/src/link/MachO/load_commands.zig +++ b/src/link/MachO/load_commands.zig @@ -72,7 +72,8 @@ pub fn calcLoadCommandsSize(macho_file: *MachO, assume_max_path_len: bool) !u32 } if (comp.config.any_sanitize_thread) { - const path = comp.tsan_lib.?.full_object_path; + const path = try comp.tsan_lib.?.full_object_path.toString(gpa); + defer gpa.free(path); const rpath = std.fs.path.dirname(path) orelse "."; sizeofcmds += calcInstallNameLen( @sizeOf(macho.rpath_command), diff --git a/src/link/MachO/relocatable.zig b/src/link/MachO/relocatable.zig index e231360928..91bb295ed6 100644 --- a/src/link/MachO/relocatable.zig +++ b/src/link/MachO/relocatable.zig @@ -1,6 +1,7 @@ -pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void { +pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Path) link.File.FlushError!void { const gpa = macho_file.base.comp.gpa; + // TODO: "positional arguments" is a CLI concept, not a linker concept. Delete this unnecessary array list. var positionals = std.ArrayList(Compilation.LinkObject).init(gpa); defer positionals.deinit(); try positionals.ensureUnusedCapacity(comp.objects.len); @@ -19,7 +20,7 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]c // TODO: in the future, when we implement `dsymutil` alternative directly in the Zig // compiler, investigate if we can get rid of this `if` prong here. const path = positionals.items[0].path; - const in_file = try std.fs.cwd().openFile(path, .{}); + const in_file = try path.root_dir.handle.openFile(path.sub_path, .{}); const stat = try in_file.stat(); const amt = try in_file.copyRangeAll(0, macho_file.base.file.?, 0, stat.size); if (amt != stat.size) return error.InputOutput; // TODO: report an actual user error @@ -72,7 +73,7 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]c try writeHeader(macho_file, ncmds, sizeofcmds); } -pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void { +pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Path) link.File.FlushError!void { const gpa = comp.gpa; var positionals = std.ArrayList(Compilation.LinkObject).init(gpa); @@ -173,21 +174,25 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ? for (files.items) |index| { const file = macho_file.getFile(index).?; - const state = switch (file) { - .zig_object => |x| &x.output_ar_state, - .object => |x| &x.output_ar_state, + switch (file) { + .zig_object => |zo| { + const state = &zo.output_ar_state; + pos = mem.alignForward(usize, pos, 2); + state.file_off = pos; + pos += @sizeOf(Archive.ar_hdr); + pos += mem.alignForward(usize, zo.basename.len + 1, ptr_width); + pos += math.cast(usize, state.size) orelse return error.Overflow; + }, + .object => |o| { + const state = &o.output_ar_state; + pos = mem.alignForward(usize, pos, 2); + state.file_off = pos; + pos += @sizeOf(Archive.ar_hdr); + pos += mem.alignForward(usize, o.path.basename().len + 1, ptr_width); + pos += math.cast(usize, state.size) orelse return error.Overflow; + }, else => unreachable, - }; - const path = switch (file) { - .zig_object => |x| x.path, - .object => |x| x.path, - else => unreachable, - }; - pos = mem.alignForward(usize, pos, 2); - state.file_off = pos; - pos += @sizeOf(Archive.ar_hdr); - pos += mem.alignForward(usize, path.len + 1, ptr_width); - pos += math.cast(usize, state.size) orelse return error.Overflow; + } } break :blk pos; @@ -777,6 +782,7 @@ const mem = std.mem; const state_log = std.log.scoped(.link_state); const std = @import("std"); const trace = @import("../../tracy.zig").trace; +const Path = std.Build.Cache.Path; const Archive = @import("Archive.zig"); const Atom = @import("Atom.zig"); diff --git a/src/link/StringTable.zig b/src/link/StringTable.zig index b03e025ff0..f8a917b249 100644 --- a/src/link/StringTable.zig +++ b/src/link/StringTable.zig @@ -15,7 +15,7 @@ pub fn insert(self: *Self, gpa: Allocator, string: []const u8) !u32 { if (gop.found_existing) return gop.key_ptr.*; try self.buffer.ensureUnusedCapacity(gpa, string.len + 1); - const new_off = @as(u32, @intCast(self.buffer.items.len)); + const new_off: u32 = @intCast(self.buffer.items.len); self.buffer.appendSliceAssumeCapacity(string); self.buffer.appendAssumeCapacity(0); diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index fed44b7331..6a27909b31 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -2507,6 +2507,7 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_no } else null; // Positional arguments to the linker such as object files and static archives. + // TODO: "positional arguments" is a CLI concept, not a linker concept. Delete this unnecessary array list. var positionals = std.ArrayList([]const u8).init(arena); try positionals.ensureUnusedCapacity(comp.objects.len); @@ -2527,23 +2528,23 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_no (output_mode == .Lib and link_mode == .dynamic); if (is_exe_or_dyn_lib) { for (comp.wasi_emulated_libs) |crt_file| { - try positionals.append(try comp.get_libc_crt_file( + try positionals.append(try comp.crtFileAsString( arena, wasi_libc.emulatedLibCRFileLibName(crt_file), )); } if (link_libc) { - try positionals.append(try comp.get_libc_crt_file( + try positionals.append(try comp.crtFileAsString( arena, wasi_libc.execModelCrtFileFullName(wasi_exec_model), )); - try positionals.append(try comp.get_libc_crt_file(arena, "libc.a")); + try positionals.append(try comp.crtFileAsString(arena, "libc.a")); } if (link_libcpp) { - try positionals.append(comp.libcxx_static_lib.?.full_object_path); - try positionals.append(comp.libcxxabi_static_lib.?.full_object_path); + try positionals.append(try comp.libcxx_static_lib.?.full_object_path.toString(arena)); + try positionals.append(try comp.libcxxabi_static_lib.?.full_object_path.toString(arena)); } } } @@ -2553,15 +2554,15 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_no } for (comp.objects) |object| { - try positionals.append(object.path); + try positionals.append(try object.path.toString(arena)); } for (comp.c_object_table.keys()) |c_object| { - try positionals.append(c_object.status.success.object_path); + try positionals.append(try c_object.status.success.object_path.toString(arena)); } - if (comp.compiler_rt_lib) |lib| try positionals.append(lib.full_object_path); - if (comp.compiler_rt_obj) |obj| try positionals.append(obj.full_object_path); + if (comp.compiler_rt_lib) |lib| try positionals.append(try lib.full_object_path.toString(arena)); + if (comp.compiler_rt_obj) |obj| try positionals.append(try obj.full_object_path.toString(arena)); try wasm.parseInputFiles(positionals.items); @@ -3365,7 +3366,7 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: defer sub_prog_node.end(); const is_obj = comp.config.output_mode == .Obj; - const compiler_rt_path: ?[]const u8 = blk: { + const compiler_rt_path: ?Path = blk: { if (comp.compiler_rt_lib) |lib| break :blk lib.full_object_path; if (comp.compiler_rt_obj) |obj| break :blk obj.full_object_path; break :blk null; @@ -3387,14 +3388,14 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: comptime assert(Compilation.link_hash_implementation_version == 14); for (comp.objects) |obj| { - _ = try man.addFile(obj.path, null); + _ = try man.addFilePath(obj.path, null); man.hash.add(obj.must_link); } for (comp.c_object_table.keys()) |key| { - _ = try man.addFile(key.status.success.object_path, null); + _ = try man.addFilePath(key.status.success.object_path, null); } try man.addOptionalFile(module_obj_path); - try man.addOptionalFile(compiler_rt_path); + try man.addOptionalFilePath(compiler_rt_path); man.hash.addOptionalBytes(wasm.entry_name); man.hash.add(wasm.base.stack_size); man.hash.add(wasm.base.build_id); @@ -3450,17 +3451,19 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: break :blk comp.c_object_table.keys()[0].status.success.object_path; if (module_obj_path) |p| - break :blk p; + break :blk Path.initCwd(p); // TODO I think this is unreachable. Audit this situation when solving the above TODO // regarding eliding redundant object -> object transformations. return error.NoObjectsToLink; }; - // This can happen when using --enable-cache and using the stage1 backend. In this case - // we can skip the file copy. - if (!mem.eql(u8, the_object_path, full_out_path)) { - try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{}); - } + try std.fs.Dir.copyFile( + the_object_path.root_dir.handle, + the_object_path.sub_path, + directory.handle, + wasm.base.emit.sub_path, + .{}, + ); } else { // Create an LLD command line and invoke it. var argv = std.ArrayList([]const u8).init(gpa); @@ -3581,23 +3584,23 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: (comp.config.output_mode == .Lib and comp.config.link_mode == .dynamic); if (is_exe_or_dyn_lib) { for (comp.wasi_emulated_libs) |crt_file| { - try argv.append(try comp.get_libc_crt_file( + try argv.append(try comp.crtFileAsString( arena, wasi_libc.emulatedLibCRFileLibName(crt_file), )); } if (comp.config.link_libc) { - try argv.append(try comp.get_libc_crt_file( + try argv.append(try comp.crtFileAsString( arena, wasi_libc.execModelCrtFileFullName(comp.config.wasi_exec_model), )); - try argv.append(try comp.get_libc_crt_file(arena, "libc.a")); + try argv.append(try comp.crtFileAsString(arena, "libc.a")); } if (comp.config.link_libcpp) { - try argv.append(comp.libcxx_static_lib.?.full_object_path); - try argv.append(comp.libcxxabi_static_lib.?.full_object_path); + try argv.append(try comp.libcxx_static_lib.?.full_object_path.toString(arena)); + try argv.append(try comp.libcxxabi_static_lib.?.full_object_path.toString(arena)); } } } @@ -3612,7 +3615,7 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: try argv.append("-no-whole-archive"); whole_archive = false; } - try argv.append(obj.path); + try argv.append(try obj.path.toString(arena)); } if (whole_archive) { try argv.append("-no-whole-archive"); @@ -3620,7 +3623,7 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: } for (comp.c_object_table.keys()) |key| { - try argv.append(key.status.success.object_path); + try argv.append(try key.status.success.object_path.toString(arena)); } if (module_obj_path) |p| { try argv.append(p); @@ -3630,11 +3633,11 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: !comp.skip_linker_dependencies and !comp.config.link_libc) { - try argv.append(comp.libc_static_lib.?.full_object_path); + try argv.append(try comp.libc_static_lib.?.full_object_path.toString(arena)); } if (compiler_rt_path) |p| { - try argv.append(p); + try argv.append(try p.toString(arena)); } if (comp.verbose_link) { diff --git a/src/main.zig b/src/main.zig index 940e88e2cf..4e29611d9c 100644 --- a/src/main.zig +++ b/src/main.zig @@ -13,6 +13,12 @@ const warn = std.log.warn; const ThreadPool = std.Thread.Pool; const cleanExit = std.process.cleanExit; const native_os = builtin.os.tag; +const Cache = std.Build.Cache; +const Path = std.Build.Cache.Path; +const EnvVar = std.zig.EnvVar; +const LibCInstallation = std.zig.LibCInstallation; +const AstGen = std.zig.AstGen; +const Server = std.zig.Server; const tracy = @import("tracy.zig"); const Compilation = @import("Compilation.zig"); @@ -20,16 +26,11 @@ const link = @import("link.zig"); const Package = @import("Package.zig"); const build_options = @import("build_options"); const introspect = @import("introspect.zig"); -const EnvVar = std.zig.EnvVar; -const LibCInstallation = std.zig.LibCInstallation; const wasi_libc = @import("wasi_libc.zig"); -const Cache = std.Build.Cache; const target_util = @import("target.zig"); const crash_report = @import("crash_report.zig"); const Zcu = @import("Zcu.zig"); -const AstGen = std.zig.AstGen; const mingw = @import("mingw.zig"); -const Server = std.zig.Server; const dev = @import("dev.zig"); pub const std_options = .{ @@ -1724,14 +1725,14 @@ fn buildOutputType( } } else switch (file_ext orelse Compilation.classifyFileExt(arg)) { .shared_library => { - try create_module.link_objects.append(arena, .{ .path = arg }); + try create_module.link_objects.append(arena, .{ .path = Path.initCwd(arg) }); create_module.opts.any_dyn_libs = true; }, .object, .static_library => { - try create_module.link_objects.append(arena, .{ .path = arg }); + try create_module.link_objects.append(arena, .{ .path = Path.initCwd(arg) }); }, .res => { - try create_module.link_objects.append(arena, .{ .path = arg }); + try create_module.link_objects.append(arena, .{ .path = Path.initCwd(arg) }); contains_res_file = true; }, .manifest => { @@ -1845,20 +1846,20 @@ fn buildOutputType( }, .shared_library => { try create_module.link_objects.append(arena, .{ - .path = it.only_arg, + .path = Path.initCwd(it.only_arg), .must_link = must_link, }); create_module.opts.any_dyn_libs = true; }, .unknown, .object, .static_library => { try create_module.link_objects.append(arena, .{ - .path = it.only_arg, + .path = Path.initCwd(it.only_arg), .must_link = must_link, }); }, .res => { try create_module.link_objects.append(arena, .{ - .path = it.only_arg, + .path = Path.initCwd(it.only_arg), .must_link = must_link, }); contains_res_file = true; @@ -1894,7 +1895,7 @@ fn buildOutputType( // binary: no extra rpaths and DSO filename exactly // as provided. Hello, Go. try create_module.link_objects.append(arena, .{ - .path = it.only_arg, + .path = Path.initCwd(it.only_arg), .must_link = must_link, .loption = true, }); @@ -2532,7 +2533,7 @@ fn buildOutputType( install_name = linker_args_it.nextOrFatal(); } else if (mem.eql(u8, arg, "-force_load")) { try create_module.link_objects.append(arena, .{ - .path = linker_args_it.nextOrFatal(), + .path = Path.initCwd(linker_args_it.nextOrFatal()), .must_link = true, }); } else if (mem.eql(u8, arg, "-hash-style") or @@ -2707,7 +2708,7 @@ fn buildOutputType( break :b create_module.c_source_files.items[0].src_path; if (create_module.link_objects.items.len >= 1) - break :b create_module.link_objects.items[0].path; + break :b create_module.link_objects.items[0].path.sub_path; if (emit_bin == .yes) break :b emit_bin.yes; @@ -2963,7 +2964,7 @@ fn buildOutputType( framework_dir_path, framework_name, )) { - const path = try arena.dupe(u8, test_path.items); + const path = Path.initCwd(try arena.dupe(u8, test_path.items)); try resolved_frameworks.append(.{ .needed = info.needed, .weak = info.weak, @@ -3635,7 +3636,7 @@ const CreateModule = struct { name: []const u8, lib: Compilation.SystemLib, }), - wasi_emulated_libs: std.ArrayListUnmanaged(wasi_libc.CRTFile), + wasi_emulated_libs: std.ArrayListUnmanaged(wasi_libc.CrtFile), c_source_files: std.ArrayListUnmanaged(Compilation.CSourceFile), rc_source_files: std.ArrayListUnmanaged(Compilation.RcSourceFile), @@ -3808,7 +3809,7 @@ fn createModule( } if (target.os.tag == .wasi) { - if (wasi_libc.getEmulatedLibCRTFile(lib_name)) |crt_file| { + if (wasi_libc.getEmulatedLibCrtFile(lib_name)) |crt_file| { try create_module.wasi_emulated_libs.append(arena, crt_file); continue; } @@ -3929,7 +3930,7 @@ fn createModule( target, info.preferred_mode, )) { - const path = try arena.dupe(u8, test_path.items); + const path = Path.initCwd(try arena.dupe(u8, test_path.items)); switch (info.preferred_mode) { .static => try create_module.link_objects.append(arena, .{ .path = path }), .dynamic => try create_module.resolved_system_libs.append(arena, .{ @@ -3963,7 +3964,7 @@ fn createModule( target, info.fallbackMode(), )) { - const path = try arena.dupe(u8, test_path.items); + const path = Path.initCwd(try arena.dupe(u8, test_path.items)); switch (info.fallbackMode()) { .static => try create_module.link_objects.append(arena, .{ .path = path }), .dynamic => try create_module.resolved_system_libs.append(arena, .{ @@ -3997,7 +3998,7 @@ fn createModule( target, info.preferred_mode, )) { - const path = try arena.dupe(u8, test_path.items); + const path = Path.initCwd(try arena.dupe(u8, test_path.items)); switch (info.preferred_mode) { .static => try create_module.link_objects.append(arena, .{ .path = path }), .dynamic => try create_module.resolved_system_libs.append(arena, .{ @@ -4021,7 +4022,7 @@ fn createModule( target, info.fallbackMode(), )) { - const path = try arena.dupe(u8, test_path.items); + const path = Path.initCwd(try arena.dupe(u8, test_path.items)); switch (info.fallbackMode()) { .static => try create_module.link_objects.append(arena, .{ .path = path }), .dynamic => try create_module.resolved_system_libs.append(arena, .{ @@ -6163,7 +6164,7 @@ fn cmdAstCheck( } file.mod = try Package.Module.createLimited(arena, .{ - .root = Cache.Path.cwd(), + .root = Path.cwd(), .root_src_path = file.sub_file_path, .fully_qualified_name = "root", }); @@ -6523,7 +6524,7 @@ fn cmdChangelist( }; file.mod = try Package.Module.createLimited(arena, .{ - .root = Cache.Path.cwd(), + .root = Path.cwd(), .root_src_path = file.sub_file_path, .fully_qualified_name = "root", }); diff --git a/src/mingw.zig b/src/mingw.zig index ab5f4f26db..d26358450c 100644 --- a/src/mingw.zig +++ b/src/mingw.zig @@ -11,13 +11,13 @@ const build_options = @import("build_options"); const Cache = std.Build.Cache; const dev = @import("dev.zig"); -pub const CRTFile = enum { +pub const CrtFile = enum { crt2_o, dllcrt2_o, mingw32_lib, }; -pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: std.Progress.Node) !void { +pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progress.Node) !void { if (!build_options.have_llvm) { return error.ZigCompilerNotBuiltWithLLVMExtensions; } @@ -160,7 +160,9 @@ fn add_cc_args( pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void { dev.check(.build_import_lib); - var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa); + const gpa = comp.gpa; + + var arena_allocator = std.heap.ArenaAllocator.init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); @@ -178,7 +180,7 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void { // Use the global cache directory. var cache: Cache = .{ - .gpa = comp.gpa, + .gpa = gpa, .manifest_dir = try comp.global_cache_directory.handle.makeOpenPath("h", .{}), }; cache.addPrefix(.{ .path = null, .handle = std.fs.cwd() }); @@ -195,17 +197,18 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void { _ = try man.addFile(def_file_path, null); - const final_lib_basename = try std.fmt.allocPrint(comp.gpa, "{s}.lib", .{lib_name}); - errdefer comp.gpa.free(final_lib_basename); + const final_lib_basename = try std.fmt.allocPrint(gpa, "{s}.lib", .{lib_name}); + errdefer gpa.free(final_lib_basename); if (try man.hit()) { const digest = man.final(); - try comp.crt_files.ensureUnusedCapacity(comp.gpa, 1); + try comp.crt_files.ensureUnusedCapacity(gpa, 1); comp.crt_files.putAssumeCapacityNoClobber(final_lib_basename, .{ - .full_object_path = try comp.global_cache_directory.join(comp.gpa, &[_][]const u8{ - "o", &digest, final_lib_basename, - }), + .full_object_path = .{ + .root_dir = comp.global_cache_directory, + .sub_path = try std.fs.path.join(gpa, &.{ "o", &digest, final_lib_basename }), + }, .lock = man.toOwnedLock(), }); return; @@ -230,7 +233,7 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void { }; const aro = @import("aro"); - var aro_comp = aro.Compilation.init(comp.gpa, std.fs.cwd()); + var aro_comp = aro.Compilation.init(gpa, std.fs.cwd()); defer aro_comp.deinit(); const include_dir = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "mingw", "def-include" }); @@ -244,7 +247,7 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void { nosuspend stderr.print("output path: {s}\n", .{def_final_path}) catch break :print; } - try aro_comp.include_dirs.append(comp.gpa, include_dir); + try aro_comp.include_dirs.append(gpa, include_dir); const builtin_macros = try aro_comp.generateBuiltinMacros(.include_system_defines); const user_macros = try aro_comp.addSourceFromBuffer("", target_defines); @@ -271,17 +274,15 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void { try pp.prettyPrintTokens(def_final_file.writer(), .result_only); } - const lib_final_path = try comp.global_cache_directory.join(comp.gpa, &[_][]const u8{ - "o", &digest, final_lib_basename, - }); - errdefer comp.gpa.free(lib_final_path); + const lib_final_path = try std.fs.path.join(gpa, &.{ "o", &digest, final_lib_basename }); + errdefer gpa.free(lib_final_path); if (!build_options.have_llvm) return error.ZigCompilerNotBuiltWithLLVMExtensions; const llvm_bindings = @import("codegen/llvm/bindings.zig"); const llvm = @import("codegen/llvm.zig"); const arch_tag = llvm.targetArch(target.cpu.arch); const def_final_path_z = try arena.dupeZ(u8, def_final_path); - const lib_final_path_z = try arena.dupeZ(u8, lib_final_path); + const lib_final_path_z = try comp.global_cache_directory.joinZ(arena, &.{lib_final_path}); if (llvm_bindings.WriteImportLibrary(def_final_path_z.ptr, arch_tag, lib_final_path_z.ptr, true)) { // TODO surface a proper error here log.err("unable to turn {s}.def into {s}.lib", .{ lib_name, lib_name }); @@ -292,8 +293,11 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void { log.warn("failed to write cache manifest for DLL import {s}.lib: {s}", .{ lib_name, @errorName(err) }); }; - try comp.crt_files.putNoClobber(comp.gpa, final_lib_basename, .{ - .full_object_path = lib_final_path, + try comp.crt_files.putNoClobber(gpa, final_lib_basename, .{ + .full_object_path = .{ + .root_dir = comp.global_cache_directory, + .sub_path = lib_final_path, + }, .lock = man.toOwnedLock(), }); } diff --git a/src/musl.zig b/src/musl.zig index 5ddbcb6652..48717dc5a1 100644 --- a/src/musl.zig +++ b/src/musl.zig @@ -9,7 +9,7 @@ const archName = std.zig.target.muslArchName; const Compilation = @import("Compilation.zig"); const build_options = @import("build_options"); -pub const CRTFile = enum { +pub const CrtFile = enum { crti_o, crtn_o, crt1_o, @@ -19,7 +19,7 @@ pub const CRTFile = enum { libc_so, }; -pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: std.Progress.Node) !void { +pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progress.Node) !void { if (!build_options.have_llvm) { return error.ZigCompilerNotBuiltWithLLVMExtensions; } diff --git a/src/wasi_libc.zig b/src/wasi_libc.zig index 57d93b6f56..ce94110b69 100644 --- a/src/wasi_libc.zig +++ b/src/wasi_libc.zig @@ -6,7 +6,7 @@ const Allocator = std.mem.Allocator; const Compilation = @import("Compilation.zig"); const build_options = @import("build_options"); -pub const CRTFile = enum { +pub const CrtFile = enum { crt1_reactor_o, crt1_command_o, libc_a, @@ -16,7 +16,7 @@ pub const CRTFile = enum { libwasi_emulated_signal_a, }; -pub fn getEmulatedLibCRTFile(lib_name: []const u8) ?CRTFile { +pub fn getEmulatedLibCrtFile(lib_name: []const u8) ?CrtFile { if (mem.eql(u8, lib_name, "wasi-emulated-process-clocks")) { return .libwasi_emulated_process_clocks_a; } @@ -32,7 +32,7 @@ pub fn getEmulatedLibCRTFile(lib_name: []const u8) ?CRTFile { return null; } -pub fn emulatedLibCRFileLibName(crt_file: CRTFile) []const u8 { +pub fn emulatedLibCRFileLibName(crt_file: CrtFile) []const u8 { return switch (crt_file) { .libwasi_emulated_process_clocks_a => "libwasi-emulated-process-clocks.a", .libwasi_emulated_getpid_a => "libwasi-emulated-getpid.a", @@ -42,10 +42,10 @@ pub fn emulatedLibCRFileLibName(crt_file: CRTFile) []const u8 { }; } -pub fn execModelCrtFile(wasi_exec_model: std.builtin.WasiExecModel) CRTFile { +pub fn execModelCrtFile(wasi_exec_model: std.builtin.WasiExecModel) CrtFile { return switch (wasi_exec_model) { - .reactor => CRTFile.crt1_reactor_o, - .command => CRTFile.crt1_command_o, + .reactor => CrtFile.crt1_reactor_o, + .command => CrtFile.crt1_command_o, }; } @@ -57,7 +57,7 @@ pub fn execModelCrtFileFullName(wasi_exec_model: std.builtin.WasiExecModel) []co }; } -pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: std.Progress.Node) !void { +pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progress.Node) !void { if (!build_options.have_llvm) { return error.ZigCompilerNotBuiltWithLLVMExtensions; }