authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-06-28 07:22:19+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-06-28 07:22:19+02:00
logd4623a8a07bf8fe3bed3b4ee50339960d623958e
tree949f6078a42f12415508667377b40ea0e84eac0b
parenta76775b50a65fd0ea0fd17d6ef3c42058df13997
parent0dd28920daa5127ffe5a3691343fa519f7547cfd
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #11941 from ziglang/macho-stripping-dylibs

macho: handle `-dead_strip_dylibs`, `-needed-lx` and `-needed_framework x` flags

20 files changed, 318 insertions(+), 100 deletions(-)

lib/std/build.zig+67-11
...@@ -11,7 +11,6 @@ const ArrayList = std.ArrayList;...@@ -11,7 +11,6 @@ const ArrayList = std.ArrayList;
11const StringHashMap = std.StringHashMap;11const StringHashMap = std.StringHashMap;
12const Allocator = mem.Allocator;12const Allocator = mem.Allocator;
13const process = std.process;13const process = std.process;
14const BufSet = std.BufSet;
15const EnvMap = std.process.EnvMap;14const EnvMap = std.process.EnvMap;
16const fmt_lib = std.fmt;15const fmt_lib = std.fmt;
17const File = std.fs.File;16const File = std.fs.File;
...@@ -1484,7 +1483,7 @@ pub const LibExeObjStep = struct {...@@ -1484,7 +1483,7 @@ pub const LibExeObjStep = struct {
1484 lib_paths: ArrayList([]const u8),1483 lib_paths: ArrayList([]const u8),
1485 rpaths: ArrayList([]const u8),1484 rpaths: ArrayList([]const u8),
1486 framework_dirs: ArrayList([]const u8),1485 framework_dirs: ArrayList([]const u8),
1487 frameworks: BufSet,1486 frameworks: StringHashMap(bool),
1488 verbose_link: bool,1487 verbose_link: bool,
1489 verbose_cc: bool,1488 verbose_cc: bool,
1490 emit_analysis: EmitOption = .default,1489 emit_analysis: EmitOption = .default,
...@@ -1601,6 +1600,9 @@ pub const LibExeObjStep = struct {...@@ -1601,6 +1600,9 @@ pub const LibExeObjStep = struct {
1601 /// and start of `__TEXT,__text` section to a value fitting all paths expanded to MAXPATHLEN.1600 /// and start of `__TEXT,__text` section to a value fitting all paths expanded to MAXPATHLEN.
1602 headerpad_max_install_names: bool = false,1601 headerpad_max_install_names: bool = false,
16031602
1603 /// (Darwin) Remove dylibs that are unreachable by the entry point or exported symbols.
1604 dead_strip_dylibs: bool = false,
1605
1604 /// Position Independent Code1606 /// Position Independent Code
1605 force_pic: ?bool = null,1607 force_pic: ?bool = null,
16061608
...@@ -1640,6 +1642,7 @@ pub const LibExeObjStep = struct {...@@ -1640,6 +1642,7 @@ pub const LibExeObjStep = struct {
16401642
1641 pub const SystemLib = struct {1643 pub const SystemLib = struct {
1642 name: []const u8,1644 name: []const u8,
1645 needed: bool,
1643 use_pkg_config: enum {1646 use_pkg_config: enum {
1644 /// Don't use pkg-config, just pass -lfoo where foo is name.1647 /// Don't use pkg-config, just pass -lfoo where foo is name.
1645 no,1648 no,
...@@ -1741,7 +1744,7 @@ pub const LibExeObjStep = struct {...@@ -1741,7 +1744,7 @@ pub const LibExeObjStep = struct {
1741 .kind = kind,1744 .kind = kind,
1742 .root_src = root_src,1745 .root_src = root_src,
1743 .name = name,1746 .name = name,
1744 .frameworks = BufSet.init(builder.allocator),1747 .frameworks = StringHashMap(bool).init(builder.allocator),
1745 .step = Step.init(base_id, name, builder.allocator, make),1748 .step = Step.init(base_id, name, builder.allocator, make),
1746 .version = ver,1749 .version = ver,
1747 .out_filename = undefined,1750 .out_filename = undefined,
...@@ -1890,8 +1893,11 @@ pub const LibExeObjStep = struct {...@@ -1890,8 +1893,11 @@ pub const LibExeObjStep = struct {
1890 }1893 }
18911894
1892 pub fn linkFramework(self: *LibExeObjStep, framework_name: []const u8) void {1895 pub fn linkFramework(self: *LibExeObjStep, framework_name: []const u8) void {
1893 // Note: No need to dupe because frameworks dupes internally.1896 self.frameworks.put(self.builder.dupe(framework_name), false) catch unreachable;
1894 self.frameworks.insert(framework_name) catch unreachable;1897 }
1898
1899 pub fn linkFrameworkNeeded(self: *LibExeObjStep, framework_name: []const u8) void {
1900 self.frameworks.put(self.builder.dupe(framework_name), true) catch unreachable;
1895 }1901 }
18961902
1897 /// Returns whether the library, executable, or object depends on a particular system library.1903 /// Returns whether the library, executable, or object depends on a particular system library.
...@@ -1932,6 +1938,7 @@ pub const LibExeObjStep = struct {...@@ -1932,6 +1938,7 @@ pub const LibExeObjStep = struct {
1932 self.link_objects.append(.{1938 self.link_objects.append(.{
1933 .system_lib = .{1939 .system_lib = .{
1934 .name = "c",1940 .name = "c",
1941 .needed = false,
1935 .use_pkg_config = .no,1942 .use_pkg_config = .no,
1936 },1943 },
1937 }) catch unreachable;1944 }) catch unreachable;
...@@ -1944,6 +1951,7 @@ pub const LibExeObjStep = struct {...@@ -1944,6 +1951,7 @@ pub const LibExeObjStep = struct {
1944 self.link_objects.append(.{1951 self.link_objects.append(.{
1945 .system_lib = .{1952 .system_lib = .{
1946 .name = "c++",1953 .name = "c++",
1954 .needed = false,
1947 .use_pkg_config = .no,1955 .use_pkg_config = .no,
1948 },1956 },
1949 }) catch unreachable;1957 }) catch unreachable;
...@@ -1968,6 +1976,19 @@ pub const LibExeObjStep = struct {...@@ -1968,6 +1976,19 @@ pub const LibExeObjStep = struct {
1968 self.link_objects.append(.{1976 self.link_objects.append(.{
1969 .system_lib = .{1977 .system_lib = .{
1970 .name = self.builder.dupe(name),1978 .name = self.builder.dupe(name),
1979 .needed = false,
1980 .use_pkg_config = .no,
1981 },
1982 }) catch unreachable;
1983 }
1984
1985 /// This one has no integration with anything, it just puts -needed-lname on the command line.
1986 /// Prefer to use `linkSystemLibraryNeeded` instead.
1987 pub fn linkSystemLibraryNeededName(self: *LibExeObjStep, name: []const u8) void {
1988 self.link_objects.append(.{
1989 .system_lib = .{
1990 .name = self.builder.dupe(name),
1991 .needed = true,
1971 .use_pkg_config = .no,1992 .use_pkg_config = .no,
1972 },1993 },
1973 }) catch unreachable;1994 }) catch unreachable;
...@@ -1979,6 +2000,19 @@ pub const LibExeObjStep = struct {...@@ -1979,6 +2000,19 @@ pub const LibExeObjStep = struct {
1979 self.link_objects.append(.{2000 self.link_objects.append(.{
1980 .system_lib = .{2001 .system_lib = .{
1981 .name = self.builder.dupe(lib_name),2002 .name = self.builder.dupe(lib_name),
2003 .needed = false,
2004 .use_pkg_config = .force,
2005 },
2006 }) catch unreachable;
2007 }
2008
2009 /// This links against a system library, exclusively using pkg-config to find the library.
2010 /// Prefer to use `linkSystemLibraryNeeded` instead.
2011 pub fn linkSystemLibraryNeededPkgConfigOnly(self: *LibExeObjStep, lib_name: []const u8) void {
2012 self.link_objects.append(.{
2013 .system_lib = .{
2014 .name = self.builder.dupe(lib_name),
2015 .needed = true,
1982 .use_pkg_config = .force,2016 .use_pkg_config = .force,
1983 },2017 },
1984 }) catch unreachable;2018 }) catch unreachable;
...@@ -2081,6 +2115,14 @@ pub const LibExeObjStep = struct {...@@ -2081,6 +2115,14 @@ pub const LibExeObjStep = struct {
2081 }2115 }
20822116
2083 pub fn linkSystemLibrary(self: *LibExeObjStep, name: []const u8) void {2117 pub fn linkSystemLibrary(self: *LibExeObjStep, name: []const u8) void {
2118 self.linkSystemLibraryInner(name, false);
2119 }
2120
2121 pub fn linkSystemLibraryNeeded(self: *LibExeObjStep, name: []const u8) void {
2122 self.linkSystemLibraryInner(name, true);
2123 }
2124
2125 fn linkSystemLibraryInner(self: *LibExeObjStep, name: []const u8, needed: bool) void {
2084 if (isLibCLibrary(name)) {2126 if (isLibCLibrary(name)) {
2085 self.linkLibC();2127 self.linkLibC();
2086 return;2128 return;
...@@ -2093,6 +2135,7 @@ pub const LibExeObjStep = struct {...@@ -2093,6 +2135,7 @@ pub const LibExeObjStep = struct {
2093 self.link_objects.append(.{2135 self.link_objects.append(.{
2094 .system_lib = .{2136 .system_lib = .{
2095 .name = self.builder.dupe(name),2137 .name = self.builder.dupe(name),
2138 .needed = needed,
2096 .use_pkg_config = .yes,2139 .use_pkg_config = .yes,
2097 },2140 },
2098 }) catch unreachable;2141 }) catch unreachable;
...@@ -2434,7 +2477,7 @@ pub const LibExeObjStep = struct {...@@ -2434,7 +2477,7 @@ pub const LibExeObjStep = struct {
2434 if (!other.isDynamicLibrary()) {2477 if (!other.isDynamicLibrary()) {
2435 var it = other.frameworks.iterator();2478 var it = other.frameworks.iterator();
2436 while (it.next()) |framework| {2479 while (it.next()) |framework| {
2437 self.frameworks.insert(framework.*) catch unreachable;2480 self.frameworks.put(framework.key_ptr.*, framework.value_ptr.*) catch unreachable;
2438 }2481 }
2439 }2482 }
2440 },2483 },
...@@ -2470,8 +2513,9 @@ pub const LibExeObjStep = struct {...@@ -2470,8 +2513,9 @@ pub const LibExeObjStep = struct {
2470 },2513 },
24712514
2472 .system_lib => |system_lib| {2515 .system_lib => |system_lib| {
2516 const prefix: []const u8 = if (system_lib.needed) "-needed-l" else "-l";
2473 switch (system_lib.use_pkg_config) {2517 switch (system_lib.use_pkg_config) {
2474 .no => try zig_args.append(builder.fmt("-l{s}", .{system_lib.name})),2518 .no => try zig_args.append(builder.fmt("{s}{s}", .{ prefix, system_lib.name })),
2475 .yes, .force => {2519 .yes, .force => {
2476 if (self.runPkgConfig(system_lib.name)) |args| {2520 if (self.runPkgConfig(system_lib.name)) |args| {
2477 try zig_args.appendSlice(args);2521 try zig_args.appendSlice(args);
...@@ -2485,7 +2529,10 @@ pub const LibExeObjStep = struct {...@@ -2485,7 +2529,10 @@ pub const LibExeObjStep = struct {
2485 .yes => {2529 .yes => {
2486 // pkg-config failed, so fall back to linking the library2530 // pkg-config failed, so fall back to linking the library
2487 // by name directly.2531 // by name directly.
2488 try zig_args.append(builder.fmt("-l{s}", .{system_lib.name}));2532 try zig_args.append(builder.fmt("{s}{s}", .{
2533 prefix,
2534 system_lib.name,
2535 }));
2489 },2536 },
2490 .force => {2537 .force => {
2491 panic("pkg-config failed for library {s}", .{system_lib.name});2538 panic("pkg-config failed for library {s}", .{system_lib.name});
...@@ -2676,6 +2723,9 @@ pub const LibExeObjStep = struct {...@@ -2676,6 +2723,9 @@ pub const LibExeObjStep = struct {
2676 if (self.headerpad_max_install_names) {2723 if (self.headerpad_max_install_names) {
2677 try zig_args.append("-headerpad_max_install_names");2724 try zig_args.append("-headerpad_max_install_names");
2678 }2725 }
2726 if (self.dead_strip_dylibs) {
2727 try zig_args.append("-dead_strip_dylibs");
2728 }
26792729
2680 if (self.bundle_compiler_rt) |x| {2730 if (self.bundle_compiler_rt) |x| {
2681 if (x) {2731 if (x) {
...@@ -2966,9 +3016,15 @@ pub const LibExeObjStep = struct {...@@ -2966,9 +3016,15 @@ pub const LibExeObjStep = struct {
2966 }3016 }
29673017
2968 var it = self.frameworks.iterator();3018 var it = self.frameworks.iterator();
2969 while (it.next()) |framework| {3019 while (it.next()) |entry| {
2970 zig_args.append("-framework") catch unreachable;3020 const name = entry.key_ptr.*;
2971 zig_args.append(framework.*) catch unreachable;3021 const needed = entry.value_ptr.*;
3022 if (needed) {
3023 zig_args.append("-needed_framework") catch unreachable;
3024 } else {
3025 zig_args.append("-framework") catch unreachable;
3026 }
3027 zig_args.append(name) catch unreachable;
2972 }3028 }
2973 } else {3029 } else {
2974 if (self.framework_dirs.items.len > 0) {3030 if (self.framework_dirs.items.len > 0) {
src/Compilation.zig+10-6
...@@ -791,7 +791,7 @@ pub const InitOptions = struct {...@@ -791,7 +791,7 @@ pub const InitOptions = struct {
791 c_source_files: []const CSourceFile = &[0]CSourceFile{},791 c_source_files: []const CSourceFile = &[0]CSourceFile{},
792 link_objects: []LinkObject = &[0]LinkObject{},792 link_objects: []LinkObject = &[0]LinkObject{},
793 framework_dirs: []const []const u8 = &[0][]const u8{},793 framework_dirs: []const []const u8 = &[0][]const u8{},
794 frameworks: []const []const u8 = &[0][]const u8{},794 frameworks: std.StringArrayHashMapUnmanaged(SystemLib) = .{},
795 system_lib_names: []const []const u8 = &.{},795 system_lib_names: []const []const u8 = &.{},
796 system_lib_infos: []const SystemLib = &.{},796 system_lib_infos: []const SystemLib = &.{},
797 /// These correspond to the WASI libc emulated subcomponents including:797 /// These correspond to the WASI libc emulated subcomponents including:
...@@ -911,6 +911,8 @@ pub const InitOptions = struct {...@@ -911,6 +911,8 @@ pub const InitOptions = struct {
911 headerpad_size: ?u32 = null,911 headerpad_size: ?u32 = null,
912 /// (Darwin) set enough space as if all paths were MATPATHLEN912 /// (Darwin) set enough space as if all paths were MATPATHLEN
913 headerpad_max_install_names: bool = false,913 headerpad_max_install_names: bool = false,
914 /// (Darwin) remove dylibs that are unreachable by the entry point or exported symbols
915 dead_strip_dylibs: bool = false,
914};916};
915917
916fn addPackageTableToCacheHash(918fn addPackageTableToCacheHash(
...@@ -1095,7 +1097,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {...@@ -1095,7 +1097,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
1095 // Our linker can't handle objects or most advanced options yet.1097 // Our linker can't handle objects or most advanced options yet.
1096 if (options.link_objects.len != 0 or1098 if (options.link_objects.len != 0 or
1097 options.c_source_files.len != 0 or1099 options.c_source_files.len != 0 or
1098 options.frameworks.len != 0 or1100 options.frameworks.count() != 0 or
1099 options.system_lib_names.len != 0 or1101 options.system_lib_names.len != 0 or
1100 options.link_libc or options.link_libcpp or1102 options.link_libc or options.link_libcpp or
1101 link_eh_frame_hdr or1103 link_eh_frame_hdr or
...@@ -1213,7 +1215,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {...@@ -1213,7 +1215,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
1213 options.target,1215 options.target,
1214 options.is_native_abi,1216 options.is_native_abi,
1215 link_libc,1217 link_libc,
1216 options.system_lib_names.len != 0 or options.frameworks.len != 0,1218 options.system_lib_names.len != 0 or options.frameworks.count() != 0,
1217 options.libc_installation,1219 options.libc_installation,
1218 options.native_darwin_sdk != null,1220 options.native_darwin_sdk != null,
1219 );1221 );
...@@ -1754,6 +1756,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {...@@ -1754,6 +1756,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
1754 .search_strategy = options.search_strategy,1756 .search_strategy = options.search_strategy,
1755 .headerpad_size = options.headerpad_size,1757 .headerpad_size = options.headerpad_size,
1756 .headerpad_max_install_names = options.headerpad_max_install_names,1758 .headerpad_max_install_names = options.headerpad_max_install_names,
1759 .dead_strip_dylibs = options.dead_strip_dylibs,
1757 });1760 });
1758 errdefer bin_file.destroy();1761 errdefer bin_file.destroy();
1759 comp.* = .{1762 comp.* = .{
...@@ -2369,7 +2372,7 @@ fn prepareWholeEmitSubPath(arena: Allocator, opt_emit: ?EmitLoc) error{OutOfMemo...@@ -2369,7 +2372,7 @@ fn prepareWholeEmitSubPath(arena: Allocator, opt_emit: ?EmitLoc) error{OutOfMemo
2369/// to remind the programmer to update multiple related pieces of code that2372/// to remind the programmer to update multiple related pieces of code that
2370/// are in different locations. Bump this number when adding or deleting2373/// are in different locations. Bump this number when adding or deleting
2371/// anything from the link cache manifest.2374/// anything from the link cache manifest.
2372pub const link_hash_implementation_version = 6;2375pub const link_hash_implementation_version = 7;
23732376
2374fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifest) !void {2377fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifest) !void {
2375 const gpa = comp.gpa;2378 const gpa = comp.gpa;
...@@ -2379,7 +2382,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes...@@ -2379,7 +2382,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes
2379 defer arena_allocator.deinit();2382 defer arena_allocator.deinit();
2380 const arena = arena_allocator.allocator();2383 const arena = arena_allocator.allocator();
23812384
2382 comptime assert(link_hash_implementation_version == 6);2385 comptime assert(link_hash_implementation_version == 7);
23832386
2384 if (comp.bin_file.options.module) |mod| {2387 if (comp.bin_file.options.module) |mod| {
2385 const main_zig_file = try mod.main_pkg.root_src_directory.join(arena, &[_][]const u8{2388 const main_zig_file = try mod.main_pkg.root_src_directory.join(arena, &[_][]const u8{
...@@ -2482,12 +2485,13 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes...@@ -2482,12 +2485,13 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes
24822485
2483 // Mach-O specific stuff2486 // Mach-O specific stuff
2484 man.hash.addListOfBytes(comp.bin_file.options.framework_dirs);2487 man.hash.addListOfBytes(comp.bin_file.options.framework_dirs);
2485 man.hash.addListOfBytes(comp.bin_file.options.frameworks);2488 link.hashAddSystemLibs(&man.hash, comp.bin_file.options.frameworks);
2486 try man.addOptionalFile(comp.bin_file.options.entitlements);2489 try man.addOptionalFile(comp.bin_file.options.entitlements);
2487 man.hash.addOptional(comp.bin_file.options.pagezero_size);2490 man.hash.addOptional(comp.bin_file.options.pagezero_size);
2488 man.hash.addOptional(comp.bin_file.options.search_strategy);2491 man.hash.addOptional(comp.bin_file.options.search_strategy);
2489 man.hash.addOptional(comp.bin_file.options.headerpad_size);2492 man.hash.addOptional(comp.bin_file.options.headerpad_size);
2490 man.hash.add(comp.bin_file.options.headerpad_max_install_names);2493 man.hash.add(comp.bin_file.options.headerpad_max_install_names);
2494 man.hash.add(comp.bin_file.options.dead_strip_dylibs);
24912495
2492 // COFF specific stuff2496 // COFF specific stuff
2493 man.hash.addOptional(comp.bin_file.options.subsystem);2497 man.hash.addOptional(comp.bin_file.options.subsystem);
src/link.zig+4-1
...@@ -162,7 +162,7 @@ pub const Options = struct {...@@ -162,7 +162,7 @@ pub const Options = struct {
162162
163 objects: []Compilation.LinkObject,163 objects: []Compilation.LinkObject,
164 framework_dirs: []const []const u8,164 framework_dirs: []const []const u8,
165 frameworks: []const []const u8,165 frameworks: std.StringArrayHashMapUnmanaged(SystemLib),
166 system_libs: std.StringArrayHashMapUnmanaged(SystemLib),166 system_libs: std.StringArrayHashMapUnmanaged(SystemLib),
167 wasi_emulated_libs: []const wasi_libc.CRTFile,167 wasi_emulated_libs: []const wasi_libc.CRTFile,
168 lib_dirs: []const []const u8,168 lib_dirs: []const []const u8,
...@@ -199,6 +199,9 @@ pub const Options = struct {...@@ -199,6 +199,9 @@ pub const Options = struct {
199 /// (Darwin) set enough space as if all paths were MATPATHLEN199 /// (Darwin) set enough space as if all paths were MATPATHLEN
200 headerpad_max_install_names: bool = false,200 headerpad_max_install_names: bool = false,
201201
202 /// (Darwin) remove dylibs that are unreachable by the entry point or exported symbols
203 dead_strip_dylibs: bool = false,
204
202 pub fn effectiveOutputMode(options: Options) std.builtin.OutputMode {205 pub fn effectiveOutputMode(options: Options) std.builtin.OutputMode {
203 return if (options.use_lld) .Obj else options.output_mode;206 return if (options.use_lld) .Obj else options.output_mode;
204 }207 }
src/link/Coff.zig+1-1
...@@ -969,7 +969,7 @@ fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Node) !...@@ -969,7 +969,7 @@ fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Node) !
969 man = comp.cache_parent.obtain();969 man = comp.cache_parent.obtain();
970 self.base.releaseLock();970 self.base.releaseLock();
971971
972 comptime assert(Compilation.link_hash_implementation_version == 6);972 comptime assert(Compilation.link_hash_implementation_version == 7);
973973
974 for (self.base.options.objects) |obj| {974 for (self.base.options.objects) |obj| {
975 _ = try man.addFile(obj.path, null);975 _ = try man.addFile(obj.path, null);
src/link/Elf.zig+1-1
...@@ -1298,7 +1298,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v...@@ -1298,7 +1298,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
1298 // We are about to obtain this lock, so here we give other processes a chance first.1298 // We are about to obtain this lock, so here we give other processes a chance first.
1299 self.base.releaseLock();1299 self.base.releaseLock();
13001300
1301 comptime assert(Compilation.link_hash_implementation_version == 6);1301 comptime assert(Compilation.link_hash_implementation_version == 7);
13021302
1303 try man.addOptionalFile(self.base.options.linker_script);1303 try man.addOptionalFile(self.base.options.linker_script);
1304 try man.addOptionalFile(self.base.options.version_script);1304 try man.addOptionalFile(self.base.options.version_script);
src/link/MachO.zig+58-28
...@@ -541,7 +541,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -541,7 +541,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
541 // We are about to obtain this lock, so here we give other processes a chance first.541 // We are about to obtain this lock, so here we give other processes a chance first.
542 self.base.releaseLock();542 self.base.releaseLock();
543543
544 comptime assert(Compilation.link_hash_implementation_version == 6);544 comptime assert(Compilation.link_hash_implementation_version == 7);
545545
546 for (self.base.options.objects) |obj| {546 for (self.base.options.objects) |obj| {
547 _ = try man.addFile(obj.path, null);547 _ = try man.addFile(obj.path, null);
...@@ -558,9 +558,10 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -558,9 +558,10 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
558 man.hash.addOptional(self.base.options.search_strategy);558 man.hash.addOptional(self.base.options.search_strategy);
559 man.hash.addOptional(self.base.options.headerpad_size);559 man.hash.addOptional(self.base.options.headerpad_size);
560 man.hash.add(self.base.options.headerpad_max_install_names);560 man.hash.add(self.base.options.headerpad_max_install_names);
561 man.hash.add(self.base.options.dead_strip_dylibs);
561 man.hash.addListOfBytes(self.base.options.lib_dirs);562 man.hash.addListOfBytes(self.base.options.lib_dirs);
562 man.hash.addListOfBytes(self.base.options.framework_dirs);563 man.hash.addListOfBytes(self.base.options.framework_dirs);
563 man.hash.addListOfBytes(self.base.options.frameworks);564 link.hashAddSystemLibs(&man.hash, self.base.options.frameworks);
564 man.hash.addListOfBytes(self.base.options.rpath_list);565 man.hash.addListOfBytes(self.base.options.rpath_list);
565 if (is_dyn_lib) {566 if (is_dyn_lib) {
566 man.hash.addOptionalBytes(self.base.options.install_name);567 man.hash.addOptionalBytes(self.base.options.install_name);
...@@ -767,19 +768,20 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -767,19 +768,20 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
767 }768 }
768769
769 // Shared and static libraries passed via `-l` flag.770 // Shared and static libraries passed via `-l` flag.
770 var search_lib_names = std.ArrayList([]const u8).init(arena);771 var candidate_libs = std.StringArrayHashMap(Compilation.SystemLib).init(arena);
771772
772 const system_libs = self.base.options.system_libs.keys();773 const system_lib_names = self.base.options.system_libs.keys();
773 for (system_libs) |link_lib| {774 for (system_lib_names) |system_lib_name| {
774 // By this time, we depend on these libs being dynamically linked libraries and not static libraries775 // By this time, we depend on these libs being dynamically linked libraries and not static libraries
775 // (the check for that needs to be earlier), but they could be full paths to .dylib files, in which776 // (the check for that needs to be earlier), but they could be full paths to .dylib files, in which
776 // case we want to avoid prepending "-l".777 // case we want to avoid prepending "-l".
777 if (Compilation.classifyFileExt(link_lib) == .shared_library) {778 if (Compilation.classifyFileExt(system_lib_name) == .shared_library) {
778 try positionals.append(link_lib);779 try positionals.append(system_lib_name);
779 continue;780 continue;
780 }781 }
781782
782 try search_lib_names.append(link_lib);783 const system_lib_info = self.base.options.system_libs.get(system_lib_name).?;
784 try candidate_libs.put(system_lib_name, system_lib_info);
783 }785 }
784786
785 var lib_dirs = std.ArrayList([]const u8).init(arena);787 var lib_dirs = std.ArrayList([]const u8).init(arena);
...@@ -791,18 +793,18 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -791,18 +793,18 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
791 }793 }
792 }794 }
793795
794 var libs = std.ArrayList([]const u8).init(arena);796 var libs = std.StringArrayHashMap(Compilation.SystemLib).init(arena);
795797
796 // Assume ld64 default -search_paths_first if no strategy specified.798 // Assume ld64 default -search_paths_first if no strategy specified.
797 const search_strategy = self.base.options.search_strategy orelse .paths_first;799 const search_strategy = self.base.options.search_strategy orelse .paths_first;
798 outer: for (search_lib_names.items) |lib_name| {800 outer: for (candidate_libs.keys()) |lib_name| {
799 switch (search_strategy) {801 switch (search_strategy) {
800 .paths_first => {802 .paths_first => {
801 // Look in each directory for a dylib (stub first), and then for archive803 // Look in each directory for a dylib (stub first), and then for archive
802 for (lib_dirs.items) |dir| {804 for (lib_dirs.items) |dir| {
803 for (&[_][]const u8{ ".tbd", ".dylib", ".a" }) |ext| {805 for (&[_][]const u8{ ".tbd", ".dylib", ".a" }) |ext| {
804 if (try resolveLib(arena, dir, lib_name, ext)) |full_path| {806 if (try resolveLib(arena, dir, lib_name, ext)) |full_path| {
805 try libs.append(full_path);807 try libs.put(full_path, candidate_libs.get(lib_name).?);
806 continue :outer;808 continue :outer;
807 }809 }
808 }810 }
...@@ -816,13 +818,13 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -816,13 +818,13 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
816 for (lib_dirs.items) |dir| {818 for (lib_dirs.items) |dir| {
817 for (&[_][]const u8{ ".tbd", ".dylib" }) |ext| {819 for (&[_][]const u8{ ".tbd", ".dylib" }) |ext| {
818 if (try resolveLib(arena, dir, lib_name, ext)) |full_path| {820 if (try resolveLib(arena, dir, lib_name, ext)) |full_path| {
819 try libs.append(full_path);821 try libs.put(full_path, candidate_libs.get(lib_name).?);
820 continue :outer;822 continue :outer;
821 }823 }
822 }824 }
823 } else for (lib_dirs.items) |dir| {825 } else for (lib_dirs.items) |dir| {
824 if (try resolveLib(arena, dir, lib_name, ".a")) |full_path| {826 if (try resolveLib(arena, dir, lib_name, ".a")) |full_path| {
825 try libs.append(full_path);827 try libs.put(full_path, candidate_libs.get(lib_name).?);
826 } else {828 } else {
827 log.warn("library not found for '-l{s}'", .{lib_name});829 log.warn("library not found for '-l{s}'", .{lib_name});
828 lib_not_found = true;830 lib_not_found = true;
...@@ -846,7 +848,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -846,7 +848,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
846 // re-exports every single symbol definition.848 // re-exports every single symbol definition.
847 for (lib_dirs.items) |dir| {849 for (lib_dirs.items) |dir| {
848 if (try resolveLib(arena, dir, "System", ".tbd")) |full_path| {850 if (try resolveLib(arena, dir, "System", ".tbd")) |full_path| {
849 try libs.append(full_path);851 try libs.put(full_path, .{ .needed = false });
850 libsystem_available = true;852 libsystem_available = true;
851 break :blk;853 break :blk;
852 }854 }
...@@ -856,8 +858,8 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -856,8 +858,8 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
856 for (lib_dirs.items) |dir| {858 for (lib_dirs.items) |dir| {
857 if (try resolveLib(arena, dir, "System", ".dylib")) |libsystem_path| {859 if (try resolveLib(arena, dir, "System", ".dylib")) |libsystem_path| {
858 if (try resolveLib(arena, dir, "c", ".dylib")) |libc_path| {860 if (try resolveLib(arena, dir, "c", ".dylib")) |libc_path| {
859 try libs.append(libsystem_path);861 try libs.put(libsystem_path, .{ .needed = false });
860 try libs.append(libc_path);862 try libs.put(libc_path, .{ .needed = false });
861 libsystem_available = true;863 libsystem_available = true;
862 break :blk;864 break :blk;
863 }865 }
...@@ -871,7 +873,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -871,7 +873,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
871 const full_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{873 const full_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{
872 "libc", "darwin", libsystem_name,874 "libc", "darwin", libsystem_name,
873 });875 });
874 try libs.append(full_path);876 try libs.put(full_path, .{ .needed = false });
875 }877 }
876878
877 // frameworks879 // frameworks
...@@ -884,16 +886,16 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -884,16 +886,16 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
884 }886 }
885 }887 }
886888
887 outer: for (self.base.options.frameworks) |framework| {889 outer: for (self.base.options.frameworks.keys()) |f_name| {
888 for (framework_dirs.items) |dir| {890 for (framework_dirs.items) |dir| {
889 for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| {891 for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| {
890 if (try resolveFramework(arena, dir, framework, ext)) |full_path| {892 if (try resolveFramework(arena, dir, f_name, ext)) |full_path| {
891 try libs.append(full_path);893 try libs.put(full_path, self.base.options.frameworks.get(f_name).?);
892 continue :outer;894 continue :outer;
893 }895 }
894 }896 }
895 } else {897 } else {
896 log.warn("framework not found for '-framework {s}'", .{framework});898 log.warn("framework not found for '-framework {s}'", .{f_name});
897 framework_not_found = true;899 framework_not_found = true;
898 }900 }
899 }901 }
...@@ -987,6 +989,10 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -987,6 +989,10 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
987 try argv.append("-headerpad_max_install_names");989 try argv.append("-headerpad_max_install_names");
988 }990 }
989991
992 if (self.base.options.dead_strip_dylibs) {
993 try argv.append("-dead_strip_dylibs");
994 }
995
990 if (self.base.options.entry) |entry| {996 if (self.base.options.entry) |entry| {
991 try argv.append("-e");997 try argv.append("-e");
992 try argv.append(entry);998 try argv.append(entry);
...@@ -1020,15 +1026,25 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -1020,15 +1026,25 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
1020 try argv.append("-lc");1026 try argv.append("-lc");
10211027
1022 for (self.base.options.system_libs.keys()) |l_name| {1028 for (self.base.options.system_libs.keys()) |l_name| {
1023 try argv.append(try std.fmt.allocPrint(arena, "-l{s}", .{l_name}));1029 const needed = self.base.options.system_libs.get(l_name).?.needed;
1030 const arg = if (needed)
1031 try std.fmt.allocPrint(arena, "-needed-l{s}", .{l_name})
1032 else
1033 try std.fmt.allocPrint(arena, "-l{s}", .{l_name});
1034 try argv.append(arg);
1024 }1035 }
10251036
1026 for (self.base.options.lib_dirs) |lib_dir| {1037 for (self.base.options.lib_dirs) |lib_dir| {
1027 try argv.append(try std.fmt.allocPrint(arena, "-L{s}", .{lib_dir}));1038 try argv.append(try std.fmt.allocPrint(arena, "-L{s}", .{lib_dir}));
1028 }1039 }
10291040
1030 for (self.base.options.frameworks) |framework| {1041 for (self.base.options.frameworks.keys()) |framework| {
1031 try argv.append(try std.fmt.allocPrint(arena, "-framework {s}", .{framework}));1042 const needed = self.base.options.frameworks.get(framework).?.needed;
1043 const arg = if (needed)
1044 try std.fmt.allocPrint(arena, "-needed_framework {s}", .{framework})
1045 else
1046 try std.fmt.allocPrint(arena, "-framework {s}", .{framework});
1047 try argv.append(arg);
1032 }1048 }
10331049
1034 for (self.base.options.framework_dirs) |framework_dir| {1050 for (self.base.options.framework_dirs) |framework_dir| {
...@@ -1051,7 +1067,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -1051,7 +1067,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
1051 defer dependent_libs.deinit();1067 defer dependent_libs.deinit();
1052 try self.parseInputFiles(positionals.items, self.base.options.sysroot, &dependent_libs);1068 try self.parseInputFiles(positionals.items, self.base.options.sysroot, &dependent_libs);
1053 try self.parseAndForceLoadStaticArchives(must_link_archives.keys());1069 try self.parseAndForceLoadStaticArchives(must_link_archives.keys());
1054 try self.parseLibs(libs.items, self.base.options.sysroot, &dependent_libs);1070 try self.parseLibs(libs.keys(), libs.values(), self.base.options.sysroot, &dependent_libs);
1055 try self.parseDependentLibs(self.base.options.sysroot, &dependent_libs);1071 try self.parseDependentLibs(self.base.options.sysroot, &dependent_libs);
1056 }1072 }
10571073
...@@ -1376,6 +1392,7 @@ const DylibCreateOpts = struct {...@@ -1376,6 +1392,7 @@ const DylibCreateOpts = struct {
1376 dependent_libs: *std.fifo.LinearFifo(Dylib.Id, .Dynamic),1392 dependent_libs: *std.fifo.LinearFifo(Dylib.Id, .Dynamic),
1377 id: ?Dylib.Id = null,1393 id: ?Dylib.Id = null,
1378 is_dependent: bool = false,1394 is_dependent: bool = false,
1395 is_needed: bool = false,
1379};1396};
13801397
1381pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDylibError!bool {1398pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDylibError!bool {
...@@ -1425,7 +1442,12 @@ pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDy...@@ -1425,7 +1442,12 @@ pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDy
1425 try self.dylibs.append(self.base.allocator, dylib);1442 try self.dylibs.append(self.base.allocator, dylib);
1426 try self.dylibs_map.putNoClobber(self.base.allocator, dylib.id.?.name, dylib_id);1443 try self.dylibs_map.putNoClobber(self.base.allocator, dylib.id.?.name, dylib_id);
14271444
1428 if (!(opts.is_dependent or self.referenced_dylibs.contains(dylib_id))) {1445 const should_link_dylib_even_if_unreachable = blk: {
1446 if (self.base.options.dead_strip_dylibs and !opts.is_needed) break :blk false;
1447 break :blk !(opts.is_dependent or self.referenced_dylibs.contains(dylib_id));
1448 };
1449
1450 if (should_link_dylib_even_if_unreachable) {
1429 try self.addLoadDylibLC(dylib_id);1451 try self.addLoadDylibLC(dylib_id);
1430 try self.referenced_dylibs.putNoClobber(self.base.allocator, dylib_id, {});1452 try self.referenced_dylibs.putNoClobber(self.base.allocator, dylib_id, {});
1431 }1453 }
...@@ -1469,12 +1491,20 @@ fn parseAndForceLoadStaticArchives(self: *MachO, files: []const []const u8) !voi...@@ -1469,12 +1491,20 @@ fn parseAndForceLoadStaticArchives(self: *MachO, files: []const []const u8) !voi
1469 }1491 }
1470}1492}
14711493
1472fn parseLibs(self: *MachO, libs: []const []const u8, syslibroot: ?[]const u8, dependent_libs: anytype) !void {1494fn parseLibs(
1473 for (libs) |lib| {1495 self: *MachO,
1496 lib_names: []const []const u8,
1497 lib_infos: []const Compilation.SystemLib,
1498 syslibroot: ?[]const u8,
1499 dependent_libs: anytype,
1500) !void {
1501 for (lib_names) |lib, i| {
1502 const lib_info = lib_infos[i];
1474 log.debug("parsing lib path '{s}'", .{lib});1503 log.debug("parsing lib path '{s}'", .{lib});
1475 if (try self.parseDylib(lib, .{1504 if (try self.parseDylib(lib, .{
1476 .syslibroot = syslibroot,1505 .syslibroot = syslibroot,
1477 .dependent_libs = dependent_libs,1506 .dependent_libs = dependent_libs,
1507 .is_needed = lib_info.needed,
1478 })) continue;1508 })) continue;
1479 if (try self.parseArchive(lib, false)) continue;1509 if (try self.parseArchive(lib, false)) continue;
14801510
src/link/Wasm.zig+1-1
...@@ -2546,7 +2546,7 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !...@@ -2546,7 +2546,7 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !
2546 // We are about to obtain this lock, so here we give other processes a chance first.2546 // We are about to obtain this lock, so here we give other processes a chance first.
2547 self.base.releaseLock();2547 self.base.releaseLock();
25482548
2549 comptime assert(Compilation.link_hash_implementation_version == 6);2549 comptime assert(Compilation.link_hash_implementation_version == 7);
25502550
2551 for (self.base.options.objects) |obj| {2551 for (self.base.options.objects) |obj| {
2552 _ = try man.addFile(obj.path, null);2552 _ = try man.addFile(obj.path, null);
src/main.zig+38-9
...@@ -444,6 +444,8 @@ const usage_build_generic =...@@ -444,6 +444,8 @@ const usage_build_generic =
444 \\ --stack [size] Override default stack size444 \\ --stack [size] Override default stack size
445 \\ --image-base [addr] Set base address for executable image445 \\ --image-base [addr] Set base address for executable image
446 \\ -framework [name] (Darwin) link against framework446 \\ -framework [name] (Darwin) link against framework
447 \\ -needed_framework [name] (Darwin) link against framework (even if unused)
448 \\ -needed_library [lib] (Darwin) link against system library (even if unused)
447 \\ -F[dir] (Darwin) add search path for frameworks449 \\ -F[dir] (Darwin) add search path for frameworks
448 \\ -install_name=[value] (Darwin) add dylib's install name450 \\ -install_name=[value] (Darwin) add dylib's install name
449 \\ --entitlements [path] (Darwin) add path to entitlements file for embedding in code signature451 \\ --entitlements [path] (Darwin) add path to entitlements file for embedding in code signature
...@@ -452,6 +454,7 @@ const usage_build_generic =...@@ -452,6 +454,7 @@ const usage_build_generic =
452 \\ -search_dylibs_first (Darwin) search `libx.dylib` in each dir in library search paths, then `libx.a`454 \\ -search_dylibs_first (Darwin) search `libx.dylib` in each dir in library search paths, then `libx.a`
453 \\ -headerpad [value] (Darwin) set minimum space for future expansion of the load commands in hexadecimal notation455 \\ -headerpad [value] (Darwin) set minimum space for future expansion of the load commands in hexadecimal notation
454 \\ -headerpad_max_install_names (Darwin) set enough space as if all paths were MAXPATHLEN456 \\ -headerpad_max_install_names (Darwin) set enough space as if all paths were MAXPATHLEN
457 \\ -dead_strip_dylibs (Darwin) remove dylibs that are unreachable by the entry point or exported symbols
455 \\ --import-memory (WebAssembly) import memory from the environment458 \\ --import-memory (WebAssembly) import memory from the environment
456 \\ --import-table (WebAssembly) import function table from the host environment459 \\ --import-table (WebAssembly) import function table from the host environment
457 \\ --export-table (WebAssembly) export function table to the host environment460 \\ --export-table (WebAssembly) export function table to the host environment
...@@ -703,6 +706,7 @@ fn buildOutputType(...@@ -703,6 +706,7 @@ fn buildOutputType(
703 var search_strategy: ?link.File.MachO.SearchStrategy = null;706 var search_strategy: ?link.File.MachO.SearchStrategy = null;
704 var headerpad_size: ?u32 = null;707 var headerpad_size: ?u32 = null;
705 var headerpad_max_install_names: bool = false;708 var headerpad_max_install_names: bool = false;
709 var dead_strip_dylibs: bool = false;
706710
707 // e.g. -m3dnow or -mno-outline-atomics. They correspond to std.Target llvm cpu feature names.711 // e.g. -m3dnow or -mno-outline-atomics. They correspond to std.Target llvm cpu feature names.
708 // This array is populated by zig cc frontend and then has to be converted to zig-style712 // This array is populated by zig cc frontend and then has to be converted to zig-style
...@@ -748,8 +752,7 @@ fn buildOutputType(...@@ -748,8 +752,7 @@ fn buildOutputType(
748 var framework_dirs = std.ArrayList([]const u8).init(gpa);752 var framework_dirs = std.ArrayList([]const u8).init(gpa);
749 defer framework_dirs.deinit();753 defer framework_dirs.deinit();
750754
751 var frameworks = std.ArrayList([]const u8).init(gpa);755 var frameworks: std.StringArrayHashMapUnmanaged(Compilation.SystemLib) = .{};
752 defer frameworks.deinit();
753756
754 // null means replace with the test executable binary757 // null means replace with the test executable binary
755 var test_exec_args = std.ArrayList(?[]const u8).init(gpa);758 var test_exec_args = std.ArrayList(?[]const u8).init(gpa);
...@@ -910,9 +913,15 @@ fn buildOutputType(...@@ -910,9 +913,15 @@ fn buildOutputType(
910 fatal("expected parameter after {s}", .{arg});913 fatal("expected parameter after {s}", .{arg});
911 });914 });
912 } else if (mem.eql(u8, arg, "-framework")) {915 } else if (mem.eql(u8, arg, "-framework")) {
913 try frameworks.append(args_iter.next() orelse {916 const path = args_iter.next() orelse {
914 fatal("expected parameter after {s}", .{arg});917 fatal("expected parameter after {s}", .{arg});
915 });918 };
919 try frameworks.put(gpa, path, .{ .needed = false });
920 } else if (mem.eql(u8, arg, "-needed_framework")) {
921 const path = args_iter.next() orelse {
922 fatal("expected parameter after {s}", .{arg});
923 };
924 try frameworks.put(gpa, path, .{ .needed = true });
916 } else if (mem.eql(u8, arg, "-install_name")) {925 } else if (mem.eql(u8, arg, "-install_name")) {
917 install_name = args_iter.next() orelse {926 install_name = args_iter.next() orelse {
918 fatal("expected parameter after {s}", .{arg});927 fatal("expected parameter after {s}", .{arg});
...@@ -937,6 +946,8 @@ fn buildOutputType(...@@ -937,6 +946,8 @@ fn buildOutputType(
937 };946 };
938 } else if (mem.eql(u8, arg, "-headerpad_max_install_names")) {947 } else if (mem.eql(u8, arg, "-headerpad_max_install_names")) {
939 headerpad_max_install_names = true;948 headerpad_max_install_names = true;
949 } else if (mem.eql(u8, arg, "-dead_strip_dylibs")) {
950 dead_strip_dylibs = true;
940 } else if (mem.eql(u8, arg, "-T") or mem.eql(u8, arg, "--script")) {951 } else if (mem.eql(u8, arg, "-T") or mem.eql(u8, arg, "--script")) {
941 linker_script = args_iter.next() orelse {952 linker_script = args_iter.next() orelse {
942 fatal("expected parameter after {s}", .{arg});953 fatal("expected parameter after {s}", .{arg});
...@@ -952,7 +963,10 @@ fn buildOutputType(...@@ -952,7 +963,10 @@ fn buildOutputType(
952 // We don't know whether this library is part of libc or libc++ until963 // We don't know whether this library is part of libc or libc++ until
953 // we resolve the target, so we simply append to the list for now.964 // we resolve the target, so we simply append to the list for now.
954 try system_libs.put(next_arg, .{ .needed = false });965 try system_libs.put(next_arg, .{ .needed = false });
955 } else if (mem.eql(u8, arg, "--needed-library") or mem.eql(u8, arg, "-needed-l")) {966 } else if (mem.eql(u8, arg, "--needed-library") or
967 mem.eql(u8, arg, "-needed-l") or
968 mem.eql(u8, arg, "--needed_library"))
969 {
956 const next_arg = args_iter.next() orelse {970 const next_arg = args_iter.next() orelse {
957 fatal("expected parameter after {s}", .{arg});971 fatal("expected parameter after {s}", .{arg});
958 };972 };
...@@ -1582,7 +1596,7 @@ fn buildOutputType(...@@ -1582,7 +1596,7 @@ fn buildOutputType(
1582 try clang_argv.appendSlice(it.other_args);1596 try clang_argv.appendSlice(it.other_args);
1583 },1597 },
1584 .framework_dir => try framework_dirs.append(it.only_arg),1598 .framework_dir => try framework_dirs.append(it.only_arg),
1585 .framework => try frameworks.append(it.only_arg),1599 .framework => try frameworks.put(gpa, it.only_arg, .{ .needed = false }),
1586 .nostdlibinc => want_native_include_dirs = false,1600 .nostdlibinc => want_native_include_dirs = false,
1587 .strip => strip = true,1601 .strip => strip = true,
1588 .exec_model => {1602 .exec_model => {
...@@ -1700,6 +1714,8 @@ fn buildOutputType(...@@ -1700,6 +1714,8 @@ fn buildOutputType(
1700 };1714 };
1701 } else if (mem.eql(u8, arg, "-headerpad_max_install_names")) {1715 } else if (mem.eql(u8, arg, "-headerpad_max_install_names")) {
1702 headerpad_max_install_names = true;1716 headerpad_max_install_names = true;
1717 } else if (mem.eql(u8, arg, "-dead_strip_dylibs")) {
1718 dead_strip_dylibs = true;
1703 } else if (mem.eql(u8, arg, "--gc-sections")) {1719 } else if (mem.eql(u8, arg, "--gc-sections")) {
1704 linker_gc_sections = true;1720 linker_gc_sections = true;
1705 } else if (mem.eql(u8, arg, "--no-gc-sections")) {1721 } else if (mem.eql(u8, arg, "--no-gc-sections")) {
...@@ -1868,7 +1884,19 @@ fn buildOutputType(...@@ -1868,7 +1884,19 @@ fn buildOutputType(
1868 if (i >= linker_args.items.len) {1884 if (i >= linker_args.items.len) {
1869 fatal("expected linker arg after '{s}'", .{arg});1885 fatal("expected linker arg after '{s}'", .{arg});
1870 }1886 }
1871 try frameworks.append(linker_args.items[i]);1887 try frameworks.put(gpa, linker_args.items[i], .{ .needed = false });
1888 } else if (mem.eql(u8, arg, "-needed_framework")) {
1889 i += 1;
1890 if (i >= linker_args.items.len) {
1891 fatal("expected linker arg after '{s}'", .{arg});
1892 }
1893 try frameworks.put(gpa, linker_args.items[i], .{ .needed = true });
1894 } else if (mem.eql(u8, arg, "-needed_library")) {
1895 i += 1;
1896 if (i >= linker_args.items.len) {
1897 fatal("expected linker arg after '{s}'", .{arg});
1898 }
1899 try system_libs.put(linker_args.items[i], .{ .needed = true });
1872 } else if (mem.eql(u8, arg, "-compatibility_version")) {1900 } else if (mem.eql(u8, arg, "-compatibility_version")) {
1873 i += 1;1901 i += 1;
1874 if (i >= linker_args.items.len) {1902 if (i >= linker_args.items.len) {
...@@ -2238,7 +2266,7 @@ fn buildOutputType(...@@ -2238,7 +2266,7 @@ fn buildOutputType(
22382266
2239 if (comptime builtin.target.isDarwin()) {2267 if (comptime builtin.target.isDarwin()) {
2240 // If we want to link against frameworks, we need system headers.2268 // If we want to link against frameworks, we need system headers.
2241 if (framework_dirs.items.len > 0 or frameworks.items.len > 0)2269 if (framework_dirs.items.len > 0 or frameworks.count() > 0)
2242 want_native_include_dirs = true;2270 want_native_include_dirs = true;
2243 }2271 }
22442272
...@@ -2728,7 +2756,7 @@ fn buildOutputType(...@@ -2728,7 +2756,7 @@ fn buildOutputType(
2728 .c_source_files = c_source_files.items,2756 .c_source_files = c_source_files.items,
2729 .link_objects = link_objects.items,2757 .link_objects = link_objects.items,
2730 .framework_dirs = framework_dirs.items,2758 .framework_dirs = framework_dirs.items,
2731 .frameworks = frameworks.items,2759 .frameworks = frameworks,
2732 .system_lib_names = system_libs.keys(),2760 .system_lib_names = system_libs.keys(),
2733 .system_lib_infos = system_libs.values(),2761 .system_lib_infos = system_libs.values(),
2734 .wasi_emulated_libs = wasi_emulated_libs.items,2762 .wasi_emulated_libs = wasi_emulated_libs.items,
...@@ -2821,6 +2849,7 @@ fn buildOutputType(...@@ -2821,6 +2849,7 @@ fn buildOutputType(
2821 .search_strategy = search_strategy,2849 .search_strategy = search_strategy,
2822 .headerpad_size = headerpad_size,2850 .headerpad_size = headerpad_size,
2823 .headerpad_max_install_names = headerpad_max_install_names,2851 .headerpad_max_install_names = headerpad_max_install_names,
2852 .dead_strip_dylibs = dead_strip_dylibs,
2824 }) catch |err| switch (err) {2853 }) catch |err| switch (err) {
2825 error.LibCUnavailable => {2854 error.LibCUnavailable => {
2826 const target = target_info.target;2855 const target = target_info.target;
test/link.zig+10-1
...@@ -40,7 +40,16 @@ pub fn addCases(cases: *tests.StandaloneContext) void {...@@ -40,7 +40,16 @@ pub fn addCases(cases: *tests.StandaloneContext) void {
40 .build_modes = true,40 .build_modes = true,
41 });41 });
4242
43 cases.addBuildFile("test/link/macho/frameworks/build.zig", .{43 cases.addBuildFile("test/link/macho/dead_strip_dylibs/build.zig", .{
44 .build_modes = true,
45 .requires_macos_sdk = true,
46 });
47
48 cases.addBuildFile("test/link/macho/needed_l/build.zig", .{
49 .build_modes = true,
50 });
51
52 cases.addBuildFile("test/link/macho/needed_framework/build.zig", .{
44 .build_modes = true,53 .build_modes = true,
45 .requires_macos_sdk = true,54 .requires_macos_sdk = true,
46 });55 });
test/link/macho/dead_strip_dylibs/build.zig created+46
...@@ -0,0 +1,46 @@
1const std = @import("std");
2const Builder = std.build.Builder;
3const LibExeObjectStep = std.build.LibExeObjStep;
4
5pub fn build(b: *Builder) void {
6 const mode = b.standardReleaseOptions();
7
8 const test_step = b.step("test", "Test the program");
9 test_step.dependOn(b.getInstallStep());
10
11 {
12 // Without -dead_strip_dylibs we expect `-la` to include liba.dylib in the final executable
13 const exe = createScenario(b, mode);
14
15 const check = exe.checkObject(.macho);
16 check.checkStart("cmd LOAD_DYLIB");
17 check.checkNext("name {*}Cocoa");
18
19 check.checkStart("cmd LOAD_DYLIB");
20 check.checkNext("name {*}libobjc{*}.dylib");
21
22 test_step.dependOn(&check.step);
23
24 const run_cmd = exe.run();
25 test_step.dependOn(&run_cmd.step);
26 }
27
28 {
29 // With -dead_strip_dylibs, we should include liba.dylib as it's unreachable
30 const exe = createScenario(b, mode);
31 exe.dead_strip_dylibs = true;
32
33 const run_cmd = exe.run();
34 run_cmd.expected_exit_code = @bitCast(u8, @as(i8, -2)); // should fail
35 test_step.dependOn(&run_cmd.step);
36 }
37}
38
39fn createScenario(b: *Builder, mode: std.builtin.Mode) *LibExeObjectStep {
40 const exe = b.addExecutable("test", null);
41 exe.addCSourceFile("main.c", &[0][]const u8{});
42 exe.setBuildMode(mode);
43 exe.linkLibC();
44 exe.linkFramework("Cocoa");
45 return exe;
46}
test/link/macho/dead_strip_dylibs/main.c created+11
...@@ -0,0 +1,11 @@
1#include <objc/runtime.h>
2
3int main(int argc, char* argv[]) {
4 if (objc_getClass("NSObject") == 0) {
5 return -1;
6 }
7 if (objc_getClass("NSApplication") == 0) {
8 return -2;
9 }
10 return 0;
11}
test/link/macho/dylib/main.c+1-1
...@@ -3,7 +3,7 @@...@@ -3,7 +3,7 @@
3char* hello();3char* hello();
4extern char world[];4extern char world[];
55
6int main() {6int main(int argc, char* argv[]) {
7 printf("%s %s", hello(), world);7 printf("%s %s", hello(), world);
8 return 0;8 return 0;
9}9}
test/link/macho/frameworks/build.zig deleted-32
...@@ -1,32 +0,0 @@
1const std = @import("std");
2const Builder = std.build.Builder;
3
4pub fn build(b: *Builder) void {
5 const mode = b.standardReleaseOptions();
6
7 const test_step = b.step("test", "Test the program");
8
9 const exe = b.addExecutable("test", null);
10 b.default_step.dependOn(&exe.step);
11 exe.addCSourceFile("main.c", &[0][]const u8{});
12 exe.setBuildMode(mode);
13 exe.linkLibC();
14 exe.linkFramework("Cocoa");
15
16 const check = exe.checkObject(.macho);
17 check.checkStart("cmd LOAD_DYLIB");
18 check.checkNext("name {*}Cocoa");
19
20 switch (mode) {
21 .Debug, .ReleaseSafe => {
22 check.checkStart("cmd LOAD_DYLIB");
23 check.checkNext("name {*}libobjc{*}.dylib");
24 },
25 else => {},
26 }
27
28 test_step.dependOn(&check.step);
29
30 const run_cmd = exe.run();
31 test_step.dependOn(&run_cmd.step);
32}
test/link/macho/frameworks/main.c deleted-7
...@@ -1,7 +0,0 @@
1#include <assert.h>
2#include <objc/runtime.h>
3
4int main() {
5 assert(objc_getClass("NSObject") > 0);
6 assert(objc_getClass("NSApplication") > 0);
7}
test/link/macho/needed_framework/build.zig created+27
...@@ -0,0 +1,27 @@
1const std = @import("std");
2const Builder = std.build.Builder;
3const LibExeObjectStep = std.build.LibExeObjStep;
4
5pub fn build(b: *Builder) void {
6 const mode = b.standardReleaseOptions();
7
8 const test_step = b.step("test", "Test the program");
9 test_step.dependOn(b.getInstallStep());
10
11 // -dead_strip_dylibs
12 // -needed_framework Cocoa
13 const exe = b.addExecutable("test", null);
14 exe.addCSourceFile("main.c", &[0][]const u8{});
15 exe.setBuildMode(mode);
16 exe.linkLibC();
17 exe.linkFrameworkNeeded("Cocoa");
18 exe.dead_strip_dylibs = true;
19
20 const check = exe.checkObject(.macho);
21 check.checkStart("cmd LOAD_DYLIB");
22 check.checkNext("name {*}Cocoa");
23 test_step.dependOn(&check.step);
24
25 const run_cmd = exe.run();
26 test_step.dependOn(&run_cmd.step);
27}
test/link/macho/needed_framework/main.c created+3
...@@ -0,0 +1,3 @@
1int main(int argc, char* argv[]) {
2 return 0;
3}
test/link/macho/needed_l/a.c created+1
...@@ -0,0 +1 @@
1int a = 42;
test/link/macho/needed_l/build.zig created+35
...@@ -0,0 +1,35 @@
1const std = @import("std");
2const Builder = std.build.Builder;
3const LibExeObjectStep = std.build.LibExeObjStep;
4
5pub fn build(b: *Builder) void {
6 const mode = b.standardReleaseOptions();
7
8 const test_step = b.step("test", "Test the program");
9 test_step.dependOn(b.getInstallStep());
10
11 const dylib = b.addSharedLibrary("a", null, b.version(1, 0, 0));
12 dylib.setBuildMode(mode);
13 dylib.addCSourceFile("a.c", &.{});
14 dylib.linkLibC();
15 dylib.install();
16
17 // -dead_strip_dylibs
18 // -needed-la
19 const exe = b.addExecutable("test", null);
20 exe.addCSourceFile("main.c", &[0][]const u8{});
21 exe.setBuildMode(mode);
22 exe.linkLibC();
23 exe.linkSystemLibraryNeeded("a");
24 exe.addLibraryPath(b.pathFromRoot("zig-out/lib"));
25 exe.addRPath(b.pathFromRoot("zig-out/lib"));
26 exe.dead_strip_dylibs = true;
27
28 const check = exe.checkObject(.macho);
29 check.checkStart("cmd LOAD_DYLIB");
30 check.checkNext("name @rpath/liba.dylib");
31 test_step.dependOn(&check.step);
32
33 const run_cmd = exe.run();
34 test_step.dependOn(&run_cmd.step);
35}
test/link/macho/needed_l/main.c created+3
...@@ -0,0 +1,3 @@
1int main(int argc, char* argv[]) {
2 return 0;
3}
test/link/macho/search_strategy/main.c+1-1
...@@ -3,7 +3,7 @@...@@ -3,7 +3,7 @@
3char* hello();3char* hello();
4extern char world[];4extern char world[];
55
6int main() {6int main(int argc, char* argv[]) {
7 printf("%s %s", hello(), world);7 printf("%s %s", hello(), world);
8 return 0;8 return 0;
9}9}