commit c4e62be62e8d8a006cc9d7861232f511c5a05385 (tree)
parent f887b0251822f75dc4a3e24ca5337cb681c1eb1f
Author: Andrew Kelley <andrew@ziglang.org>
Date: Thu, 3 Aug 2023 09:53:18 -0700
Merge pull request #16058 from ziglang/frontend-lib-paths
compiler: resolve library paths in the frontend
Diffstat:
21 files changed, 914 insertions(+), 717 deletions(-)
diff --git a/build.zig b/build.zig
@@ -204,10 +204,9 @@ pub fn build(b: *std.Build) !void {
);
if (!no_bin) {
- const install_exe = b.addInstallArtifact(exe, .{});
- if (flat) {
- install_exe.dest_dir = .prefix;
- }
+ const install_exe = b.addInstallArtifact(exe, .{
+ .dest_dir = if (flat) .{ .override = .prefix } else .default,
+ });
b.getInstallStep().dependOn(&install_exe.step);
}
diff --git a/ci/aarch64-linux-debug.sh b/ci/aarch64-linux-debug.sh
@@ -72,7 +72,7 @@ stage3-debug/bin/zig build test docs \
# Look for HTML errors.
# TODO: move this to a build.zig flag (-Denable-tidy)
-tidy --drop-empty-elements no -qe "zig-out/doc/langref.html"
+tidy --drop-empty-elements no -qe "../zig-out/doc/langref.html"
# Ensure that updating the wasm binary from this commit will result in a viable build.
stage3-debug/bin/zig build update-zig1
diff --git a/ci/aarch64-linux-release.sh b/ci/aarch64-linux-release.sh
@@ -72,7 +72,7 @@ stage3-release/bin/zig build test docs \
# Look for HTML errors.
# TODO: move this to a build.zig flag (-Denable-tidy)
-tidy --drop-empty-elements no -qe "zig-out/doc/langref.html"
+tidy --drop-empty-elements no -qe "../zig-out/doc/langref.html"
# Ensure that updating the wasm binary from this commit will result in a viable build.
stage3-release/bin/zig build update-zig1
diff --git a/ci/x86_64-linux-debug.sh b/ci/x86_64-linux-debug.sh
@@ -72,7 +72,7 @@ stage3-debug/bin/zig build test docs \
# Look for HTML errors.
# TODO: move this to a build.zig flag (-Denable-tidy)
-tidy --drop-empty-elements no -qe "zig-out/doc/langref.html"
+tidy --drop-empty-elements no -qe "../zig-out/doc/langref.html"
# Ensure that updating the wasm binary from this commit will result in a viable build.
stage3-debug/bin/zig build update-zig1
diff --git a/ci/x86_64-linux-release.sh b/ci/x86_64-linux-release.sh
@@ -73,7 +73,7 @@ stage3-release/bin/zig build test docs \
# Look for HTML errors.
# TODO: move this to a build.zig flag (-Denable-tidy)
-tidy --drop-empty-elements no -qe "zig-out/doc/langref.html"
+tidy --drop-empty-elements no -qe "../zig-out/doc/langref.html"
# Ensure that stage3 and stage4 are byte-for-byte identical.
stage3-release/bin/zig build \
diff --git a/lib/std/Build/Step/Compile.zig b/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 });
diff --git a/lib/std/zig/system/NativePaths.zig b/lib/std/zig/system/NativePaths.zig
@@ -1,6 +1,5 @@
const std = @import("../../std.zig");
const builtin = @import("builtin");
-const ArrayList = std.ArrayList;
const Allocator = std.mem.Allocator;
const process = std.process;
const mem = std.mem;
@@ -8,28 +7,18 @@ const mem = std.mem;
const NativePaths = @This();
const NativeTargetInfo = std.zig.system.NativeTargetInfo;
-include_dirs: ArrayList([:0]u8),
-lib_dirs: ArrayList([:0]u8),
-framework_dirs: ArrayList([:0]u8),
-rpaths: ArrayList([:0]u8),
-warnings: ArrayList([:0]u8),
+arena: Allocator,
+include_dirs: std.ArrayListUnmanaged([]const u8) = .{},
+lib_dirs: std.ArrayListUnmanaged([]const u8) = .{},
+framework_dirs: std.ArrayListUnmanaged([]const u8) = .{},
+rpaths: std.ArrayListUnmanaged([]const u8) = .{},
+warnings: std.ArrayListUnmanaged([]const u8) = .{},
-pub fn detect(allocator: Allocator, native_info: NativeTargetInfo) !NativePaths {
+pub fn detect(arena: Allocator, native_info: NativeTargetInfo) !NativePaths {
const native_target = native_info.target;
-
- var self: NativePaths = .{
- .include_dirs = ArrayList([:0]u8).init(allocator),
- .lib_dirs = ArrayList([:0]u8).init(allocator),
- .framework_dirs = ArrayList([:0]u8).init(allocator),
- .rpaths = ArrayList([:0]u8).init(allocator),
- .warnings = ArrayList([:0]u8).init(allocator),
- };
- errdefer self.deinit();
-
+ var self: NativePaths = .{ .arena = arena };
var is_nix = false;
- if (process.getEnvVarOwned(allocator, "NIX_CFLAGS_COMPILE")) |nix_cflags_compile| {
- defer allocator.free(nix_cflags_compile);
-
+ if (process.getEnvVarOwned(arena, "NIX_CFLAGS_COMPILE")) |nix_cflags_compile| {
is_nix = true;
var it = mem.tokenizeScalar(u8, nix_cflags_compile, ' ');
while (true) {
@@ -58,9 +47,7 @@ pub fn detect(allocator: Allocator, native_info: NativeTargetInfo) !NativePaths
error.EnvironmentVariableNotFound => {},
error.OutOfMemory => |e| return e,
}
- if (process.getEnvVarOwned(allocator, "NIX_LDFLAGS")) |nix_ldflags| {
- defer allocator.free(nix_ldflags);
-
+ if (process.getEnvVarOwned(arena, "NIX_LDFLAGS")) |nix_ldflags| {
is_nix = true;
var it = mem.tokenizeScalar(u8, nix_ldflags, ' ');
while (true) {
@@ -89,17 +76,16 @@ pub fn detect(allocator: Allocator, native_info: NativeTargetInfo) !NativePaths
return self;
}
+ // TODO: consider also adding homebrew paths
+ // TODO: consider also adding macports paths
if (comptime builtin.target.isDarwin()) {
- try self.addIncludeDir("/usr/include");
- try self.addLibDir("/usr/lib");
- try self.addFrameworkDir("/System/Library/Frameworks");
-
- if (builtin.target.os.version_range.semver.min.major < 11) {
- try self.addIncludeDir("/usr/local/include");
- try self.addLibDir("/usr/local/lib");
- try self.addFrameworkDir("/Library/Frameworks");
+ if (std.zig.system.darwin.isSdkInstalled(arena)) sdk: {
+ const sdk = std.zig.system.darwin.getSdk(arena, native_target) orelse break :sdk;
+ try self.addLibDir(try std.fs.path.join(arena, &.{ sdk.path, "usr/lib" }));
+ try self.addFrameworkDir(try std.fs.path.join(arena, &.{ sdk.path, "System/Library/Frameworks" }));
+ try self.addIncludeDir(try std.fs.path.join(arena, &.{ sdk.path, "usr/include" }));
+ return self;
}
-
return self;
}
@@ -115,8 +101,7 @@ pub fn detect(allocator: Allocator, native_info: NativeTargetInfo) !NativePaths
}
if (builtin.os.tag != .windows) {
- const triple = try native_target.linuxTriple(allocator);
- defer allocator.free(triple);
+ const triple = try native_target.linuxTriple(arena);
const qual = native_target.ptrBitWidth();
@@ -172,69 +157,42 @@ pub fn detect(allocator: Allocator, native_info: NativeTargetInfo) !NativePaths
return self;
}
-pub fn deinit(self: *NativePaths) void {
- deinitArray(&self.include_dirs);
- deinitArray(&self.lib_dirs);
- deinitArray(&self.framework_dirs);
- deinitArray(&self.rpaths);
- deinitArray(&self.warnings);
- self.* = undefined;
-}
-
-fn deinitArray(array: *ArrayList([:0]u8)) void {
- for (array.items) |item| {
- array.allocator.free(item);
- }
- array.deinit();
-}
-
pub fn addIncludeDir(self: *NativePaths, s: []const u8) !void {
- return self.appendArray(&self.include_dirs, s);
+ return self.include_dirs.append(self.arena, s);
}
pub fn addIncludeDirFmt(self: *NativePaths, comptime fmt: []const u8, args: anytype) !void {
- const item = try std.fmt.allocPrintZ(self.include_dirs.allocator, fmt, args);
- errdefer self.include_dirs.allocator.free(item);
- try self.include_dirs.append(item);
+ const item = try std.fmt.allocPrint(self.arena, fmt, args);
+ try self.include_dirs.append(self.arena, item);
}
pub fn addLibDir(self: *NativePaths, s: []const u8) !void {
- return self.appendArray(&self.lib_dirs, s);
+ try self.lib_dirs.append(self.arena, s);
}
pub fn addLibDirFmt(self: *NativePaths, comptime fmt: []const u8, args: anytype) !void {
- const item = try std.fmt.allocPrintZ(self.lib_dirs.allocator, fmt, args);
- errdefer self.lib_dirs.allocator.free(item);
- try self.lib_dirs.append(item);
+ const item = try std.fmt.allocPrint(self.arena, fmt, args);
+ try self.lib_dirs.append(self.arena, item);
}
pub fn addWarning(self: *NativePaths, s: []const u8) !void {
- return self.appendArray(&self.warnings, s);
+ return self.warnings.append(self.arena, s);
}
pub fn addFrameworkDir(self: *NativePaths, s: []const u8) !void {
- return self.appendArray(&self.framework_dirs, s);
+ return self.framework_dirs.append(self.arena, s);
}
pub fn addFrameworkDirFmt(self: *NativePaths, comptime fmt: []const u8, args: anytype) !void {
- const item = try std.fmt.allocPrintZ(self.framework_dirs.allocator, fmt, args);
- errdefer self.framework_dirs.allocator.free(item);
- try self.framework_dirs.append(item);
+ const item = try std.fmt.allocPrint(self.arena, fmt, args);
+ try self.framework_dirs.append(self.arena, item);
}
pub fn addWarningFmt(self: *NativePaths, comptime fmt: []const u8, args: anytype) !void {
- const item = try std.fmt.allocPrintZ(self.warnings.allocator, fmt, args);
- errdefer self.warnings.allocator.free(item);
- try self.warnings.append(item);
+ const item = try std.fmt.allocPrint(self.arena, fmt, args);
+ try self.warnings.append(self.arena, item);
}
pub fn addRPath(self: *NativePaths, s: []const u8) !void {
- return self.appendArray(&self.rpaths, s);
-}
-
-fn appendArray(self: *NativePaths, array: *ArrayList([:0]u8), s: []const u8) !void {
- _ = self;
- const item = try array.allocator.dupeZ(u8, s);
- errdefer array.allocator.free(item);
- try array.append(item);
+ try self.rpaths.append(self.arena, s);
}
diff --git a/lib/std/zig/system/darwin.zig b/lib/std/zig/system/darwin.zig
@@ -8,28 +8,34 @@ pub const macos = @import("darwin/macos.zig");
/// Check if SDK is installed on Darwin without triggering CLT installation popup window.
/// Note: simply invoking `xcrun` will inevitably trigger the CLT installation popup.
-/// Therefore, we resort to the same tool used by Homebrew, namely, invoking `xcode-select --print-path`
-/// and checking if the status is nonzero or the returned string in nonempty.
-/// https://github.com/Homebrew/brew/blob/e119bdc571dcb000305411bc1e26678b132afb98/Library/Homebrew/brew.sh#L630
-pub fn isDarwinSDKInstalled(allocator: Allocator) bool {
- const argv = &[_][]const u8{ "/usr/bin/xcode-select", "--print-path" };
- const result = std.ChildProcess.exec(.{ .allocator = allocator, .argv = argv }) catch return false;
+/// Therefore, we resort to invoking `xcode-select --print-path` and checking
+/// if the status is nonzero.
+/// stderr from xcode-select is ignored.
+/// If error.OutOfMemory occurs in Allocator, this function returns null.
+pub fn isSdkInstalled(allocator: Allocator) bool {
+ const result = std.process.Child.exec(.{
+ .allocator = allocator,
+ .argv = &.{ "/usr/bin/xcode-select", "--print-path" },
+ }) catch return false;
+
defer {
allocator.free(result.stderr);
allocator.free(result.stdout);
}
- if (result.stderr.len != 0 or result.term.Exited != 0) {
- // We don't actually care if there were errors as this is best-effort check anyhow.
- return false;
- }
- return result.stdout.len > 0;
+
+ return switch (result.term) {
+ .Exited => |code| if (code == 0) result.stdout.len > 0 else false,
+ else => false,
+ };
}
/// Detect SDK on Darwin.
/// Calls `xcrun --sdk <target_sdk> --show-sdk-path` which fetches the path to the SDK sysroot (if any).
/// Subsequently calls `xcrun --sdk <target_sdk> --show-sdk-version` which fetches version of the SDK.
/// The caller needs to deinit the resulting struct.
-pub fn getDarwinSDK(allocator: Allocator, target: Target) ?DarwinSDK {
+/// stderr from xcrun is ignored.
+/// If error.OutOfMemory occurs in Allocator, this function returns null.
+pub fn getSdk(allocator: Allocator, target: Target) ?Sdk {
const is_simulator_abi = target.abi == .simulator;
const sdk = switch (target.os.tag) {
.macos => "macosx",
@@ -40,30 +46,28 @@ pub fn getDarwinSDK(allocator: Allocator, target: Target) ?DarwinSDK {
};
const path = path: {
const argv = &[_][]const u8{ "/usr/bin/xcrun", "--sdk", sdk, "--show-sdk-path" };
- const result = std.ChildProcess.exec(.{ .allocator = allocator, .argv = argv }) catch return null;
+ const result = std.process.Child.exec(.{ .allocator = allocator, .argv = argv }) catch return null;
defer {
allocator.free(result.stderr);
allocator.free(result.stdout);
}
- if (result.stderr.len != 0 or result.term.Exited != 0) {
- // We don't actually care if there were errors as this is best-effort check anyhow
- // and in the worst case the user can specify the sysroot manually.
- return null;
+ switch (result.term) {
+ .Exited => |code| if (code != 0) return null,
+ else => return null,
}
const path = allocator.dupe(u8, mem.trimRight(u8, result.stdout, "\r\n")) catch return null;
break :path path;
};
const version = version: {
const argv = &[_][]const u8{ "/usr/bin/xcrun", "--sdk", sdk, "--show-sdk-version" };
- const result = std.ChildProcess.exec(.{ .allocator = allocator, .argv = argv }) catch return null;
+ const result = std.process.Child.exec(.{ .allocator = allocator, .argv = argv }) catch return null;
defer {
allocator.free(result.stderr);
allocator.free(result.stdout);
}
- if (result.stderr.len != 0 or result.term.Exited != 0) {
- // We don't actually care if there were errors as this is best-effort check anyhow
- // and in the worst case the user can specify the sysroot manually.
- return null;
+ switch (result.term) {
+ .Exited => |code| if (code != 0) return null,
+ else => return null,
}
const raw_version = mem.trimRight(u8, result.stdout, "\r\n");
const version = parseSdkVersion(raw_version) orelse Version{
@@ -73,7 +77,7 @@ pub fn getDarwinSDK(allocator: Allocator, target: Target) ?DarwinSDK {
};
break :version version;
};
- return DarwinSDK{
+ return Sdk{
.path = path,
.version = version,
};
@@ -96,11 +100,11 @@ fn parseSdkVersion(raw: []const u8) ?Version {
return Version.parse(buffer[0..len]) catch null;
}
-pub const DarwinSDK = struct {
+pub const Sdk = struct {
path: []const u8,
version: Version,
- pub fn deinit(self: DarwinSDK, allocator: Allocator) void {
+ pub fn deinit(self: Sdk, allocator: Allocator) void {
allocator.free(self.path);
}
};
diff --git a/src/Compilation.zig b/src/Compilation.zig
@@ -124,6 +124,7 @@ zig_lib_directory: Directory,
local_cache_directory: Directory,
global_cache_directory: Directory,
libc_include_dir_list: []const []const u8,
+libc_framework_dir_list: []const []const u8,
thread_pool: *ThreadPool,
/// Populated when we build the libc++ static library. A Job to build this is placed in the queue
@@ -448,6 +449,7 @@ pub const ClangPreprocessorMode = enum {
stdout,
};
+pub const Framework = link.Framework;
pub const SystemLib = link.SystemLib;
pub const CacheMode = link.CacheMode;
@@ -505,7 +507,7 @@ pub const InitOptions = struct {
c_source_files: []const CSourceFile = &[0]CSourceFile{},
link_objects: []LinkObject = &[0]LinkObject{},
framework_dirs: []const []const u8 = &[0][]const u8{},
- frameworks: std.StringArrayHashMapUnmanaged(SystemLib) = .{},
+ frameworks: std.StringArrayHashMapUnmanaged(Framework) = .{},
system_lib_names: []const []const u8 = &.{},
system_lib_infos: []const SystemLib = &.{},
/// These correspond to the WASI libc emulated subcomponents including:
@@ -636,16 +638,12 @@ pub const InitOptions = struct {
wasi_exec_model: ?std.builtin.WasiExecModel = null,
/// (Zig compiler development) Enable dumping linker's state as JSON.
enable_link_snapshots: bool = false,
- /// (Darwin) Path and version of the native SDK if detected.
- native_darwin_sdk: ?std.zig.system.darwin.DarwinSDK = null,
/// (Darwin) Install name of the dylib
install_name: ?[]const u8 = null,
/// (Darwin) Path to entitlements file
entitlements: ?[]const u8 = null,
/// (Darwin) size of the __PAGEZERO segment
pagezero_size: ?u64 = null,
- /// (Darwin) search strategy for system libraries
- search_strategy: ?link.File.MachO.SearchStrategy = null,
/// (Darwin) set minimum space for future expansion of the load commands
headerpad_size: ?u32 = null,
/// (Darwin) set enough space as if all paths were MATPATHLEN
@@ -855,16 +853,6 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
break :blk false;
};
- const sysroot = blk: {
- if (options.sysroot) |sysroot| {
- break :blk sysroot;
- } else if (options.native_darwin_sdk) |sdk| {
- break :blk sdk.path;
- } else {
- break :blk null;
- }
- };
-
const lto = blk: {
if (options.want_lto) |explicit| {
if (!use_lld and !options.target.isDarwin())
@@ -948,9 +936,10 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
options.is_native_abi,
link_libc,
options.libc_installation,
- options.native_darwin_sdk != null,
);
+ const sysroot = options.sysroot orelse libc_dirs.sysroot;
+
const must_pie = target_util.requiresPIE(options.target);
const pie: bool = if (options.want_pie) |explicit| pie: {
if (!explicit and must_pie) {
@@ -1563,11 +1552,9 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
.wasi_exec_model = wasi_exec_model,
.hash_style = options.hash_style,
.enable_link_snapshots = options.enable_link_snapshots,
- .native_darwin_sdk = options.native_darwin_sdk,
.install_name = options.install_name,
.entitlements = options.entitlements,
.pagezero_size = options.pagezero_size,
- .search_strategy = options.search_strategy,
.headerpad_size = options.headerpad_size,
.headerpad_max_install_names = options.headerpad_max_install_names,
.dead_strip_dylibs = options.dead_strip_dylibs,
@@ -1601,6 +1588,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
.cache_parent = cache,
.self_exe_path = options.self_exe_path,
.libc_include_dir_list = libc_dirs.libc_include_dir_list,
+ .libc_framework_dir_list = libc_dirs.libc_framework_dir_list,
.sanitize_c = sanitize_c,
.thread_pool = options.thread_pool,
.clang_passthrough_mode = options.clang_passthrough_mode,
@@ -1727,15 +1715,18 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
// When linking mingw-w64 there are some import libs we always need.
for (mingw.always_link_libs) |name| {
- try comp.bin_file.options.system_libs.put(comp.gpa, name, .{});
+ try comp.bin_file.options.system_libs.put(comp.gpa, name, .{
+ .needed = false,
+ .weak = false,
+ .path = null,
+ });
}
}
// Generate Windows import libs.
if (target.os.tag == .windows) {
const count = comp.bin_file.options.system_libs.count();
try comp.work_queue.ensureUnusedCapacity(count);
- var i: usize = 0;
- while (i < count) : (i += 1) {
+ for (0..count) |i| {
comp.work_queue.writeItemAssumeCapacity(.{ .windows_import_lib = i });
}
}
@@ -2367,17 +2358,17 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes
if (comp.bin_file.options.link_libc) {
man.hash.add(comp.bin_file.options.libc_installation != null);
if (comp.bin_file.options.libc_installation) |libc_installation| {
- man.hash.addBytes(libc_installation.crt_dir.?);
+ man.hash.addOptionalBytes(libc_installation.crt_dir);
if (target.abi == .msvc) {
- man.hash.addBytes(libc_installation.msvc_lib_dir.?);
- man.hash.addBytes(libc_installation.kernel32_lib_dir.?);
+ man.hash.addOptionalBytes(libc_installation.msvc_lib_dir);
+ man.hash.addOptionalBytes(libc_installation.kernel32_lib_dir);
}
}
man.hash.addOptionalBytes(comp.bin_file.options.dynamic_linker);
}
man.hash.addOptionalBytes(comp.bin_file.options.soname);
man.hash.addOptional(comp.bin_file.options.version);
- link.hashAddSystemLibs(&man.hash, comp.bin_file.options.system_libs);
+ try link.hashAddSystemLibs(man, comp.bin_file.options.system_libs);
man.hash.addListOfBytes(comp.bin_file.options.force_undefined_symbols.keys());
man.hash.addOptional(comp.bin_file.options.allow_shlib_undefined);
man.hash.add(comp.bin_file.options.bind_global_refs_locally);
@@ -2395,10 +2386,9 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes
// Mach-O specific stuff
man.hash.addListOfBytes(comp.bin_file.options.framework_dirs);
- link.hashAddSystemLibs(&man.hash, comp.bin_file.options.frameworks);
+ link.hashAddFrameworks(&man.hash, comp.bin_file.options.frameworks);
try man.addOptionalFile(comp.bin_file.options.entitlements);
man.hash.addOptional(comp.bin_file.options.pagezero_size);
- man.hash.addOptional(comp.bin_file.options.search_strategy);
man.hash.addOptional(comp.bin_file.options.headerpad_size);
man.hash.add(comp.bin_file.options.headerpad_max_install_names);
man.hash.add(comp.bin_file.options.dead_strip_dylibs);
@@ -4341,6 +4331,14 @@ pub fn addCCArgs(
try argv.append("-ObjC++");
}
+ for (comp.libc_framework_dir_list) |framework_dir| {
+ try argv.appendSlice(&.{ "-iframework", framework_dir });
+ }
+
+ for (comp.bin_file.options.framework_dirs) |framework_dir| {
+ try argv.appendSlice(&.{ "-F", framework_dir });
+ }
+
// According to Rich Felker libc headers are supposed to go before C language headers.
// However as noted by @dimenus, appending libc headers before c_headers breaks intrinsics
// and other compiler specific items.
@@ -4823,6 +4821,8 @@ test "classifyFileExt" {
const LibCDirs = struct {
libc_include_dir_list: []const []const u8,
libc_installation: ?*const LibCInstallation,
+ libc_framework_dir_list: []const []const u8,
+ sysroot: ?[]const u8,
};
fn getZigShippedLibCIncludeDirsDarwin(arena: Allocator, zig_lib_dir: []const u8, target: Target) !LibCDirs {
@@ -4853,6 +4853,8 @@ fn getZigShippedLibCIncludeDirsDarwin(arena: Allocator, zig_lib_dir: []const u8,
return LibCDirs{
.libc_include_dir_list = list,
.libc_installation = null,
+ .libc_framework_dir_list = &.{},
+ .sysroot = null,
};
}
@@ -4863,12 +4865,13 @@ fn detectLibCIncludeDirs(
is_native_abi: bool,
link_libc: bool,
libc_installation: ?*const LibCInstallation,
- has_macos_sdk: bool,
) !LibCDirs {
if (!link_libc) {
return LibCDirs{
.libc_include_dir_list = &[0][]u8{},
.libc_installation = null,
+ .libc_framework_dir_list = &.{},
+ .sysroot = null,
};
}
@@ -4879,28 +4882,19 @@ fn detectLibCIncludeDirs(
// If linking system libraries and targeting the native abi, default to
// using the system libc installation.
if (is_native_abi and !target.isMinGW()) {
- if (target.isDarwin()) {
- return if (has_macos_sdk)
- // For Darwin/macOS, we are all set with getDarwinSDK found earlier.
- LibCDirs{
- .libc_include_dir_list = &[0][]u8{},
- .libc_installation = null,
- }
- else
- getZigShippedLibCIncludeDirsDarwin(arena, zig_lib_dir, target);
- }
const libc = try arena.create(LibCInstallation);
- libc.* = LibCInstallation.findNative(.{ .allocator = arena }) catch |err| switch (err) {
+ libc.* = LibCInstallation.findNative(.{ .allocator = arena, .target = target }) catch |err| switch (err) {
error.CCompilerExitCode,
error.CCompilerCrashed,
error.CCompilerCannotFindHeaders,
error.UnableToSpawnCCompiler,
+ error.DarwinSdkNotFound,
=> |e| {
// We tried to integrate with the native system C compiler,
// however, it is not installed. So we must rely on our bundled
// libc files.
if (target_util.canBuildLibC(target)) {
- return detectLibCFromBuilding(arena, zig_lib_dir, target, has_macos_sdk);
+ return detectLibCFromBuilding(arena, zig_lib_dir, target);
}
return e;
},
@@ -4912,7 +4906,7 @@ fn detectLibCIncludeDirs(
// If not linking system libraries, build and provide our own libc by
// default if possible.
if (target_util.canBuildLibC(target)) {
- return detectLibCFromBuilding(arena, zig_lib_dir, target, has_macos_sdk);
+ return detectLibCFromBuilding(arena, zig_lib_dir, target);
}
// If zig can't build the libc for the target and we are targeting the
@@ -4926,18 +4920,21 @@ fn detectLibCIncludeDirs(
if (use_system_abi) {
const libc = try arena.create(LibCInstallation);
- libc.* = try LibCInstallation.findNative(.{ .allocator = arena, .verbose = true });
+ libc.* = try LibCInstallation.findNative(.{ .allocator = arena, .verbose = true, .target = target });
return detectLibCFromLibCInstallation(arena, target, libc);
}
return LibCDirs{
.libc_include_dir_list = &[0][]u8{},
.libc_installation = null,
+ .libc_framework_dir_list = &.{},
+ .sysroot = null,
};
}
fn detectLibCFromLibCInstallation(arena: Allocator, target: Target, lci: *const LibCInstallation) !LibCDirs {
var list = try std.ArrayList([]const u8).initCapacity(arena, 5);
+ var framework_list = std.ArrayList([]const u8).init(arena);
list.appendAssumeCapacity(lci.include_dir.?);
@@ -4965,9 +4962,20 @@ fn detectLibCFromLibCInstallation(arena: Allocator, target: Target, lci: *const
list.appendAssumeCapacity(config_dir);
}
+ var sysroot: ?[]const u8 = null;
+
+ if (target.isDarwin()) d: {
+ const down1 = std.fs.path.dirname(lci.sys_include_dir.?) orelse break :d;
+ const down2 = std.fs.path.dirname(down1) orelse break :d;
+ try framework_list.append(try std.fs.path.join(arena, &.{ down2, "System", "Library", "Frameworks" }));
+ sysroot = down2;
+ }
+
return LibCDirs{
.libc_include_dir_list = list.items,
.libc_installation = lci,
+ .libc_framework_dir_list = framework_list.items,
+ .sysroot = sysroot,
};
}
@@ -4975,69 +4983,61 @@ fn detectLibCFromBuilding(
arena: Allocator,
zig_lib_dir: []const u8,
target: std.Target,
- has_macos_sdk: bool,
) !LibCDirs {
- switch (target.os.tag) {
- .macos => return if (has_macos_sdk)
- // For Darwin/macOS, we are all set with getDarwinSDK found earlier.
- LibCDirs{
- .libc_include_dir_list = &[0][]u8{},
- .libc_installation = null,
- }
- else
- getZigShippedLibCIncludeDirsDarwin(arena, zig_lib_dir, target),
- else => {
- const generic_name = target_util.libCGenericName(target);
- // Some architectures are handled by the same set of headers.
- const arch_name = if (target.abi.isMusl())
- musl.archNameHeaders(target.cpu.arch)
- else if (target.cpu.arch.isThumb())
- // ARM headers are valid for Thumb too.
- switch (target.cpu.arch) {
- .thumb => "arm",
- .thumbeb => "armeb",
- else => unreachable,
- }
- else
- @tagName(target.cpu.arch);
- const os_name = @tagName(target.os.tag);
- // Musl's headers are ABI-agnostic and so they all have the "musl" ABI name.
- const abi_name = if (target.abi.isMusl()) "musl" else @tagName(target.abi);
- const s = std.fs.path.sep_str;
- const arch_include_dir = try std.fmt.allocPrint(
- arena,
- "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{s}-{s}-{s}",
- .{ zig_lib_dir, arch_name, os_name, abi_name },
- );
- const generic_include_dir = try std.fmt.allocPrint(
- arena,
- "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "generic-{s}",
- .{ zig_lib_dir, generic_name },
- );
- const generic_arch_name = target_util.osArchName(target);
- const arch_os_include_dir = try std.fmt.allocPrint(
- arena,
- "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{s}-{s}-any",
- .{ zig_lib_dir, generic_arch_name, os_name },
- );
- const generic_os_include_dir = try std.fmt.allocPrint(
- arena,
- "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "any-{s}-any",
- .{ zig_lib_dir, os_name },
- );
+ if (target.isDarwin())
+ return getZigShippedLibCIncludeDirsDarwin(arena, zig_lib_dir, target);
+
+ const generic_name = target_util.libCGenericName(target);
+ // Some architectures are handled by the same set of headers.
+ const arch_name = if (target.abi.isMusl())
+ musl.archNameHeaders(target.cpu.arch)
+ else if (target.cpu.arch.isThumb())
+ // ARM headers are valid for Thumb too.
+ switch (target.cpu.arch) {
+ .thumb => "arm",
+ .thumbeb => "armeb",
+ else => unreachable,
+ }
+ else
+ @tagName(target.cpu.arch);
+ const os_name = @tagName(target.os.tag);
+ // Musl's headers are ABI-agnostic and so they all have the "musl" ABI name.
+ const abi_name = if (target.abi.isMusl()) "musl" else @tagName(target.abi);
+ const s = std.fs.path.sep_str;
+ const arch_include_dir = try std.fmt.allocPrint(
+ arena,
+ "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{s}-{s}-{s}",
+ .{ zig_lib_dir, arch_name, os_name, abi_name },
+ );
+ const generic_include_dir = try std.fmt.allocPrint(
+ arena,
+ "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "generic-{s}",
+ .{ zig_lib_dir, generic_name },
+ );
+ const generic_arch_name = target_util.osArchName(target);
+ const arch_os_include_dir = try std.fmt.allocPrint(
+ arena,
+ "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{s}-{s}-any",
+ .{ zig_lib_dir, generic_arch_name, os_name },
+ );
+ const generic_os_include_dir = try std.fmt.allocPrint(
+ arena,
+ "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "any-{s}-any",
+ .{ zig_lib_dir, os_name },
+ );
- const list = try arena.alloc([]const u8, 4);
- list[0] = arch_include_dir;
- list[1] = generic_include_dir;
- list[2] = arch_os_include_dir;
- list[3] = generic_os_include_dir;
+ const list = try arena.alloc([]const u8, 4);
+ list[0] = arch_include_dir;
+ list[1] = generic_include_dir;
+ list[2] = arch_os_include_dir;
+ list[3] = generic_os_include_dir;
- return LibCDirs{
- .libc_include_dir_list = list,
- .libc_installation = null,
- };
- },
- }
+ return LibCDirs{
+ .libc_include_dir_list = list,
+ .libc_installation = null,
+ .libc_framework_dir_list = &.{},
+ .sysroot = null,
+ };
}
pub fn get_libc_crt_file(comp: *Compilation, arena: Allocator, basename: []const u8) ![]const u8 {
@@ -5618,6 +5618,11 @@ pub fn addLinkLib(comp: *Compilation, lib_name: []const u8) !void {
// to queue up a work item to produce the DLL import library for this.
const gop = try comp.bin_file.options.system_libs.getOrPut(comp.gpa, lib_name);
if (!gop.found_existing and comp.getTarget().os.tag == .windows) {
+ gop.value_ptr.* = .{
+ .needed = true,
+ .weak = false,
+ .path = null,
+ };
try comp.work_queue.writeItem(.{
.windows_import_lib = comp.bin_file.options.system_libs.count() - 1,
});
diff --git a/src/libc_installation.zig b/src/libc_installation.zig
@@ -33,6 +33,7 @@ pub const LibCInstallation = struct {
LibCKernel32LibNotFound,
UnsupportedArchitecture,
WindowsSdkNotFound,
+ DarwinSdkNotFound,
ZigIsTheCCompiler,
};
@@ -171,6 +172,7 @@ pub const LibCInstallation = struct {
pub const FindNativeOptions = struct {
allocator: Allocator,
+ target: std.Target,
/// If enabled, will print human-friendly errors to stderr.
verbose: bool = false,
@@ -181,7 +183,19 @@ pub const LibCInstallation = struct {
var self: LibCInstallation = .{};
if (is_darwin) {
- @panic("Darwin is handled separately via std.zig.system.darwin module");
+ if (!std.zig.system.darwin.isSdkInstalled(args.allocator))
+ return error.DarwinSdkNotFound;
+ const sdk = std.zig.system.darwin.getSdk(args.allocator, args.target) orelse
+ return error.DarwinSdkNotFound;
+ defer args.allocator.free(sdk.path);
+
+ self.include_dir = try fs.path.join(args.allocator, &.{
+ sdk.path, "usr/include",
+ });
+ self.sys_include_dir = try fs.path.join(args.allocator, &.{
+ sdk.path, "usr/include",
+ });
+ return self;
} else if (is_windows) {
var sdk: ZigWindowsSDK = ZigWindowsSDK.find(args.allocator) catch |err| switch (err) {
error.NotFound => return error.WindowsSdkNotFound,
diff --git a/src/link.zig b/src/link.zig
@@ -21,7 +21,20 @@ const Type = @import("type.zig").Type;
const TypedValue = @import("TypedValue.zig");
/// When adding a new field, remember to update `hashAddSystemLibs`.
+/// These are *always* dynamically linked. Static libraries will be
+/// provided as positional arguments.
pub const SystemLib = struct {
+ needed: bool,
+ weak: bool,
+ /// This can be null in two cases right now:
+ /// 1. Windows DLLs that zig ships such as advapi32.
+ /// 2. extern "foo" fn declarations where we find out about libraries too late
+ /// TODO: make this non-optional and resolve those two cases somehow.
+ path: ?[]const u8,
+};
+
+/// When adding a new field, remember to update `hashAddFrameworks`.
+pub const Framework = struct {
needed: bool = false,
weak: bool = false,
};
@@ -31,11 +44,23 @@ pub const SortSection = enum { name, alignment };
pub const CacheMode = enum { incremental, whole };
pub fn hashAddSystemLibs(
- hh: *Cache.HashHelper,
+ man: *Cache.Manifest,
hm: std.StringArrayHashMapUnmanaged(SystemLib),
+) !void {
+ const keys = hm.keys();
+ man.hash.addListOfBytes(keys);
+ for (hm.values()) |value| {
+ man.hash.add(value.needed);
+ man.hash.add(value.weak);
+ if (value.path) |p| _ = try man.addFile(p, null);
+ }
+}
+
+pub fn hashAddFrameworks(
+ hh: *Cache.HashHelper,
+ hm: std.StringArrayHashMapUnmanaged(Framework),
) void {
const keys = hm.keys();
- hh.add(keys.len);
hh.addListOfBytes(keys);
for (hm.values()) |value| {
hh.add(value.needed);
@@ -183,9 +208,12 @@ pub const Options = struct {
objects: []Compilation.LinkObject,
framework_dirs: []const []const u8,
- frameworks: std.StringArrayHashMapUnmanaged(SystemLib),
+ frameworks: std.StringArrayHashMapUnmanaged(Framework),
+ /// These are *always* dynamically linked. Static libraries will be
+ /// provided as positional arguments.
system_libs: std.StringArrayHashMapUnmanaged(SystemLib),
wasi_emulated_libs: []const wasi_libc.CRTFile,
+ // TODO: remove this. libraries are resolved by the frontend.
lib_dirs: []const []const u8,
rpath_list: []const []const u8,
@@ -203,6 +231,7 @@ pub const Options = struct {
version: ?std.SemanticVersion,
compatibility_version: ?std.SemanticVersion,
+ darwin_sdk_version: ?std.SemanticVersion = null,
libc_installation: ?*const LibCInstallation,
dwarf_format: ?std.dwarf.Format,
@@ -213,9 +242,6 @@ pub const Options = struct {
/// (Zig compiler development) Enable dumping of linker's state as JSON.
enable_link_snapshots: bool = false,
- /// (Darwin) Path and version of the native SDK if detected.
- native_darwin_sdk: ?std.zig.system.darwin.DarwinSDK = null,
-
/// (Darwin) Install name for the dylib
install_name: ?[]const u8 = null,
@@ -225,9 +251,6 @@ pub const Options = struct {
/// (Darwin) size of the __PAGEZERO segment
pagezero_size: ?u64 = null,
- /// (Darwin) search strategy for system libraries
- search_strategy: ?File.MachO.SearchStrategy = null,
-
/// (Darwin) set minimum space for future expansion of the load commands
headerpad_size: ?u32 = null,
diff --git a/src/link/Coff/lld.zig b/src/link/Coff/lld.zig
@@ -88,7 +88,7 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
}
}
}
- link.hashAddSystemLibs(&man.hash, self.base.options.system_libs);
+ try link.hashAddSystemLibs(&man, self.base.options.system_libs);
man.hash.addListOfBytes(self.base.options.force_undefined_symbols.keys());
man.hash.addOptional(self.base.options.subsystem);
man.hash.add(self.base.options.is_test);
@@ -405,6 +405,7 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
try argv.append(try comp.get_libc_crt_file(arena, "mingw32.lib"));
try argv.append(try comp.get_libc_crt_file(arena, "mingwex.lib"));
try argv.append(try comp.get_libc_crt_file(arena, "msvcrt-os.lib"));
+ try argv.append(try comp.get_libc_crt_file(arena, "uuid.lib"));
for (mingw.always_link_libs) |name| {
if (!self.base.options.system_libs.contains(name)) {
diff --git a/src/link/Elf.zig b/src/link/Elf.zig
@@ -1428,7 +1428,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
}
man.hash.addOptionalBytes(self.base.options.soname);
man.hash.addOptional(self.base.options.version);
- link.hashAddSystemLibs(&man.hash, self.base.options.system_libs);
+ try link.hashAddSystemLibs(&man, self.base.options.system_libs);
man.hash.addListOfBytes(self.base.options.force_undefined_symbols.keys());
man.hash.add(allow_shlib_undefined);
man.hash.add(self.base.options.bind_global_refs_locally);
@@ -1824,8 +1824,8 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
argv.appendAssumeCapacity("--as-needed");
var as_needed = true;
- for (system_libs, 0..) |link_lib, i| {
- const lib_as_needed = !system_libs_values[i].needed;
+ for (system_libs_values) |lib_info| {
+ const lib_as_needed = !lib_info.needed;
switch ((@as(u2, @intFromBool(lib_as_needed)) << 1) | @intFromBool(as_needed)) {
0b00, 0b11 => {},
0b01 => {
@@ -1842,9 +1842,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
// libraries and not static libraries (the check for that needs to be earlier),
// but they could be full paths to .so files, in which case we
// want to avoid prepending "-l".
- const ext = Compilation.classifyFileExt(link_lib);
- const arg = if (ext == .shared_library) link_lib else try std.fmt.allocPrint(arena, "-l{s}", .{link_lib});
- argv.appendAssumeCapacity(arg);
+ argv.appendAssumeCapacity(lib_info.path.?);
}
if (!as_needed) {
diff --git a/src/link/MachO.zig b/src/link/MachO.zig
@@ -58,11 +58,6 @@ const Rebase = @import("MachO/dyld_info/Rebase.zig");
pub const base_tag: File.Tag = File.Tag.macho;
-pub const SearchStrategy = enum {
- paths_first,
- dylibs_first,
-};
-
/// Mode of operation of the linker.
pub const Mode = enum {
/// Incremental mode will preallocate segments/sections and is compatible with
@@ -834,39 +829,50 @@ pub fn resolveLibSystem(
out_libs: anytype,
) !void {
// If we were given the sysroot, try to look there first for libSystem.B.{dylib, tbd}.
- var libsystem_available = false;
- if (syslibroot != null) blk: {
- // Try stub file first. If we hit it, then we're done as the stub file
- // re-exports every single symbol definition.
- for (search_dirs) |dir| {
- if (try resolveLib(arena, dir, "System", ".tbd")) |full_path| {
- try out_libs.put(full_path, .{ .needed = true });
- libsystem_available = true;
- break :blk;
- }
+ if (syslibroot) |root| {
+ const full_dir_path = try std.fs.path.join(arena, &.{ root, "usr", "lib" });
+ if (try resolveLibSystemInDirs(arena, &.{full_dir_path}, out_libs)) return;
+ }
+
+ // Next, try input search dirs if we are linking on a custom host such as Nix.
+ if (try resolveLibSystemInDirs(arena, search_dirs, out_libs)) return;
+
+ // As a fallback, try linking against Zig shipped stub.
+ const libsystem_name = try std.fmt.allocPrint(arena, "libSystem.{d}.tbd", .{
+ target.os.version_range.semver.min.major,
+ });
+ const full_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{
+ "libc", "darwin", libsystem_name,
+ });
+ try out_libs.put(full_path, .{
+ .needed = true,
+ .weak = false,
+ .path = full_path,
+ });
+}
+
+fn resolveLibSystemInDirs(arena: Allocator, dirs: []const []const u8, out_libs: anytype) !bool {
+ // Try stub file first. If we hit it, then we're done as the stub file
+ // re-exports every single symbol definition.
+ for (dirs) |dir| {
+ if (try resolveLib(arena, dir, "System", ".tbd")) |full_path| {
+ try out_libs.put(full_path, .{ .needed = true, .weak = false, .path = full_path });
+ return true;
}
- // If we didn't hit the stub file, try .dylib next. However, libSystem.dylib
- // doesn't export libc.dylib which we'll need to resolve subsequently also.
- for (search_dirs) |dir| {
- if (try resolveLib(arena, dir, "System", ".dylib")) |libsystem_path| {
- if (try resolveLib(arena, dir, "c", ".dylib")) |libc_path| {
- try out_libs.put(libsystem_path, .{ .needed = true });
- try out_libs.put(libc_path, .{ .needed = true });
- libsystem_available = true;
- break :blk;
- }
+ }
+ // If we didn't hit the stub file, try .dylib next. However, libSystem.dylib
+ // doesn't export libc.dylib which we'll need to resolve subsequently also.
+ for (dirs) |dir| {
+ if (try resolveLib(arena, dir, "System", ".dylib")) |libsystem_path| {
+ if (try resolveLib(arena, dir, "c", ".dylib")) |libc_path| {
+ try out_libs.put(libsystem_path, .{ .needed = true, .weak = false, .path = libsystem_path });
+ try out_libs.put(libc_path, .{ .needed = true, .weak = false, .path = libc_path });
+ return true;
}
}
}
- if (!libsystem_available) {
- const libsystem_name = try std.fmt.allocPrint(arena, "libSystem.{d}.tbd", .{
- target.os.version_range.semver.min.major,
- });
- const full_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{
- "libc", "darwin", libsystem_name,
- });
- try out_libs.put(full_path, .{ .needed = true });
- }
+
+ return false;
}
pub fn resolveSearchDir(
diff --git a/src/link/MachO/load_commands.zig b/src/link/MachO/load_commands.zig
@@ -278,11 +278,10 @@ pub fn writeBuildVersionLC(options: *const link.Options, lc_writer: anytype) !vo
const platform_version = @as(u32, @intCast(ver.major << 16 | ver.minor << 8));
break :blk platform_version;
};
- const sdk_version = if (options.native_darwin_sdk) |sdk| blk: {
- const ver = sdk.version;
- const sdk_version = @as(u32, @intCast(ver.major << 16 | ver.minor << 8));
- break :blk sdk_version;
- } else platform_version;
+ const sdk_version: u32 = if (options.darwin_sdk_version) |ver|
+ @intCast(ver.major << 16 | ver.minor << 8)
+ else
+ platform_version;
const is_simulator_abi = options.target.abi == .simulator;
try lc_writer.writeStruct(macho.build_version_command{
.cmdsize = cmdsize,
diff --git a/src/link/MachO/zld.zig b/src/link/MachO/zld.zig
@@ -3410,7 +3410,6 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
// installation sources because they are always a product of the compiler version + target information.
man.hash.add(stack_size);
man.hash.addOptional(options.pagezero_size);
- man.hash.addOptional(options.search_strategy);
man.hash.addOptional(options.headerpad_size);
man.hash.add(options.headerpad_max_install_names);
man.hash.add(gc_sections);
@@ -3418,13 +3417,13 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
man.hash.add(options.strip);
man.hash.addListOfBytes(options.lib_dirs);
man.hash.addListOfBytes(options.framework_dirs);
- link.hashAddSystemLibs(&man.hash, options.frameworks);
+ link.hashAddFrameworks(&man.hash, options.frameworks);
man.hash.addListOfBytes(options.rpath_list);
if (is_dyn_lib) {
man.hash.addOptionalBytes(options.install_name);
man.hash.addOptional(options.version);
}
- link.hashAddSystemLibs(&man.hash, options.system_libs);
+ try link.hashAddSystemLibs(&man, options.system_libs);
man.hash.addOptionalBytes(options.sysroot);
man.hash.addListOfBytes(options.force_undefined_symbols.keys());
try man.addOptionalFile(options.entitlements);
@@ -3550,84 +3549,15 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
try positionals.append(comp.libcxx_static_lib.?.full_object_path);
}
- // Shared and static libraries passed via `-l` flag.
- var candidate_libs = std.StringArrayHashMap(link.SystemLib).init(arena);
-
- const system_lib_names = options.system_libs.keys();
- for (system_lib_names) |system_lib_name| {
- // By this time, we depend on these libs being dynamically linked libraries and not static libraries
- // (the check for that needs to be earlier), but they could be full paths to .dylib files, in which
- // case we want to avoid prepending "-l".
- if (Compilation.classifyFileExt(system_lib_name) == .shared_library) {
- try positionals.append(system_lib_name);
- continue;
- }
-
- const system_lib_info = options.system_libs.get(system_lib_name).?;
- try candidate_libs.put(system_lib_name, .{
- .needed = system_lib_info.needed,
- .weak = system_lib_info.weak,
- });
- }
-
- var lib_dirs = std.ArrayList([]const u8).init(arena);
- for (options.lib_dirs) |dir| {
- if (try MachO.resolveSearchDir(arena, dir, options.sysroot)) |search_dir| {
- try lib_dirs.append(search_dir);
- } else {
- log.warn("directory not found for '-L{s}'", .{dir});
- }
- }
-
var libs = std.StringArrayHashMap(link.SystemLib).init(arena);
- // Assume ld64 default -search_paths_first if no strategy specified.
- const search_strategy = options.search_strategy orelse .paths_first;
- outer: for (candidate_libs.keys()) |lib_name| {
- switch (search_strategy) {
- .paths_first => {
- // Look in each directory for a dylib (stub first), and then for archive
- for (lib_dirs.items) |dir| {
- for (&[_][]const u8{ ".tbd", ".dylib", ".a" }) |ext| {
- if (try MachO.resolveLib(arena, dir, lib_name, ext)) |full_path| {
- try libs.put(full_path, candidate_libs.get(lib_name).?);
- continue :outer;
- }
- }
- } else {
- log.warn("library not found for '-l{s}'", .{lib_name});
- lib_not_found = true;
- }
- },
- .dylibs_first => {
- // First, look for a dylib in each search dir
- for (lib_dirs.items) |dir| {
- for (&[_][]const u8{ ".tbd", ".dylib" }) |ext| {
- if (try MachO.resolveLib(arena, dir, lib_name, ext)) |full_path| {
- try libs.put(full_path, candidate_libs.get(lib_name).?);
- continue :outer;
- }
- }
- } else for (lib_dirs.items) |dir| {
- if (try MachO.resolveLib(arena, dir, lib_name, ".a")) |full_path| {
- try libs.put(full_path, candidate_libs.get(lib_name).?);
- } else {
- log.warn("library not found for '-l{s}'", .{lib_name});
- lib_not_found = true;
- }
- }
- },
- }
- }
-
- if (lib_not_found) {
- log.warn("Library search paths:", .{});
- for (lib_dirs.items) |dir| {
- log.warn(" {s}", .{dir});
- }
+ {
+ const vals = options.system_libs.values();
+ try libs.ensureUnusedCapacity(vals.len);
+ for (vals) |v| libs.putAssumeCapacity(v.path.?, v);
}
- try MachO.resolveLibSystem(arena, comp, options.sysroot, target, lib_dirs.items, &libs);
+ try MachO.resolveLibSystem(arena, comp, options.sysroot, target, options.lib_dirs, &libs);
// frameworks
var framework_dirs = std.ArrayList([]const u8).init(arena);
@@ -3647,6 +3577,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
try libs.put(full_path, .{
.needed = info.needed,
.weak = info.weak,
+ .path = full_path,
});
continue :outer;
}
@@ -3698,11 +3629,6 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{pagezero_size}));
}
- if (options.search_strategy) |strat| switch (strat) {
- .paths_first => try argv.append("-search_paths_first"),
- .dylibs_first => try argv.append("-search_dylibs_first"),
- };
-
if (options.headerpad_size) |headerpad_size| {
try argv.append("-headerpad_size");
try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{headerpad_size}));
diff --git a/src/main.zig b/src/main.zig
@@ -28,6 +28,7 @@ const target_util = @import("target.zig");
const crash_report = @import("crash_report.zig");
const Module = @import("Module.zig");
const AstGen = @import("AstGen.zig");
+const mingw = @import("mingw.zig");
const Server = std.zig.Server;
pub const std_options = struct {
@@ -476,7 +477,19 @@ const usage_build_generic =
\\ -l[lib], --library [lib] Link against system library (only if actually used)
\\ -needed-l[lib], Link against system library (even if unused)
\\ --needed-library [lib]
+ \\ -weak-l[lib] link against system library marking it and all
+ \\ -weak_library [lib] referenced symbols as weak
\\ -L[d], --library-directory [d] Add a directory to the library search path
+ \\ -search_paths_first For each library search path, check for dynamic
+ \\ lib then static lib before proceeding to next path.
+ \\ -search_paths_first_static For each library search path, check for static
+ \\ lib then dynamic lib before proceeding to next path.
+ \\ -search_dylibs_first Search for dynamic libs in all library search
+ \\ paths, then static libs.
+ \\ -search_static_first Search for static libs in all library search
+ \\ paths, then dynamic libs.
+ \\ -search_dylibs_only Only search for dynamic libs.
+ \\ -search_static_only Only search for static libs.
\\ -T[script], --script [script] Use a custom linker script
\\ --version-script [path] Provide a version .map file
\\ --dynamic-linker [path] Set the dynamic interpreter path (usually ld.so)
@@ -527,18 +540,14 @@ const usage_build_generic =
\\ --subsystem [subsystem] (Windows) /SUBSYSTEM:<subsystem> to the linker
\\ --stack [size] Override default stack size
\\ --image-base [addr] Set base address for executable image
- \\ -weak-l[lib] (Darwin) link against system library and mark it and all referenced symbols as weak
- \\ -weak_library [lib]
\\ -framework [name] (Darwin) link against framework
\\ -needed_framework [name] (Darwin) link against framework (even if unused)
- \\ -needed_library [lib] (Darwin) link against system library (even if unused)
+ \\ -needed_library [lib] link against system library (even if unused)
\\ -weak_framework [name] (Darwin) link against framework and mark it and all referenced symbols as weak
\\ -F[dir] (Darwin) add search path for frameworks
\\ -install_name=[value] (Darwin) add dylib's install name
\\ --entitlements [path] (Darwin) add path to entitlements file for embedding in code signature
\\ -pagezero_size [value] (Darwin) size of the __PAGEZERO segment in hexadecimal notation
- \\ -search_paths_first (Darwin) search each dir in library search paths for `libx.dylib` then `libx.a`
- \\ -search_dylibs_first (Darwin) search `libx.dylib` in each dir in library search paths, then `libx.a`
\\ -headerpad [value] (Darwin) set minimum space for future expansion of the load commands in hexadecimal notation
\\ -headerpad_max_install_names (Darwin) set enough space as if all paths were MAXPATHLEN
\\ -dead_strip (Darwin) remove functions and data that are unreachable by the entry point or exported symbols
@@ -716,6 +725,39 @@ const ArgsIterator = struct {
}
};
+/// In contrast to `link.SystemLib`, this stores arguments that may need to be
+/// resolved into static libraries so that we can pass only dynamic libraries
+/// as system libs to `Compilation`.
+const SystemLib = struct {
+ needed: bool,
+ weak: bool,
+
+ preferred_mode: std.builtin.LinkMode,
+ search_strategy: SearchStrategy,
+
+ const SearchStrategy = enum { paths_first, mode_first, no_fallback };
+
+ fn fallbackMode(this: SystemLib) std.builtin.LinkMode {
+ assert(this.search_strategy != .no_fallback);
+ return switch (this.preferred_mode) {
+ .Dynamic => .Static,
+ .Static => .Dynamic,
+ };
+ }
+};
+
+const CliModule = struct {
+ mod: *Package,
+ /// still in CLI arg format
+ deps_str: []const u8,
+};
+
+fn cleanupModules(modules: *std.StringArrayHashMap(CliModule)) void {
+ var it = modules.iterator();
+ while (it.next()) |kv| kv.value_ptr.mod.destroy(modules.allocator);
+ modules.deinit();
+}
+
fn buildOutputType(
gpa: Allocator,
arena: Allocator,
@@ -849,12 +891,12 @@ fn buildOutputType(
var minor_subsystem_version: ?u32 = null;
var wasi_exec_model: ?std.builtin.WasiExecModel = null;
var enable_link_snapshots: bool = false;
- var native_darwin_sdk: ?std.zig.system.darwin.DarwinSDK = null;
var install_name: ?[]const u8 = null;
var hash_style: link.HashStyle = .both;
var entitlements: ?[]const u8 = null;
var pagezero_size: ?u64 = null;
- var search_strategy: ?link.File.MachO.SearchStrategy = null;
+ var lib_search_strategy: SystemLib.SearchStrategy = .paths_first;
+ var lib_preferred_mode: std.builtin.LinkMode = .Dynamic;
var headerpad_size: ?u32 = null;
var headerpad_max_install_names: bool = false;
var dead_strip_dylibs: bool = false;
@@ -862,66 +904,30 @@ fn buildOutputType(
var error_tracing: ?bool = null;
var pdb_out_path: ?[]const u8 = null;
var dwarf_format: ?std.dwarf.Format = null;
-
// e.g. -m3dnow or -mno-outline-atomics. They correspond to std.Target llvm cpu feature names.
// This array is populated by zig cc frontend and then has to be converted to zig-style
// CPU features.
- var llvm_m_args = std.ArrayList([]const u8).init(gpa);
- defer llvm_m_args.deinit();
-
- var system_libs = std.StringArrayHashMap(Compilation.SystemLib).init(gpa);
- defer system_libs.deinit();
-
- var static_libs = std.ArrayList([]const u8).init(gpa);
- defer static_libs.deinit();
-
- var wasi_emulated_libs = std.ArrayList(wasi_libc.CRTFile).init(gpa);
- defer wasi_emulated_libs.deinit();
-
- var clang_argv = std.ArrayList([]const u8).init(gpa);
- defer clang_argv.deinit();
-
- var extra_cflags = std.ArrayList([]const u8).init(gpa);
- defer extra_cflags.deinit();
-
- var lib_dirs = std.ArrayList([]const u8).init(gpa);
- defer lib_dirs.deinit();
-
- var rpath_list = std.ArrayList([]const u8).init(gpa);
- defer rpath_list.deinit();
-
+ var llvm_m_args = std.ArrayList([]const u8).init(arena);
+ var system_libs = std.StringArrayHashMap(SystemLib).init(arena);
+ var wasi_emulated_libs = std.ArrayList(wasi_libc.CRTFile).init(arena);
+ var clang_argv = std.ArrayList([]const u8).init(arena);
+ var extra_cflags = std.ArrayList([]const u8).init(arena);
+ // These are before resolving sysroot.
+ var lib_dir_args = std.ArrayList([]const u8).init(arena);
+ var rpath_list = std.ArrayList([]const u8).init(arena);
var symbol_wrap_set: std.StringArrayHashMapUnmanaged(void) = .{};
-
- var c_source_files = std.ArrayList(Compilation.CSourceFile).init(gpa);
- defer c_source_files.deinit();
-
- var link_objects = std.ArrayList(Compilation.LinkObject).init(gpa);
- defer link_objects.deinit();
-
- var framework_dirs = std.ArrayList([]const u8).init(gpa);
- defer framework_dirs.deinit();
-
- var frameworks: std.StringArrayHashMapUnmanaged(Compilation.SystemLib) = .{};
-
+ var c_source_files = std.ArrayList(Compilation.CSourceFile).init(arena);
+ var link_objects = std.ArrayList(Compilation.LinkObject).init(arena);
+ var framework_dirs = std.ArrayList([]const u8).init(arena);
+ var frameworks: std.StringArrayHashMapUnmanaged(Compilation.Framework) = .{};
// null means replace with the test executable binary
- var test_exec_args = std.ArrayList(?[]const u8).init(gpa);
- defer test_exec_args.deinit();
-
- var linker_export_symbol_names = std.ArrayList([]const u8).init(gpa);
- defer linker_export_symbol_names.deinit();
-
+ var test_exec_args = std.ArrayList(?[]const u8).init(arena);
+ var linker_export_symbol_names = std.ArrayList([]const u8).init(arena);
// Contains every module specified via --mod. The dependencies are added
// after argument parsing is completed. We use a StringArrayHashMap to make
// error output consistent.
- var modules = std.StringArrayHashMap(struct {
- mod: *Package,
- deps_str: []const u8, // still in CLI arg format
- }).init(gpa);
- defer {
- var it = modules.iterator();
- while (it.next()) |kv| kv.value_ptr.mod.destroy(gpa);
- modules.deinit();
- }
+ var modules = std.StringArrayHashMap(CliModule).init(gpa);
+ defer cleanupModules(&modules);
// The dependency string for the root package
var root_deps_str: ?[]const u8 = null;
@@ -1061,7 +1067,7 @@ fn buildOutputType(
} else if (mem.eql(u8, arg, "-rpath")) {
try rpath_list.append(args_iter.nextOrFatal());
} else if (mem.eql(u8, arg, "--library-directory") or mem.eql(u8, arg, "-L")) {
- try lib_dirs.append(args_iter.nextOrFatal());
+ try lib_dir_args.append(args_iter.nextOrFatal());
} else if (mem.eql(u8, arg, "-F")) {
try framework_dirs.append(args_iter.nextOrFatal());
} else if (mem.eql(u8, arg, "-framework")) {
@@ -1085,9 +1091,23 @@ fn buildOutputType(
fatal("unable to parse pagezero size'{s}': {s}", .{ next_arg, @errorName(err) });
};
} else if (mem.eql(u8, arg, "-search_paths_first")) {
- search_strategy = .paths_first;
+ lib_search_strategy = .paths_first;
+ lib_preferred_mode = .Dynamic;
+ } else if (mem.eql(u8, arg, "-search_paths_first_static")) {
+ lib_search_strategy = .paths_first;
+ lib_preferred_mode = .Static;
} else if (mem.eql(u8, arg, "-search_dylibs_first")) {
- search_strategy = .dylibs_first;
+ lib_search_strategy = .mode_first;
+ lib_preferred_mode = .Dynamic;
+ } else if (mem.eql(u8, arg, "-search_static_first")) {
+ lib_search_strategy = .mode_first;
+ lib_preferred_mode = .Static;
+ } else if (mem.eql(u8, arg, "-search_dylibs_only")) {
+ lib_search_strategy = .no_fallback;
+ lib_preferred_mode = .Dynamic;
+ } else if (mem.eql(u8, arg, "-search_static_only")) {
+ lib_search_strategy = .no_fallback;
+ lib_preferred_mode = .Static;
} else if (mem.eql(u8, arg, "-headerpad")) {
const next_arg = args_iter.nextOrFatal();
headerpad_size = std.fmt.parseUnsigned(u32, eatIntPrefix(next_arg, 16), 16) catch |err| {
@@ -1104,17 +1124,33 @@ fn buildOutputType(
} else if (mem.eql(u8, arg, "-version-script") or mem.eql(u8, arg, "--version-script")) {
version_script = args_iter.nextOrFatal();
} else if (mem.eql(u8, arg, "--library") or mem.eql(u8, arg, "-l")) {
- // We don't know whether this library is part of libc or libc++ until
- // we resolve the target, so we simply append to the list for now.
- try system_libs.put(args_iter.nextOrFatal(), .{});
+ // We don't know whether this library is part of libc
+ // or libc++ until we resolve the target, so we append
+ // to the list for now.
+ try system_libs.put(args_iter.nextOrFatal(), .{
+ .needed = false,
+ .weak = false,
+ .preferred_mode = lib_preferred_mode,
+ .search_strategy = lib_search_strategy,
+ });
} else if (mem.eql(u8, arg, "--needed-library") or
mem.eql(u8, arg, "-needed-l") or
mem.eql(u8, arg, "-needed_library"))
{
const next_arg = args_iter.nextOrFatal();
- try system_libs.put(next_arg, .{ .needed = true });
+ try system_libs.put(next_arg, .{
+ .needed = true,
+ .weak = false,
+ .preferred_mode = lib_preferred_mode,
+ .search_strategy = lib_search_strategy,
+ });
} else if (mem.eql(u8, arg, "-weak_library") or mem.eql(u8, arg, "-weak-l")) {
- try system_libs.put(args_iter.nextOrFatal(), .{ .weak = true });
+ try system_libs.put(args_iter.nextOrFatal(), .{
+ .needed = false,
+ .weak = true,
+ .preferred_mode = lib_preferred_mode,
+ .search_strategy = lib_search_strategy,
+ });
} else if (mem.eql(u8, arg, "-D")) {
try clang_argv.append(arg);
try clang_argv.append(args_iter.nextOrFatal());
@@ -1346,8 +1382,12 @@ fn buildOutputType(
emit_implib_arg_provided = true;
} else if (mem.eql(u8, arg, "-dynamic")) {
link_mode = .Dynamic;
+ lib_preferred_mode = .Dynamic;
+ lib_search_strategy = .mode_first;
} else if (mem.eql(u8, arg, "-static")) {
link_mode = .Static;
+ lib_preferred_mode = .Static;
+ lib_search_strategy = .no_fallback;
} else if (mem.eql(u8, arg, "-fdll-export-fns")) {
dll_export_fns = true;
} else if (mem.eql(u8, arg, "-fno-dll-export-fns")) {
@@ -1486,17 +1526,33 @@ fn buildOutputType(
} else if (mem.startsWith(u8, arg, "-T")) {
linker_script = arg[2..];
} else if (mem.startsWith(u8, arg, "-L")) {
- try lib_dirs.append(arg[2..]);
+ try lib_dir_args.append(arg[2..]);
} else if (mem.startsWith(u8, arg, "-F")) {
try framework_dirs.append(arg[2..]);
} else if (mem.startsWith(u8, arg, "-l")) {
- // We don't know whether this library is part of libc or libc++ until
- // we resolve the target, so we simply append to the list for now.
- try system_libs.put(arg["-l".len..], .{});
+ // We don't know whether this library is part of libc
+ // or libc++ until we resolve the target, so we append
+ // to the list for now.
+ try system_libs.put(arg["-l".len..], .{
+ .needed = false,
+ .weak = false,
+ .preferred_mode = lib_preferred_mode,
+ .search_strategy = lib_search_strategy,
+ });
} else if (mem.startsWith(u8, arg, "-needed-l")) {
- try system_libs.put(arg["-needed-l".len..], .{ .needed = true });
+ try system_libs.put(arg["-needed-l".len..], .{
+ .needed = true,
+ .weak = false,
+ .preferred_mode = lib_preferred_mode,
+ .search_strategy = lib_search_strategy,
+ });
} else if (mem.startsWith(u8, arg, "-weak-l")) {
- try system_libs.put(arg["-weak-l".len..], .{ .weak = true });
+ try system_libs.put(arg["-weak-l".len..], .{
+ .needed = false,
+ .weak = true,
+ .preferred_mode = lib_preferred_mode,
+ .search_strategy = lib_search_strategy,
+ });
} else if (mem.startsWith(u8, arg, "-D")) {
try clang_argv.append(arg);
} else if (mem.startsWith(u8, arg, "-I")) {
@@ -1571,7 +1627,6 @@ fn buildOutputType(
var emit_llvm = false;
var needed = false;
var must_link = false;
- var force_static_libs = false;
var file_ext: ?Compilation.FileExt = null;
while (it.has_next) {
it.next() catch |err| {
@@ -1641,10 +1696,13 @@ fn buildOutputType(
.must_link = must_link,
.loption = true,
});
- } else if (force_static_libs) {
- try static_libs.append(it.only_arg);
} else {
- try system_libs.put(it.only_arg, .{ .needed = needed });
+ try system_libs.put(it.only_arg, .{
+ .needed = needed,
+ .weak = false,
+ .preferred_mode = lib_preferred_mode,
+ .search_strategy = lib_search_strategy,
+ });
}
},
.ignore => {},
@@ -1740,17 +1798,21 @@ fn buildOutputType(
mem.eql(u8, linker_arg, "-dy") or
mem.eql(u8, linker_arg, "-call_shared"))
{
- force_static_libs = false;
+ lib_search_strategy = .no_fallback;
+ lib_preferred_mode = .Dynamic;
} else if (mem.eql(u8, linker_arg, "-Bstatic") or
mem.eql(u8, linker_arg, "-dn") or
mem.eql(u8, linker_arg, "-non_shared") or
mem.eql(u8, linker_arg, "-static"))
{
- force_static_libs = true;
+ lib_search_strategy = .no_fallback;
+ lib_preferred_mode = .Static;
} else if (mem.eql(u8, linker_arg, "-search_paths_first")) {
- search_strategy = .paths_first;
+ lib_search_strategy = .paths_first;
+ lib_preferred_mode = .Dynamic;
} else if (mem.eql(u8, linker_arg, "-search_dylibs_first")) {
- search_strategy = .dylibs_first;
+ lib_search_strategy = .mode_first;
+ lib_preferred_mode = .Dynamic;
} else {
try linker_args.append(linker_arg);
}
@@ -1828,7 +1890,7 @@ fn buildOutputType(
try linker_args.append("-z");
try linker_args.append(it.only_arg);
},
- .lib_dir => try lib_dirs.append(it.only_arg),
+ .lib_dir => try lib_dir_args.append(it.only_arg),
.mcpu => target_mcpu = it.only_arg,
.m => try llvm_m_args.append(it.only_arg),
.dep_file => {
@@ -1860,7 +1922,12 @@ fn buildOutputType(
.force_undefined_symbol => {
try force_undefined_symbols.put(gpa, it.only_arg, {});
},
- .weak_library => try system_libs.put(it.only_arg, .{ .weak = true }),
+ .weak_library => try system_libs.put(it.only_arg, .{
+ .needed = false,
+ .weak = true,
+ .preferred_mode = lib_preferred_mode,
+ .search_strategy = lib_search_strategy,
+ }),
.weak_framework => try frameworks.put(gpa, it.only_arg, .{ .weak = true }),
.headerpad_max_install_names => headerpad_max_install_names = true,
.compress_debug_sections => {
@@ -2156,11 +2223,26 @@ fn buildOutputType(
} else if (mem.eql(u8, arg, "-needed_framework")) {
try frameworks.put(gpa, linker_args_it.nextOrFatal(), .{ .needed = true });
} else if (mem.eql(u8, arg, "-needed_library")) {
- try system_libs.put(linker_args_it.nextOrFatal(), .{ .needed = true });
+ try system_libs.put(linker_args_it.nextOrFatal(), .{
+ .weak = false,
+ .needed = true,
+ .preferred_mode = lib_preferred_mode,
+ .search_strategy = lib_search_strategy,
+ });
} else if (mem.startsWith(u8, arg, "-weak-l")) {
- try system_libs.put(arg["-weak-l".len..], .{ .weak = true });
+ try system_libs.put(arg["-weak-l".len..], .{
+ .weak = true,
+ .needed = false,
+ .preferred_mode = lib_preferred_mode,
+ .search_strategy = lib_search_strategy,
+ });
} else if (mem.eql(u8, arg, "-weak_library")) {
- try system_libs.put(linker_args_it.nextOrFatal(), .{ .weak = true });
+ try system_libs.put(linker_args_it.nextOrFatal(), .{
+ .weak = true,
+ .needed = false,
+ .preferred_mode = lib_preferred_mode,
+ .search_strategy = lib_search_strategy,
+ });
} else if (mem.eql(u8, arg, "-compatibility_version")) {
const compat_version = linker_args_it.nextOrFatal();
compatibility_version = std.SemanticVersion.parse(compat_version) catch |err| {
@@ -2458,105 +2540,6 @@ fn buildOutputType(
}
}
- // Now that we have target info, we can find out if any of the system libraries
- // are part of libc or libc++. We remove them from the list and communicate their
- // existence via flags instead.
- {
- // Similarly, if any libs in this list are statically provided, we remove
- // them from this list and populate the link_objects array instead.
- const sep = fs.path.sep_str;
- var test_path = std.ArrayList(u8).init(gpa);
- defer test_path.deinit();
-
- var i: usize = 0;
- syslib: while (i < system_libs.count()) {
- const lib_name = system_libs.keys()[i];
-
- if (target_util.is_libc_lib_name(target_info.target, lib_name)) {
- link_libc = true;
- system_libs.orderedRemoveAt(i);
- continue;
- }
- if (target_util.is_libcpp_lib_name(target_info.target, lib_name)) {
- link_libcpp = true;
- system_libs.orderedRemoveAt(i);
- continue;
- }
- switch (target_util.classifyCompilerRtLibName(target_info.target, lib_name)) {
- .none => {},
- .only_libunwind, .both => {
- link_libunwind = true;
- system_libs.orderedRemoveAt(i);
- continue;
- },
- .only_compiler_rt => {
- std.log.warn("ignoring superfluous library '{s}': this dependency is fulfilled instead by compiler-rt which zig unconditionally provides", .{lib_name});
- system_libs.orderedRemoveAt(i);
- continue;
- },
- }
-
- if (fs.path.isAbsolute(lib_name)) {
- fatal("cannot use absolute path as a system library: {s}", .{lib_name});
- }
-
- if (target_info.target.os.tag == .wasi) {
- if (wasi_libc.getEmulatedLibCRTFile(lib_name)) |crt_file| {
- try wasi_emulated_libs.append(crt_file);
- system_libs.orderedRemoveAt(i);
- continue;
- }
- }
-
- for (lib_dirs.items) |lib_dir_path| {
- if (cross_target.isDarwin()) break; // Targeting Darwin we let the linker resolve the libraries in the correct order
- test_path.clearRetainingCapacity();
- try test_path.writer().print("{s}" ++ sep ++ "{s}{s}{s}", .{
- lib_dir_path,
- target_info.target.libPrefix(),
- lib_name,
- target_info.target.staticLibSuffix(),
- });
- fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
- error.FileNotFound => continue,
- else => |e| fatal("unable to search for static library '{s}': {s}", .{
- test_path.items, @errorName(e),
- }),
- };
- try link_objects.append(.{ .path = try arena.dupe(u8, test_path.items) });
- system_libs.orderedRemoveAt(i);
- continue :syslib;
- }
-
- // Unfortunately, in the case of MinGW we also need to look for `libfoo.a`.
- if (target_info.target.isMinGW()) {
- for (lib_dirs.items) |lib_dir_path| {
- test_path.clearRetainingCapacity();
- try test_path.writer().print("{s}" ++ sep ++ "lib{s}.a", .{
- lib_dir_path, lib_name,
- });
- fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
- error.FileNotFound => continue,
- else => |e| fatal("unable to search for static library '{s}': {s}", .{
- test_path.items, @errorName(e),
- }),
- };
- try link_objects.append(.{ .path = try arena.dupe(u8, test_path.items) });
- system_libs.orderedRemoveAt(i);
- continue :syslib;
- }
- }
-
- std.log.scoped(.cli).debug("depending on system for -l{s}", .{lib_name});
-
- i += 1;
- }
- }
- // libc++ depends on libc
- if (link_libcpp) {
- link_libc = true;
- }
-
if (use_lld) |opt| {
if (opt and cross_target.isDarwin()) {
fatal("LLD requested with Mach-O object format. Only the self-hosted linker is supported for this target.", .{});
@@ -2575,8 +2558,124 @@ fn buildOutputType(
want_native_include_dirs = true;
}
- if (sysroot == null and cross_target.isNativeOs() and
- (system_libs.count() != 0 or want_native_include_dirs))
+ // Resolve the library path arguments with respect to sysroot.
+ var lib_dirs = std.ArrayList([]const u8).init(arena);
+ if (sysroot) |root| {
+ for (lib_dir_args.items) |dir| {
+ if (fs.path.isAbsolute(dir)) {
+ const stripped_dir = dir[fs.path.diskDesignator(dir).len..];
+ const full_path = try fs.path.join(arena, &[_][]const u8{ root, stripped_dir });
+ try lib_dirs.append(full_path);
+ }
+ try lib_dirs.append(dir);
+ }
+ } else {
+ lib_dirs = lib_dir_args;
+ }
+ lib_dir_args = undefined; // From here we use lib_dirs instead.
+
+ const self_exe_path: ?[]const u8 = if (!process.can_spawn)
+ null
+ else
+ introspect.findZigExePath(arena) catch |err| {
+ fatal("unable to find zig self exe path: {s}", .{@errorName(err)});
+ };
+
+ var zig_lib_directory: Compilation.Directory = d: {
+ if (override_lib_dir) |unresolved_lib_dir| {
+ const lib_dir = try introspect.resolvePath(arena, unresolved_lib_dir);
+ break :d .{
+ .path = lib_dir,
+ .handle = fs.cwd().openDir(lib_dir, .{}) catch |err| {
+ fatal("unable to open zig lib directory '{s}': {s}", .{ lib_dir, @errorName(err) });
+ },
+ };
+ } else if (builtin.os.tag == .wasi) {
+ break :d getWasiPreopen("/lib");
+ } else if (self_exe_path) |p| {
+ break :d introspect.findZigLibDirFromSelfExe(arena, p) catch |err| {
+ fatal("unable to find zig installation directory: {s}", .{@errorName(err)});
+ };
+ } else {
+ unreachable;
+ }
+ };
+ defer zig_lib_directory.handle.close();
+
+ // First, remove libc, libc++, and compiler_rt libraries from the system libraries list.
+ // We need to know whether the set of system libraries contains anything besides these
+ // to decide whether to trigger native path detection logic.
+ var external_system_libs: std.MultiArrayList(struct {
+ name: []const u8,
+ info: SystemLib,
+ }) = .{};
+
+ var resolved_system_libs: std.MultiArrayList(struct {
+ name: []const u8,
+ lib: Compilation.SystemLib,
+ }) = .{};
+
+ for (system_libs.keys(), system_libs.values()) |lib_name, info| {
+ if (target_util.is_libc_lib_name(target_info.target, lib_name)) {
+ link_libc = true;
+ continue;
+ }
+ if (target_util.is_libcpp_lib_name(target_info.target, lib_name)) {
+ link_libcpp = true;
+ continue;
+ }
+ switch (target_util.classifyCompilerRtLibName(target_info.target, lib_name)) {
+ .none => {},
+ .only_libunwind, .both => {
+ link_libunwind = true;
+ continue;
+ },
+ .only_compiler_rt => {
+ std.log.warn("ignoring superfluous library '{s}': this dependency is fulfilled instead by compiler-rt which zig unconditionally provides", .{lib_name});
+ continue;
+ },
+ }
+
+ if (target_info.target.os.tag == .windows) {
+ const exists = mingw.libExists(arena, target_info.target, zig_lib_directory, lib_name) catch |err| {
+ fatal("failed to check zig installation for DLL import libs: {s}", .{
+ @errorName(err),
+ });
+ };
+ if (exists) {
+ try resolved_system_libs.append(arena, .{
+ .name = lib_name,
+ .lib = .{
+ .needed = true,
+ .weak = false,
+ .path = null,
+ },
+ });
+ continue;
+ }
+ }
+
+ if (fs.path.isAbsolute(lib_name)) {
+ fatal("cannot use absolute path as a system library: {s}", .{lib_name});
+ }
+
+ if (target_info.target.os.tag == .wasi) {
+ if (wasi_libc.getEmulatedLibCRTFile(lib_name)) |crt_file| {
+ try wasi_emulated_libs.append(crt_file);
+ continue;
+ }
+ }
+
+ try external_system_libs.append(arena, .{
+ .name = lib_name,
+ .info = info,
+ });
+ }
+ // After this point, external_system_libs is used instead of system_libs.
+
+ // Trigger native system library path detection if necessary.
+ if (sysroot == null and cross_target.isNativeOs() and cross_target.isNativeAbi() and
+ (external_system_libs.len != 0 or want_native_include_dirs))
{
const paths = std.zig.system.NativePaths.detect(arena, target_info) catch |err| {
fatal("unable to detect native system paths: {s}", .{@errorName(err)});
@@ -2585,83 +2684,181 @@ fn buildOutputType(
warn("{s}", .{warning});
}
- const has_sysroot = if (comptime builtin.target.isDarwin()) outer: {
- if (std.zig.system.darwin.isDarwinSDKInstalled(arena)) {
- const sdk = std.zig.system.darwin.getDarwinSDK(arena, target_info.target) orelse
- break :outer false;
- native_darwin_sdk = sdk;
- try clang_argv.ensureUnusedCapacity(2);
- clang_argv.appendAssumeCapacity("-isysroot");
- clang_argv.appendAssumeCapacity(sdk.path);
- break :outer true;
- } else break :outer false;
- } else false;
-
try clang_argv.ensureUnusedCapacity(paths.include_dirs.items.len * 2);
- const isystem_flag = if (has_sysroot) "-iwithsysroot" else "-isystem";
for (paths.include_dirs.items) |include_dir| {
- clang_argv.appendAssumeCapacity(isystem_flag);
+ clang_argv.appendAssumeCapacity("-isystem");
clang_argv.appendAssumeCapacity(include_dir);
}
- try clang_argv.ensureUnusedCapacity(paths.framework_dirs.items.len * 2);
- try framework_dirs.ensureUnusedCapacity(paths.framework_dirs.items.len);
- const iframework_flag = if (has_sysroot) "-iframeworkwithsysroot" else "-iframework";
- for (paths.framework_dirs.items) |framework_dir| {
- clang_argv.appendAssumeCapacity(iframework_flag);
- clang_argv.appendAssumeCapacity(framework_dir);
- framework_dirs.appendAssumeCapacity(framework_dir);
- }
-
- for (paths.lib_dirs.items) |lib_dir| {
- try lib_dirs.append(lib_dir);
- }
- for (paths.rpaths.items) |rpath| {
- try rpath_list.append(rpath);
- }
+ try framework_dirs.appendSlice(paths.framework_dirs.items);
+ try lib_dirs.appendSlice(paths.lib_dirs.items);
+ try rpath_list.appendSlice(paths.rpaths.items);
}
+ // If any libs in this list are statically provided, we omit them from the
+ // resolved list and populate the link_objects array instead.
{
- // Resolve static libraries into full paths.
- const sep = fs.path.sep_str;
-
var test_path = std.ArrayList(u8).init(gpa);
defer test_path.deinit();
- for (static_libs.items) |static_lib| {
- for (lib_dirs.items) |lib_dir_path| {
- test_path.clearRetainingCapacity();
- try test_path.writer().print("{s}" ++ sep ++ "{s}{s}{s}", .{
- lib_dir_path,
- target_info.target.libPrefix(),
- static_lib,
- target_info.target.staticLibSuffix(),
- });
- fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
- error.FileNotFound => continue,
- else => |e| fatal("unable to search for static library '{s}': {s}", .{
- test_path.items, @errorName(e),
- }),
- };
- try link_objects.append(.{ .path = try arena.dupe(u8, test_path.items) });
- break;
- } else {
- var search_paths = std.ArrayList(u8).init(arena);
- for (lib_dirs.items) |lib_dir_path| {
- try search_paths.writer().print("\n {s}" ++ sep ++ "{s}{s}{s}", .{
- lib_dir_path,
- target_info.target.libPrefix(),
- static_lib,
- target_info.target.staticLibSuffix(),
+ var checked_paths = std.ArrayList(u8).init(gpa);
+ defer checked_paths.deinit();
+
+ var failed_libs = std.ArrayList(struct {
+ name: []const u8,
+ strategy: SystemLib.SearchStrategy,
+ checked_paths: []const u8,
+ preferred_mode: std.builtin.LinkMode,
+ }).init(arena);
+
+ syslib: for (external_system_libs.items(.name), external_system_libs.items(.info)) |lib_name, info| {
+ // Checked in the first pass above while looking for libc libraries.
+ assert(!fs.path.isAbsolute(lib_name));
+
+ checked_paths.clearRetainingCapacity();
+
+ switch (info.search_strategy) {
+ .mode_first, .no_fallback => {
+ // check for preferred mode
+ for (lib_dirs.items) |lib_dir_path| {
+ if (try accessLibPath(
+ &test_path,
+ &checked_paths,
+ lib_dir_path,
+ lib_name,
+ target_info.target,
+ info.preferred_mode,
+ )) {
+ const path = try arena.dupe(u8, test_path.items);
+ switch (info.preferred_mode) {
+ .Static => try link_objects.append(.{ .path = path }),
+ .Dynamic => try resolved_system_libs.append(arena, .{
+ .name = lib_name,
+ .lib = .{
+ .needed = info.needed,
+ .weak = info.weak,
+ .path = path,
+ },
+ }),
+ }
+ continue :syslib;
+ }
+ }
+ // check for fallback mode
+ if (info.search_strategy == .no_fallback) {
+ try failed_libs.append(.{
+ .name = lib_name,
+ .strategy = info.search_strategy,
+ .checked_paths = try arena.dupe(u8, checked_paths.items),
+ .preferred_mode = info.preferred_mode,
+ });
+ continue :syslib;
+ }
+ for (lib_dirs.items) |lib_dir_path| {
+ if (try accessLibPath(
+ &test_path,
+ &checked_paths,
+ lib_dir_path,
+ lib_name,
+ target_info.target,
+ info.fallbackMode(),
+ )) {
+ const path = try arena.dupe(u8, test_path.items);
+ switch (info.fallbackMode()) {
+ .Static => try link_objects.append(.{ .path = path }),
+ .Dynamic => try resolved_system_libs.append(arena, .{
+ .name = lib_name,
+ .lib = .{
+ .needed = info.needed,
+ .weak = info.weak,
+ .path = path,
+ },
+ }),
+ }
+ continue :syslib;
+ }
+ }
+ try failed_libs.append(.{
+ .name = lib_name,
+ .strategy = info.search_strategy,
+ .checked_paths = try arena.dupe(u8, checked_paths.items),
+ .preferred_mode = info.preferred_mode,
});
- }
- try search_paths.appendSlice("\n suggestion: use full paths to static libraries on the command line rather than using -l and -L arguments");
- fatal("static library '{s}' not found. search paths: {s}", .{
- static_lib, search_paths.items,
+ continue :syslib;
+ },
+ .paths_first => {
+ for (lib_dirs.items) |lib_dir_path| {
+ // check for preferred mode
+ if (try accessLibPath(
+ &test_path,
+ &checked_paths,
+ lib_dir_path,
+ lib_name,
+ target_info.target,
+ info.preferred_mode,
+ )) {
+ const path = try arena.dupe(u8, test_path.items);
+ switch (info.preferred_mode) {
+ .Static => try link_objects.append(.{ .path = path }),
+ .Dynamic => try resolved_system_libs.append(arena, .{
+ .name = lib_name,
+ .lib = .{
+ .needed = info.needed,
+ .weak = info.weak,
+ .path = path,
+ },
+ }),
+ }
+ continue :syslib;
+ }
+
+ // check for fallback mode
+ if (try accessLibPath(
+ &test_path,
+ &checked_paths,
+ lib_dir_path,
+ lib_name,
+ target_info.target,
+ info.fallbackMode(),
+ )) {
+ const path = try arena.dupe(u8, test_path.items);
+ switch (info.fallbackMode()) {
+ .Static => try link_objects.append(.{ .path = path }),
+ .Dynamic => try resolved_system_libs.append(arena, .{
+ .name = lib_name,
+ .lib = .{
+ .needed = info.needed,
+ .weak = info.weak,
+ .path = path,
+ },
+ }),
+ }
+ continue :syslib;
+ }
+ }
+ try failed_libs.append(.{
+ .name = lib_name,
+ .strategy = info.search_strategy,
+ .checked_paths = try arena.dupe(u8, checked_paths.items),
+ .preferred_mode = info.preferred_mode,
+ });
+ continue :syslib;
+ },
+ }
+ @compileError("unreachable");
+ }
+
+ if (failed_libs.items.len > 0) {
+ for (failed_libs.items) |f| {
+ const searched_paths = if (f.checked_paths.len == 0) " none" else f.checked_paths;
+ std.log.err("unable to find {s} system library '{s}' using strategy '{s}'. searched paths:{s}", .{
+ @tagName(f.preferred_mode), f.name, @tagName(f.strategy), searched_paths,
});
}
+ process.exit(1);
}
}
+ // After this point, resolved_system_libs is used instead of external_system_libs.
const object_format = target_info.target.ofmt;
@@ -2912,35 +3109,6 @@ fn buildOutputType(
}
}
- const self_exe_path: ?[]const u8 = if (!process.can_spawn)
- null
- else
- introspect.findZigExePath(arena) catch |err| {
- fatal("unable to find zig self exe path: {s}", .{@errorName(err)});
- };
-
- var zig_lib_directory: Compilation.Directory = d: {
- if (override_lib_dir) |unresolved_lib_dir| {
- const lib_dir = try introspect.resolvePath(arena, unresolved_lib_dir);
- break :d .{
- .path = lib_dir,
- .handle = fs.cwd().openDir(lib_dir, .{}) catch |err| {
- fatal("unable to open zig lib directory '{s}': {s}", .{ lib_dir, @errorName(err) });
- },
- };
- } else if (builtin.os.tag == .wasi) {
- break :d getWasiPreopen("/lib");
- } else if (self_exe_path) |p| {
- break :d introspect.findZigLibDirFromSelfExe(arena, p) catch |err| {
- fatal("unable to find zig installation directory: {s}", .{@errorName(err)});
- };
- } else {
- unreachable;
- }
- };
-
- defer zig_lib_directory.handle.close();
-
var thread_pool: ThreadPool = undefined;
try thread_pool.init(.{ .allocator = gpa });
defer thread_pool.deinit();
@@ -3086,8 +3254,8 @@ fn buildOutputType(
.link_objects = link_objects.items,
.framework_dirs = framework_dirs.items,
.frameworks = frameworks,
- .system_lib_names = system_libs.keys(),
- .system_lib_infos = system_libs.values(),
+ .system_lib_names = resolved_system_libs.items(.name),
+ .system_lib_infos = resolved_system_libs.items(.lib),
.wasi_emulated_libs = wasi_emulated_libs.items,
.link_libc = link_libc,
.link_libcpp = link_libcpp,
@@ -3192,11 +3360,9 @@ fn buildOutputType(
.wasi_exec_model = wasi_exec_model,
.debug_compile_errors = debug_compile_errors,
.enable_link_snapshots = enable_link_snapshots,
- .native_darwin_sdk = native_darwin_sdk,
.install_name = install_name,
.entitlements = entitlements,
.pagezero_size = pagezero_size,
- .search_strategy = search_strategy,
.headerpad_size = headerpad_size,
.headerpad_max_install_names = headerpad_max_install_names,
.dead_strip_dylibs = dead_strip_dylibs,
@@ -4069,10 +4235,12 @@ pub fn cmdLibC(gpa: Allocator, args: []const []const u8) !void {
if (!cross_target.isNative()) {
fatal("unable to detect libc for non-native target", .{});
}
+ const target_info = try detectNativeTargetInfo(cross_target);
var libc = LibCInstallation.findNative(.{
.allocator = gpa,
.verbose = true,
+ .target = target_info.target,
}) catch |err| {
fatal("unable to detect native libc: {s}", .{@errorName(err)});
};
@@ -6068,3 +6236,83 @@ fn renderOptions(color: Color) std.zig.ErrorBundle.RenderOptions {
.include_reference_trace = ttyconf != .no_color,
};
}
+
+fn accessLibPath(
+ test_path: *std.ArrayList(u8),
+ checked_paths: *std.ArrayList(u8),
+ lib_dir_path: []const u8,
+ lib_name: []const u8,
+ target: std.Target,
+ link_mode: std.builtin.LinkMode,
+) !bool {
+ const sep = fs.path.sep_str;
+
+ if (target.isDarwin() and link_mode == .Dynamic) tbd: {
+ // Prefer .tbd over .dylib.
+ test_path.clearRetainingCapacity();
+ try test_path.writer().print("{s}" ++ sep ++ "lib{s}.tbd", .{ lib_dir_path, lib_name });
+ try checked_paths.writer().print("\n {s}", .{test_path.items});
+ fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
+ error.FileNotFound => break :tbd,
+ else => |e| fatal("unable to search for tbd library '{s}': {s}", .{
+ test_path.items, @errorName(e),
+ }),
+ };
+ return true;
+ }
+
+ main_check: {
+ test_path.clearRetainingCapacity();
+ try test_path.writer().print("{s}" ++ sep ++ "{s}{s}{s}", .{
+ lib_dir_path,
+ target.libPrefix(),
+ lib_name,
+ switch (link_mode) {
+ .Static => target.staticLibSuffix(),
+ .Dynamic => target.dynamicLibSuffix(),
+ },
+ });
+ try checked_paths.writer().print("\n {s}", .{test_path.items});
+ fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
+ error.FileNotFound => break :main_check,
+ else => |e| fatal("unable to search for {s} library '{s}': {s}", .{
+ @tagName(link_mode), test_path.items, @errorName(e),
+ }),
+ };
+ return true;
+ }
+
+ // In the case of Darwin, the main check will be .dylib, so here we
+ // additionally check for .so files.
+ if (target.isDarwin() and link_mode == .Dynamic) so: {
+ test_path.clearRetainingCapacity();
+ try test_path.writer().print("{s}" ++ sep ++ "lib{s}.so", .{ lib_dir_path, lib_name });
+ try checked_paths.writer().print("\n {s}", .{test_path.items});
+ fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
+ error.FileNotFound => break :so,
+ else => |e| fatal("unable to search for so library '{s}': {s}", .{
+ test_path.items, @errorName(e),
+ }),
+ };
+ return true;
+ }
+
+ // In the case of MinGW, the main check will be .lib but we also need to
+ // look for `libfoo.a`.
+ if (target.isMinGW() and link_mode == .Static) mingw: {
+ test_path.clearRetainingCapacity();
+ try test_path.writer().print("{s}" ++ sep ++ "lib{s}.a", .{
+ lib_dir_path, lib_name,
+ });
+ try checked_paths.writer().print("\n {s}", .{test_path.items});
+ fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
+ error.FileNotFound => break :mingw,
+ else => |e| fatal("unable to search for static library '{s}': {s}", .{
+ test_path.items, @errorName(e),
+ }),
+ };
+ return true;
+ }
+
+ return false;
+}
diff --git a/src/mingw.zig b/src/mingw.zig
@@ -283,7 +283,7 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {
defer arena_allocator.deinit();
const arena = arena_allocator.allocator();
- const def_file_path = findDef(comp, arena, lib_name) catch |err| switch (err) {
+ const def_file_path = findDef(arena, comp.getTarget(), comp.zig_lib_directory, lib_name) catch |err| switch (err) {
error.FileNotFound => {
log.debug("no {s}.def file available to make a DLL import {s}.lib", .{ lib_name, lib_name });
// In this case we will end up putting foo.lib onto the linker line and letting the linker
@@ -431,10 +431,28 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {
});
}
-/// This function body is verbose but all it does is test 3 different paths and see if a .def file exists.
-fn findDef(comp: *Compilation, allocator: Allocator, lib_name: []const u8) ![]u8 {
- const target = comp.getTarget();
+pub fn libExists(
+ allocator: Allocator,
+ target: std.Target,
+ zig_lib_directory: Cache.Directory,
+ lib_name: []const u8,
+) !bool {
+ const s = findDef(allocator, target, zig_lib_directory, lib_name) catch |err| switch (err) {
+ error.FileNotFound => return false,
+ else => |e| return e,
+ };
+ defer allocator.free(s);
+ return true;
+}
+/// This function body is verbose but all it does is test 3 different paths and
+/// see if a .def file exists.
+fn findDef(
+ allocator: Allocator,
+ target: std.Target,
+ zig_lib_directory: Cache.Directory,
+ lib_name: []const u8,
+) ![]u8 {
const lib_path = switch (target.cpu.arch) {
.x86 => "lib32",
.x86_64 => "lib64",
@@ -451,7 +469,7 @@ fn findDef(comp: *Compilation, allocator: Allocator, lib_name: []const u8) ![]u8
{
// Try the archtecture-specific path first.
const fmt_path = "libc" ++ s ++ "mingw" ++ s ++ "{s}" ++ s ++ "{s}.def";
- if (comp.zig_lib_directory.path) |p| {
+ if (zig_lib_directory.path) |p| {
try override_path.writer().print("{s}" ++ s ++ fmt_path, .{ p, lib_path, lib_name });
} else {
try override_path.writer().print(fmt_path, .{ lib_path, lib_name });
@@ -468,7 +486,7 @@ fn findDef(comp: *Compilation, allocator: Allocator, lib_name: []const u8) ![]u8
// Try the generic version.
override_path.shrinkRetainingCapacity(0);
const fmt_path = "libc" ++ s ++ "mingw" ++ s ++ "lib-common" ++ s ++ "{s}.def";
- if (comp.zig_lib_directory.path) |p| {
+ if (zig_lib_directory.path) |p| {
try override_path.writer().print("{s}" ++ s ++ fmt_path, .{ p, lib_name });
} else {
try override_path.writer().print(fmt_path, .{lib_name});
@@ -485,7 +503,7 @@ fn findDef(comp: *Compilation, allocator: Allocator, lib_name: []const u8) ![]u8
// Try the generic version and preprocess it.
override_path.shrinkRetainingCapacity(0);
const fmt_path = "libc" ++ s ++ "mingw" ++ s ++ "lib-common" ++ s ++ "{s}.def.in";
- if (comp.zig_lib_directory.path) |p| {
+ if (zig_lib_directory.path) |p| {
try override_path.writer().print("{s}" ++ s ++ fmt_path, .{ p, lib_name });
} else {
try override_path.writer().print(fmt_path, .{lib_name});
diff --git a/src/target.zig b/src/target.zig
@@ -366,6 +366,15 @@ pub fn is_libc_lib_name(target: std.Target, name: []const u8) bool {
if (eqlIgnoreCase(ignore_case, name, "m"))
return true;
+ if (eqlIgnoreCase(ignore_case, name, "uuid"))
+ return true;
+ if (eqlIgnoreCase(ignore_case, name, "mingw32"))
+ return true;
+ if (eqlIgnoreCase(ignore_case, name, "msvcrt-os"))
+ return true;
+ if (eqlIgnoreCase(ignore_case, name, "mingwex"))
+ return true;
+
return false;
}
diff --git a/test/link/macho/bugs/13056/build.zig b/test/link/macho/bugs/13056/build.zig
@@ -16,7 +16,7 @@ pub fn build(b: *std.Build) void {
fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
const target: std.zig.CrossTarget = .{ .os_tag = .macos };
const target_info = std.zig.system.NativeTargetInfo.detect(target) catch unreachable;
- const sdk = std.zig.system.darwin.getDarwinSDK(b.allocator, target_info.target) orelse
+ const sdk = std.zig.system.darwin.getSdk(b.allocator, target_info.target) orelse
@panic("macOS SDK is required to run the test");
const exe = b.addExecutable(.{
diff --git a/test/link/macho/search_strategy/build.zig b/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());