Merge pull request #16058 from ziglang/frontend-lib-paths

compiler: resolve library paths in the frontend
This commit is contained in:
Andrew Kelley 2023-08-03 09:53:18 -07:00 committed by GitHub
commit c4e62be62e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 914 additions and 717 deletions

View file

@ -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);
}

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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 \

View file

@ -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 });

View file

@ -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);
}

View file

@ -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);
}
};

View file

@ -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,18 +4983,10 @@ 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 => {
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())
@ -5035,9 +5035,9 @@ fn detectLibCFromBuilding(
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,
});

View file

@ -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,

View file

@ -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,

View file

@ -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)) {

View file

@ -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) {

View file

@ -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;
}
}
// 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 (!libsystem_available) {
// 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 });
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 (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;
}
}
}
return false;
}
pub fn resolveSearchDir(

View file

@ -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,

View file

@ -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;
}
}
},
}
{
const vals = options.system_libs.values();
try libs.ensureUnusedCapacity(vals.len);
for (vals) |v| libs.putAssumeCapacity(v.path.?, v);
}
if (lib_not_found) {
log.warn("Library search paths:", .{});
for (lib_dirs.items) |dir| {
log.warn(" {s}", .{dir});
}
}
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}));

File diff suppressed because it is too large Load diff

View file

@ -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});

View file

@ -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;
}

View file

@ -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(.{

View file

@ -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());