Commit aef8bcf776

Andrew Kelley <andrew@ziglang.org>
2023-08-02 20:05:35
std.Build.Step.Compile: fine-grained system lib search control
For each library you can specify the preferred mode and search strategy. The old way of setting global state is eliminated.
1 parent a0e94ec
Changed files (2)
lib
std
Build
test
link
macho
search_strategy
lib/std/Build/Step/Compile.zig
@@ -149,13 +149,6 @@ entitlements: ?[]const u8 = null,
 /// (Darwin) Size of the pagezero segment.
 pagezero_size: ?u64 = null,
 
-/// (Darwin) Search strategy for searching system libraries. Either `paths_first` or `dylibs_first`.
-/// The former lowers to `-search_paths_first` linker option, while the latter to `-search_dylibs_first`
-/// option.
-/// By default, if no option is specified, the linker assumes `paths_first` as the default
-/// search strategy.
-search_strategy: ?enum { paths_first, dylibs_first } = null,
-
 /// (Darwin) Set size of the padding between the end of load commands
 /// and start of `__TEXT,__text` section.
 headerpad_size: ?u32 = null,
@@ -242,7 +235,11 @@ pub const SystemLib = struct {
     name: []const u8,
     needed: bool,
     weak: bool,
-    use_pkg_config: enum {
+    use_pkg_config: UsePkgConfig,
+    preferred_link_mode: std.builtin.LinkMode,
+    search_strategy: SystemLib.SearchStrategy,
+
+    pub const UsePkgConfig = enum {
         /// Don't use pkg-config, just pass -lfoo where foo is name.
         no,
         /// Try to get information on how to link the library from pkg-config.
@@ -251,7 +248,9 @@ pub const SystemLib = struct {
         /// Try to get information on how to link the library from pkg-config.
         /// If that fails, error out.
         force,
-    },
+    };
+
+    pub const SearchStrategy = enum { paths_first, mode_first, no_fallback };
 };
 
 const FrameworkLinkInfo = struct {
@@ -718,74 +717,29 @@ pub fn defineCMacroRaw(self: *Compile, name_and_value: []const u8) void {
     self.c_macros.append(b.dupe(name_and_value)) catch @panic("OOM");
 }
 
-/// This one has no integration with anything, it just puts -lname on the command line.
-/// Prefer to use `linkSystemLibrary` instead.
+/// deprecated: use linkSystemLibrary2
 pub fn linkSystemLibraryName(self: *Compile, name: []const u8) void {
-    const b = self.step.owner;
-    self.link_objects.append(.{
-        .system_lib = .{
-            .name = b.dupe(name),
-            .needed = false,
-            .weak = false,
-            .use_pkg_config = .no,
-        },
-    }) catch @panic("OOM");
+    return linkSystemLibrary2(self, name, .{ .use_pkg_config = .no });
 }
 
-/// This one has no integration with anything, it just puts -needed-lname on the command line.
-/// Prefer to use `linkSystemLibraryNeeded` instead.
+/// deprecated: use linkSystemLibrary2
 pub fn linkSystemLibraryNeededName(self: *Compile, name: []const u8) void {
-    const b = self.step.owner;
-    self.link_objects.append(.{
-        .system_lib = .{
-            .name = b.dupe(name),
-            .needed = true,
-            .weak = false,
-            .use_pkg_config = .no,
-        },
-    }) catch @panic("OOM");
+    return linkSystemLibrary2(self, name, .{ .needed = true, .use_pkg_config = .no });
 }
 
-/// Darwin-only. This one has no integration with anything, it just puts -weak-lname on the
-/// command line. Prefer to use `linkSystemLibraryWeak` instead.
+/// deprecated: use linkSystemLibrary2
 pub fn linkSystemLibraryWeakName(self: *Compile, name: []const u8) void {
-    const b = self.step.owner;
-    self.link_objects.append(.{
-        .system_lib = .{
-            .name = b.dupe(name),
-            .needed = false,
-            .weak = true,
-            .use_pkg_config = .no,
-        },
-    }) catch @panic("OOM");
+    return linkSystemLibrary2(self, name, .{ .weak = true, .use_pkg_config = .no });
 }
 
-/// This links against a system library, exclusively using pkg-config to find the library.
-/// Prefer to use `linkSystemLibrary` instead.
+/// deprecated: use linkSystemLibrary2
 pub fn linkSystemLibraryPkgConfigOnly(self: *Compile, lib_name: []const u8) void {
-    const b = self.step.owner;
-    self.link_objects.append(.{
-        .system_lib = .{
-            .name = b.dupe(lib_name),
-            .needed = false,
-            .weak = false,
-            .use_pkg_config = .force,
-        },
-    }) catch @panic("OOM");
+    return linkSystemLibrary2(self, lib_name, .{ .use_pkg_config = .force });
 }
 
-/// This links against a system library, exclusively using pkg-config to find the library.
-/// Prefer to use `linkSystemLibraryNeeded` instead.
+/// deprecated: use linkSystemLibrary2
 pub fn linkSystemLibraryNeededPkgConfigOnly(self: *Compile, lib_name: []const u8) void {
-    const b = self.step.owner;
-    self.link_objects.append(.{
-        .system_lib = .{
-            .name = b.dupe(lib_name),
-            .needed = true,
-            .weak = false,
-            .use_pkg_config = .force,
-        },
-    }) catch @panic("OOM");
+    return linkSystemLibrary2(self, lib_name, .{ .needed = true, .use_pkg_config = .force });
 }
 
 /// Run pkg-config for the given library name and parse the output, returning the arguments
@@ -885,21 +839,32 @@ fn runPkgConfig(self: *Compile, lib_name: []const u8) ![]const []const u8 {
 }
 
 pub fn linkSystemLibrary(self: *Compile, name: []const u8) void {
-    self.linkSystemLibraryInner(name, .{});
+    self.linkSystemLibrary2(name, .{});
 }
 
+/// deprecated: use linkSystemLibrary2
 pub fn linkSystemLibraryNeeded(self: *Compile, name: []const u8) void {
-    self.linkSystemLibraryInner(name, .{ .needed = true });
+    return linkSystemLibrary2(self, name, .{ .needed = true });
 }
 
+/// deprecated: use linkSystemLibrary2
 pub fn linkSystemLibraryWeak(self: *Compile, name: []const u8) void {
-    self.linkSystemLibraryInner(name, .{ .weak = true });
+    return linkSystemLibrary2(self, name, .{ .weak = true });
 }
 
-fn linkSystemLibraryInner(self: *Compile, name: []const u8, opts: struct {
+pub const LinkSystemLibraryOptions = struct {
     needed: bool = false,
     weak: bool = false,
-}) void {
+    use_pkg_config: SystemLib.UsePkgConfig = .yes,
+    preferred_link_mode: std.builtin.LinkMode = .Dynamic,
+    search_strategy: SystemLib.SearchStrategy = .paths_first,
+};
+
+pub fn linkSystemLibrary2(
+    self: *Compile,
+    name: []const u8,
+    options: LinkSystemLibraryOptions,
+) void {
     const b = self.step.owner;
     if (isLibCLibrary(name)) {
         self.linkLibC();
@@ -913,9 +878,11 @@ fn linkSystemLibraryInner(self: *Compile, name: []const u8, opts: struct {
     self.link_objects.append(.{
         .system_lib = .{
             .name = b.dupe(name),
-            .needed = opts.needed,
-            .weak = opts.weak,
-            .use_pkg_config = .yes,
+            .needed = options.needed,
+            .weak = options.weak,
+            .use_pkg_config = options.use_pkg_config,
+            .preferred_link_mode = options.preferred_link_mode,
+            .search_strategy = options.search_strategy,
         },
     }) catch @panic("OOM");
 }
@@ -1385,6 +1352,8 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
     try transitive_deps.add(self.link_objects.items);
 
     var prev_has_cflags = false;
+    var prev_search_strategy: SystemLib.SearchStrategy = .paths_first;
+    var prev_preferred_link_mode: std.builtin.LinkMode = .Dynamic;
 
     for (transitive_deps.link_objects.items) |link_object| {
         switch (link_object) {
@@ -1420,6 +1389,28 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
             },
 
             .system_lib => |system_lib| {
+                if ((system_lib.search_strategy != prev_search_strategy or
+                    system_lib.preferred_link_mode != prev_preferred_link_mode) and
+                    self.linkage != .static)
+                {
+                    switch (system_lib.search_strategy) {
+                        .no_fallback => switch (system_lib.preferred_link_mode) {
+                            .Dynamic => try zig_args.append("-search_dylibs_only"),
+                            .Static => try zig_args.append("-search_static_only"),
+                        },
+                        .paths_first => switch (system_lib.preferred_link_mode) {
+                            .Dynamic => try zig_args.append("-search_paths_first"),
+                            .Static => try zig_args.append("-search_paths_first_static"),
+                        },
+                        .mode_first => switch (system_lib.preferred_link_mode) {
+                            .Dynamic => try zig_args.append("-search_dylibs_first"),
+                            .Static => try zig_args.append("-search_static_first"),
+                        },
+                    }
+                    prev_search_strategy = system_lib.search_strategy;
+                    prev_preferred_link_mode = system_lib.preferred_link_mode;
+                }
+
                 const prefix: []const u8 = prefix: {
                     if (system_lib.needed) break :prefix "-needed-l";
                     if (system_lib.weak) break :prefix "-weak-l";
@@ -1662,10 +1653,6 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
         const size = try std.fmt.allocPrint(b.allocator, "{x}", .{pagezero_size});
         try zig_args.appendSlice(&[_][]const u8{ "-pagezero_size", size });
     }
-    if (self.search_strategy) |strat| switch (strat) {
-        .paths_first => try zig_args.append("-search_paths_first"),
-        .dylibs_first => try zig_args.append("-search_dylibs_first"),
-    };
     if (self.headerpad_size) |headerpad_size| {
         const size = try std.fmt.allocPrint(b.allocator, "{x}", .{headerpad_size});
         try zig_args.appendSlice(&[_][]const u8{ "-headerpad", size });
test/link/macho/search_strategy/build.zig
@@ -17,8 +17,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize
 
     {
         // -search_dylibs_first
-        const exe = createScenario(b, optimize, target, "search_dylibs_first");
-        exe.search_strategy = .dylibs_first;
+        const exe = createScenario(b, optimize, target, "search_dylibs_first", .mode_first);
 
         const check = exe.checkObject();
         check.checkStart();
@@ -34,8 +33,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize
 
     {
         // -search_paths_first
-        const exe = createScenario(b, optimize, target, "search_paths_first");
-        exe.search_strategy = .paths_first;
+        const exe = createScenario(b, optimize, target, "search_paths_first", .paths_first);
 
         const run = b.addRunArtifact(exe);
         run.skip_foreign_checks = true;
@@ -49,6 +47,7 @@ fn createScenario(
     optimize: std.builtin.OptimizeMode,
     target: std.zig.CrossTarget,
     name: []const u8,
+    search_strategy: std.Build.Step.Compile.SystemLib.SearchStrategy,
 ) *std.Build.Step.Compile {
     const static = b.addStaticLibrary(.{
         .name = name,
@@ -73,7 +72,10 @@ fn createScenario(
         .target = target,
     });
     exe.addCSourceFile(.{ .file = .{ .path = "main.c" }, .flags = &.{} });
-    exe.linkSystemLibraryName(name);
+    exe.linkSystemLibrary2(name, .{
+        .use_pkg_config = .no,
+        .search_strategy = search_strategy,
+    });
     exe.linkLibC();
     exe.addLibraryPath(static.getEmittedBinDirectory());
     exe.addLibraryPath(dylib.getEmittedBinDirectory());