diff --git a/lib/std/process.zig b/lib/std/process.zig index 28d4bfcb25c0..6bde56de4afd 100644 --- a/lib/std/process.zig +++ b/lib/std/process.zig @@ -351,13 +351,7 @@ test "getEnvMap" { defer env.deinit(); } -pub const GetEnvVarOwnedError = error{ - OutOfMemory, - EnvironmentVariableNotFound, - - /// See https://github.com/ziglang/zig/issues/1774 - InvalidUtf8, -}; +pub const GetEnvVarOwnedError = error{EnvironmentVariableNotFound} || HasEnvVarError; /// Caller must free returned memory. pub fn getEnvVarOwned(allocator: Allocator, key: []const u8) GetEnvVarOwnedError![]u8 { @@ -396,11 +390,18 @@ pub fn hasEnvVarConstant(comptime key: []const u8) bool { } } -pub fn hasEnvVar(allocator: Allocator, key: []const u8) error{OutOfMemory}!bool { +pub const HasEnvVarError = error{ + OutOfMemory, + /// See https://github.com/ziglang/zig/issues/1774 + InvalidUtf8, +}; + +pub fn hasEnvVar(allocator: Allocator, key: []const u8) HasEnvVarError!bool { if (builtin.os.tag == .windows) { - var stack_alloc = std.heap.stackFallback(256 * @sizeOf(u16), allocator); - const key_w = try std.unicode.utf8ToUtf16LeWithNull(stack_alloc.get(), key); - defer stack_alloc.allocator.free(key_w); + var stack_allocator = std.heap.stackFallback(256 * @sizeOf(u16), allocator); + const stack_alloc = stack_allocator.get(); + const key_w = try std.unicode.utf8ToUtf16LeWithNull(stack_alloc, key); + defer stack_alloc.free(key_w); return std.os.getenvW(key_w) != null; } else if (builtin.os.tag == .wasi and !builtin.link_libc) { var envmap = getEnvMap(allocator) catch return error.OutOfMemory; diff --git a/lib/std/zig/system/NativePaths.zig b/lib/std/zig/system/NativePaths.zig index f9798695ad5f..516ee5c0c755 100644 --- a/lib/std/zig/system/NativePaths.zig +++ b/lib/std/zig/system/NativePaths.zig @@ -6,7 +6,6 @@ const process = std.process; const mem = std.mem; const NativePaths = @This(); -const NativeTargetInfo = std.zig.system.NativeTargetInfo; include_dirs: ArrayList([:0]u8), lib_dirs: ArrayList([:0]u8), @@ -14,9 +13,13 @@ framework_dirs: ArrayList([:0]u8), rpaths: ArrayList([:0]u8), warnings: ArrayList([:0]u8), -pub fn detect(allocator: Allocator, native_info: NativeTargetInfo) !NativePaths { - const native_target = native_info.target; +pub fn isNix(allocator: Allocator) bool { + const cflags = process.hasEnvVar(allocator, "NIX_CFLAGS_COMPILE") catch return false; + const ldflags = process.hasEnvVar(allocator, "NIX_LDFLAGS") catch return false; + return cflags and ldflags; +} +pub fn detect(allocator: Allocator, native_target: std.Target) !NativePaths { var self: NativePaths = .{ .include_dirs = ArrayList([:0]u8).init(allocator), .lib_dirs = ArrayList([:0]u8).init(allocator), @@ -188,6 +191,22 @@ fn deinitArray(array: *ArrayList([:0]u8)) void { array.deinit(); } +pub inline fn getIncludeDirs(self: *const NativePaths) []const [:0]const u8 { + return self.include_dirs.items; +} + +pub inline fn getLibDirs(self: *const NativePaths) []const [:0]const u8 { + return self.lib_dirs.items; +} + +pub inline fn getFrameworkDirs(self: *const NativePaths) []const [:0]const u8 { + return self.framework_dirs.items; +} + +pub inline fn getRpaths(self: *const NativePaths) []const [:0]const u8 { + return self.rpaths.items; +} + pub fn addIncludeDir(self: *NativePaths, s: []const u8) !void { return self.appendArray(&self.include_dirs, s); } diff --git a/src/Compilation.zig b/src/Compilation.zig index 4019e43c8d13..1989341cd048 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -124,6 +124,7 @@ zig_lib_directory: Directory, local_cache_directory: Directory, global_cache_directory: Directory, libc_include_dir_list: []const []const u8, +libc_framework_dir_list: []const []const u8, thread_pool: *ThreadPool, /// Populated when we build the libc++ static library. A Job to build this is placed in the queue @@ -517,6 +518,7 @@ pub const InitOptions = struct { link_libc: bool = false, link_libcpp: bool = false, link_libunwind: bool = false, + want_native_include_dirs: ?bool = null, want_pic: ?bool = null, /// This means that if the output mode is an executable it will be a /// Position Independent Executable. If the output mode is not an @@ -636,8 +638,6 @@ pub const InitOptions = struct { wasi_exec_model: ?std.builtin.WasiExecModel = null, /// (Zig compiler development) Enable dumping linker's state as JSON. enable_link_snapshots: bool = false, - /// (Darwin) Path and version of the native SDK if detected. - native_darwin_sdk: ?std.zig.system.darwin.DarwinSDK = null, /// (Darwin) Install name of the dylib install_name: ?[]const u8 = null, /// (Darwin) Path to entitlements file @@ -855,14 +855,31 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { break :blk false; }; - const sysroot = blk: { - if (options.sysroot) |sysroot| { - break :blk sysroot; - } else if (options.native_darwin_sdk) |sdk| { - break :blk sdk.path; - } else { - break :blk null; + const darwin_native = (comptime builtin.target.isDarwin()) and options.target.isDarwin() and + options.sysroot == null; + const darwin_sdk: ?std.zig.system.darwin.DarwinSDK = if (darwin_native and + !std.zig.system.NativePaths.isNix(arena) and + std.zig.system.darwin.isDarwinSDKInstalled(arena)) + std.zig.system.darwin.getDarwinSDK(arena, options.target) + else + null; + const want_native_paths = (options.sysroot == null and options.is_native_os and + (options.system_lib_names.len > 0 or options.want_native_include_dirs orelse false)) or + darwin_native; + const native_paths = if (want_native_paths) blk: { + const paths = std.zig.system.NativePaths.detect(arena, options.target) catch |err| { + fatal("unable to detect native system paths: {s}", .{@errorName(err)}); + }; + for (paths.warnings.items) |warning| { + log.warn("{s}", .{warning}); } + break :blk paths; + } else null; + + const sysroot = blk: { + if (options.sysroot) |sysroot| break :blk sysroot; + if (darwin_sdk) |sdk| break :blk sdk.path; + break :blk null; }; const lto = blk: { @@ -941,14 +958,14 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { const dll_export_fns = options.dll_export_fns orelse (is_dyn_lib or options.rdynamic); - const libc_dirs = try detectLibCIncludeDirs( + const libc_dirs = try detectLibCDirs( arena, options.zig_lib_directory.path.?, options.target, options.is_native_abi, link_libc, options.libc_installation, - options.native_darwin_sdk != null, + darwin_sdk, ); const must_pie = target_util.requiresPIE(options.target); @@ -1535,6 +1552,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { .soname = options.soname, .version = options.version, .compatibility_version = options.compatibility_version, + .darwin_sdk_version = if (darwin_sdk) |sdk| sdk.version else null, .libc_installation = libc_dirs.libc_installation, .pic = pic, .pie = pie, @@ -1563,7 +1581,6 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { .wasi_exec_model = wasi_exec_model, .hash_style = options.hash_style, .enable_link_snapshots = options.enable_link_snapshots, - .native_darwin_sdk = options.native_darwin_sdk, .install_name = options.install_name, .entitlements = options.entitlements, .pagezero_size = options.pagezero_size, @@ -1574,6 +1591,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { .force_undefined_symbols = options.force_undefined_symbols, .pdb_source_path = options.pdb_source_path, .pdb_out_path = options.pdb_out_path, + .native_paths = native_paths, }); errdefer bin_file.destroy(); comp.* = .{ @@ -1601,6 +1619,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { .cache_parent = cache, .self_exe_path = options.self_exe_path, .libc_include_dir_list = libc_dirs.libc_include_dir_list, + .libc_framework_dir_list = libc_dirs.libc_framework_dir_list, .sanitize_c = sanitize_c, .thread_pool = options.thread_pool, .clang_passthrough_mode = options.clang_passthrough_mode, @@ -2347,6 +2366,10 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes man.hash.add(comp.bin_file.options.rdynamic); man.hash.addListOfBytes(comp.bin_file.options.lib_dirs); man.hash.addListOfBytes(comp.bin_file.options.rpath_list); + if (comp.bin_file.options.native_paths) |paths| { + man.hash.addListOfBytes(paths.getLibDirs()); + man.hash.addListOfBytes(paths.getRpaths()); + } man.hash.addListOfBytes(comp.bin_file.options.symbol_wrap_set.keys()); man.hash.add(comp.bin_file.options.each_lib_rpath); man.hash.add(comp.bin_file.options.build_id); @@ -2367,7 +2390,8 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes if (comp.bin_file.options.link_libc) { man.hash.add(comp.bin_file.options.libc_installation != null); if (comp.bin_file.options.libc_installation) |libc_installation| { - man.hash.addBytes(libc_installation.crt_dir.?); + man.hash.addOptionalBytes(libc_installation.crt_dir); + man.hash.addOptionalBytes(libc_installation.framework_dir); if (target.abi == .msvc) { man.hash.addBytes(libc_installation.msvc_lib_dir.?); man.hash.addBytes(libc_installation.kernel32_lib_dir.?); @@ -2395,6 +2419,8 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes // Mach-O specific stuff man.hash.addListOfBytes(comp.bin_file.options.framework_dirs); + if (comp.bin_file.options.native_paths) |paths| + man.hash.addListOfBytes(paths.getFrameworkDirs()); link.hashAddSystemLibs(&man.hash, comp.bin_file.options.frameworks); try man.addOptionalFile(comp.bin_file.options.entitlements); man.hash.addOptional(comp.bin_file.options.pagezero_size); @@ -3635,6 +3661,7 @@ pub fn obtainCObjectCacheManifest(comp: *const Compilation) Cache.Manifest { // decision should not be overridden here. if (comp.bin_file.options.libc_installation != null) { man.hash.addListOfBytes(comp.libc_include_dir_list); + man.hash.addListOfBytes(comp.libc_framework_dir_list); } return man; @@ -4348,9 +4375,30 @@ pub fn addCCArgs( try argv.append("-isystem"); try argv.append(c_headers_dir); + try argv.ensureUnusedCapacity(comp.libc_include_dir_list.len * 2); for (comp.libc_include_dir_list) |include_dir| { - try argv.append("-isystem"); - try argv.append(include_dir); + argv.appendAssumeCapacity("-isystem"); + argv.appendAssumeCapacity(include_dir); + } + + try argv.ensureUnusedCapacity(comp.libc_framework_dir_list.len * 2); + for (comp.libc_framework_dir_list) |framework_dir| { + argv.appendAssumeCapacity("-iframework"); + argv.appendAssumeCapacity(framework_dir); + } + + if (comp.bin_file.options.native_paths) |paths| { + try argv.ensureUnusedCapacity(paths.getIncludeDirs().len * 2); + for (paths.getIncludeDirs()) |include_dir| { + argv.appendAssumeCapacity("-isystem"); + argv.appendAssumeCapacity(include_dir); + } + + try argv.ensureUnusedCapacity(paths.getFrameworkDirs().len * 2); + for (paths.getFrameworkDirs()) |framework_dir| { + argv.appendAssumeCapacity("-iframework"); + argv.appendAssumeCapacity(framework_dir); + } } if (target.cpu.model.llvm_name) |llvm_name| { @@ -4822,52 +4870,23 @@ test "classifyFileExt" { const LibCDirs = struct { libc_include_dir_list: []const []const u8, + libc_framework_dir_list: []const []const u8, libc_installation: ?*const LibCInstallation, }; -fn getZigShippedLibCIncludeDirsDarwin(arena: Allocator, zig_lib_dir: []const u8, target: Target) !LibCDirs { - const arch_name = @tagName(target.cpu.arch); - const os_name = try std.fmt.allocPrint(arena, "{s}.{d}", .{ - @tagName(target.os.tag), - target.os.version_range.semver.min.major, - }); - const s = std.fs.path.sep_str; - const list = try arena.alloc([]const u8, 3); - - list[0] = try std.fmt.allocPrint( - arena, - "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{s}-{s}-none", - .{ zig_lib_dir, arch_name, os_name }, - ); - list[1] = try std.fmt.allocPrint( - arena, - "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "any-{s}-any", - .{ zig_lib_dir, os_name }, - ); - list[2] = try std.fmt.allocPrint( - arena, - "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "any-macos-any", - .{zig_lib_dir}, - ); - - return LibCDirs{ - .libc_include_dir_list = list, - .libc_installation = null, - }; -} - -fn detectLibCIncludeDirs( +fn detectLibCDirs( arena: Allocator, zig_lib_dir: []const u8, target: Target, is_native_abi: bool, link_libc: bool, libc_installation: ?*const LibCInstallation, - has_macos_sdk: bool, + darwin_sdk: ?std.zig.system.darwin.DarwinSDK, ) !LibCDirs { if (!link_libc) { return LibCDirs{ .libc_include_dir_list = &[0][]u8{}, + .libc_framework_dir_list = &[0][]u8{}, .libc_installation = null, }; } @@ -4879,18 +4898,11 @@ fn detectLibCIncludeDirs( // If linking system libraries and targeting the native abi, default to // using the system libc installation. if (is_native_abi and !target.isMinGW()) { - if (target.isDarwin()) { - return if (has_macos_sdk) - // For Darwin/macOS, we are all set with getDarwinSDK found earlier. - LibCDirs{ - .libc_include_dir_list = &[0][]u8{}, - .libc_installation = null, - } - else - getZigShippedLibCIncludeDirsDarwin(arena, zig_lib_dir, target); - } const libc = try arena.create(LibCInstallation); - libc.* = LibCInstallation.findNative(.{ .allocator = arena }) catch |err| switch (err) { + libc.* = LibCInstallation.findNative(.{ + .allocator = arena, + .darwin_sdk = darwin_sdk, + }) catch |err| switch (err) { error.CCompilerExitCode, error.CCompilerCrashed, error.CCompilerCannotFindHeaders, @@ -4900,7 +4912,7 @@ fn detectLibCIncludeDirs( // however, it is not installed. So we must rely on our bundled // libc files. if (target_util.canBuildLibC(target)) { - return detectLibCFromBuilding(arena, zig_lib_dir, target, has_macos_sdk); + return detectLibCFromBuilding(arena, zig_lib_dir, target); } return e; }, @@ -4912,7 +4924,7 @@ fn detectLibCIncludeDirs( // If not linking system libraries, build and provide our own libc by // default if possible. if (target_util.canBuildLibC(target)) { - return detectLibCFromBuilding(arena, zig_lib_dir, target, has_macos_sdk); + return detectLibCFromBuilding(arena, zig_lib_dir, target); } // If zig can't build the libc for the target and we are targeting the @@ -4926,24 +4938,34 @@ fn detectLibCIncludeDirs( if (use_system_abi) { const libc = try arena.create(LibCInstallation); - libc.* = try LibCInstallation.findNative(.{ .allocator = arena, .verbose = true }); + libc.* = try LibCInstallation.findNative(.{ + .allocator = arena, + .darwin_sdk = darwin_sdk, + .verbose = true, + }); return detectLibCFromLibCInstallation(arena, target, libc); } return LibCDirs{ .libc_include_dir_list = &[0][]u8{}, + .libc_framework_dir_list = &[0][]u8{}, .libc_installation = null, }; } fn detectLibCFromLibCInstallation(arena: Allocator, target: Target, lci: *const LibCInstallation) !LibCDirs { var list = try std.ArrayList([]const u8).initCapacity(arena, 5); + var framework_list = std.ArrayList([]const u8).init(arena); list.appendAssumeCapacity(lci.include_dir.?); const is_redundant = mem.eql(u8, lci.sys_include_dir.?, lci.include_dir.?); if (!is_redundant) list.appendAssumeCapacity(lci.sys_include_dir.?); + if (target.isDarwin()) { + try framework_list.append(lci.framework_dir.?); + } + if (target.os.tag == .windows) { if (std.fs.path.dirname(lci.include_dir.?)) |include_dir_parent| { const um_dir = try std.fs.path.join(arena, &[_][]const u8{ include_dir_parent, "um" }); @@ -4967,25 +4989,44 @@ fn detectLibCFromLibCInstallation(arena: Allocator, target: Target, lci: *const return LibCDirs{ .libc_include_dir_list = list.items, + .libc_framework_dir_list = framework_list.items, .libc_installation = lci, }; } -fn detectLibCFromBuilding( - arena: Allocator, - zig_lib_dir: []const u8, - target: std.Target, - has_macos_sdk: bool, -) !LibCDirs { +fn detectLibCFromBuilding(arena: Allocator, zig_lib_dir: []const u8, target: std.Target) !LibCDirs { switch (target.os.tag) { - .macos => return if (has_macos_sdk) - // For Darwin/macOS, we are all set with getDarwinSDK found earlier. - LibCDirs{ - .libc_include_dir_list = &[0][]u8{}, + .macos => { + const arch_name = @tagName(target.cpu.arch); + const os_name = try std.fmt.allocPrint(arena, "{s}.{d}", .{ + @tagName(target.os.tag), + target.os.version_range.semver.min.major, + }); + const s = std.fs.path.sep_str; + const list = try arena.alloc([]const u8, 3); + + list[0] = try std.fmt.allocPrint( + arena, + "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{s}-{s}-none", + .{ zig_lib_dir, arch_name, os_name }, + ); + list[1] = try std.fmt.allocPrint( + arena, + "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "any-{s}-any", + .{ zig_lib_dir, os_name }, + ); + list[2] = try std.fmt.allocPrint( + arena, + "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "any-macos-any", + .{zig_lib_dir}, + ); + + return LibCDirs{ + .libc_include_dir_list = list, + .libc_framework_dir_list = &[0][]u8{}, .libc_installation = null, - } - else - getZigShippedLibCIncludeDirsDarwin(arena, zig_lib_dir, target), + }; + }, else => { const generic_name = target_util.libCGenericName(target); // Some architectures are handled by the same set of headers. @@ -5034,6 +5075,7 @@ fn detectLibCFromBuilding( return LibCDirs{ .libc_include_dir_list = list, + .libc_framework_dir_list = &[0][]u8{}, .libc_installation = null, }; }, diff --git a/src/libc_installation.zig b/src/libc_installation.zig index 8466087e15c0..7661e3fa6c0e 100644 --- a/src/libc_installation.zig +++ b/src/libc_installation.zig @@ -11,6 +11,7 @@ const is_haiku = builtin.target.os.tag == .haiku; const log = std.log.scoped(.libc_installation); +const DarwinSDK = std.zig.system.darwin.DarwinSDK; const ZigWindowsSDK = @import("windows_sdk.zig").ZigWindowsSDK; /// See the render function implementation for documentation of the fields. @@ -21,6 +22,7 @@ pub const LibCInstallation = struct { msvc_lib_dir: ?[]const u8 = null, kernel32_lib_dir: ?[]const u8 = null, gcc_dir: ?[]const u8 = null, + framework_dir: ?[]const u8 = null, pub const FindError = error{ OutOfMemory, @@ -121,6 +123,11 @@ pub const LibCInstallation = struct { return error.ParseError; } + if (self.framework_dir == null and target.isDarwin()) { + log.err("framework_dir may not be empty for {s}\n", .{@tagName(os_tag)}); + return error.ParseError; + } + return self; } @@ -132,6 +139,7 @@ pub const LibCInstallation = struct { const msvc_lib_dir = self.msvc_lib_dir orelse ""; const kernel32_lib_dir = self.kernel32_lib_dir orelse ""; const gcc_dir = self.gcc_dir orelse ""; + const framework_dir = self.framework_dir orelse ""; try out.print( \\# The directory that contains `stdlib.h`. @@ -160,6 +168,8 @@ pub const LibCInstallation = struct { \\# Only needed when targeting Haiku. \\gcc_dir={s} \\ + \\# The directory that contains system frameworks (Darwin only). + \\framework_dir={s} , .{ include_dir, sys_include_dir, @@ -167,6 +177,7 @@ pub const LibCInstallation = struct { msvc_lib_dir, kernel32_lib_dir, gcc_dir, + framework_dir, }); } @@ -175,6 +186,9 @@ pub const LibCInstallation = struct { /// If enabled, will print human-friendly errors to stderr. verbose: bool = false, + + /// Darwin SDK structure. + darwin_sdk: ?DarwinSDK = null, }; /// Finds the default, native libc. @@ -182,7 +196,22 @@ pub const LibCInstallation = struct { var self: LibCInstallation = .{}; if (is_darwin) { - @panic("Darwin is handled separately via std.zig.system.darwin module"); + if (args.darwin_sdk) |sdk| { + const is_pre_11 = builtin.target.os.version_range.semver.min.major < 11; + const include_path = if (is_pre_11) "/usr/local/include" else "/usr/include"; + const framework_path = if (is_pre_11) "/Library/Frameworks" else "/System/Library/Frameworks"; + self.include_dir = try std.fmt.allocPrintZ(args.allocator, "{s}{s}", .{ + sdk.path, + include_path, + }); + self.sys_include_dir = try args.allocator.dupeZ(u8, self.include_dir.?); + self.framework_dir = try std.fmt.allocPrintZ(args.allocator, "{s}{s}", .{ + sdk.path, + framework_path, + }); + } else if (std.process.can_spawn) { + try self.findNativeIncludeDirPosix(args); + } } else if (is_windows) { if (!build_options.have_llvm) return error.WindowsSdkNotFound; @@ -316,6 +345,19 @@ pub const LibCInstallation = struct { // search in reverse order const search_path_untrimmed = search_paths.items[search_paths.items.len - path_i - 1]; const search_path = std.mem.trimLeft(u8, search_path_untrimmed, " "); + + if (is_darwin and self.framework_dir == null) { + // Path to frameworks is reported as + // "/path/to/sdk/System/Library/Frameworks (framework directory)" + // and so we parse that before trying to open it as a directory due to the nonsensical + // suffix. + if (std.mem.indexOf(u8, search_path, "(framework directory)")) |pos| { + const framework_dir = std.mem.trimRight(u8, search_path[0..pos], " "); + self.framework_dir = try allocator.dupeZ(u8, framework_dir); + continue; + } + } + var search_dir = fs.cwd().openDir(search_path, .{}) catch |err| switch (err) { error.FileNotFound, error.NotDir, @@ -344,7 +386,11 @@ pub const LibCInstallation = struct { } } - if (self.include_dir != null and self.sys_include_dir != null) { + var success = self.include_dir != null and self.sys_include_dir != null; + if (is_darwin) { + success = success and self.framework_dir != null; + } + if (success) { // Success. return; } diff --git a/src/link.zig b/src/link.zig index 703dfb87394b..5462bb7a2ab0 100644 --- a/src/link.zig +++ b/src/link.zig @@ -16,6 +16,7 @@ const Compilation = @import("Compilation.zig"); const LibCInstallation = @import("libc_installation.zig").LibCInstallation; const Liveness = @import("Liveness.zig"); const Module = @import("Module.zig"); +const NativePaths = std.zig.system.NativePaths; const InternPool = @import("InternPool.zig"); const Type = @import("type.zig").Type; const TypedValue = @import("TypedValue.zig"); @@ -204,6 +205,8 @@ pub const Options = struct { version: ?std.SemanticVersion, compatibility_version: ?std.SemanticVersion, libc_installation: ?*const LibCInstallation, + // TODO figure out if we can make it part of LibCInstallation + native_paths: ?NativePaths = null, dwarf_format: ?std.dwarf.Format, @@ -213,8 +216,8 @@ pub const Options = struct { /// (Zig compiler development) Enable dumping of linker's state as JSON. enable_link_snapshots: bool = false, - /// (Darwin) Path and version of the native SDK if detected. - native_darwin_sdk: ?std.zig.system.darwin.DarwinSDK = null, + /// (Darwin) Version of the native SDK if detected. + darwin_sdk_version: ?std.SemanticVersion = null, /// (Darwin) Install name for the dylib install_name: ?[]const u8 = null, diff --git a/src/link/Coff/lld.zig b/src/link/Coff/lld.zig index 800b12808133..82a129a5f49c 100644 --- a/src/link/Coff/lld.zig +++ b/src/link/Coff/lld.zig @@ -54,6 +54,12 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod // See link/Elf.zig for comments on how this mechanism works. const id_symlink_basename = "lld.id"; + var input_lib_dirs = std.ArrayList([]const u8).init(arena); + try input_lib_dirs.appendSlice(self.base.options.lib_dirs); + if (self.base.options.native_paths) |paths| { + try input_lib_dirs.appendSlice(paths.getLibDirs()); + } + var man: Cache.Manifest = undefined; defer if (!self.base.options.disable_lld_caching) man.deinit(); @@ -76,7 +82,7 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod man.hash.addOptionalBytes(self.base.options.entry); man.hash.addOptional(self.base.options.stack_size_override); man.hash.addOptional(self.base.options.image_base_override); - man.hash.addListOfBytes(self.base.options.lib_dirs); + man.hash.addListOfBytes(input_lib_dirs.items); man.hash.add(self.base.options.skip_linker_dependencies); if (self.base.options.link_libc) { man.hash.add(self.base.options.libc_installation != null); @@ -251,7 +257,7 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod } } - for (self.base.options.lib_dirs) |lib_dir| { + for (input_lib_dirs.items) |lib_dir| { try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{lib_dir})); } @@ -491,13 +497,13 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod argv.appendAssumeCapacity(crt_file.full_object_path); continue; } - if (try findLib(arena, lib_basename, self.base.options.lib_dirs)) |full_path| { + if (try findLib(arena, lib_basename, input_lib_dirs.items)) |full_path| { argv.appendAssumeCapacity(full_path); continue; } if (target.abi.isGnu()) { const fallback_name = try allocPrint(arena, "lib{s}.dll.a", .{key}); - if (try findLib(arena, fallback_name, self.base.options.lib_dirs)) |full_path| { + if (try findLib(arena, fallback_name, input_lib_dirs.items)) |full_path| { argv.appendAssumeCapacity(full_path); continue; } diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 4bb049e07403..2366793c9c82 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1362,6 +1362,17 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v // our digest. If so, we can skip linking. Otherwise, we proceed with invoking LLD. const id_symlink_basename = "lld.id"; + var input_lib_dirs = std.ArrayList([]const u8).init(arena); + var input_rpath_list = std.ArrayList([]const u8).init(arena); + + try input_lib_dirs.appendSlice(self.base.options.lib_dirs); + try input_rpath_list.appendSlice(self.base.options.rpath_list); + + if (self.base.options.native_paths) |paths| { + try input_lib_dirs.appendSlice(paths.getLibDirs()); + try input_rpath_list.appendSlice(paths.getRpaths()); + } + var man: Cache.Manifest = undefined; defer if (!self.base.options.disable_lld_caching) man.deinit(); @@ -1397,8 +1408,8 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v man.hash.add(self.base.options.eh_frame_hdr); man.hash.add(self.base.options.emit_relocs); man.hash.add(self.base.options.rdynamic); - man.hash.addListOfBytes(self.base.options.lib_dirs); - man.hash.addListOfBytes(self.base.options.rpath_list); + man.hash.addListOfBytes(input_lib_dirs.items); + man.hash.addListOfBytes(input_rpath_list.items); man.hash.add(self.base.options.each_lib_rpath); if (self.base.options.output_mode == .Exe) { man.hash.add(stack_size); @@ -1688,7 +1699,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v // rpaths var rpath_table = std.StringHashMap(void).init(self.base.allocator); defer rpath_table.deinit(); - for (self.base.options.rpath_list) |rpath| { + for (input_rpath_list.items) |rpath| { if ((try rpath_table.fetchPut(rpath, {})) == null) { try argv.append("-rpath"); try argv.append(rpath); @@ -1702,7 +1713,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v if (self.base.options.each_lib_rpath) { var test_path = std.ArrayList(u8).init(self.base.allocator); defer test_path.deinit(); - for (self.base.options.lib_dirs) |lib_dir_path| { + for (input_lib_dirs.items) |lib_dir_path| { for (self.base.options.system_libs.keys()) |link_lib| { test_path.clearRetainingCapacity(); const sep = fs.path.sep_str; @@ -1732,7 +1743,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v } } - for (self.base.options.lib_dirs) |lib_dir| { + for (input_lib_dirs.items) |lib_dir| { try argv.append("-L"); try argv.append(lib_dir); } diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 6953cda92977..f555ab69db27 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -498,8 +498,15 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No sub_prog_node.activate(); defer sub_prog_node.end(); + const options = &self.base.options; const module = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented; + var input_rpath_list = std.ArrayList([]const u8).init(arena); + + if (options.native_paths) |paths| { + try input_rpath_list.appendSlice(paths.getRpaths()); + } + if (self.lazy_syms.getPtr(.none)) |metadata| { // Most lazy symbols can be updated on first use, but // anyerror needs to wait for everything to be flushed. @@ -751,7 +758,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No else => {}, } - try load_commands.writeRpathLCs(self.base.allocator, &self.base.options, lc_writer); + try load_commands.writeRpathLCs(self.base.allocator, input_rpath_list.items, lc_writer); try lc_writer.writeStruct(macho.source_version_command{ .version = 0, }); diff --git a/src/link/MachO/load_commands.zig b/src/link/MachO/load_commands.zig index 10f446f19159..6a91b9ff0d80 100644 --- a/src/link/MachO/load_commands.zig +++ b/src/link/MachO/load_commands.zig @@ -247,8 +247,8 @@ const RpathIterator = struct { } }; -pub fn writeRpathLCs(gpa: Allocator, options: *const link.Options, lc_writer: anytype) !void { - var it = RpathIterator.init(gpa, options.rpath_list); +pub fn writeRpathLCs(gpa: Allocator, rpaths: []const []const u8, lc_writer: anytype) !void { + var it = RpathIterator.init(gpa, rpaths); defer it.deinit(); while (try it.next()) |rpath| { @@ -278,8 +278,7 @@ pub fn writeBuildVersionLC(options: *const link.Options, lc_writer: anytype) !vo const platform_version = @as(u32, @intCast(ver.major << 16 | ver.minor << 8)); break :blk platform_version; }; - const sdk_version = if (options.native_darwin_sdk) |sdk| blk: { - const ver = sdk.version; + const sdk_version = if (options.darwin_sdk_version) |ver| blk: { const sdk_version = @as(u32, @intCast(ver.major << 16 | ver.minor << 8)); break :blk sdk_version; } else platform_version; diff --git a/src/link/MachO/zld.zig b/src/link/MachO/zld.zig index 3e828984a96d..01769d778601 100644 --- a/src/link/MachO/zld.zig +++ b/src/link/MachO/zld.zig @@ -1,5 +1,6 @@ const std = @import("std"); const build_options = @import("build_options"); +const builtin = @import("builtin"); const assert = std.debug.assert; const dwarf = std.dwarf; const fs = std.fs; @@ -3348,6 +3349,20 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr const id_symlink_basename = "zld.id"; + var input_lib_dirs = std.ArrayList([]const u8).init(arena); + var input_framework_dirs = std.ArrayList([]const u8).init(arena); + var input_rpath_list = std.ArrayList([]const u8).init(arena); + + try input_lib_dirs.appendSlice(options.lib_dirs); + try input_framework_dirs.appendSlice(options.framework_dirs); + try input_rpath_list.appendSlice(options.rpath_list); + + if (options.native_paths) |paths| { + try input_lib_dirs.appendSlice(paths.getLibDirs()); + try input_framework_dirs.appendSlice(paths.getFrameworkDirs()); + try input_rpath_list.appendSlice(paths.getRpaths()); + } + var man: Cache.Manifest = undefined; defer if (!options.disable_lld_caching) man.deinit(); @@ -3379,10 +3394,10 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr man.hash.add(gc_sections); man.hash.add(options.dead_strip_dylibs); man.hash.add(options.strip); - man.hash.addListOfBytes(options.lib_dirs); - man.hash.addListOfBytes(options.framework_dirs); + man.hash.addListOfBytes(input_lib_dirs.items); + man.hash.addListOfBytes(input_framework_dirs.items); link.hashAddSystemLibs(&man.hash, options.frameworks); - man.hash.addListOfBytes(options.rpath_list); + man.hash.addListOfBytes(input_rpath_list.items); if (is_dyn_lib) { man.hash.addOptionalBytes(options.install_name); man.hash.addOptional(options.version); @@ -3534,7 +3549,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr } var lib_dirs = std.ArrayList([]const u8).init(arena); - for (options.lib_dirs) |dir| { + for (input_lib_dirs.items) |dir| { if (try MachO.resolveSearchDir(arena, dir, options.sysroot)) |search_dir| { try lib_dirs.append(search_dir); } else { @@ -3594,7 +3609,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr // frameworks var framework_dirs = std.ArrayList([]const u8).init(arena); - for (options.framework_dirs) |dir| { + for (input_framework_dirs.items) |dir| { if (try MachO.resolveSearchDir(arena, dir, options.sysroot)) |search_dir| { try framework_dirs.append(search_dir); } else { @@ -3651,7 +3666,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr try argv.append(syslibroot); } - for (options.rpath_list) |rpath| { + for (input_rpath_list.items) |rpath| { try argv.append("-rpath"); try argv.append(rpath); } @@ -3726,7 +3741,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr try argv.append(arg); } - for (options.lib_dirs) |lib_dir| { + for (input_lib_dirs.items) |lib_dir| { try argv.append(try std.fmt.allocPrint(arena, "-L{s}", .{lib_dir})); } @@ -3741,7 +3756,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr try argv.append(arg); } - for (options.framework_dirs) |framework_dir| { + for (input_framework_dirs.items) |framework_dir| { try argv.append(try std.fmt.allocPrint(arena, "-F{s}", .{framework_dir})); } @@ -3937,7 +3952,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr try load_commands.writeDylibIdLC(zld.gpa, zld.options, lc_writer); } - try load_commands.writeRpathLCs(zld.gpa, zld.options, lc_writer); + try load_commands.writeRpathLCs(zld.gpa, input_rpath_list.items, lc_writer); try lc_writer.writeStruct(macho.source_version_command{ .version = 0, }); diff --git a/src/main.zig b/src/main.zig index 39a7adc42446..cfb45be4fd9c 100644 --- a/src/main.zig +++ b/src/main.zig @@ -769,7 +769,7 @@ fn buildOutputType( var link_libc = false; var link_libcpp = false; var link_libunwind = false; - var want_native_include_dirs = false; + var want_native_include_dirs: ?bool = null; var want_pic: ?bool = null; var want_pie: ?bool = null; var want_lto: ?bool = null; @@ -849,7 +849,6 @@ fn buildOutputType( var minor_subsystem_version: ?u32 = null; var wasi_exec_model: ?std.builtin.WasiExecModel = null; var enable_link_snapshots: bool = false; - var native_darwin_sdk: ?std.zig.system.darwin.DarwinSDK = null; var install_name: ?[]const u8 = null; var hash_style: link.HashStyle = .both; var entitlements: ?[]const u8 = null; @@ -2569,58 +2568,6 @@ fn buildOutputType( } } - if (comptime builtin.target.isDarwin()) { - // If we want to link against frameworks, we need system headers. - if (framework_dirs.items.len > 0 or frameworks.count() > 0) - want_native_include_dirs = true; - } - - if (sysroot == null and cross_target.isNativeOs() and - (system_libs.count() != 0 or want_native_include_dirs)) - { - const paths = std.zig.system.NativePaths.detect(arena, target_info) catch |err| { - fatal("unable to detect native system paths: {s}", .{@errorName(err)}); - }; - for (paths.warnings.items) |warning| { - warn("{s}", .{warning}); - } - - const has_sysroot = if (comptime builtin.target.isDarwin()) outer: { - if (std.zig.system.darwin.isDarwinSDKInstalled(arena)) { - const sdk = std.zig.system.darwin.getDarwinSDK(arena, target_info.target) orelse - break :outer false; - native_darwin_sdk = sdk; - try clang_argv.ensureUnusedCapacity(2); - clang_argv.appendAssumeCapacity("-isysroot"); - clang_argv.appendAssumeCapacity(sdk.path); - break :outer true; - } else break :outer false; - } else false; - - try clang_argv.ensureUnusedCapacity(paths.include_dirs.items.len * 2); - const isystem_flag = if (has_sysroot) "-iwithsysroot" else "-isystem"; - for (paths.include_dirs.items) |include_dir| { - clang_argv.appendAssumeCapacity(isystem_flag); - clang_argv.appendAssumeCapacity(include_dir); - } - - try clang_argv.ensureUnusedCapacity(paths.framework_dirs.items.len * 2); - try framework_dirs.ensureUnusedCapacity(paths.framework_dirs.items.len); - const iframework_flag = if (has_sysroot) "-iframeworkwithsysroot" else "-iframework"; - for (paths.framework_dirs.items) |framework_dir| { - clang_argv.appendAssumeCapacity(iframework_flag); - clang_argv.appendAssumeCapacity(framework_dir); - framework_dirs.appendAssumeCapacity(framework_dir); - } - - for (paths.lib_dirs.items) |lib_dir| { - try lib_dirs.append(lib_dir); - } - for (paths.rpaths.items) |rpath| { - try rpath_list.append(rpath); - } - } - { // Resolve static libraries into full paths. const sep = fs.path.sep_str; @@ -3092,6 +3039,7 @@ fn buildOutputType( .link_libc = link_libc, .link_libcpp = link_libcpp, .link_libunwind = link_libunwind, + .want_native_include_dirs = want_native_include_dirs, .want_pic = want_pic, .want_pie = want_pie, .want_lto = want_lto, @@ -3192,7 +3140,6 @@ fn buildOutputType( .wasi_exec_model = wasi_exec_model, .debug_compile_errors = debug_compile_errors, .enable_link_snapshots = enable_link_snapshots, - .native_darwin_sdk = native_darwin_sdk, .install_name = install_name, .entitlements = entitlements, .pagezero_size = pagezero_size, @@ -4070,8 +4017,17 @@ pub fn cmdLibC(gpa: Allocator, args: []const []const u8) !void { fatal("unable to detect libc for non-native target", .{}); } + var darwin_sdk: ?std.zig.system.darwin.DarwinSDK = if ((comptime builtin.target.isDarwin()) and + !std.zig.system.NativePaths.isNix(gpa) and + std.zig.system.darwin.isDarwinSDKInstalled(gpa)) + std.zig.system.darwin.getDarwinSDK(gpa, builtin.target) + else + null; + defer if (darwin_sdk) |*sdk| sdk.deinit(gpa); + var libc = LibCInstallation.findNative(.{ .allocator = gpa, + .darwin_sdk = darwin_sdk, .verbose = true, }) catch |err| { fatal("unable to detect native libc: {s}", .{@errorName(err)});