Skip to content

Commit 0dd2892

Browse files
committed
macho: implement and handle -needed-* and -needed_* family of flags
MachO linker now handles `-needed-l<name>`, `-needed_library=<name>` and `-needed_framework=<name>`. While on macOS `-l` is equivalent to `-needed-l`, and `-framework` to `-needed_framework`, it can be used to the same effect as on Linux if combined with `-dead_strip_dylibs`. This commit also adds handling for `-needed_library` which is macOS specific flag only (in addition to `-needed-l`). Finally, in order to leverage new linker testing harness, this commit added ability to specify lowering to those flags via `build.zig`: `linkSystemLibraryNeeded` (and related), and `linkFrameworkNeeded`.
1 parent efc5c97 commit 0dd2892

File tree

15 files changed

+227
-56
lines changed

15 files changed

+227
-56
lines changed

lib/std/build.zig

Lines changed: 61 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ const ArrayList = std.ArrayList;
1111
const StringHashMap = std.StringHashMap;
1212
const Allocator = mem.Allocator;
1313
const process = std.process;
14-
const BufSet = std.BufSet;
1514
const EnvMap = std.process.EnvMap;
1615
const fmt_lib = std.fmt;
1716
const File = std.fs.File;
@@ -1484,7 +1483,7 @@ pub const LibExeObjStep = struct {
14841483
lib_paths: ArrayList([]const u8),
14851484
rpaths: ArrayList([]const u8),
14861485
framework_dirs: ArrayList([]const u8),
1487-
frameworks: BufSet,
1486+
frameworks: StringHashMap(bool),
14881487
verbose_link: bool,
14891488
verbose_cc: bool,
14901489
emit_analysis: EmitOption = .default,
@@ -1643,6 +1642,7 @@ pub const LibExeObjStep = struct {
16431642

16441643
pub const SystemLib = struct {
16451644
name: []const u8,
1645+
needed: bool,
16461646
use_pkg_config: enum {
16471647
/// Don't use pkg-config, just pass -lfoo where foo is name.
16481648
no,
@@ -1744,7 +1744,7 @@ pub const LibExeObjStep = struct {
17441744
.kind = kind,
17451745
.root_src = root_src,
17461746
.name = name,
1747-
.frameworks = BufSet.init(builder.allocator),
1747+
.frameworks = StringHashMap(bool).init(builder.allocator),
17481748
.step = Step.init(base_id, name, builder.allocator, make),
17491749
.version = ver,
17501750
.out_filename = undefined,
@@ -1893,8 +1893,11 @@ pub const LibExeObjStep = struct {
18931893
}
18941894

18951895
pub fn linkFramework(self: *LibExeObjStep, framework_name: []const u8) void {
1896-
// Note: No need to dupe because frameworks dupes internally.
1897-
self.frameworks.insert(framework_name) catch unreachable;
1896+
self.frameworks.put(self.builder.dupe(framework_name), false) 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;
18981901
}
18991902

19001903
/// Returns whether the library, executable, or object depends on a particular system library.
@@ -1935,6 +1938,7 @@ pub const LibExeObjStep = struct {
19351938
self.link_objects.append(.{
19361939
.system_lib = .{
19371940
.name = "c",
1941+
.needed = false,
19381942
.use_pkg_config = .no,
19391943
},
19401944
}) catch unreachable;
@@ -1947,6 +1951,7 @@ pub const LibExeObjStep = struct {
19471951
self.link_objects.append(.{
19481952
.system_lib = .{
19491953
.name = "c++",
1954+
.needed = false,
19501955
.use_pkg_config = .no,
19511956
},
19521957
}) catch unreachable;
@@ -1971,6 +1976,19 @@ pub const LibExeObjStep = struct {
19711976
self.link_objects.append(.{
19721977
.system_lib = .{
19731978
.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,
19741992
.use_pkg_config = .no,
19751993
},
19761994
}) catch unreachable;
@@ -1982,6 +2000,19 @@ pub const LibExeObjStep = struct {
19822000
self.link_objects.append(.{
19832001
.system_lib = .{
19842002
.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,
19852016
.use_pkg_config = .force,
19862017
},
19872018
}) catch unreachable;
@@ -2084,6 +2115,14 @@ pub const LibExeObjStep = struct {
20842115
}
20852116

20862117
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 {
20872126
if (isLibCLibrary(name)) {
20882127
self.linkLibC();
20892128
return;
@@ -2096,6 +2135,7 @@ pub const LibExeObjStep = struct {
20962135
self.link_objects.append(.{
20972136
.system_lib = .{
20982137
.name = self.builder.dupe(name),
2138+
.needed = needed,
20992139
.use_pkg_config = .yes,
21002140
},
21012141
}) catch unreachable;
@@ -2437,7 +2477,7 @@ pub const LibExeObjStep = struct {
24372477
if (!other.isDynamicLibrary()) {
24382478
var it = other.frameworks.iterator();
24392479
while (it.next()) |framework| {
2440-
self.frameworks.insert(framework.*) catch unreachable;
2480+
self.frameworks.put(framework.key_ptr.*, framework.value_ptr.*) catch unreachable;
24412481
}
24422482
}
24432483
},
@@ -2473,8 +2513,9 @@ pub const LibExeObjStep = struct {
24732513
},
24742514

24752515
.system_lib => |system_lib| {
2516+
const prefix: []const u8 = if (system_lib.needed) "-needed-l" else "-l";
24762517
switch (system_lib.use_pkg_config) {
2477-
.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 })),
24782519
.yes, .force => {
24792520
if (self.runPkgConfig(system_lib.name)) |args| {
24802521
try zig_args.appendSlice(args);
@@ -2488,7 +2529,10 @@ pub const LibExeObjStep = struct {
24882529
.yes => {
24892530
// pkg-config failed, so fall back to linking the library
24902531
// by name directly.
2491-
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+
}));
24922536
},
24932537
.force => {
24942538
panic("pkg-config failed for library {s}", .{system_lib.name});
@@ -2972,9 +3016,15 @@ pub const LibExeObjStep = struct {
29723016
}
29733017

29743018
var it = self.frameworks.iterator();
2975-
while (it.next()) |framework| {
2976-
zig_args.append("-framework") catch unreachable;
2977-
zig_args.append(framework.*) catch unreachable;
3019+
while (it.next()) |entry| {
3020+
const name = entry.key_ptr.*;
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;
29783028
}
29793029
} else {
29803030
if (self.framework_dirs.items.len > 0) {

src/Compilation.zig

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -791,7 +791,7 @@ pub const InitOptions = struct {
791791
c_source_files: []const CSourceFile = &[0]CSourceFile{},
792792
link_objects: []LinkObject = &[0]LinkObject{},
793793
framework_dirs: []const []const u8 = &[0][]const u8{},
794-
frameworks: []const []const u8 = &[0][]const u8{},
794+
frameworks: std.StringArrayHashMapUnmanaged(SystemLib) = .{},
795795
system_lib_names: []const []const u8 = &.{},
796796
system_lib_infos: []const SystemLib = &.{},
797797
/// These correspond to the WASI libc emulated subcomponents including:
@@ -1097,7 +1097,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
10971097
// Our linker can't handle objects or most advanced options yet.
10981098
if (options.link_objects.len != 0 or
10991099
options.c_source_files.len != 0 or
1100-
options.frameworks.len != 0 or
1100+
options.frameworks.count() != 0 or
11011101
options.system_lib_names.len != 0 or
11021102
options.link_libc or options.link_libcpp or
11031103
link_eh_frame_hdr or
@@ -1215,7 +1215,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
12151215
options.target,
12161216
options.is_native_abi,
12171217
link_libc,
1218-
options.system_lib_names.len != 0 or options.frameworks.len != 0,
1218+
options.system_lib_names.len != 0 or options.frameworks.count() != 0,
12191219
options.libc_installation,
12201220
options.native_darwin_sdk != null,
12211221
);
@@ -2485,7 +2485,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes
24852485

24862486
// Mach-O specific stuff
24872487
man.hash.addListOfBytes(comp.bin_file.options.framework_dirs);
2488-
man.hash.addListOfBytes(comp.bin_file.options.frameworks);
2488+
link.hashAddSystemLibs(&man.hash, comp.bin_file.options.frameworks);
24892489
try man.addOptionalFile(comp.bin_file.options.entitlements);
24902490
man.hash.addOptional(comp.bin_file.options.pagezero_size);
24912491
man.hash.addOptional(comp.bin_file.options.search_strategy);

src/link.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ pub const Options = struct {
162162

163163
objects: []Compilation.LinkObject,
164164
framework_dirs: []const []const u8,
165-
frameworks: []const []const u8,
165+
frameworks: std.StringArrayHashMapUnmanaged(SystemLib),
166166
system_libs: std.StringArrayHashMapUnmanaged(SystemLib),
167167
wasi_emulated_libs: []const wasi_libc.CRTFile,
168168
lib_dirs: []const []const u8,

0 commit comments

Comments
 (0)