| author | |
| committer | |
| log | d4623a8a07bf8fe3bed3b4ee50339960d623958e |
| tree | 949f6078a42f12415508667377b40ea0e84eac0b |
| parent | a76775b50a65fd0ea0fd17d6ef3c42058df13997 |
| parent | 0dd28920daa5127ffe5a3691343fa519f7547cfd |
| signature |
macho: handle `-dead_strip_dylibs`, `-needed-lx` and `-needed_framework x` flags20 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; |
| 11 | const StringHashMap = std.StringHashMap; | 11 | const StringHashMap = std.StringHashMap; |
| 12 | const Allocator = mem.Allocator; | 12 | const Allocator = mem.Allocator; |
| 13 | const process = std.process; | 13 | const process = std.process; |
| 14 | const BufSet = std.BufSet; | ||
| 15 | const EnvMap = std.process.EnvMap; | 14 | const EnvMap = std.process.EnvMap; |
| 16 | const fmt_lib = std.fmt; | 15 | const fmt_lib = std.fmt; |
| 17 | const File = std.fs.File; | 16 | const 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, |
| 1603 | 1602 | ||
| 1603 | /// (Darwin) Remove dylibs that are unreachable by the entry point or exported symbols. | ||
| 1604 | dead_strip_dylibs: bool = false, | ||
| 1605 | |||
| 1604 | /// Position Independent Code | 1606 | /// Position Independent Code |
| 1605 | force_pic: ?bool = null, | 1607 | force_pic: ?bool = null, |
| 1606 | 1608 | ||
| ... | @@ -1640,6 +1642,7 @@ pub const LibExeObjStep = struct { | ... | @@ -1640,6 +1642,7 @@ pub const LibExeObjStep = struct { |
| 1640 | 1642 | ||
| 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 | } |
| 1891 | 1894 | ||
| 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 | } |
| 1896 | 1902 | ||
| 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 | } |
| 2082 | 2116 | ||
| 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 | }, |
| 2471 | 2514 | ||
| 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 library | 2530 | // 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 | } | ||
| 2679 | 2729 | ||
| 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 | } |
| 2967 | 3017 | ||
| 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 MATPATHLEN | 912 | /// (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 | }; |
| 915 | 917 | ||
| 916 | fn addPackageTableToCacheHash( | 918 | fn 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 or | 1098 | if (options.link_objects.len != 0 or |
| 1097 | options.c_source_files.len != 0 or | 1099 | options.c_source_files.len != 0 or |
| 1098 | options.frameworks.len != 0 or | 1100 | options.frameworks.count() != 0 or |
| 1099 | options.system_lib_names.len != 0 or | 1101 | options.system_lib_names.len != 0 or |
| 1100 | options.link_libc or options.link_libcpp or | 1102 | options.link_libc or options.link_libcpp or |
| 1101 | link_eh_frame_hdr or | 1103 | 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 that | 2372 | /// to remind the programmer to update multiple related pieces of code that |
| 2370 | /// are in different locations. Bump this number when adding or deleting | 2373 | /// 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. |
| 2372 | pub const link_hash_implementation_version = 6; | 2375 | pub const link_hash_implementation_version = 7; |
| 2373 | 2376 | ||
| 2374 | fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifest) !void { | 2377 | fn 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(); |
| 2381 | 2384 | ||
| 2382 | comptime assert(link_hash_implementation_version == 6); | 2385 | comptime assert(link_hash_implementation_version == 7); |
| 2383 | 2386 | ||
| 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 |
| 2482 | 2485 | ||
| 2483 | // Mach-O specific stuff | 2486 | // 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); | ||
| 2491 | 2495 | ||
| 2492 | // COFF specific stuff | 2496 | // 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 { |
| 162 | 162 | ||
| 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 MATPATHLEN | 199 | /// (Darwin) set enough space as if all paths were MATPATHLEN |
| 200 | headerpad_max_install_names: bool = false, | 200 | headerpad_max_install_names: bool = false, |
| 201 | 201 | ||
| 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(); |
| 971 | 971 | ||
| 972 | comptime assert(Compilation.link_hash_implementation_version == 6); | 972 | comptime assert(Compilation.link_hash_implementation_version == 7); |
| 973 | 973 | ||
| 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(); |
| 1300 | 1300 | ||
| 1301 | comptime assert(Compilation.link_hash_implementation_version == 6); | 1301 | comptime assert(Compilation.link_hash_implementation_version == 7); |
| 1302 | 1302 | ||
| 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(); |
| 543 | 543 | ||
| 544 | comptime assert(Compilation.link_hash_implementation_version == 6); | 544 | comptime assert(Compilation.link_hash_implementation_version == 7); |
| 545 | 545 | ||
| 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 | } |
| 768 | 769 | ||
| 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); |
| 771 | 772 | ||
| 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 libraries | 775 | // 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 which | 776 | // (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 | } |
| 781 | 782 | ||
| 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 | } |
| 784 | 786 | ||
| 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 | } |
| 793 | 795 | ||
| 794 | var libs = std.ArrayList([]const u8).init(arena); | 796 | var libs = std.StringArrayHashMap(Compilation.SystemLib).init(arena); |
| 795 | 797 | ||
| 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 archive | 803 | // 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 | } |
| 876 | 878 | ||
| 877 | // frameworks | 879 | // 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 | } |
| 886 | 888 | ||
| 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 | } |
| 989 | 991 | ||
| 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"); |
| 1021 | 1027 | ||
| 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 | } |
| 1025 | 1036 | ||
| 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 | } |
| 1029 | 1040 | ||
| 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 | } |
| 1033 | 1049 | ||
| 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 | } |
| 1057 | 1073 | ||
| ... | @@ -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 | }; |
| 1380 | 1397 | ||
| 1381 | pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDylibError!bool { | 1398 | pub 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); |
| 1427 | 1444 | ||
| 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 | } |
| 1471 | 1493 | ||
| 1472 | fn parseLibs(self: *MachO, libs: []const []const u8, syslibroot: ?[]const u8, dependent_libs: anytype) !void { | 1494 | fn 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; |
| 1480 | 1510 |
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(); |
| 2548 | 2548 | ||
| 2549 | comptime assert(Compilation.link_hash_implementation_version == 6); | 2549 | comptime assert(Compilation.link_hash_implementation_version == 7); |
| 2550 | 2550 | ||
| 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 size | 444 | \\ --stack [size] Override default stack size |
| 445 | \\ --image-base [addr] Set base address for executable image | 445 | \\ --image-base [addr] Set base address for executable image |
| 446 | \\ -framework [name] (Darwin) link against framework | 446 | \\ -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 frameworks | 449 | \\ -F[dir] (Darwin) add search path for frameworks |
| 448 | \\ -install_name=[value] (Darwin) add dylib's install name | 450 | \\ -install_name=[value] (Darwin) add dylib's install name |
| 449 | \\ --entitlements [path] (Darwin) add path to entitlements file for embedding in code signature | 451 | \\ --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 notation | 455 | \\ -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 MAXPATHLEN | 456 | \\ -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 environment | 458 | \\ --import-memory (WebAssembly) import memory from the environment |
| 456 | \\ --import-table (WebAssembly) import function table from the host environment | 459 | \\ --import-table (WebAssembly) import function table from the host environment |
| 457 | \\ --export-table (WebAssembly) export function table to the host environment | 460 | \\ --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; | ||
| 706 | 710 | ||
| 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-style | 712 | // 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(); |
| 750 | 754 | ||
| 751 | var frameworks = std.ArrayList([]const u8).init(gpa); | 755 | var frameworks: std.StringArrayHashMapUnmanaged(Compilation.SystemLib) = .{}; |
| 752 | defer frameworks.deinit(); | ||
| 753 | 756 | ||
| 754 | // null means replace with the test executable binary | 757 | // 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++ until | 963 | // 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( |
| 2238 | 2266 | ||
| 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 | } |
| 2244 | 2272 | ||
| ... | @@ -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 | }); |
| 42 | 42 | ||
| 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 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const Builder = std.build.Builder; | ||
| 3 | const LibExeObjectStep = std.build.LibExeObjStep; | ||
| 4 | |||
| 5 | pub 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 | |||
| 39 | fn 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 | |||
| 3 | int 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 @@ |
| 3 | char* hello(); | 3 | char* hello(); |
| 4 | extern char world[]; | 4 | extern char world[]; |
| 5 | 5 | ||
| 6 | int main() { | 6 | int 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 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const Builder = std.build.Builder; | ||
| 3 | |||
| 4 | pub 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 | |||
| 4 | int 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 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const Builder = std.build.Builder; | ||
| 3 | const LibExeObjectStep = std.build.LibExeObjStep; | ||
| 4 | |||
| 5 | pub 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 @@ | ||
| 1 | int main(int argc, char* argv[]) { | ||
| 2 | return 0; | ||
| 3 | } | ||
test/link/macho/needed_l/a.c created+1| ... | @@ -0,0 +1 @@ | ||
| 1 | int a = 42; | ||
test/link/macho/needed_l/build.zig created+35| ... | @@ -0,0 +1,35 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const Builder = std.build.Builder; | ||
| 3 | const LibExeObjectStep = std.build.LibExeObjStep; | ||
| 4 | |||
| 5 | pub 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 @@ | ||
| 1 | int 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 @@ |
| 3 | char* hello(); | 3 | char* hello(); |
| 4 | extern char world[]; | 4 | extern char world[]; |
| 5 | 5 | ||
| 6 | int main() { | 6 | int 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 | } |