authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-08-03 09:53:18-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-08-03 09:53:18-07:00
logc4e62be62e8d8a006cc9d7861232f511c5a05385
tree2e28a575a3eef3bbf80ddd7d8f27ab607d720d85
parentf887b0251822f75dc4a3e24ca5337cb681c1eb1f
parentd0fd67cffe664ff70d9a70dd4d2d28aba5a378e8
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

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

compiler: resolve library paths in the frontend

21 files changed, 914 insertions(+), 717 deletions(-)

build.zig+3-4
......@@ -204,10 +204,9 @@ pub fn build(b: *std.Build) !void {
204204 );
205205
206206 if (!no_bin) {
207 const install_exe = b.addInstallArtifact(exe, .{});
208 if (flat) {
209 install_exe.dest_dir = .prefix;
210 }
207 const install_exe = b.addInstallArtifact(exe, .{
208 .dest_dir = if (flat) .{ .override = .prefix } else .default,
209 });
211210 b.getInstallStep().dependOn(&install_exe.step);
212211 }
213212
ci/aarch64-linux-debug.sh+1-1
......@@ -72,7 +72,7 @@ stage3-debug/bin/zig build test docs \
7272
7373# Look for HTML errors.
7474# TODO: move this to a build.zig flag (-Denable-tidy)
75tidy --drop-empty-elements no -qe "zig-out/doc/langref.html"
75tidy --drop-empty-elements no -qe "../zig-out/doc/langref.html"
7676
7777# Ensure that updating the wasm binary from this commit will result in a viable build.
7878stage3-debug/bin/zig build update-zig1
ci/aarch64-linux-release.sh+1-1
......@@ -72,7 +72,7 @@ stage3-release/bin/zig build test docs \
7272
7373# Look for HTML errors.
7474# TODO: move this to a build.zig flag (-Denable-tidy)
75tidy --drop-empty-elements no -qe "zig-out/doc/langref.html"
75tidy --drop-empty-elements no -qe "../zig-out/doc/langref.html"
7676
7777# Ensure that updating the wasm binary from this commit will result in a viable build.
7878stage3-release/bin/zig build update-zig1
ci/x86_64-linux-debug.sh+1-1
......@@ -72,7 +72,7 @@ stage3-debug/bin/zig build test docs \
7272
7373# Look for HTML errors.
7474# TODO: move this to a build.zig flag (-Denable-tidy)
75tidy --drop-empty-elements no -qe "zig-out/doc/langref.html"
75tidy --drop-empty-elements no -qe "../zig-out/doc/langref.html"
7676
7777# Ensure that updating the wasm binary from this commit will result in a viable build.
7878stage3-debug/bin/zig build update-zig1
ci/x86_64-linux-release.sh+1-1
......@@ -73,7 +73,7 @@ stage3-release/bin/zig build test docs \
7373
7474# Look for HTML errors.
7575# TODO: move this to a build.zig flag (-Denable-tidy)
76tidy --drop-empty-elements no -qe "zig-out/doc/langref.html"
76tidy --drop-empty-elements no -qe "../zig-out/doc/langref.html"
7777
7878# Ensure that stage3 and stage4 are byte-for-byte identical.
7979stage3-release/bin/zig build \
lib/std/Build/Step/Compile.zig+63-76
......@@ -149,13 +149,6 @@ entitlements: ?[]const u8 = null,
149149/// (Darwin) Size of the pagezero segment.
150150pagezero_size: ?u64 = null,
151151
152/// (Darwin) Search strategy for searching system libraries. Either `paths_first` or `dylibs_first`.
153/// The former lowers to `-search_paths_first` linker option, while the latter to `-search_dylibs_first`
154/// option.
155/// By default, if no option is specified, the linker assumes `paths_first` as the default
156/// search strategy.
157search_strategy: ?enum { paths_first, dylibs_first } = null,
158
159152/// (Darwin) Set size of the padding between the end of load commands
160153/// and start of `__TEXT,__text` section.
161154headerpad_size: ?u32 = null,
......@@ -242,7 +235,11 @@ pub const SystemLib = struct {
242235 name: []const u8,
243236 needed: bool,
244237 weak: bool,
245 use_pkg_config: enum {
238 use_pkg_config: UsePkgConfig,
239 preferred_link_mode: std.builtin.LinkMode,
240 search_strategy: SystemLib.SearchStrategy,
241
242 pub const UsePkgConfig = enum {
246243 /// Don't use pkg-config, just pass -lfoo where foo is name.
247244 no,
248245 /// Try to get information on how to link the library from pkg-config.
......@@ -251,7 +248,9 @@ pub const SystemLib = struct {
251248 /// Try to get information on how to link the library from pkg-config.
252249 /// If that fails, error out.
253250 force,
254 },
251 };
252
253 pub const SearchStrategy = enum { paths_first, mode_first, no_fallback };
255254};
256255
257256const FrameworkLinkInfo = struct {
......@@ -718,74 +717,29 @@ pub fn defineCMacroRaw(self: *Compile, name_and_value: []const u8) void {
718717 self.c_macros.append(b.dupe(name_and_value)) catch @panic("OOM");
719718}
720719
721/// This one has no integration with anything, it just puts -lname on the command line.
722/// Prefer to use `linkSystemLibrary` instead.
720/// deprecated: use linkSystemLibrary2
723721pub fn linkSystemLibraryName(self: *Compile, name: []const u8) void {
724 const b = self.step.owner;
725 self.link_objects.append(.{
726 .system_lib = .{
727 .name = b.dupe(name),
728 .needed = false,
729 .weak = false,
730 .use_pkg_config = .no,
731 },
732 }) catch @panic("OOM");
722 return linkSystemLibrary2(self, name, .{ .use_pkg_config = .no });
733723}
734724
735/// This one has no integration with anything, it just puts -needed-lname on the command line.
736/// Prefer to use `linkSystemLibraryNeeded` instead.
725/// deprecated: use linkSystemLibrary2
737726pub fn linkSystemLibraryNeededName(self: *Compile, name: []const u8) void {
738 const b = self.step.owner;
739 self.link_objects.append(.{
740 .system_lib = .{
741 .name = b.dupe(name),
742 .needed = true,
743 .weak = false,
744 .use_pkg_config = .no,
745 },
746 }) catch @panic("OOM");
727 return linkSystemLibrary2(self, name, .{ .needed = true, .use_pkg_config = .no });
747728}
748729
749/// Darwin-only. This one has no integration with anything, it just puts -weak-lname on the
750/// command line. Prefer to use `linkSystemLibraryWeak` instead.
730/// deprecated: use linkSystemLibrary2
751731pub fn linkSystemLibraryWeakName(self: *Compile, name: []const u8) void {
752 const b = self.step.owner;
753 self.link_objects.append(.{
754 .system_lib = .{
755 .name = b.dupe(name),
756 .needed = false,
757 .weak = true,
758 .use_pkg_config = .no,
759 },
760 }) catch @panic("OOM");
732 return linkSystemLibrary2(self, name, .{ .weak = true, .use_pkg_config = .no });
761733}
762734
763/// This links against a system library, exclusively using pkg-config to find the library.
764/// Prefer to use `linkSystemLibrary` instead.
735/// deprecated: use linkSystemLibrary2
765736pub fn linkSystemLibraryPkgConfigOnly(self: *Compile, lib_name: []const u8) void {
766 const b = self.step.owner;
767 self.link_objects.append(.{
768 .system_lib = .{
769 .name = b.dupe(lib_name),
770 .needed = false,
771 .weak = false,
772 .use_pkg_config = .force,
773 },
774 }) catch @panic("OOM");
737 return linkSystemLibrary2(self, lib_name, .{ .use_pkg_config = .force });
775738}
776739
777/// This links against a system library, exclusively using pkg-config to find the library.
778/// Prefer to use `linkSystemLibraryNeeded` instead.
740/// deprecated: use linkSystemLibrary2
779741pub fn linkSystemLibraryNeededPkgConfigOnly(self: *Compile, lib_name: []const u8) void {
780 const b = self.step.owner;
781 self.link_objects.append(.{
782 .system_lib = .{
783 .name = b.dupe(lib_name),
784 .needed = true,
785 .weak = false,
786 .use_pkg_config = .force,
787 },
788 }) catch @panic("OOM");
742 return linkSystemLibrary2(self, lib_name, .{ .needed = true, .use_pkg_config = .force });
789743}
790744
791745/// 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 {
885839}
886840
887841pub fn linkSystemLibrary(self: *Compile, name: []const u8) void {
888 self.linkSystemLibraryInner(name, .{});
842 self.linkSystemLibrary2(name, .{});
889843}
890844
845/// deprecated: use linkSystemLibrary2
891846pub fn linkSystemLibraryNeeded(self: *Compile, name: []const u8) void {
892 self.linkSystemLibraryInner(name, .{ .needed = true });
847 return linkSystemLibrary2(self, name, .{ .needed = true });
893848}
894849
850/// deprecated: use linkSystemLibrary2
895851pub fn linkSystemLibraryWeak(self: *Compile, name: []const u8) void {
896 self.linkSystemLibraryInner(name, .{ .weak = true });
852 return linkSystemLibrary2(self, name, .{ .weak = true });
897853}
898854
899fn linkSystemLibraryInner(self: *Compile, name: []const u8, opts: struct {
855pub const LinkSystemLibraryOptions = struct {
900856 needed: bool = false,
901857 weak: bool = false,
902}) void {
858 use_pkg_config: SystemLib.UsePkgConfig = .yes,
859 preferred_link_mode: std.builtin.LinkMode = .Dynamic,
860 search_strategy: SystemLib.SearchStrategy = .paths_first,
861};
862
863pub fn linkSystemLibrary2(
864 self: *Compile,
865 name: []const u8,
866 options: LinkSystemLibraryOptions,
867) void {
903868 const b = self.step.owner;
904869 if (isLibCLibrary(name)) {
905870 self.linkLibC();
......@@ -913,9 +878,11 @@ fn linkSystemLibraryInner(self: *Compile, name: []const u8, opts: struct {
913878 self.link_objects.append(.{
914879 .system_lib = .{
915880 .name = b.dupe(name),
916 .needed = opts.needed,
917 .weak = opts.weak,
918 .use_pkg_config = .yes,
881 .needed = options.needed,
882 .weak = options.weak,
883 .use_pkg_config = options.use_pkg_config,
884 .preferred_link_mode = options.preferred_link_mode,
885 .search_strategy = options.search_strategy,
919886 },
920887 }) catch @panic("OOM");
921888}
......@@ -1385,6 +1352,8 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
13851352 try transitive_deps.add(self.link_objects.items);
13861353
13871354 var prev_has_cflags = false;
1355 var prev_search_strategy: SystemLib.SearchStrategy = .paths_first;
1356 var prev_preferred_link_mode: std.builtin.LinkMode = .Dynamic;
13881357
13891358 for (transitive_deps.link_objects.items) |link_object| {
13901359 switch (link_object) {
......@@ -1420,6 +1389,28 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
14201389 },
14211390
14221391 .system_lib => |system_lib| {
1392 if ((system_lib.search_strategy != prev_search_strategy or
1393 system_lib.preferred_link_mode != prev_preferred_link_mode) and
1394 self.linkage != .static)
1395 {
1396 switch (system_lib.search_strategy) {
1397 .no_fallback => switch (system_lib.preferred_link_mode) {
1398 .Dynamic => try zig_args.append("-search_dylibs_only"),
1399 .Static => try zig_args.append("-search_static_only"),
1400 },
1401 .paths_first => switch (system_lib.preferred_link_mode) {
1402 .Dynamic => try zig_args.append("-search_paths_first"),
1403 .Static => try zig_args.append("-search_paths_first_static"),
1404 },
1405 .mode_first => switch (system_lib.preferred_link_mode) {
1406 .Dynamic => try zig_args.append("-search_dylibs_first"),
1407 .Static => try zig_args.append("-search_static_first"),
1408 },
1409 }
1410 prev_search_strategy = system_lib.search_strategy;
1411 prev_preferred_link_mode = system_lib.preferred_link_mode;
1412 }
1413
14231414 const prefix: []const u8 = prefix: {
14241415 if (system_lib.needed) break :prefix "-needed-l";
14251416 if (system_lib.weak) break :prefix "-weak-l";
......@@ -1662,10 +1653,6 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
16621653 const size = try std.fmt.allocPrint(b.allocator, "{x}", .{pagezero_size});
16631654 try zig_args.appendSlice(&[_][]const u8{ "-pagezero_size", size });
16641655 }
1665 if (self.search_strategy) |strat| switch (strat) {
1666 .paths_first => try zig_args.append("-search_paths_first"),
1667 .dylibs_first => try zig_args.append("-search_dylibs_first"),
1668 };
16691656 if (self.headerpad_size) |headerpad_size| {
16701657 const size = try std.fmt.allocPrint(b.allocator, "{x}", .{headerpad_size});
16711658 try zig_args.appendSlice(&[_][]const u8{ "-headerpad", size });
lib/std/zig/system/NativePaths.zig+32-74
......@@ -1,6 +1,5 @@
11const std = @import("../../std.zig");
22const builtin = @import("builtin");
3const ArrayList = std.ArrayList;
43const Allocator = std.mem.Allocator;
54const process = std.process;
65const mem = std.mem;
......@@ -8,28 +7,18 @@ const mem = std.mem;
87const NativePaths = @This();
98const NativeTargetInfo = std.zig.system.NativeTargetInfo;
109
11include_dirs: ArrayList([:0]u8),
12lib_dirs: ArrayList([:0]u8),
13framework_dirs: ArrayList([:0]u8),
14rpaths: ArrayList([:0]u8),
15warnings: ArrayList([:0]u8),
10arena: Allocator,
11include_dirs: std.ArrayListUnmanaged([]const u8) = .{},
12lib_dirs: std.ArrayListUnmanaged([]const u8) = .{},
13framework_dirs: std.ArrayListUnmanaged([]const u8) = .{},
14rpaths: std.ArrayListUnmanaged([]const u8) = .{},
15warnings: std.ArrayListUnmanaged([]const u8) = .{},
1616
17pub fn detect(allocator: Allocator, native_info: NativeTargetInfo) !NativePaths {
17pub fn detect(arena: Allocator, native_info: NativeTargetInfo) !NativePaths {
1818 const native_target = native_info.target;
19
20 var self: NativePaths = .{
21 .include_dirs = ArrayList([:0]u8).init(allocator),
22 .lib_dirs = ArrayList([:0]u8).init(allocator),
23 .framework_dirs = ArrayList([:0]u8).init(allocator),
24 .rpaths = ArrayList([:0]u8).init(allocator),
25 .warnings = ArrayList([:0]u8).init(allocator),
26 };
27 errdefer self.deinit();
28
19 var self: NativePaths = .{ .arena = arena };
2920 var is_nix = false;
30 if (process.getEnvVarOwned(allocator, "NIX_CFLAGS_COMPILE")) |nix_cflags_compile| {
31 defer allocator.free(nix_cflags_compile);
32
21 if (process.getEnvVarOwned(arena, "NIX_CFLAGS_COMPILE")) |nix_cflags_compile| {
3322 is_nix = true;
3423 var it = mem.tokenizeScalar(u8, nix_cflags_compile, ' ');
3524 while (true) {
......@@ -58,9 +47,7 @@ pub fn detect(allocator: Allocator, native_info: NativeTargetInfo) !NativePaths
5847 error.EnvironmentVariableNotFound => {},
5948 error.OutOfMemory => |e| return e,
6049 }
61 if (process.getEnvVarOwned(allocator, "NIX_LDFLAGS")) |nix_ldflags| {
62 defer allocator.free(nix_ldflags);
63
50 if (process.getEnvVarOwned(arena, "NIX_LDFLAGS")) |nix_ldflags| {
6451 is_nix = true;
6552 var it = mem.tokenizeScalar(u8, nix_ldflags, ' ');
6653 while (true) {
......@@ -89,17 +76,16 @@ pub fn detect(allocator: Allocator, native_info: NativeTargetInfo) !NativePaths
8976 return self;
9077 }
9178
79 // TODO: consider also adding homebrew paths
80 // TODO: consider also adding macports paths
9281 if (comptime builtin.target.isDarwin()) {
93 try self.addIncludeDir("/usr/include");
94 try self.addLibDir("/usr/lib");
95 try self.addFrameworkDir("/System/Library/Frameworks");
96
97 if (builtin.target.os.version_range.semver.min.major < 11) {
98 try self.addIncludeDir("/usr/local/include");
99 try self.addLibDir("/usr/local/lib");
100 try self.addFrameworkDir("/Library/Frameworks");
82 if (std.zig.system.darwin.isSdkInstalled(arena)) sdk: {
83 const sdk = std.zig.system.darwin.getSdk(arena, native_target) orelse break :sdk;
84 try self.addLibDir(try std.fs.path.join(arena, &.{ sdk.path, "usr/lib" }));
85 try self.addFrameworkDir(try std.fs.path.join(arena, &.{ sdk.path, "System/Library/Frameworks" }));
86 try self.addIncludeDir(try std.fs.path.join(arena, &.{ sdk.path, "usr/include" }));
87 return self;
10188 }
102
10389 return self;
10490 }
10591
......@@ -115,8 +101,7 @@ pub fn detect(allocator: Allocator, native_info: NativeTargetInfo) !NativePaths
115101 }
116102
117103 if (builtin.os.tag != .windows) {
118 const triple = try native_target.linuxTriple(allocator);
119 defer allocator.free(triple);
104 const triple = try native_target.linuxTriple(arena);
120105
121106 const qual = native_target.ptrBitWidth();
122107
......@@ -172,69 +157,42 @@ pub fn detect(allocator: Allocator, native_info: NativeTargetInfo) !NativePaths
172157 return self;
173158}
174159
175pub fn deinit(self: *NativePaths) void {
176 deinitArray(&self.include_dirs);
177 deinitArray(&self.lib_dirs);
178 deinitArray(&self.framework_dirs);
179 deinitArray(&self.rpaths);
180 deinitArray(&self.warnings);
181 self.* = undefined;
182}
183
184fn deinitArray(array: *ArrayList([:0]u8)) void {
185 for (array.items) |item| {
186 array.allocator.free(item);
187 }
188 array.deinit();
189}
190
191160pub fn addIncludeDir(self: *NativePaths, s: []const u8) !void {
192 return self.appendArray(&self.include_dirs, s);
161 return self.include_dirs.append(self.arena, s);
193162}
194163
195164pub fn addIncludeDirFmt(self: *NativePaths, comptime fmt: []const u8, args: anytype) !void {
196 const item = try std.fmt.allocPrintZ(self.include_dirs.allocator, fmt, args);
197 errdefer self.include_dirs.allocator.free(item);
198 try self.include_dirs.append(item);
165 const item = try std.fmt.allocPrint(self.arena, fmt, args);
166 try self.include_dirs.append(self.arena, item);
199167}
200168
201169pub fn addLibDir(self: *NativePaths, s: []const u8) !void {
202 return self.appendArray(&self.lib_dirs, s);
170 try self.lib_dirs.append(self.arena, s);
203171}
204172
205173pub fn addLibDirFmt(self: *NativePaths, comptime fmt: []const u8, args: anytype) !void {
206 const item = try std.fmt.allocPrintZ(self.lib_dirs.allocator, fmt, args);
207 errdefer self.lib_dirs.allocator.free(item);
208 try self.lib_dirs.append(item);
174 const item = try std.fmt.allocPrint(self.arena, fmt, args);
175 try self.lib_dirs.append(self.arena, item);
209176}
210177
211178pub fn addWarning(self: *NativePaths, s: []const u8) !void {
212 return self.appendArray(&self.warnings, s);
179 return self.warnings.append(self.arena, s);
213180}
214181
215182pub fn addFrameworkDir(self: *NativePaths, s: []const u8) !void {
216 return self.appendArray(&self.framework_dirs, s);
183 return self.framework_dirs.append(self.arena, s);
217184}
218185
219186pub fn addFrameworkDirFmt(self: *NativePaths, comptime fmt: []const u8, args: anytype) !void {
220 const item = try std.fmt.allocPrintZ(self.framework_dirs.allocator, fmt, args);
221 errdefer self.framework_dirs.allocator.free(item);
222 try self.framework_dirs.append(item);
187 const item = try std.fmt.allocPrint(self.arena, fmt, args);
188 try self.framework_dirs.append(self.arena, item);
223189}
224190
225191pub fn addWarningFmt(self: *NativePaths, comptime fmt: []const u8, args: anytype) !void {
226 const item = try std.fmt.allocPrintZ(self.warnings.allocator, fmt, args);
227 errdefer self.warnings.allocator.free(item);
228 try self.warnings.append(item);
192 const item = try std.fmt.allocPrint(self.arena, fmt, args);
193 try self.warnings.append(self.arena, item);
229194}
230195
231196pub fn addRPath(self: *NativePaths, s: []const u8) !void {
232 return self.appendArray(&self.rpaths, s);
233}
234
235fn appendArray(self: *NativePaths, array: *ArrayList([:0]u8), s: []const u8) !void {
236 _ = self;
237 const item = try array.allocator.dupeZ(u8, s);
238 errdefer array.allocator.free(item);
239 try array.append(item);
197 try self.rpaths.append(self.arena, s);
240198}
lib/std/zig/system/darwin.zig+29-25
......@@ -8,28 +8,34 @@ pub const macos = @import("darwin/macos.zig");
88
99/// Check if SDK is installed on Darwin without triggering CLT installation popup window.
1010/// Note: simply invoking `xcrun` will inevitably trigger the CLT installation popup.
11/// Therefore, we resort to the same tool used by Homebrew, namely, invoking `xcode-select --print-path`
12/// and checking if the status is nonzero or the returned string in nonempty.
13/// https://github.com/Homebrew/brew/blob/e119bdc571dcb000305411bc1e26678b132afb98/Library/Homebrew/brew.sh#L630
14pub fn isDarwinSDKInstalled(allocator: Allocator) bool {
15 const argv = &[_][]const u8{ "/usr/bin/xcode-select", "--print-path" };
16 const result = std.ChildProcess.exec(.{ .allocator = allocator, .argv = argv }) catch return false;
11/// Therefore, we resort to invoking `xcode-select --print-path` and checking
12/// if the status is nonzero.
13/// stderr from xcode-select is ignored.
14/// If error.OutOfMemory occurs in Allocator, this function returns null.
15pub fn isSdkInstalled(allocator: Allocator) bool {
16 const result = std.process.Child.exec(.{
17 .allocator = allocator,
18 .argv = &.{ "/usr/bin/xcode-select", "--print-path" },
19 }) catch return false;
20
1721 defer {
1822 allocator.free(result.stderr);
1923 allocator.free(result.stdout);
2024 }
21 if (result.stderr.len != 0 or result.term.Exited != 0) {
22 // We don't actually care if there were errors as this is best-effort check anyhow.
23 return false;
24 }
25 return result.stdout.len > 0;
25
26 return switch (result.term) {
27 .Exited => |code| if (code == 0) result.stdout.len > 0 else false,
28 else => false,
29 };
2630}
2731
2832/// Detect SDK on Darwin.
2933/// Calls `xcrun --sdk <target_sdk> --show-sdk-path` which fetches the path to the SDK sysroot (if any).
3034/// Subsequently calls `xcrun --sdk <target_sdk> --show-sdk-version` which fetches version of the SDK.
3135/// The caller needs to deinit the resulting struct.
32pub fn getDarwinSDK(allocator: Allocator, target: Target) ?DarwinSDK {
36/// stderr from xcrun is ignored.
37/// If error.OutOfMemory occurs in Allocator, this function returns null.
38pub fn getSdk(allocator: Allocator, target: Target) ?Sdk {
3339 const is_simulator_abi = target.abi == .simulator;
3440 const sdk = switch (target.os.tag) {
3541 .macos => "macosx",
......@@ -40,30 +46,28 @@ pub fn getDarwinSDK(allocator: Allocator, target: Target) ?DarwinSDK {
4046 };
4147 const path = path: {
4248 const argv = &[_][]const u8{ "/usr/bin/xcrun", "--sdk", sdk, "--show-sdk-path" };
43 const result = std.ChildProcess.exec(.{ .allocator = allocator, .argv = argv }) catch return null;
49 const result = std.process.Child.exec(.{ .allocator = allocator, .argv = argv }) catch return null;
4450 defer {
4551 allocator.free(result.stderr);
4652 allocator.free(result.stdout);
4753 }
48 if (result.stderr.len != 0 or result.term.Exited != 0) {
49 // We don't actually care if there were errors as this is best-effort check anyhow
50 // and in the worst case the user can specify the sysroot manually.
51 return null;
54 switch (result.term) {
55 .Exited => |code| if (code != 0) return null,
56 else => return null,
5257 }
5358 const path = allocator.dupe(u8, mem.trimRight(u8, result.stdout, "\r\n")) catch return null;
5459 break :path path;
5560 };
5661 const version = version: {
5762 const argv = &[_][]const u8{ "/usr/bin/xcrun", "--sdk", sdk, "--show-sdk-version" };
58 const result = std.ChildProcess.exec(.{ .allocator = allocator, .argv = argv }) catch return null;
63 const result = std.process.Child.exec(.{ .allocator = allocator, .argv = argv }) catch return null;
5964 defer {
6065 allocator.free(result.stderr);
6166 allocator.free(result.stdout);
6267 }
63 if (result.stderr.len != 0 or result.term.Exited != 0) {
64 // We don't actually care if there were errors as this is best-effort check anyhow
65 // and in the worst case the user can specify the sysroot manually.
66 return null;
68 switch (result.term) {
69 .Exited => |code| if (code != 0) return null,
70 else => return null,
6771 }
6872 const raw_version = mem.trimRight(u8, result.stdout, "\r\n");
6973 const version = parseSdkVersion(raw_version) orelse Version{
......@@ -73,7 +77,7 @@ pub fn getDarwinSDK(allocator: Allocator, target: Target) ?DarwinSDK {
7377 };
7478 break :version version;
7579 };
76 return DarwinSDK{
80 return Sdk{
7781 .path = path,
7882 .version = version,
7983 };
......@@ -96,11 +100,11 @@ fn parseSdkVersion(raw: []const u8) ?Version {
96100 return Version.parse(buffer[0..len]) catch null;
97101}
98102
99pub const DarwinSDK = struct {
103pub const Sdk = struct {
100104 path: []const u8,
101105 version: Version,
102106
103 pub fn deinit(self: DarwinSDK, allocator: Allocator) void {
107 pub fn deinit(self: Sdk, allocator: Allocator) void {
104108 allocator.free(self.path);
105109 }
106110};
src/Compilation.zig+107-102
......@@ -124,6 +124,7 @@ zig_lib_directory: Directory,
124124local_cache_directory: Directory,
125125global_cache_directory: Directory,
126126libc_include_dir_list: []const []const u8,
127libc_framework_dir_list: []const []const u8,
127128thread_pool: *ThreadPool,
128129
129130/// 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 {
448449 stdout,
449450};
450451
452pub const Framework = link.Framework;
451453pub const SystemLib = link.SystemLib;
452454pub const CacheMode = link.CacheMode;
453455
......@@ -505,7 +507,7 @@ pub const InitOptions = struct {
505507 c_source_files: []const CSourceFile = &[0]CSourceFile{},
506508 link_objects: []LinkObject = &[0]LinkObject{},
507509 framework_dirs: []const []const u8 = &[0][]const u8{},
508 frameworks: std.StringArrayHashMapUnmanaged(SystemLib) = .{},
510 frameworks: std.StringArrayHashMapUnmanaged(Framework) = .{},
509511 system_lib_names: []const []const u8 = &.{},
510512 system_lib_infos: []const SystemLib = &.{},
511513 /// These correspond to the WASI libc emulated subcomponents including:
......@@ -636,16 +638,12 @@ pub const InitOptions = struct {
636638 wasi_exec_model: ?std.builtin.WasiExecModel = null,
637639 /// (Zig compiler development) Enable dumping linker's state as JSON.
638640 enable_link_snapshots: bool = false,
639 /// (Darwin) Path and version of the native SDK if detected.
640 native_darwin_sdk: ?std.zig.system.darwin.DarwinSDK = null,
641641 /// (Darwin) Install name of the dylib
642642 install_name: ?[]const u8 = null,
643643 /// (Darwin) Path to entitlements file
644644 entitlements: ?[]const u8 = null,
645645 /// (Darwin) size of the __PAGEZERO segment
646646 pagezero_size: ?u64 = null,
647 /// (Darwin) search strategy for system libraries
648 search_strategy: ?link.File.MachO.SearchStrategy = null,
649647 /// (Darwin) set minimum space for future expansion of the load commands
650648 headerpad_size: ?u32 = null,
651649 /// (Darwin) set enough space as if all paths were MATPATHLEN
......@@ -855,16 +853,6 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
855853 break :blk false;
856854 };
857855
858 const sysroot = blk: {
859 if (options.sysroot) |sysroot| {
860 break :blk sysroot;
861 } else if (options.native_darwin_sdk) |sdk| {
862 break :blk sdk.path;
863 } else {
864 break :blk null;
865 }
866 };
867
868856 const lto = blk: {
869857 if (options.want_lto) |explicit| {
870858 if (!use_lld and !options.target.isDarwin())
......@@ -948,9 +936,10 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
948936 options.is_native_abi,
949937 link_libc,
950938 options.libc_installation,
951 options.native_darwin_sdk != null,
952939 );
953940
941 const sysroot = options.sysroot orelse libc_dirs.sysroot;
942
954943 const must_pie = target_util.requiresPIE(options.target);
955944 const pie: bool = if (options.want_pie) |explicit| pie: {
956945 if (!explicit and must_pie) {
......@@ -1563,11 +1552,9 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
15631552 .wasi_exec_model = wasi_exec_model,
15641553 .hash_style = options.hash_style,
15651554 .enable_link_snapshots = options.enable_link_snapshots,
1566 .native_darwin_sdk = options.native_darwin_sdk,
15671555 .install_name = options.install_name,
15681556 .entitlements = options.entitlements,
15691557 .pagezero_size = options.pagezero_size,
1570 .search_strategy = options.search_strategy,
15711558 .headerpad_size = options.headerpad_size,
15721559 .headerpad_max_install_names = options.headerpad_max_install_names,
15731560 .dead_strip_dylibs = options.dead_strip_dylibs,
......@@ -1601,6 +1588,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
16011588 .cache_parent = cache,
16021589 .self_exe_path = options.self_exe_path,
16031590 .libc_include_dir_list = libc_dirs.libc_include_dir_list,
1591 .libc_framework_dir_list = libc_dirs.libc_framework_dir_list,
16041592 .sanitize_c = sanitize_c,
16051593 .thread_pool = options.thread_pool,
16061594 .clang_passthrough_mode = options.clang_passthrough_mode,
......@@ -1727,15 +1715,18 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
17271715
17281716 // When linking mingw-w64 there are some import libs we always need.
17291717 for (mingw.always_link_libs) |name| {
1730 try comp.bin_file.options.system_libs.put(comp.gpa, name, .{});
1718 try comp.bin_file.options.system_libs.put(comp.gpa, name, .{
1719 .needed = false,
1720 .weak = false,
1721 .path = null,
1722 });
17311723 }
17321724 }
17331725 // Generate Windows import libs.
17341726 if (target.os.tag == .windows) {
17351727 const count = comp.bin_file.options.system_libs.count();
17361728 try comp.work_queue.ensureUnusedCapacity(count);
1737 var i: usize = 0;
1738 while (i < count) : (i += 1) {
1729 for (0..count) |i| {
17391730 comp.work_queue.writeItemAssumeCapacity(.{ .windows_import_lib = i });
17401731 }
17411732 }
......@@ -2367,17 +2358,17 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes
23672358 if (comp.bin_file.options.link_libc) {
23682359 man.hash.add(comp.bin_file.options.libc_installation != null);
23692360 if (comp.bin_file.options.libc_installation) |libc_installation| {
2370 man.hash.addBytes(libc_installation.crt_dir.?);
2361 man.hash.addOptionalBytes(libc_installation.crt_dir);
23712362 if (target.abi == .msvc) {
2372 man.hash.addBytes(libc_installation.msvc_lib_dir.?);
2373 man.hash.addBytes(libc_installation.kernel32_lib_dir.?);
2363 man.hash.addOptionalBytes(libc_installation.msvc_lib_dir);
2364 man.hash.addOptionalBytes(libc_installation.kernel32_lib_dir);
23742365 }
23752366 }
23762367 man.hash.addOptionalBytes(comp.bin_file.options.dynamic_linker);
23772368 }
23782369 man.hash.addOptionalBytes(comp.bin_file.options.soname);
23792370 man.hash.addOptional(comp.bin_file.options.version);
2380 link.hashAddSystemLibs(&man.hash, comp.bin_file.options.system_libs);
2371 try link.hashAddSystemLibs(man, comp.bin_file.options.system_libs);
23812372 man.hash.addListOfBytes(comp.bin_file.options.force_undefined_symbols.keys());
23822373 man.hash.addOptional(comp.bin_file.options.allow_shlib_undefined);
23832374 man.hash.add(comp.bin_file.options.bind_global_refs_locally);
......@@ -2395,10 +2386,9 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes
23952386
23962387 // Mach-O specific stuff
23972388 man.hash.addListOfBytes(comp.bin_file.options.framework_dirs);
2398 link.hashAddSystemLibs(&man.hash, comp.bin_file.options.frameworks);
2389 link.hashAddFrameworks(&man.hash, comp.bin_file.options.frameworks);
23992390 try man.addOptionalFile(comp.bin_file.options.entitlements);
24002391 man.hash.addOptional(comp.bin_file.options.pagezero_size);
2401 man.hash.addOptional(comp.bin_file.options.search_strategy);
24022392 man.hash.addOptional(comp.bin_file.options.headerpad_size);
24032393 man.hash.add(comp.bin_file.options.headerpad_max_install_names);
24042394 man.hash.add(comp.bin_file.options.dead_strip_dylibs);
......@@ -4341,6 +4331,14 @@ pub fn addCCArgs(
43414331 try argv.append("-ObjC++");
43424332 }
43434333
4334 for (comp.libc_framework_dir_list) |framework_dir| {
4335 try argv.appendSlice(&.{ "-iframework", framework_dir });
4336 }
4337
4338 for (comp.bin_file.options.framework_dirs) |framework_dir| {
4339 try argv.appendSlice(&.{ "-F", framework_dir });
4340 }
4341
43444342 // According to Rich Felker libc headers are supposed to go before C language headers.
43454343 // However as noted by @dimenus, appending libc headers before c_headers breaks intrinsics
43464344 // and other compiler specific items.
......@@ -4823,6 +4821,8 @@ test "classifyFileExt" {
48234821const LibCDirs = struct {
48244822 libc_include_dir_list: []const []const u8,
48254823 libc_installation: ?*const LibCInstallation,
4824 libc_framework_dir_list: []const []const u8,
4825 sysroot: ?[]const u8,
48264826};
48274827
48284828fn getZigShippedLibCIncludeDirsDarwin(arena: Allocator, zig_lib_dir: []const u8, target: Target) !LibCDirs {
......@@ -4853,6 +4853,8 @@ fn getZigShippedLibCIncludeDirsDarwin(arena: Allocator, zig_lib_dir: []const u8,
48534853 return LibCDirs{
48544854 .libc_include_dir_list = list,
48554855 .libc_installation = null,
4856 .libc_framework_dir_list = &.{},
4857 .sysroot = null,
48564858 };
48574859}
48584860
......@@ -4863,12 +4865,13 @@ fn detectLibCIncludeDirs(
48634865 is_native_abi: bool,
48644866 link_libc: bool,
48654867 libc_installation: ?*const LibCInstallation,
4866 has_macos_sdk: bool,
48674868) !LibCDirs {
48684869 if (!link_libc) {
48694870 return LibCDirs{
48704871 .libc_include_dir_list = &[0][]u8{},
48714872 .libc_installation = null,
4873 .libc_framework_dir_list = &.{},
4874 .sysroot = null,
48724875 };
48734876 }
48744877
......@@ -4879,28 +4882,19 @@ fn detectLibCIncludeDirs(
48794882 // If linking system libraries and targeting the native abi, default to
48804883 // using the system libc installation.
48814884 if (is_native_abi and !target.isMinGW()) {
4882 if (target.isDarwin()) {
4883 return if (has_macos_sdk)
4884 // For Darwin/macOS, we are all set with getDarwinSDK found earlier.
4885 LibCDirs{
4886 .libc_include_dir_list = &[0][]u8{},
4887 .libc_installation = null,
4888 }
4889 else
4890 getZigShippedLibCIncludeDirsDarwin(arena, zig_lib_dir, target);
4891 }
48924885 const libc = try arena.create(LibCInstallation);
4893 libc.* = LibCInstallation.findNative(.{ .allocator = arena }) catch |err| switch (err) {
4886 libc.* = LibCInstallation.findNative(.{ .allocator = arena, .target = target }) catch |err| switch (err) {
48944887 error.CCompilerExitCode,
48954888 error.CCompilerCrashed,
48964889 error.CCompilerCannotFindHeaders,
48974890 error.UnableToSpawnCCompiler,
4891 error.DarwinSdkNotFound,
48984892 => |e| {
48994893 // We tried to integrate with the native system C compiler,
49004894 // however, it is not installed. So we must rely on our bundled
49014895 // libc files.
49024896 if (target_util.canBuildLibC(target)) {
4903 return detectLibCFromBuilding(arena, zig_lib_dir, target, has_macos_sdk);
4897 return detectLibCFromBuilding(arena, zig_lib_dir, target);
49044898 }
49054899 return e;
49064900 },
......@@ -4912,7 +4906,7 @@ fn detectLibCIncludeDirs(
49124906 // If not linking system libraries, build and provide our own libc by
49134907 // default if possible.
49144908 if (target_util.canBuildLibC(target)) {
4915 return detectLibCFromBuilding(arena, zig_lib_dir, target, has_macos_sdk);
4909 return detectLibCFromBuilding(arena, zig_lib_dir, target);
49164910 }
49174911
49184912 // If zig can't build the libc for the target and we are targeting the
......@@ -4926,18 +4920,21 @@ fn detectLibCIncludeDirs(
49264920
49274921 if (use_system_abi) {
49284922 const libc = try arena.create(LibCInstallation);
4929 libc.* = try LibCInstallation.findNative(.{ .allocator = arena, .verbose = true });
4923 libc.* = try LibCInstallation.findNative(.{ .allocator = arena, .verbose = true, .target = target });
49304924 return detectLibCFromLibCInstallation(arena, target, libc);
49314925 }
49324926
49334927 return LibCDirs{
49344928 .libc_include_dir_list = &[0][]u8{},
49354929 .libc_installation = null,
4930 .libc_framework_dir_list = &.{},
4931 .sysroot = null,
49364932 };
49374933}
49384934
49394935fn detectLibCFromLibCInstallation(arena: Allocator, target: Target, lci: *const LibCInstallation) !LibCDirs {
49404936 var list = try std.ArrayList([]const u8).initCapacity(arena, 5);
4937 var framework_list = std.ArrayList([]const u8).init(arena);
49414938
49424939 list.appendAssumeCapacity(lci.include_dir.?);
49434940
......@@ -4965,9 +4962,20 @@ fn detectLibCFromLibCInstallation(arena: Allocator, target: Target, lci: *const
49654962 list.appendAssumeCapacity(config_dir);
49664963 }
49674964
4965 var sysroot: ?[]const u8 = null;
4966
4967 if (target.isDarwin()) d: {
4968 const down1 = std.fs.path.dirname(lci.sys_include_dir.?) orelse break :d;
4969 const down2 = std.fs.path.dirname(down1) orelse break :d;
4970 try framework_list.append(try std.fs.path.join(arena, &.{ down2, "System", "Library", "Frameworks" }));
4971 sysroot = down2;
4972 }
4973
49684974 return LibCDirs{
49694975 .libc_include_dir_list = list.items,
49704976 .libc_installation = lci,
4977 .libc_framework_dir_list = framework_list.items,
4978 .sysroot = sysroot,
49714979 };
49724980}
49734981
......@@ -4975,69 +4983,61 @@ fn detectLibCFromBuilding(
49754983 arena: Allocator,
49764984 zig_lib_dir: []const u8,
49774985 target: std.Target,
4978 has_macos_sdk: bool,
49794986) !LibCDirs {
4980 switch (target.os.tag) {
4981 .macos => return if (has_macos_sdk)
4982 // For Darwin/macOS, we are all set with getDarwinSDK found earlier.
4983 LibCDirs{
4984 .libc_include_dir_list = &[0][]u8{},
4985 .libc_installation = null,
4986 }
4987 else
4988 getZigShippedLibCIncludeDirsDarwin(arena, zig_lib_dir, target),
4989 else => {
4990 const generic_name = target_util.libCGenericName(target);
4991 // Some architectures are handled by the same set of headers.
4992 const arch_name = if (target.abi.isMusl())
4993 musl.archNameHeaders(target.cpu.arch)
4994 else if (target.cpu.arch.isThumb())
4995 // ARM headers are valid for Thumb too.
4996 switch (target.cpu.arch) {
4997 .thumb => "arm",
4998 .thumbeb => "armeb",
4999 else => unreachable,
5000 }
5001 else
5002 @tagName(target.cpu.arch);
5003 const os_name = @tagName(target.os.tag);
5004 // Musl's headers are ABI-agnostic and so they all have the "musl" ABI name.
5005 const abi_name = if (target.abi.isMusl()) "musl" else @tagName(target.abi);
5006 const s = std.fs.path.sep_str;
5007 const arch_include_dir = try std.fmt.allocPrint(
5008 arena,
5009 "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{s}-{s}-{s}",
5010 .{ zig_lib_dir, arch_name, os_name, abi_name },
5011 );
5012 const generic_include_dir = try std.fmt.allocPrint(
5013 arena,
5014 "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "generic-{s}",
5015 .{ zig_lib_dir, generic_name },
5016 );
5017 const generic_arch_name = target_util.osArchName(target);
5018 const arch_os_include_dir = try std.fmt.allocPrint(
5019 arena,
5020 "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{s}-{s}-any",
5021 .{ zig_lib_dir, generic_arch_name, os_name },
5022 );
5023 const generic_os_include_dir = try std.fmt.allocPrint(
5024 arena,
5025 "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "any-{s}-any",
5026 .{ zig_lib_dir, os_name },
5027 );
4987 if (target.isDarwin())
4988 return getZigShippedLibCIncludeDirsDarwin(arena, zig_lib_dir, target);
4989
4990 const generic_name = target_util.libCGenericName(target);
4991 // Some architectures are handled by the same set of headers.
4992 const arch_name = if (target.abi.isMusl())
4993 musl.archNameHeaders(target.cpu.arch)
4994 else if (target.cpu.arch.isThumb())
4995 // ARM headers are valid for Thumb too.
4996 switch (target.cpu.arch) {
4997 .thumb => "arm",
4998 .thumbeb => "armeb",
4999 else => unreachable,
5000 }
5001 else
5002 @tagName(target.cpu.arch);
5003 const os_name = @tagName(target.os.tag);
5004 // Musl's headers are ABI-agnostic and so they all have the "musl" ABI name.
5005 const abi_name = if (target.abi.isMusl()) "musl" else @tagName(target.abi);
5006 const s = std.fs.path.sep_str;
5007 const arch_include_dir = try std.fmt.allocPrint(
5008 arena,
5009 "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{s}-{s}-{s}",
5010 .{ zig_lib_dir, arch_name, os_name, abi_name },
5011 );
5012 const generic_include_dir = try std.fmt.allocPrint(
5013 arena,
5014 "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "generic-{s}",
5015 .{ zig_lib_dir, generic_name },
5016 );
5017 const generic_arch_name = target_util.osArchName(target);
5018 const arch_os_include_dir = try std.fmt.allocPrint(
5019 arena,
5020 "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{s}-{s}-any",
5021 .{ zig_lib_dir, generic_arch_name, os_name },
5022 );
5023 const generic_os_include_dir = try std.fmt.allocPrint(
5024 arena,
5025 "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "any-{s}-any",
5026 .{ zig_lib_dir, os_name },
5027 );
50285028
5029 const list = try arena.alloc([]const u8, 4);
5030 list[0] = arch_include_dir;
5031 list[1] = generic_include_dir;
5032 list[2] = arch_os_include_dir;
5033 list[3] = generic_os_include_dir;
5029 const list = try arena.alloc([]const u8, 4);
5030 list[0] = arch_include_dir;
5031 list[1] = generic_include_dir;
5032 list[2] = arch_os_include_dir;
5033 list[3] = generic_os_include_dir;
50345034
5035 return LibCDirs{
5036 .libc_include_dir_list = list,
5037 .libc_installation = null,
5038 };
5039 },
5040 }
5035 return LibCDirs{
5036 .libc_include_dir_list = list,
5037 .libc_installation = null,
5038 .libc_framework_dir_list = &.{},
5039 .sysroot = null,
5040 };
50415041}
50425042
50435043pub 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 {
56185618 // to queue up a work item to produce the DLL import library for this.
56195619 const gop = try comp.bin_file.options.system_libs.getOrPut(comp.gpa, lib_name);
56205620 if (!gop.found_existing and comp.getTarget().os.tag == .windows) {
5621 gop.value_ptr.* = .{
5622 .needed = true,
5623 .weak = false,
5624 .path = null,
5625 };
56215626 try comp.work_queue.writeItem(.{
56225627 .windows_import_lib = comp.bin_file.options.system_libs.count() - 1,
56235628 });
src/libc_installation.zig+15-1
......@@ -33,6 +33,7 @@ pub const LibCInstallation = struct {
3333 LibCKernel32LibNotFound,
3434 UnsupportedArchitecture,
3535 WindowsSdkNotFound,
36 DarwinSdkNotFound,
3637 ZigIsTheCCompiler,
3738 };
3839
......@@ -171,6 +172,7 @@ pub const LibCInstallation = struct {
171172
172173 pub const FindNativeOptions = struct {
173174 allocator: Allocator,
175 target: std.Target,
174176
175177 /// If enabled, will print human-friendly errors to stderr.
176178 verbose: bool = false,
......@@ -181,7 +183,19 @@ pub const LibCInstallation = struct {
181183 var self: LibCInstallation = .{};
182184
183185 if (is_darwin) {
184 @panic("Darwin is handled separately via std.zig.system.darwin module");
186 if (!std.zig.system.darwin.isSdkInstalled(args.allocator))
187 return error.DarwinSdkNotFound;
188 const sdk = std.zig.system.darwin.getSdk(args.allocator, args.target) orelse
189 return error.DarwinSdkNotFound;
190 defer args.allocator.free(sdk.path);
191
192 self.include_dir = try fs.path.join(args.allocator, &.{
193 sdk.path, "usr/include",
194 });
195 self.sys_include_dir = try fs.path.join(args.allocator, &.{
196 sdk.path, "usr/include",
197 });
198 return self;
185199 } else if (is_windows) {
186200 var sdk: ZigWindowsSDK = ZigWindowsSDK.find(args.allocator) catch |err| switch (err) {
187201 error.NotFound => return error.WindowsSdkNotFound,
src/link.zig+32-9
......@@ -21,7 +21,20 @@ const Type = @import("type.zig").Type;
2121const TypedValue = @import("TypedValue.zig");
2222
2323/// When adding a new field, remember to update `hashAddSystemLibs`.
24/// These are *always* dynamically linked. Static libraries will be
25/// provided as positional arguments.
2426pub const SystemLib = struct {
27 needed: bool,
28 weak: bool,
29 /// This can be null in two cases right now:
30 /// 1. Windows DLLs that zig ships such as advapi32.
31 /// 2. extern "foo" fn declarations where we find out about libraries too late
32 /// TODO: make this non-optional and resolve those two cases somehow.
33 path: ?[]const u8,
34};
35
36/// When adding a new field, remember to update `hashAddFrameworks`.
37pub const Framework = struct {
2538 needed: bool = false,
2639 weak: bool = false,
2740};
......@@ -31,11 +44,23 @@ pub const SortSection = enum { name, alignment };
3144pub const CacheMode = enum { incremental, whole };
3245
3346pub fn hashAddSystemLibs(
34 hh: *Cache.HashHelper,
47 man: *Cache.Manifest,
3548 hm: std.StringArrayHashMapUnmanaged(SystemLib),
49) !void {
50 const keys = hm.keys();
51 man.hash.addListOfBytes(keys);
52 for (hm.values()) |value| {
53 man.hash.add(value.needed);
54 man.hash.add(value.weak);
55 if (value.path) |p| _ = try man.addFile(p, null);
56 }
57}
58
59pub fn hashAddFrameworks(
60 hh: *Cache.HashHelper,
61 hm: std.StringArrayHashMapUnmanaged(Framework),
3662) void {
3763 const keys = hm.keys();
38 hh.add(keys.len);
3964 hh.addListOfBytes(keys);
4065 for (hm.values()) |value| {
4166 hh.add(value.needed);
......@@ -183,9 +208,12 @@ pub const Options = struct {
183208
184209 objects: []Compilation.LinkObject,
185210 framework_dirs: []const []const u8,
186 frameworks: std.StringArrayHashMapUnmanaged(SystemLib),
211 frameworks: std.StringArrayHashMapUnmanaged(Framework),
212 /// These are *always* dynamically linked. Static libraries will be
213 /// provided as positional arguments.
187214 system_libs: std.StringArrayHashMapUnmanaged(SystemLib),
188215 wasi_emulated_libs: []const wasi_libc.CRTFile,
216 // TODO: remove this. libraries are resolved by the frontend.
189217 lib_dirs: []const []const u8,
190218 rpath_list: []const []const u8,
191219
......@@ -203,6 +231,7 @@ pub const Options = struct {
203231
204232 version: ?std.SemanticVersion,
205233 compatibility_version: ?std.SemanticVersion,
234 darwin_sdk_version: ?std.SemanticVersion = null,
206235 libc_installation: ?*const LibCInstallation,
207236
208237 dwarf_format: ?std.dwarf.Format,
......@@ -213,9 +242,6 @@ pub const Options = struct {
213242 /// (Zig compiler development) Enable dumping of linker's state as JSON.
214243 enable_link_snapshots: bool = false,
215244
216 /// (Darwin) Path and version of the native SDK if detected.
217 native_darwin_sdk: ?std.zig.system.darwin.DarwinSDK = null,
218
219245 /// (Darwin) Install name for the dylib
220246 install_name: ?[]const u8 = null,
221247
......@@ -225,9 +251,6 @@ pub const Options = struct {
225251 /// (Darwin) size of the __PAGEZERO segment
226252 pagezero_size: ?u64 = null,
227253
228 /// (Darwin) search strategy for system libraries
229 search_strategy: ?File.MachO.SearchStrategy = null,
230
231254 /// (Darwin) set minimum space for future expansion of the load commands
232255 headerpad_size: ?u32 = null,
233256
src/link/Coff/lld.zig+2-1
......@@ -88,7 +88,7 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
8888 }
8989 }
9090 }
91 link.hashAddSystemLibs(&man.hash, self.base.options.system_libs);
91 try link.hashAddSystemLibs(&man, self.base.options.system_libs);
9292 man.hash.addListOfBytes(self.base.options.force_undefined_symbols.keys());
9393 man.hash.addOptional(self.base.options.subsystem);
9494 man.hash.add(self.base.options.is_test);
......@@ -405,6 +405,7 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
405405 try argv.append(try comp.get_libc_crt_file(arena, "mingw32.lib"));
406406 try argv.append(try comp.get_libc_crt_file(arena, "mingwex.lib"));
407407 try argv.append(try comp.get_libc_crt_file(arena, "msvcrt-os.lib"));
408 try argv.append(try comp.get_libc_crt_file(arena, "uuid.lib"));
408409
409410 for (mingw.always_link_libs) |name| {
410411 if (!self.base.options.system_libs.contains(name)) {
src/link/Elf.zig+4-6
......@@ -1428,7 +1428,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
14281428 }
14291429 man.hash.addOptionalBytes(self.base.options.soname);
14301430 man.hash.addOptional(self.base.options.version);
1431 link.hashAddSystemLibs(&man.hash, self.base.options.system_libs);
1431 try link.hashAddSystemLibs(&man, self.base.options.system_libs);
14321432 man.hash.addListOfBytes(self.base.options.force_undefined_symbols.keys());
14331433 man.hash.add(allow_shlib_undefined);
14341434 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
18241824 argv.appendAssumeCapacity("--as-needed");
18251825 var as_needed = true;
18261826
1827 for (system_libs, 0..) |link_lib, i| {
1828 const lib_as_needed = !system_libs_values[i].needed;
1827 for (system_libs_values) |lib_info| {
1828 const lib_as_needed = !lib_info.needed;
18291829 switch ((@as(u2, @intFromBool(lib_as_needed)) << 1) | @intFromBool(as_needed)) {
18301830 0b00, 0b11 => {},
18311831 0b01 => {
......@@ -1842,9 +1842,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
18421842 // libraries and not static libraries (the check for that needs to be earlier),
18431843 // but they could be full paths to .so files, in which case we
18441844 // want to avoid prepending "-l".
1845 const ext = Compilation.classifyFileExt(link_lib);
1846 const arg = if (ext == .shared_library) link_lib else try std.fmt.allocPrint(arena, "-l{s}", .{link_lib});
1847 argv.appendAssumeCapacity(arg);
1845 argv.appendAssumeCapacity(lib_info.path.?);
18481846 }
18491847
18501848 if (!as_needed) {
src/link/MachO.zig+40-34
......@@ -58,11 +58,6 @@ const Rebase = @import("MachO/dyld_info/Rebase.zig");
5858
5959pub const base_tag: File.Tag = File.Tag.macho;
6060
61pub const SearchStrategy = enum {
62 paths_first,
63 dylibs_first,
64};
65
6661/// Mode of operation of the linker.
6762pub const Mode = enum {
6863 /// Incremental mode will preallocate segments/sections and is compatible with
......@@ -834,39 +829,50 @@ pub fn resolveLibSystem(
834829 out_libs: anytype,
835830) !void {
836831 // If we were given the sysroot, try to look there first for libSystem.B.{dylib, tbd}.
837 var libsystem_available = false;
838 if (syslibroot != null) blk: {
839 // Try stub file first. If we hit it, then we're done as the stub file
840 // re-exports every single symbol definition.
841 for (search_dirs) |dir| {
842 if (try resolveLib(arena, dir, "System", ".tbd")) |full_path| {
843 try out_libs.put(full_path, .{ .needed = true });
844 libsystem_available = true;
845 break :blk;
846 }
832 if (syslibroot) |root| {
833 const full_dir_path = try std.fs.path.join(arena, &.{ root, "usr", "lib" });
834 if (try resolveLibSystemInDirs(arena, &.{full_dir_path}, out_libs)) return;
835 }
836
837 // Next, try input search dirs if we are linking on a custom host such as Nix.
838 if (try resolveLibSystemInDirs(arena, search_dirs, out_libs)) return;
839
840 // As a fallback, try linking against Zig shipped stub.
841 const libsystem_name = try std.fmt.allocPrint(arena, "libSystem.{d}.tbd", .{
842 target.os.version_range.semver.min.major,
843 });
844 const full_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{
845 "libc", "darwin", libsystem_name,
846 });
847 try out_libs.put(full_path, .{
848 .needed = true,
849 .weak = false,
850 .path = full_path,
851 });
852}
853
854fn resolveLibSystemInDirs(arena: Allocator, dirs: []const []const u8, out_libs: anytype) !bool {
855 // Try stub file first. If we hit it, then we're done as the stub file
856 // re-exports every single symbol definition.
857 for (dirs) |dir| {
858 if (try resolveLib(arena, dir, "System", ".tbd")) |full_path| {
859 try out_libs.put(full_path, .{ .needed = true, .weak = false, .path = full_path });
860 return true;
847861 }
848 // If we didn't hit the stub file, try .dylib next. However, libSystem.dylib
849 // doesn't export libc.dylib which we'll need to resolve subsequently also.
850 for (search_dirs) |dir| {
851 if (try resolveLib(arena, dir, "System", ".dylib")) |libsystem_path| {
852 if (try resolveLib(arena, dir, "c", ".dylib")) |libc_path| {
853 try out_libs.put(libsystem_path, .{ .needed = true });
854 try out_libs.put(libc_path, .{ .needed = true });
855 libsystem_available = true;
856 break :blk;
857 }
862 }
863 // If we didn't hit the stub file, try .dylib next. However, libSystem.dylib
864 // doesn't export libc.dylib which we'll need to resolve subsequently also.
865 for (dirs) |dir| {
866 if (try resolveLib(arena, dir, "System", ".dylib")) |libsystem_path| {
867 if (try resolveLib(arena, dir, "c", ".dylib")) |libc_path| {
868 try out_libs.put(libsystem_path, .{ .needed = true, .weak = false, .path = libsystem_path });
869 try out_libs.put(libc_path, .{ .needed = true, .weak = false, .path = libc_path });
870 return true;
858871 }
859872 }
860873 }
861 if (!libsystem_available) {
862 const libsystem_name = try std.fmt.allocPrint(arena, "libSystem.{d}.tbd", .{
863 target.os.version_range.semver.min.major,
864 });
865 const full_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{
866 "libc", "darwin", libsystem_name,
867 });
868 try out_libs.put(full_path, .{ .needed = true });
869 }
874
875 return false;
870876}
871877
872878pub fn resolveSearchDir(
src/link/MachO/load_commands.zig+4-5
......@@ -278,11 +278,10 @@ pub fn writeBuildVersionLC(options: *const link.Options, lc_writer: anytype) !vo
278278 const platform_version = @as(u32, @intCast(ver.major << 16 | ver.minor << 8));
279279 break :blk platform_version;
280280 };
281 const sdk_version = if (options.native_darwin_sdk) |sdk| blk: {
282 const ver = sdk.version;
283 const sdk_version = @as(u32, @intCast(ver.major << 16 | ver.minor << 8));
284 break :blk sdk_version;
285 } else platform_version;
281 const sdk_version: u32 = if (options.darwin_sdk_version) |ver|
282 @intCast(ver.major << 16 | ver.minor << 8)
283 else
284 platform_version;
286285 const is_simulator_abi = options.target.abi == .simulator;
287286 try lc_writer.writeStruct(macho.build_version_command{
288287 .cmdsize = cmdsize,
src/link/MachO/zld.zig+8-82
......@@ -3410,7 +3410,6 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
34103410 // installation sources because they are always a product of the compiler version + target information.
34113411 man.hash.add(stack_size);
34123412 man.hash.addOptional(options.pagezero_size);
3413 man.hash.addOptional(options.search_strategy);
34143413 man.hash.addOptional(options.headerpad_size);
34153414 man.hash.add(options.headerpad_max_install_names);
34163415 man.hash.add(gc_sections);
......@@ -3418,13 +3417,13 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
34183417 man.hash.add(options.strip);
34193418 man.hash.addListOfBytes(options.lib_dirs);
34203419 man.hash.addListOfBytes(options.framework_dirs);
3421 link.hashAddSystemLibs(&man.hash, options.frameworks);
3420 link.hashAddFrameworks(&man.hash, options.frameworks);
34223421 man.hash.addListOfBytes(options.rpath_list);
34233422 if (is_dyn_lib) {
34243423 man.hash.addOptionalBytes(options.install_name);
34253424 man.hash.addOptional(options.version);
34263425 }
3427 link.hashAddSystemLibs(&man.hash, options.system_libs);
3426 try link.hashAddSystemLibs(&man, options.system_libs);
34283427 man.hash.addOptionalBytes(options.sysroot);
34293428 man.hash.addListOfBytes(options.force_undefined_symbols.keys());
34303429 try man.addOptionalFile(options.entitlements);
......@@ -3550,84 +3549,15 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
35503549 try positionals.append(comp.libcxx_static_lib.?.full_object_path);
35513550 }
35523551
3553 // Shared and static libraries passed via `-l` flag.
3554 var candidate_libs = std.StringArrayHashMap(link.SystemLib).init(arena);
3555
3556 const system_lib_names = options.system_libs.keys();
3557 for (system_lib_names) |system_lib_name| {
3558 // By this time, we depend on these libs being dynamically linked libraries and not static libraries
3559 // (the check for that needs to be earlier), but they could be full paths to .dylib files, in which
3560 // case we want to avoid prepending "-l".
3561 if (Compilation.classifyFileExt(system_lib_name) == .shared_library) {
3562 try positionals.append(system_lib_name);
3563 continue;
3564 }
3565
3566 const system_lib_info = options.system_libs.get(system_lib_name).?;
3567 try candidate_libs.put(system_lib_name, .{
3568 .needed = system_lib_info.needed,
3569 .weak = system_lib_info.weak,
3570 });
3571 }
3572
3573 var lib_dirs = std.ArrayList([]const u8).init(arena);
3574 for (options.lib_dirs) |dir| {
3575 if (try MachO.resolveSearchDir(arena, dir, options.sysroot)) |search_dir| {
3576 try lib_dirs.append(search_dir);
3577 } else {
3578 log.warn("directory not found for '-L{s}'", .{dir});
3579 }
3580 }
3581
35823552 var libs = std.StringArrayHashMap(link.SystemLib).init(arena);
35833553
3584 // Assume ld64 default -search_paths_first if no strategy specified.
3585 const search_strategy = options.search_strategy orelse .paths_first;
3586 outer: for (candidate_libs.keys()) |lib_name| {
3587 switch (search_strategy) {
3588 .paths_first => {
3589 // Look in each directory for a dylib (stub first), and then for archive
3590 for (lib_dirs.items) |dir| {
3591 for (&[_][]const u8{ ".tbd", ".dylib", ".a" }) |ext| {
3592 if (try MachO.resolveLib(arena, dir, lib_name, ext)) |full_path| {
3593 try libs.put(full_path, candidate_libs.get(lib_name).?);
3594 continue :outer;
3595 }
3596 }
3597 } else {
3598 log.warn("library not found for '-l{s}'", .{lib_name});
3599 lib_not_found = true;
3600 }
3601 },
3602 .dylibs_first => {
3603 // First, look for a dylib in each search dir
3604 for (lib_dirs.items) |dir| {
3605 for (&[_][]const u8{ ".tbd", ".dylib" }) |ext| {
3606 if (try MachO.resolveLib(arena, dir, lib_name, ext)) |full_path| {
3607 try libs.put(full_path, candidate_libs.get(lib_name).?);
3608 continue :outer;
3609 }
3610 }
3611 } else for (lib_dirs.items) |dir| {
3612 if (try MachO.resolveLib(arena, dir, lib_name, ".a")) |full_path| {
3613 try libs.put(full_path, candidate_libs.get(lib_name).?);
3614 } else {
3615 log.warn("library not found for '-l{s}'", .{lib_name});
3616 lib_not_found = true;
3617 }
3618 }
3619 },
3620 }
3621 }
3622
3623 if (lib_not_found) {
3624 log.warn("Library search paths:", .{});
3625 for (lib_dirs.items) |dir| {
3626 log.warn(" {s}", .{dir});
3627 }
3554 {
3555 const vals = options.system_libs.values();
3556 try libs.ensureUnusedCapacity(vals.len);
3557 for (vals) |v| libs.putAssumeCapacity(v.path.?, v);
36283558 }
36293559
3630 try MachO.resolveLibSystem(arena, comp, options.sysroot, target, lib_dirs.items, &libs);
3560 try MachO.resolveLibSystem(arena, comp, options.sysroot, target, options.lib_dirs, &libs);
36313561
36323562 // frameworks
36333563 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
36473577 try libs.put(full_path, .{
36483578 .needed = info.needed,
36493579 .weak = info.weak,
3580 .path = full_path,
36503581 });
36513582 continue :outer;
36523583 }
......@@ -3698,11 +3629,6 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
36983629 try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{pagezero_size}));
36993630 }
37003631
3701 if (options.search_strategy) |strat| switch (strat) {
3702 .paths_first => try argv.append("-search_paths_first"),
3703 .dylibs_first => try argv.append("-search_dylibs_first"),
3704 };
3705
37063632 if (options.headerpad_size) |headerpad_size| {
37073633 try argv.append("-headerpad_size");
37083634 try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{headerpad_size}));
src/main.zig+529-281
......@@ -28,6 +28,7 @@ const target_util = @import("target.zig");
2828const crash_report = @import("crash_report.zig");
2929const Module = @import("Module.zig");
3030const AstGen = @import("AstGen.zig");
31const mingw = @import("mingw.zig");
3132const Server = std.zig.Server;
3233
3334pub const std_options = struct {
......@@ -476,7 +477,19 @@ const usage_build_generic =
476477 \\ -l[lib], --library [lib] Link against system library (only if actually used)
477478 \\ -needed-l[lib], Link against system library (even if unused)
478479 \\ --needed-library [lib]
480 \\ -weak-l[lib] link against system library marking it and all
481 \\ -weak_library [lib] referenced symbols as weak
479482 \\ -L[d], --library-directory [d] Add a directory to the library search path
483 \\ -search_paths_first For each library search path, check for dynamic
484 \\ lib then static lib before proceeding to next path.
485 \\ -search_paths_first_static For each library search path, check for static
486 \\ lib then dynamic lib before proceeding to next path.
487 \\ -search_dylibs_first Search for dynamic libs in all library search
488 \\ paths, then static libs.
489 \\ -search_static_first Search for static libs in all library search
490 \\ paths, then dynamic libs.
491 \\ -search_dylibs_only Only search for dynamic libs.
492 \\ -search_static_only Only search for static libs.
480493 \\ -T[script], --script [script] Use a custom linker script
481494 \\ --version-script [path] Provide a version .map file
482495 \\ --dynamic-linker [path] Set the dynamic interpreter path (usually ld.so)
......@@ -527,18 +540,14 @@ const usage_build_generic =
527540 \\ --subsystem [subsystem] (Windows) /SUBSYSTEM:<subsystem> to the linker
528541 \\ --stack [size] Override default stack size
529542 \\ --image-base [addr] Set base address for executable image
530 \\ -weak-l[lib] (Darwin) link against system library and mark it and all referenced symbols as weak
531 \\ -weak_library [lib]
532543 \\ -framework [name] (Darwin) link against framework
533544 \\ -needed_framework [name] (Darwin) link against framework (even if unused)
534 \\ -needed_library [lib] (Darwin) link against system library (even if unused)
545 \\ -needed_library [lib] link against system library (even if unused)
535546 \\ -weak_framework [name] (Darwin) link against framework and mark it and all referenced symbols as weak
536547 \\ -F[dir] (Darwin) add search path for frameworks
537548 \\ -install_name=[value] (Darwin) add dylib's install name
538549 \\ --entitlements [path] (Darwin) add path to entitlements file for embedding in code signature
539550 \\ -pagezero_size [value] (Darwin) size of the __PAGEZERO segment in hexadecimal notation
540 \\ -search_paths_first (Darwin) search each dir in library search paths for `libx.dylib` then `libx.a`
541 \\ -search_dylibs_first (Darwin) search `libx.dylib` in each dir in library search paths, then `libx.a`
542551 \\ -headerpad [value] (Darwin) set minimum space for future expansion of the load commands in hexadecimal notation
543552 \\ -headerpad_max_install_names (Darwin) set enough space as if all paths were MAXPATHLEN
544553 \\ -dead_strip (Darwin) remove functions and data that are unreachable by the entry point or exported symbols
......@@ -716,6 +725,39 @@ const ArgsIterator = struct {
716725 }
717726};
718727
728/// In contrast to `link.SystemLib`, this stores arguments that may need to be
729/// resolved into static libraries so that we can pass only dynamic libraries
730/// as system libs to `Compilation`.
731const SystemLib = struct {
732 needed: bool,
733 weak: bool,
734
735 preferred_mode: std.builtin.LinkMode,
736 search_strategy: SearchStrategy,
737
738 const SearchStrategy = enum { paths_first, mode_first, no_fallback };
739
740 fn fallbackMode(this: SystemLib) std.builtin.LinkMode {
741 assert(this.search_strategy != .no_fallback);
742 return switch (this.preferred_mode) {
743 .Dynamic => .Static,
744 .Static => .Dynamic,
745 };
746 }
747};
748
749const CliModule = struct {
750 mod: *Package,
751 /// still in CLI arg format
752 deps_str: []const u8,
753};
754
755fn cleanupModules(modules: *std.StringArrayHashMap(CliModule)) void {
756 var it = modules.iterator();
757 while (it.next()) |kv| kv.value_ptr.mod.destroy(modules.allocator);
758 modules.deinit();
759}
760
719761fn buildOutputType(
720762 gpa: Allocator,
721763 arena: Allocator,
......@@ -849,12 +891,12 @@ fn buildOutputType(
849891 var minor_subsystem_version: ?u32 = null;
850892 var wasi_exec_model: ?std.builtin.WasiExecModel = null;
851893 var enable_link_snapshots: bool = false;
852 var native_darwin_sdk: ?std.zig.system.darwin.DarwinSDK = null;
853894 var install_name: ?[]const u8 = null;
854895 var hash_style: link.HashStyle = .both;
855896 var entitlements: ?[]const u8 = null;
856897 var pagezero_size: ?u64 = null;
857 var search_strategy: ?link.File.MachO.SearchStrategy = null;
898 var lib_search_strategy: SystemLib.SearchStrategy = .paths_first;
899 var lib_preferred_mode: std.builtin.LinkMode = .Dynamic;
858900 var headerpad_size: ?u32 = null;
859901 var headerpad_max_install_names: bool = false;
860902 var dead_strip_dylibs: bool = false;
......@@ -862,66 +904,30 @@ fn buildOutputType(
862904 var error_tracing: ?bool = null;
863905 var pdb_out_path: ?[]const u8 = null;
864906 var dwarf_format: ?std.dwarf.Format = null;
865
866907 // e.g. -m3dnow or -mno-outline-atomics. They correspond to std.Target llvm cpu feature names.
867908 // This array is populated by zig cc frontend and then has to be converted to zig-style
868909 // CPU features.
869 var llvm_m_args = std.ArrayList([]const u8).init(gpa);
870 defer llvm_m_args.deinit();
871
872 var system_libs = std.StringArrayHashMap(Compilation.SystemLib).init(gpa);
873 defer system_libs.deinit();
874
875 var static_libs = std.ArrayList([]const u8).init(gpa);
876 defer static_libs.deinit();
877
878 var wasi_emulated_libs = std.ArrayList(wasi_libc.CRTFile).init(gpa);
879 defer wasi_emulated_libs.deinit();
880
881 var clang_argv = std.ArrayList([]const u8).init(gpa);
882 defer clang_argv.deinit();
883
884 var extra_cflags = std.ArrayList([]const u8).init(gpa);
885 defer extra_cflags.deinit();
886
887 var lib_dirs = std.ArrayList([]const u8).init(gpa);
888 defer lib_dirs.deinit();
889
890 var rpath_list = std.ArrayList([]const u8).init(gpa);
891 defer rpath_list.deinit();
892
910 var llvm_m_args = std.ArrayList([]const u8).init(arena);
911 var system_libs = std.StringArrayHashMap(SystemLib).init(arena);
912 var wasi_emulated_libs = std.ArrayList(wasi_libc.CRTFile).init(arena);
913 var clang_argv = std.ArrayList([]const u8).init(arena);
914 var extra_cflags = std.ArrayList([]const u8).init(arena);
915 // These are before resolving sysroot.
916 var lib_dir_args = std.ArrayList([]const u8).init(arena);
917 var rpath_list = std.ArrayList([]const u8).init(arena);
893918 var symbol_wrap_set: std.StringArrayHashMapUnmanaged(void) = .{};
894
895 var c_source_files = std.ArrayList(Compilation.CSourceFile).init(gpa);
896 defer c_source_files.deinit();
897
898 var link_objects = std.ArrayList(Compilation.LinkObject).init(gpa);
899 defer link_objects.deinit();
900
901 var framework_dirs = std.ArrayList([]const u8).init(gpa);
902 defer framework_dirs.deinit();
903
904 var frameworks: std.StringArrayHashMapUnmanaged(Compilation.SystemLib) = .{};
905
919 var c_source_files = std.ArrayList(Compilation.CSourceFile).init(arena);
920 var link_objects = std.ArrayList(Compilation.LinkObject).init(arena);
921 var framework_dirs = std.ArrayList([]const u8).init(arena);
922 var frameworks: std.StringArrayHashMapUnmanaged(Compilation.Framework) = .{};
906923 // null means replace with the test executable binary
907 var test_exec_args = std.ArrayList(?[]const u8).init(gpa);
908 defer test_exec_args.deinit();
909
910 var linker_export_symbol_names = std.ArrayList([]const u8).init(gpa);
911 defer linker_export_symbol_names.deinit();
912
924 var test_exec_args = std.ArrayList(?[]const u8).init(arena);
925 var linker_export_symbol_names = std.ArrayList([]const u8).init(arena);
913926 // Contains every module specified via --mod. The dependencies are added
914927 // after argument parsing is completed. We use a StringArrayHashMap to make
915928 // error output consistent.
916 var modules = std.StringArrayHashMap(struct {
917 mod: *Package,
918 deps_str: []const u8, // still in CLI arg format
919 }).init(gpa);
920 defer {
921 var it = modules.iterator();
922 while (it.next()) |kv| kv.value_ptr.mod.destroy(gpa);
923 modules.deinit();
924 }
929 var modules = std.StringArrayHashMap(CliModule).init(gpa);
930 defer cleanupModules(&modules);
925931
926932 // The dependency string for the root package
927933 var root_deps_str: ?[]const u8 = null;
......@@ -1061,7 +1067,7 @@ fn buildOutputType(
10611067 } else if (mem.eql(u8, arg, "-rpath")) {
10621068 try rpath_list.append(args_iter.nextOrFatal());
10631069 } else if (mem.eql(u8, arg, "--library-directory") or mem.eql(u8, arg, "-L")) {
1064 try lib_dirs.append(args_iter.nextOrFatal());
1070 try lib_dir_args.append(args_iter.nextOrFatal());
10651071 } else if (mem.eql(u8, arg, "-F")) {
10661072 try framework_dirs.append(args_iter.nextOrFatal());
10671073 } else if (mem.eql(u8, arg, "-framework")) {
......@@ -1085,9 +1091,23 @@ fn buildOutputType(
10851091 fatal("unable to parse pagezero size'{s}': {s}", .{ next_arg, @errorName(err) });
10861092 };
10871093 } else if (mem.eql(u8, arg, "-search_paths_first")) {
1088 search_strategy = .paths_first;
1094 lib_search_strategy = .paths_first;
1095 lib_preferred_mode = .Dynamic;
1096 } else if (mem.eql(u8, arg, "-search_paths_first_static")) {
1097 lib_search_strategy = .paths_first;
1098 lib_preferred_mode = .Static;
10891099 } else if (mem.eql(u8, arg, "-search_dylibs_first")) {
1090 search_strategy = .dylibs_first;
1100 lib_search_strategy = .mode_first;
1101 lib_preferred_mode = .Dynamic;
1102 } else if (mem.eql(u8, arg, "-search_static_first")) {
1103 lib_search_strategy = .mode_first;
1104 lib_preferred_mode = .Static;
1105 } else if (mem.eql(u8, arg, "-search_dylibs_only")) {
1106 lib_search_strategy = .no_fallback;
1107 lib_preferred_mode = .Dynamic;
1108 } else if (mem.eql(u8, arg, "-search_static_only")) {
1109 lib_search_strategy = .no_fallback;
1110 lib_preferred_mode = .Static;
10911111 } else if (mem.eql(u8, arg, "-headerpad")) {
10921112 const next_arg = args_iter.nextOrFatal();
10931113 headerpad_size = std.fmt.parseUnsigned(u32, eatIntPrefix(next_arg, 16), 16) catch |err| {
......@@ -1104,17 +1124,33 @@ fn buildOutputType(
11041124 } else if (mem.eql(u8, arg, "-version-script") or mem.eql(u8, arg, "--version-script")) {
11051125 version_script = args_iter.nextOrFatal();
11061126 } else if (mem.eql(u8, arg, "--library") or mem.eql(u8, arg, "-l")) {
1107 // We don't know whether this library is part of libc or libc++ until
1108 // we resolve the target, so we simply append to the list for now.
1109 try system_libs.put(args_iter.nextOrFatal(), .{});
1127 // We don't know whether this library is part of libc
1128 // or libc++ until we resolve the target, so we append
1129 // to the list for now.
1130 try system_libs.put(args_iter.nextOrFatal(), .{
1131 .needed = false,
1132 .weak = false,
1133 .preferred_mode = lib_preferred_mode,
1134 .search_strategy = lib_search_strategy,
1135 });
11101136 } else if (mem.eql(u8, arg, "--needed-library") or
11111137 mem.eql(u8, arg, "-needed-l") or
11121138 mem.eql(u8, arg, "-needed_library"))
11131139 {
11141140 const next_arg = args_iter.nextOrFatal();
1115 try system_libs.put(next_arg, .{ .needed = true });
1141 try system_libs.put(next_arg, .{
1142 .needed = true,
1143 .weak = false,
1144 .preferred_mode = lib_preferred_mode,
1145 .search_strategy = lib_search_strategy,
1146 });
11161147 } else if (mem.eql(u8, arg, "-weak_library") or mem.eql(u8, arg, "-weak-l")) {
1117 try system_libs.put(args_iter.nextOrFatal(), .{ .weak = true });
1148 try system_libs.put(args_iter.nextOrFatal(), .{
1149 .needed = false,
1150 .weak = true,
1151 .preferred_mode = lib_preferred_mode,
1152 .search_strategy = lib_search_strategy,
1153 });
11181154 } else if (mem.eql(u8, arg, "-D")) {
11191155 try clang_argv.append(arg);
11201156 try clang_argv.append(args_iter.nextOrFatal());
......@@ -1346,8 +1382,12 @@ fn buildOutputType(
13461382 emit_implib_arg_provided = true;
13471383 } else if (mem.eql(u8, arg, "-dynamic")) {
13481384 link_mode = .Dynamic;
1385 lib_preferred_mode = .Dynamic;
1386 lib_search_strategy = .mode_first;
13491387 } else if (mem.eql(u8, arg, "-static")) {
13501388 link_mode = .Static;
1389 lib_preferred_mode = .Static;
1390 lib_search_strategy = .no_fallback;
13511391 } else if (mem.eql(u8, arg, "-fdll-export-fns")) {
13521392 dll_export_fns = true;
13531393 } else if (mem.eql(u8, arg, "-fno-dll-export-fns")) {
......@@ -1486,17 +1526,33 @@ fn buildOutputType(
14861526 } else if (mem.startsWith(u8, arg, "-T")) {
14871527 linker_script = arg[2..];
14881528 } else if (mem.startsWith(u8, arg, "-L")) {
1489 try lib_dirs.append(arg[2..]);
1529 try lib_dir_args.append(arg[2..]);
14901530 } else if (mem.startsWith(u8, arg, "-F")) {
14911531 try framework_dirs.append(arg[2..]);
14921532 } else if (mem.startsWith(u8, arg, "-l")) {
1493 // We don't know whether this library is part of libc or libc++ until
1494 // we resolve the target, so we simply append to the list for now.
1495 try system_libs.put(arg["-l".len..], .{});
1533 // We don't know whether this library is part of libc
1534 // or libc++ until we resolve the target, so we append
1535 // to the list for now.
1536 try system_libs.put(arg["-l".len..], .{
1537 .needed = false,
1538 .weak = false,
1539 .preferred_mode = lib_preferred_mode,
1540 .search_strategy = lib_search_strategy,
1541 });
14961542 } else if (mem.startsWith(u8, arg, "-needed-l")) {
1497 try system_libs.put(arg["-needed-l".len..], .{ .needed = true });
1543 try system_libs.put(arg["-needed-l".len..], .{
1544 .needed = true,
1545 .weak = false,
1546 .preferred_mode = lib_preferred_mode,
1547 .search_strategy = lib_search_strategy,
1548 });
14981549 } else if (mem.startsWith(u8, arg, "-weak-l")) {
1499 try system_libs.put(arg["-weak-l".len..], .{ .weak = true });
1550 try system_libs.put(arg["-weak-l".len..], .{
1551 .needed = false,
1552 .weak = true,
1553 .preferred_mode = lib_preferred_mode,
1554 .search_strategy = lib_search_strategy,
1555 });
15001556 } else if (mem.startsWith(u8, arg, "-D")) {
15011557 try clang_argv.append(arg);
15021558 } else if (mem.startsWith(u8, arg, "-I")) {
......@@ -1571,7 +1627,6 @@ fn buildOutputType(
15711627 var emit_llvm = false;
15721628 var needed = false;
15731629 var must_link = false;
1574 var force_static_libs = false;
15751630 var file_ext: ?Compilation.FileExt = null;
15761631 while (it.has_next) {
15771632 it.next() catch |err| {
......@@ -1641,10 +1696,13 @@ fn buildOutputType(
16411696 .must_link = must_link,
16421697 .loption = true,
16431698 });
1644 } else if (force_static_libs) {
1645 try static_libs.append(it.only_arg);
16461699 } else {
1647 try system_libs.put(it.only_arg, .{ .needed = needed });
1700 try system_libs.put(it.only_arg, .{
1701 .needed = needed,
1702 .weak = false,
1703 .preferred_mode = lib_preferred_mode,
1704 .search_strategy = lib_search_strategy,
1705 });
16481706 }
16491707 },
16501708 .ignore => {},
......@@ -1740,17 +1798,21 @@ fn buildOutputType(
17401798 mem.eql(u8, linker_arg, "-dy") or
17411799 mem.eql(u8, linker_arg, "-call_shared"))
17421800 {
1743 force_static_libs = false;
1801 lib_search_strategy = .no_fallback;
1802 lib_preferred_mode = .Dynamic;
17441803 } else if (mem.eql(u8, linker_arg, "-Bstatic") or
17451804 mem.eql(u8, linker_arg, "-dn") or
17461805 mem.eql(u8, linker_arg, "-non_shared") or
17471806 mem.eql(u8, linker_arg, "-static"))
17481807 {
1749 force_static_libs = true;
1808 lib_search_strategy = .no_fallback;
1809 lib_preferred_mode = .Static;
17501810 } else if (mem.eql(u8, linker_arg, "-search_paths_first")) {
1751 search_strategy = .paths_first;
1811 lib_search_strategy = .paths_first;
1812 lib_preferred_mode = .Dynamic;
17521813 } else if (mem.eql(u8, linker_arg, "-search_dylibs_first")) {
1753 search_strategy = .dylibs_first;
1814 lib_search_strategy = .mode_first;
1815 lib_preferred_mode = .Dynamic;
17541816 } else {
17551817 try linker_args.append(linker_arg);
17561818 }
......@@ -1828,7 +1890,7 @@ fn buildOutputType(
18281890 try linker_args.append("-z");
18291891 try linker_args.append(it.only_arg);
18301892 },
1831 .lib_dir => try lib_dirs.append(it.only_arg),
1893 .lib_dir => try lib_dir_args.append(it.only_arg),
18321894 .mcpu => target_mcpu = it.only_arg,
18331895 .m => try llvm_m_args.append(it.only_arg),
18341896 .dep_file => {
......@@ -1860,7 +1922,12 @@ fn buildOutputType(
18601922 .force_undefined_symbol => {
18611923 try force_undefined_symbols.put(gpa, it.only_arg, {});
18621924 },
1863 .weak_library => try system_libs.put(it.only_arg, .{ .weak = true }),
1925 .weak_library => try system_libs.put(it.only_arg, .{
1926 .needed = false,
1927 .weak = true,
1928 .preferred_mode = lib_preferred_mode,
1929 .search_strategy = lib_search_strategy,
1930 }),
18641931 .weak_framework => try frameworks.put(gpa, it.only_arg, .{ .weak = true }),
18651932 .headerpad_max_install_names => headerpad_max_install_names = true,
18661933 .compress_debug_sections => {
......@@ -2156,11 +2223,26 @@ fn buildOutputType(
21562223 } else if (mem.eql(u8, arg, "-needed_framework")) {
21572224 try frameworks.put(gpa, linker_args_it.nextOrFatal(), .{ .needed = true });
21582225 } else if (mem.eql(u8, arg, "-needed_library")) {
2159 try system_libs.put(linker_args_it.nextOrFatal(), .{ .needed = true });
2226 try system_libs.put(linker_args_it.nextOrFatal(), .{
2227 .weak = false,
2228 .needed = true,
2229 .preferred_mode = lib_preferred_mode,
2230 .search_strategy = lib_search_strategy,
2231 });
21602232 } else if (mem.startsWith(u8, arg, "-weak-l")) {
2161 try system_libs.put(arg["-weak-l".len..], .{ .weak = true });
2233 try system_libs.put(arg["-weak-l".len..], .{
2234 .weak = true,
2235 .needed = false,
2236 .preferred_mode = lib_preferred_mode,
2237 .search_strategy = lib_search_strategy,
2238 });
21622239 } else if (mem.eql(u8, arg, "-weak_library")) {
2163 try system_libs.put(linker_args_it.nextOrFatal(), .{ .weak = true });
2240 try system_libs.put(linker_args_it.nextOrFatal(), .{
2241 .weak = true,
2242 .needed = false,
2243 .preferred_mode = lib_preferred_mode,
2244 .search_strategy = lib_search_strategy,
2245 });
21642246 } else if (mem.eql(u8, arg, "-compatibility_version")) {
21652247 const compat_version = linker_args_it.nextOrFatal();
21662248 compatibility_version = std.SemanticVersion.parse(compat_version) catch |err| {
......@@ -2458,105 +2540,6 @@ fn buildOutputType(
24582540 }
24592541 }
24602542
2461 // Now that we have target info, we can find out if any of the system libraries
2462 // are part of libc or libc++. We remove them from the list and communicate their
2463 // existence via flags instead.
2464 {
2465 // Similarly, if any libs in this list are statically provided, we remove
2466 // them from this list and populate the link_objects array instead.
2467 const sep = fs.path.sep_str;
2468 var test_path = std.ArrayList(u8).init(gpa);
2469 defer test_path.deinit();
2470
2471 var i: usize = 0;
2472 syslib: while (i < system_libs.count()) {
2473 const lib_name = system_libs.keys()[i];
2474
2475 if (target_util.is_libc_lib_name(target_info.target, lib_name)) {
2476 link_libc = true;
2477 system_libs.orderedRemoveAt(i);
2478 continue;
2479 }
2480 if (target_util.is_libcpp_lib_name(target_info.target, lib_name)) {
2481 link_libcpp = true;
2482 system_libs.orderedRemoveAt(i);
2483 continue;
2484 }
2485 switch (target_util.classifyCompilerRtLibName(target_info.target, lib_name)) {
2486 .none => {},
2487 .only_libunwind, .both => {
2488 link_libunwind = true;
2489 system_libs.orderedRemoveAt(i);
2490 continue;
2491 },
2492 .only_compiler_rt => {
2493 std.log.warn("ignoring superfluous library '{s}': this dependency is fulfilled instead by compiler-rt which zig unconditionally provides", .{lib_name});
2494 system_libs.orderedRemoveAt(i);
2495 continue;
2496 },
2497 }
2498
2499 if (fs.path.isAbsolute(lib_name)) {
2500 fatal("cannot use absolute path as a system library: {s}", .{lib_name});
2501 }
2502
2503 if (target_info.target.os.tag == .wasi) {
2504 if (wasi_libc.getEmulatedLibCRTFile(lib_name)) |crt_file| {
2505 try wasi_emulated_libs.append(crt_file);
2506 system_libs.orderedRemoveAt(i);
2507 continue;
2508 }
2509 }
2510
2511 for (lib_dirs.items) |lib_dir_path| {
2512 if (cross_target.isDarwin()) break; // Targeting Darwin we let the linker resolve the libraries in the correct order
2513 test_path.clearRetainingCapacity();
2514 try test_path.writer().print("{s}" ++ sep ++ "{s}{s}{s}", .{
2515 lib_dir_path,
2516 target_info.target.libPrefix(),
2517 lib_name,
2518 target_info.target.staticLibSuffix(),
2519 });
2520 fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
2521 error.FileNotFound => continue,
2522 else => |e| fatal("unable to search for static library '{s}': {s}", .{
2523 test_path.items, @errorName(e),
2524 }),
2525 };
2526 try link_objects.append(.{ .path = try arena.dupe(u8, test_path.items) });
2527 system_libs.orderedRemoveAt(i);
2528 continue :syslib;
2529 }
2530
2531 // Unfortunately, in the case of MinGW we also need to look for `libfoo.a`.
2532 if (target_info.target.isMinGW()) {
2533 for (lib_dirs.items) |lib_dir_path| {
2534 test_path.clearRetainingCapacity();
2535 try test_path.writer().print("{s}" ++ sep ++ "lib{s}.a", .{
2536 lib_dir_path, lib_name,
2537 });
2538 fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
2539 error.FileNotFound => continue,
2540 else => |e| fatal("unable to search for static library '{s}': {s}", .{
2541 test_path.items, @errorName(e),
2542 }),
2543 };
2544 try link_objects.append(.{ .path = try arena.dupe(u8, test_path.items) });
2545 system_libs.orderedRemoveAt(i);
2546 continue :syslib;
2547 }
2548 }
2549
2550 std.log.scoped(.cli).debug("depending on system for -l{s}", .{lib_name});
2551
2552 i += 1;
2553 }
2554 }
2555 // libc++ depends on libc
2556 if (link_libcpp) {
2557 link_libc = true;
2558 }
2559
25602543 if (use_lld) |opt| {
25612544 if (opt and cross_target.isDarwin()) {
25622545 fatal("LLD requested with Mach-O object format. Only the self-hosted linker is supported for this target.", .{});
......@@ -2575,8 +2558,124 @@ fn buildOutputType(
25752558 want_native_include_dirs = true;
25762559 }
25772560
2578 if (sysroot == null and cross_target.isNativeOs() and
2579 (system_libs.count() != 0 or want_native_include_dirs))
2561 // Resolve the library path arguments with respect to sysroot.
2562 var lib_dirs = std.ArrayList([]const u8).init(arena);
2563 if (sysroot) |root| {
2564 for (lib_dir_args.items) |dir| {
2565 if (fs.path.isAbsolute(dir)) {
2566 const stripped_dir = dir[fs.path.diskDesignator(dir).len..];
2567 const full_path = try fs.path.join(arena, &[_][]const u8{ root, stripped_dir });
2568 try lib_dirs.append(full_path);
2569 }
2570 try lib_dirs.append(dir);
2571 }
2572 } else {
2573 lib_dirs = lib_dir_args;
2574 }
2575 lib_dir_args = undefined; // From here we use lib_dirs instead.
2576
2577 const self_exe_path: ?[]const u8 = if (!process.can_spawn)
2578 null
2579 else
2580 introspect.findZigExePath(arena) catch |err| {
2581 fatal("unable to find zig self exe path: {s}", .{@errorName(err)});
2582 };
2583
2584 var zig_lib_directory: Compilation.Directory = d: {
2585 if (override_lib_dir) |unresolved_lib_dir| {
2586 const lib_dir = try introspect.resolvePath(arena, unresolved_lib_dir);
2587 break :d .{
2588 .path = lib_dir,
2589 .handle = fs.cwd().openDir(lib_dir, .{}) catch |err| {
2590 fatal("unable to open zig lib directory '{s}': {s}", .{ lib_dir, @errorName(err) });
2591 },
2592 };
2593 } else if (builtin.os.tag == .wasi) {
2594 break :d getWasiPreopen("/lib");
2595 } else if (self_exe_path) |p| {
2596 break :d introspect.findZigLibDirFromSelfExe(arena, p) catch |err| {
2597 fatal("unable to find zig installation directory: {s}", .{@errorName(err)});
2598 };
2599 } else {
2600 unreachable;
2601 }
2602 };
2603 defer zig_lib_directory.handle.close();
2604
2605 // First, remove libc, libc++, and compiler_rt libraries from the system libraries list.
2606 // We need to know whether the set of system libraries contains anything besides these
2607 // to decide whether to trigger native path detection logic.
2608 var external_system_libs: std.MultiArrayList(struct {
2609 name: []const u8,
2610 info: SystemLib,
2611 }) = .{};
2612
2613 var resolved_system_libs: std.MultiArrayList(struct {
2614 name: []const u8,
2615 lib: Compilation.SystemLib,
2616 }) = .{};
2617
2618 for (system_libs.keys(), system_libs.values()) |lib_name, info| {
2619 if (target_util.is_libc_lib_name(target_info.target, lib_name)) {
2620 link_libc = true;
2621 continue;
2622 }
2623 if (target_util.is_libcpp_lib_name(target_info.target, lib_name)) {
2624 link_libcpp = true;
2625 continue;
2626 }
2627 switch (target_util.classifyCompilerRtLibName(target_info.target, lib_name)) {
2628 .none => {},
2629 .only_libunwind, .both => {
2630 link_libunwind = true;
2631 continue;
2632 },
2633 .only_compiler_rt => {
2634 std.log.warn("ignoring superfluous library '{s}': this dependency is fulfilled instead by compiler-rt which zig unconditionally provides", .{lib_name});
2635 continue;
2636 },
2637 }
2638
2639 if (target_info.target.os.tag == .windows) {
2640 const exists = mingw.libExists(arena, target_info.target, zig_lib_directory, lib_name) catch |err| {
2641 fatal("failed to check zig installation for DLL import libs: {s}", .{
2642 @errorName(err),
2643 });
2644 };
2645 if (exists) {
2646 try resolved_system_libs.append(arena, .{
2647 .name = lib_name,
2648 .lib = .{
2649 .needed = true,
2650 .weak = false,
2651 .path = null,
2652 },
2653 });
2654 continue;
2655 }
2656 }
2657
2658 if (fs.path.isAbsolute(lib_name)) {
2659 fatal("cannot use absolute path as a system library: {s}", .{lib_name});
2660 }
2661
2662 if (target_info.target.os.tag == .wasi) {
2663 if (wasi_libc.getEmulatedLibCRTFile(lib_name)) |crt_file| {
2664 try wasi_emulated_libs.append(crt_file);
2665 continue;
2666 }
2667 }
2668
2669 try external_system_libs.append(arena, .{
2670 .name = lib_name,
2671 .info = info,
2672 });
2673 }
2674 // After this point, external_system_libs is used instead of system_libs.
2675
2676 // Trigger native system library path detection if necessary.
2677 if (sysroot == null and cross_target.isNativeOs() and cross_target.isNativeAbi() and
2678 (external_system_libs.len != 0 or want_native_include_dirs))
25802679 {
25812680 const paths = std.zig.system.NativePaths.detect(arena, target_info) catch |err| {
25822681 fatal("unable to detect native system paths: {s}", .{@errorName(err)});
......@@ -2585,83 +2684,181 @@ fn buildOutputType(
25852684 warn("{s}", .{warning});
25862685 }
25872686
2588 const has_sysroot = if (comptime builtin.target.isDarwin()) outer: {
2589 if (std.zig.system.darwin.isDarwinSDKInstalled(arena)) {
2590 const sdk = std.zig.system.darwin.getDarwinSDK(arena, target_info.target) orelse
2591 break :outer false;
2592 native_darwin_sdk = sdk;
2593 try clang_argv.ensureUnusedCapacity(2);
2594 clang_argv.appendAssumeCapacity("-isysroot");
2595 clang_argv.appendAssumeCapacity(sdk.path);
2596 break :outer true;
2597 } else break :outer false;
2598 } else false;
2599
26002687 try clang_argv.ensureUnusedCapacity(paths.include_dirs.items.len * 2);
2601 const isystem_flag = if (has_sysroot) "-iwithsysroot" else "-isystem";
26022688 for (paths.include_dirs.items) |include_dir| {
2603 clang_argv.appendAssumeCapacity(isystem_flag);
2689 clang_argv.appendAssumeCapacity("-isystem");
26042690 clang_argv.appendAssumeCapacity(include_dir);
26052691 }
26062692
2607 try clang_argv.ensureUnusedCapacity(paths.framework_dirs.items.len * 2);
2608 try framework_dirs.ensureUnusedCapacity(paths.framework_dirs.items.len);
2609 const iframework_flag = if (has_sysroot) "-iframeworkwithsysroot" else "-iframework";
2610 for (paths.framework_dirs.items) |framework_dir| {
2611 clang_argv.appendAssumeCapacity(iframework_flag);
2612 clang_argv.appendAssumeCapacity(framework_dir);
2613 framework_dirs.appendAssumeCapacity(framework_dir);
2614 }
2615
2616 for (paths.lib_dirs.items) |lib_dir| {
2617 try lib_dirs.append(lib_dir);
2618 }
2619 for (paths.rpaths.items) |rpath| {
2620 try rpath_list.append(rpath);
2621 }
2693 try framework_dirs.appendSlice(paths.framework_dirs.items);
2694 try lib_dirs.appendSlice(paths.lib_dirs.items);
2695 try rpath_list.appendSlice(paths.rpaths.items);
26222696 }
26232697
2698 // If any libs in this list are statically provided, we omit them from the
2699 // resolved list and populate the link_objects array instead.
26242700 {
2625 // Resolve static libraries into full paths.
2626 const sep = fs.path.sep_str;
2627
26282701 var test_path = std.ArrayList(u8).init(gpa);
26292702 defer test_path.deinit();
26302703
2631 for (static_libs.items) |static_lib| {
2632 for (lib_dirs.items) |lib_dir_path| {
2633 test_path.clearRetainingCapacity();
2634 try test_path.writer().print("{s}" ++ sep ++ "{s}{s}{s}", .{
2635 lib_dir_path,
2636 target_info.target.libPrefix(),
2637 static_lib,
2638 target_info.target.staticLibSuffix(),
2639 });
2640 fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
2641 error.FileNotFound => continue,
2642 else => |e| fatal("unable to search for static library '{s}': {s}", .{
2643 test_path.items, @errorName(e),
2644 }),
2645 };
2646 try link_objects.append(.{ .path = try arena.dupe(u8, test_path.items) });
2647 break;
2648 } else {
2649 var search_paths = std.ArrayList(u8).init(arena);
2650 for (lib_dirs.items) |lib_dir_path| {
2651 try search_paths.writer().print("\n {s}" ++ sep ++ "{s}{s}{s}", .{
2652 lib_dir_path,
2653 target_info.target.libPrefix(),
2654 static_lib,
2655 target_info.target.staticLibSuffix(),
2704 var checked_paths = std.ArrayList(u8).init(gpa);
2705 defer checked_paths.deinit();
2706
2707 var failed_libs = std.ArrayList(struct {
2708 name: []const u8,
2709 strategy: SystemLib.SearchStrategy,
2710 checked_paths: []const u8,
2711 preferred_mode: std.builtin.LinkMode,
2712 }).init(arena);
2713
2714 syslib: for (external_system_libs.items(.name), external_system_libs.items(.info)) |lib_name, info| {
2715 // Checked in the first pass above while looking for libc libraries.
2716 assert(!fs.path.isAbsolute(lib_name));
2717
2718 checked_paths.clearRetainingCapacity();
2719
2720 switch (info.search_strategy) {
2721 .mode_first, .no_fallback => {
2722 // check for preferred mode
2723 for (lib_dirs.items) |lib_dir_path| {
2724 if (try accessLibPath(
2725 &test_path,
2726 &checked_paths,
2727 lib_dir_path,
2728 lib_name,
2729 target_info.target,
2730 info.preferred_mode,
2731 )) {
2732 const path = try arena.dupe(u8, test_path.items);
2733 switch (info.preferred_mode) {
2734 .Static => try link_objects.append(.{ .path = path }),
2735 .Dynamic => try resolved_system_libs.append(arena, .{
2736 .name = lib_name,
2737 .lib = .{
2738 .needed = info.needed,
2739 .weak = info.weak,
2740 .path = path,
2741 },
2742 }),
2743 }
2744 continue :syslib;
2745 }
2746 }
2747 // check for fallback mode
2748 if (info.search_strategy == .no_fallback) {
2749 try failed_libs.append(.{
2750 .name = lib_name,
2751 .strategy = info.search_strategy,
2752 .checked_paths = try arena.dupe(u8, checked_paths.items),
2753 .preferred_mode = info.preferred_mode,
2754 });
2755 continue :syslib;
2756 }
2757 for (lib_dirs.items) |lib_dir_path| {
2758 if (try accessLibPath(
2759 &test_path,
2760 &checked_paths,
2761 lib_dir_path,
2762 lib_name,
2763 target_info.target,
2764 info.fallbackMode(),
2765 )) {
2766 const path = try arena.dupe(u8, test_path.items);
2767 switch (info.fallbackMode()) {
2768 .Static => try link_objects.append(.{ .path = path }),
2769 .Dynamic => try resolved_system_libs.append(arena, .{
2770 .name = lib_name,
2771 .lib = .{
2772 .needed = info.needed,
2773 .weak = info.weak,
2774 .path = path,
2775 },
2776 }),
2777 }
2778 continue :syslib;
2779 }
2780 }
2781 try failed_libs.append(.{
2782 .name = lib_name,
2783 .strategy = info.search_strategy,
2784 .checked_paths = try arena.dupe(u8, checked_paths.items),
2785 .preferred_mode = info.preferred_mode,
26562786 });
2657 }
2658 try search_paths.appendSlice("\n suggestion: use full paths to static libraries on the command line rather than using -l and -L arguments");
2659 fatal("static library '{s}' not found. search paths: {s}", .{
2660 static_lib, search_paths.items,
2787 continue :syslib;
2788 },
2789 .paths_first => {
2790 for (lib_dirs.items) |lib_dir_path| {
2791 // check for preferred mode
2792 if (try accessLibPath(
2793 &test_path,
2794 &checked_paths,
2795 lib_dir_path,
2796 lib_name,
2797 target_info.target,
2798 info.preferred_mode,
2799 )) {
2800 const path = try arena.dupe(u8, test_path.items);
2801 switch (info.preferred_mode) {
2802 .Static => try link_objects.append(.{ .path = path }),
2803 .Dynamic => try resolved_system_libs.append(arena, .{
2804 .name = lib_name,
2805 .lib = .{
2806 .needed = info.needed,
2807 .weak = info.weak,
2808 .path = path,
2809 },
2810 }),
2811 }
2812 continue :syslib;
2813 }
2814
2815 // check for fallback mode
2816 if (try accessLibPath(
2817 &test_path,
2818 &checked_paths,
2819 lib_dir_path,
2820 lib_name,
2821 target_info.target,
2822 info.fallbackMode(),
2823 )) {
2824 const path = try arena.dupe(u8, test_path.items);
2825 switch (info.fallbackMode()) {
2826 .Static => try link_objects.append(.{ .path = path }),
2827 .Dynamic => try resolved_system_libs.append(arena, .{
2828 .name = lib_name,
2829 .lib = .{
2830 .needed = info.needed,
2831 .weak = info.weak,
2832 .path = path,
2833 },
2834 }),
2835 }
2836 continue :syslib;
2837 }
2838 }
2839 try failed_libs.append(.{
2840 .name = lib_name,
2841 .strategy = info.search_strategy,
2842 .checked_paths = try arena.dupe(u8, checked_paths.items),
2843 .preferred_mode = info.preferred_mode,
2844 });
2845 continue :syslib;
2846 },
2847 }
2848 @compileError("unreachable");
2849 }
2850
2851 if (failed_libs.items.len > 0) {
2852 for (failed_libs.items) |f| {
2853 const searched_paths = if (f.checked_paths.len == 0) " none" else f.checked_paths;
2854 std.log.err("unable to find {s} system library '{s}' using strategy '{s}'. searched paths:{s}", .{
2855 @tagName(f.preferred_mode), f.name, @tagName(f.strategy), searched_paths,
26612856 });
26622857 }
2858 process.exit(1);
26632859 }
26642860 }
2861 // After this point, resolved_system_libs is used instead of external_system_libs.
26652862
26662863 const object_format = target_info.target.ofmt;
26672864
......@@ -2912,35 +3109,6 @@ fn buildOutputType(
29123109 }
29133110 }
29143111
2915 const self_exe_path: ?[]const u8 = if (!process.can_spawn)
2916 null
2917 else
2918 introspect.findZigExePath(arena) catch |err| {
2919 fatal("unable to find zig self exe path: {s}", .{@errorName(err)});
2920 };
2921
2922 var zig_lib_directory: Compilation.Directory = d: {
2923 if (override_lib_dir) |unresolved_lib_dir| {
2924 const lib_dir = try introspect.resolvePath(arena, unresolved_lib_dir);
2925 break :d .{
2926 .path = lib_dir,
2927 .handle = fs.cwd().openDir(lib_dir, .{}) catch |err| {
2928 fatal("unable to open zig lib directory '{s}': {s}", .{ lib_dir, @errorName(err) });
2929 },
2930 };
2931 } else if (builtin.os.tag == .wasi) {
2932 break :d getWasiPreopen("/lib");
2933 } else if (self_exe_path) |p| {
2934 break :d introspect.findZigLibDirFromSelfExe(arena, p) catch |err| {
2935 fatal("unable to find zig installation directory: {s}", .{@errorName(err)});
2936 };
2937 } else {
2938 unreachable;
2939 }
2940 };
2941
2942 defer zig_lib_directory.handle.close();
2943
29443112 var thread_pool: ThreadPool = undefined;
29453113 try thread_pool.init(.{ .allocator = gpa });
29463114 defer thread_pool.deinit();
......@@ -3086,8 +3254,8 @@ fn buildOutputType(
30863254 .link_objects = link_objects.items,
30873255 .framework_dirs = framework_dirs.items,
30883256 .frameworks = frameworks,
3089 .system_lib_names = system_libs.keys(),
3090 .system_lib_infos = system_libs.values(),
3257 .system_lib_names = resolved_system_libs.items(.name),
3258 .system_lib_infos = resolved_system_libs.items(.lib),
30913259 .wasi_emulated_libs = wasi_emulated_libs.items,
30923260 .link_libc = link_libc,
30933261 .link_libcpp = link_libcpp,
......@@ -3192,11 +3360,9 @@ fn buildOutputType(
31923360 .wasi_exec_model = wasi_exec_model,
31933361 .debug_compile_errors = debug_compile_errors,
31943362 .enable_link_snapshots = enable_link_snapshots,
3195 .native_darwin_sdk = native_darwin_sdk,
31963363 .install_name = install_name,
31973364 .entitlements = entitlements,
31983365 .pagezero_size = pagezero_size,
3199 .search_strategy = search_strategy,
32003366 .headerpad_size = headerpad_size,
32013367 .headerpad_max_install_names = headerpad_max_install_names,
32023368 .dead_strip_dylibs = dead_strip_dylibs,
......@@ -4069,10 +4235,12 @@ pub fn cmdLibC(gpa: Allocator, args: []const []const u8) !void {
40694235 if (!cross_target.isNative()) {
40704236 fatal("unable to detect libc for non-native target", .{});
40714237 }
4238 const target_info = try detectNativeTargetInfo(cross_target);
40724239
40734240 var libc = LibCInstallation.findNative(.{
40744241 .allocator = gpa,
40754242 .verbose = true,
4243 .target = target_info.target,
40764244 }) catch |err| {
40774245 fatal("unable to detect native libc: {s}", .{@errorName(err)});
40784246 };
......@@ -6068,3 +6236,83 @@ fn renderOptions(color: Color) std.zig.ErrorBundle.RenderOptions {
60686236 .include_reference_trace = ttyconf != .no_color,
60696237 };
60706238}
6239
6240fn accessLibPath(
6241 test_path: *std.ArrayList(u8),
6242 checked_paths: *std.ArrayList(u8),
6243 lib_dir_path: []const u8,
6244 lib_name: []const u8,
6245 target: std.Target,
6246 link_mode: std.builtin.LinkMode,
6247) !bool {
6248 const sep = fs.path.sep_str;
6249
6250 if (target.isDarwin() and link_mode == .Dynamic) tbd: {
6251 // Prefer .tbd over .dylib.
6252 test_path.clearRetainingCapacity();
6253 try test_path.writer().print("{s}" ++ sep ++ "lib{s}.tbd", .{ lib_dir_path, lib_name });
6254 try checked_paths.writer().print("\n {s}", .{test_path.items});
6255 fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
6256 error.FileNotFound => break :tbd,
6257 else => |e| fatal("unable to search for tbd library '{s}': {s}", .{
6258 test_path.items, @errorName(e),
6259 }),
6260 };
6261 return true;
6262 }
6263
6264 main_check: {
6265 test_path.clearRetainingCapacity();
6266 try test_path.writer().print("{s}" ++ sep ++ "{s}{s}{s}", .{
6267 lib_dir_path,
6268 target.libPrefix(),
6269 lib_name,
6270 switch (link_mode) {
6271 .Static => target.staticLibSuffix(),
6272 .Dynamic => target.dynamicLibSuffix(),
6273 },
6274 });
6275 try checked_paths.writer().print("\n {s}", .{test_path.items});
6276 fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
6277 error.FileNotFound => break :main_check,
6278 else => |e| fatal("unable to search for {s} library '{s}': {s}", .{
6279 @tagName(link_mode), test_path.items, @errorName(e),
6280 }),
6281 };
6282 return true;
6283 }
6284
6285 // In the case of Darwin, the main check will be .dylib, so here we
6286 // additionally check for .so files.
6287 if (target.isDarwin() and link_mode == .Dynamic) so: {
6288 test_path.clearRetainingCapacity();
6289 try test_path.writer().print("{s}" ++ sep ++ "lib{s}.so", .{ lib_dir_path, lib_name });
6290 try checked_paths.writer().print("\n {s}", .{test_path.items});
6291 fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
6292 error.FileNotFound => break :so,
6293 else => |e| fatal("unable to search for so library '{s}': {s}", .{
6294 test_path.items, @errorName(e),
6295 }),
6296 };
6297 return true;
6298 }
6299
6300 // In the case of MinGW, the main check will be .lib but we also need to
6301 // look for `libfoo.a`.
6302 if (target.isMinGW() and link_mode == .Static) mingw: {
6303 test_path.clearRetainingCapacity();
6304 try test_path.writer().print("{s}" ++ sep ++ "lib{s}.a", .{
6305 lib_dir_path, lib_name,
6306 });
6307 try checked_paths.writer().print("\n {s}", .{test_path.items});
6308 fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
6309 error.FileNotFound => break :mingw,
6310 else => |e| fatal("unable to search for static library '{s}': {s}", .{
6311 test_path.items, @errorName(e),
6312 }),
6313 };
6314 return true;
6315 }
6316
6317 return false;
6318}
src/mingw.zig+25-7
......@@ -283,7 +283,7 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {
283283 defer arena_allocator.deinit();
284284 const arena = arena_allocator.allocator();
285285
286 const def_file_path = findDef(comp, arena, lib_name) catch |err| switch (err) {
286 const def_file_path = findDef(arena, comp.getTarget(), comp.zig_lib_directory, lib_name) catch |err| switch (err) {
287287 error.FileNotFound => {
288288 log.debug("no {s}.def file available to make a DLL import {s}.lib", .{ lib_name, lib_name });
289289 // 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 {
431431 });
432432}
433433
434/// This function body is verbose but all it does is test 3 different paths and see if a .def file exists.
435fn findDef(comp: *Compilation, allocator: Allocator, lib_name: []const u8) ![]u8 {
436 const target = comp.getTarget();
434pub fn libExists(
435 allocator: Allocator,
436 target: std.Target,
437 zig_lib_directory: Cache.Directory,
438 lib_name: []const u8,
439) !bool {
440 const s = findDef(allocator, target, zig_lib_directory, lib_name) catch |err| switch (err) {
441 error.FileNotFound => return false,
442 else => |e| return e,
443 };
444 defer allocator.free(s);
445 return true;
446}
437447
448/// This function body is verbose but all it does is test 3 different paths and
449/// see if a .def file exists.
450fn findDef(
451 allocator: Allocator,
452 target: std.Target,
453 zig_lib_directory: Cache.Directory,
454 lib_name: []const u8,
455) ![]u8 {
438456 const lib_path = switch (target.cpu.arch) {
439457 .x86 => "lib32",
440458 .x86_64 => "lib64",
......@@ -451,7 +469,7 @@ fn findDef(comp: *Compilation, allocator: Allocator, lib_name: []const u8) ![]u8
451469 {
452470 // Try the archtecture-specific path first.
453471 const fmt_path = "libc" ++ s ++ "mingw" ++ s ++ "{s}" ++ s ++ "{s}.def";
454 if (comp.zig_lib_directory.path) |p| {
472 if (zig_lib_directory.path) |p| {
455473 try override_path.writer().print("{s}" ++ s ++ fmt_path, .{ p, lib_path, lib_name });
456474 } else {
457475 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
468486 // Try the generic version.
469487 override_path.shrinkRetainingCapacity(0);
470488 const fmt_path = "libc" ++ s ++ "mingw" ++ s ++ "lib-common" ++ s ++ "{s}.def";
471 if (comp.zig_lib_directory.path) |p| {
489 if (zig_lib_directory.path) |p| {
472490 try override_path.writer().print("{s}" ++ s ++ fmt_path, .{ p, lib_name });
473491 } else {
474492 try override_path.writer().print(fmt_path, .{lib_name});
......@@ -485,7 +503,7 @@ fn findDef(comp: *Compilation, allocator: Allocator, lib_name: []const u8) ![]u8
485503 // Try the generic version and preprocess it.
486504 override_path.shrinkRetainingCapacity(0);
487505 const fmt_path = "libc" ++ s ++ "mingw" ++ s ++ "lib-common" ++ s ++ "{s}.def.in";
488 if (comp.zig_lib_directory.path) |p| {
506 if (zig_lib_directory.path) |p| {
489507 try override_path.writer().print("{s}" ++ s ++ fmt_path, .{ p, lib_name });
490508 } else {
491509 try override_path.writer().print(fmt_path, .{lib_name});
src/target.zig+9
......@@ -366,6 +366,15 @@ pub fn is_libc_lib_name(target: std.Target, name: []const u8) bool {
366366 if (eqlIgnoreCase(ignore_case, name, "m"))
367367 return true;
368368
369 if (eqlIgnoreCase(ignore_case, name, "uuid"))
370 return true;
371 if (eqlIgnoreCase(ignore_case, name, "mingw32"))
372 return true;
373 if (eqlIgnoreCase(ignore_case, name, "msvcrt-os"))
374 return true;
375 if (eqlIgnoreCase(ignore_case, name, "mingwex"))
376 return true;
377
369378 return false;
370379 }
371380
test/link/macho/bugs/13056/build.zig+1-1
......@@ -16,7 +16,7 @@ pub fn build(b: *std.Build) void {
1616fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
1717 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
1818 const target_info = std.zig.system.NativeTargetInfo.detect(target) catch unreachable;
19 const sdk = std.zig.system.darwin.getDarwinSDK(b.allocator, target_info.target) orelse
19 const sdk = std.zig.system.darwin.getSdk(b.allocator, target_info.target) orelse
2020 @panic("macOS SDK is required to run the test");
2121
2222 const exe = b.addExecutable(.{
test/link/macho/search_strategy/build.zig+7-5
......@@ -17,8 +17,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize
1717
1818 {
1919 // -search_dylibs_first
20 const exe = createScenario(b, optimize, target, "search_dylibs_first");
21 exe.search_strategy = .dylibs_first;
20 const exe = createScenario(b, optimize, target, "search_dylibs_first", .mode_first);
2221
2322 const check = exe.checkObject();
2423 check.checkStart();
......@@ -34,8 +33,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize
3433
3534 {
3635 // -search_paths_first
37 const exe = createScenario(b, optimize, target, "search_paths_first");
38 exe.search_strategy = .paths_first;
36 const exe = createScenario(b, optimize, target, "search_paths_first", .paths_first);
3937
4038 const run = b.addRunArtifact(exe);
4139 run.skip_foreign_checks = true;
......@@ -49,6 +47,7 @@ fn createScenario(
4947 optimize: std.builtin.OptimizeMode,
5048 target: std.zig.CrossTarget,
5149 name: []const u8,
50 search_strategy: std.Build.Step.Compile.SystemLib.SearchStrategy,
5251) *std.Build.Step.Compile {
5352 const static = b.addStaticLibrary(.{
5453 .name = name,
......@@ -73,7 +72,10 @@ fn createScenario(
7372 .target = target,
7473 });
7574 exe.addCSourceFile(.{ .file = .{ .path = "main.c" }, .flags = &.{} });
76 exe.linkSystemLibraryName(name);
75 exe.linkSystemLibrary2(name, .{
76 .use_pkg_config = .no,
77 .search_strategy = search_strategy,
78 });
7779 exe.linkLibC();
7880 exe.addLibraryPath(static.getEmittedBinDirectory());
7981 exe.addLibraryPath(dylib.getEmittedBinDirectory());