From 049ff45430f7d556d1c6947c0bc44f3df67a8b00 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 5 Aug 2021 10:33:42 +0200 Subject: [PATCH 1/7] macho: add basic support for building iOS binaries * ensure we correctly transfer `-iwithsysroot` and `-iframeworkwithsysroot` flags with values from `build.zig` and that they are correctly transferred forward to `zig cc` * try to look for `libSystem.tbd` in the provided syslibroot - one caveat that the user will have to specify library search paths too --- lib/std/build.zig | 12 +++++++++++- src/link/MachO.zig | 29 +++++++++++++++++------------ src/main.zig | 7 ++++++- 3 files changed, 34 insertions(+), 14 deletions(-) diff --git a/lib/std/build.zig b/lib/std/build.zig index 17cad016e8..efb305d4a3 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -2672,7 +2672,11 @@ pub const LibExeObjStep = struct { try zig_args.append(self.builder.pathFromRoot(include_path)); }, .raw_path_system => |include_path| { - try zig_args.append("-isystem"); + if (builder.sysroot != null) { + try zig_args.append("-iwithsysroot"); + } else { + try zig_args.append("-isystem"); + } try zig_args.append(self.builder.pathFromRoot(include_path)); }, .other_step => |other| if (other.emit_h) { @@ -2700,6 +2704,12 @@ pub const LibExeObjStep = struct { if (self.target.isDarwin()) { for (self.framework_dirs.items) |dir| { + if (builder.sysroot != null) { + try zig_args.append("-iframeworkwithsysroot"); + } else { + try zig_args.append("-iframework"); + } + try zig_args.append(dir); try zig_args.append("-F"); try zig_args.append(dir); } diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 5d9b0e6fe1..6b0710129f 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -794,15 +794,14 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void { } } - // If we're compiling native and we can find libSystem.B.{dylib, tbd}, - // we link against that instead of embedded libSystem.B.tbd file. - var native_libsystem_available = false; - if (self.base.options.is_native_os) blk: { + // If we were given the sysroot, try to look there first for libSystem.B.{dylib, tbd}. + var libsystem_available = false; + if (self.base.options.sysroot != null) blk: { // Try stub file first. If we hit it, then we're done as the stub file // re-exports every single symbol definition. if (try resolveLib(arena, lib_dirs.items, "System", ".tbd")) |full_path| { try libs.append(full_path); - native_libsystem_available = true; + libsystem_available = true; break :blk; } // If we didn't hit the stub file, try .dylib next. However, libSystem.dylib @@ -811,12 +810,12 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void { if (try resolveLib(arena, lib_dirs.items, "c", ".dylib")) |libc_path| { try libs.append(libsystem_path); try libs.append(libc_path); - native_libsystem_available = true; + libsystem_available = true; break :blk; } } } - if (!native_libsystem_available) { + if (!libsystem_available) { const full_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "darwin", "libSystem.B.tbd", }); @@ -841,7 +840,7 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void { break; } } else { - log.warn("framework not found for '-f{s}'", .{framework}); + log.warn("framework not found for '-framework {s}'", .{framework}); framework_not_found = true; } } @@ -901,10 +900,8 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void { try argv.append("-o"); try argv.append(full_out_path); - if (native_libsystem_available) { - try argv.append("-lSystem"); - try argv.append("-lc"); - } + try argv.append("-lSystem"); + try argv.append("-lc"); for (search_lib_names.items) |l_name| { try argv.append(try std.fmt.allocPrint(arena, "-l{s}", .{l_name})); @@ -914,6 +911,14 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void { try argv.append(try std.fmt.allocPrint(arena, "-L{s}", .{lib_dir})); } + for (self.base.options.frameworks) |framework| { + try argv.append(try std.fmt.allocPrint(arena, "-framework {s}", .{framework})); + } + + for (self.base.options.framework_dirs) |framework_dir| { + try argv.append(try std.fmt.allocPrint(arena, "-F{s}", .{framework_dir})); + } + Compilation.dump_argv(argv.items); } diff --git a/src/main.zig b/src/main.zig index 0987a46989..73c4560ddf 100644 --- a/src/main.zig +++ b/src/main.zig @@ -830,7 +830,10 @@ fn buildOutputType( } else if (mem.eql(u8, arg, "-D") or mem.eql(u8, arg, "-isystem") or mem.eql(u8, arg, "-I") or - mem.eql(u8, arg, "-dirafter")) + mem.eql(u8, arg, "-dirafter") or + mem.eql(u8, arg, "-iwithsysroot") or + mem.eql(u8, arg, "-iframework") or + mem.eql(u8, arg, "-iframeworkwithsysroot")) { if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); i += 1; @@ -873,6 +876,8 @@ fn buildOutputType( if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); i += 1; sysroot = args[i]; + try clang_argv.append("-isysroot"); + try clang_argv.append(args[i]); } else if (mem.eql(u8, arg, "--libc")) { if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); i += 1; From ace9b3de642ab89dd356d877ee6b46ce88640e1d Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 5 Aug 2021 11:56:32 +0200 Subject: [PATCH 2/7] macho: fix parsing target string when linking against tbds --- src/link/MachO.zig | 23 ++++++--------------- src/link/MachO/Archive.zig | 13 ++++++------ src/link/MachO/Dylib.zig | 42 ++++++++++++++++++++++---------------- src/link/MachO/Object.zig | 13 ++++++------ src/link/MachO/fat.zig | 10 ++++----- 5 files changed, 46 insertions(+), 55 deletions(-) diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 6b0710129f..81f0808dc7 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -995,7 +995,6 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void { } fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const u8) !void { - const arch = self.base.options.target.cpu.arch; for (files) |file_name| { const full_path = full_path: { var buffer: [fs.MAX_PATH_BYTES]u8 = undefined; @@ -1004,17 +1003,17 @@ fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const }; defer self.base.allocator.free(full_path); - if (try Object.createAndParseFromPath(self.base.allocator, arch, full_path)) |object| { + if (try Object.createAndParseFromPath(self.base.allocator, self.base.options.target, full_path)) |object| { try self.objects.append(self.base.allocator, object); continue; } - if (try Archive.createAndParseFromPath(self.base.allocator, arch, full_path)) |archive| { + if (try Archive.createAndParseFromPath(self.base.allocator, self.base.options.target, full_path)) |archive| { try self.archives.append(self.base.allocator, archive); continue; } - if (try Dylib.createAndParseFromPath(self.base.allocator, arch, full_path, .{ + if (try Dylib.createAndParseFromPath(self.base.allocator, self.base.options.target, full_path, .{ .syslibroot = syslibroot, })) |dylibs| { defer self.base.allocator.free(dylibs); @@ -1032,9 +1031,8 @@ fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const } fn parseLibs(self: *MachO, libs: []const []const u8, syslibroot: ?[]const u8) !void { - const arch = self.base.options.target.cpu.arch; for (libs) |lib| { - if (try Dylib.createAndParseFromPath(self.base.allocator, arch, lib, .{ + if (try Dylib.createAndParseFromPath(self.base.allocator, self.base.options.target, lib, .{ .syslibroot = syslibroot, })) |dylibs| { defer self.base.allocator.free(dylibs); @@ -1047,7 +1045,7 @@ fn parseLibs(self: *MachO, libs: []const []const u8, syslibroot: ?[]const u8) !v continue; } - if (try Archive.createAndParseFromPath(self.base.allocator, arch, lib)) |archive| { + if (try Archive.createAndParseFromPath(self.base.allocator, self.base.options.target, lib)) |archive| { try self.archives.append(self.base.allocator, archive); continue; } @@ -2236,11 +2234,7 @@ fn resolveSymbols(self: *MachO) !void { const object_id = @intCast(u16, self.objects.items.len); const object = try self.objects.addOne(self.base.allocator); - object.* = try archive.parseObject( - self.base.allocator, - self.base.options.target.cpu.arch, - offsets.items[0], - ); + object.* = try archive.parseObject(self.base.allocator, self.base.options.target, offsets.items[0]); try self.resolveSymbolsInObject(object_id); continue :loop; @@ -2885,11 +2879,6 @@ fn flushZld(self: *MachO) !void { if (self.base.options.target.cpu.arch == .aarch64) { try self.writeCodeSignature(); } - - // if (comptime std.Target.current.isDarwin() and std.Target.current.cpu.arch == .aarch64) { - // const out_path = self.output.?.path; - // try fs.cwd().copyFile(out_path, fs.cwd(), out_path, .{}); - // } } fn writeGotEntries(self: *MachO) !void { diff --git a/src/link/MachO/Archive.zig b/src/link/MachO/Archive.zig index 6959dbac89..3b01233e1f 100644 --- a/src/link/MachO/Archive.zig +++ b/src/link/MachO/Archive.zig @@ -9,7 +9,6 @@ const mem = std.mem; const fat = @import("fat.zig"); const Allocator = mem.Allocator; -const Arch = std.Target.Cpu.Arch; const Object = @import("Object.zig"); file: fs.File, @@ -104,7 +103,7 @@ pub fn deinit(self: *Archive, allocator: *Allocator) void { allocator.free(self.name); } -pub fn createAndParseFromPath(allocator: *Allocator, arch: Arch, path: []const u8) !?Archive { +pub fn createAndParseFromPath(allocator: *Allocator, target: std.Target, path: []const u8) !?Archive { const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) { error.FileNotFound => return null, else => |e| return e, @@ -119,7 +118,7 @@ pub fn createAndParseFromPath(allocator: *Allocator, arch: Arch, path: []const u .file = file, }; - archive.parse(allocator, arch) catch |err| switch (err) { + archive.parse(allocator, target) catch |err| switch (err) { error.EndOfStream, error.NotArchive => { archive.deinit(allocator); return null; @@ -130,9 +129,9 @@ pub fn createAndParseFromPath(allocator: *Allocator, arch: Arch, path: []const u return archive; } -pub fn parse(self: *Archive, allocator: *Allocator, arch: Arch) !void { +pub fn parse(self: *Archive, allocator: *Allocator, target: std.Target) !void { const reader = self.file.reader(); - self.library_offset = try fat.getLibraryOffset(reader, arch); + self.library_offset = try fat.getLibraryOffset(reader, target); try self.file.seekTo(self.library_offset); const magic = try reader.readBytesNoEof(SARMAG); @@ -215,7 +214,7 @@ fn parseTableOfContents(self: *Archive, allocator: *Allocator, reader: anytype) } } -pub fn parseObject(self: Archive, allocator: *Allocator, arch: Arch, offset: u32) !Object { +pub fn parseObject(self: Archive, allocator: *Allocator, target: std.Target, offset: u32) !Object { const reader = self.file.reader(); try reader.context.seekTo(offset + self.library_offset); @@ -244,7 +243,7 @@ pub fn parseObject(self: Archive, allocator: *Allocator, arch: Arch, offset: u32 .mtime = try self.header.?.date(), }; - try object.parse(allocator, arch); + try object.parse(allocator, target); try reader.context.seekTo(0); return object; diff --git a/src/link/MachO/Dylib.zig b/src/link/MachO/Dylib.zig index 71301ccbbf..580159a13c 100644 --- a/src/link/MachO/Dylib.zig +++ b/src/link/MachO/Dylib.zig @@ -12,7 +12,6 @@ const fat = @import("fat.zig"); const commands = @import("commands.zig"); const Allocator = mem.Allocator; -const Arch = std.Target.Cpu.Arch; const LibStub = @import("../tapi.zig").LibStub; const LoadCommand = commands.LoadCommand; const MachO = @import("../MachO.zig"); @@ -139,11 +138,12 @@ pub const Error = error{ pub const CreateOpts = struct { syslibroot: ?[]const u8 = null, id: ?Id = null, + target: ?std.Target = null, }; pub fn createAndParseFromPath( allocator: *Allocator, - arch: Arch, + target: std.Target, path: []const u8, opts: CreateOpts, ) Error!?[]Dylib { @@ -161,7 +161,7 @@ pub fn createAndParseFromPath( .file = file, }; - dylib.parse(allocator, arch) catch |err| switch (err) { + dylib.parse(allocator, target) catch |err| switch (err) { error.EndOfStream, error.NotDylib => { try file.seekTo(0); @@ -171,7 +171,7 @@ pub fn createAndParseFromPath( }; defer lib_stub.deinit(); - try dylib.parseFromStub(allocator, arch, lib_stub); + try dylib.parseFromStub(allocator, target, lib_stub); }, else => |e| return e, }; @@ -195,7 +195,7 @@ pub fn createAndParseFromPath( try dylibs.append(dylib); // TODO this should not be performed if the user specifies `-flat_namespace` flag. // See ld64 manpages. - try dylib.parseDependentLibs(allocator, arch, &dylibs, opts.syslibroot); + try dylib.parseDependentLibs(allocator, target, &dylibs, opts.syslibroot); return dylibs.toOwnedSlice(); } @@ -222,10 +222,10 @@ pub fn deinit(self: *Dylib, allocator: *Allocator) void { } } -pub fn parse(self: *Dylib, allocator: *Allocator, arch: Arch) !void { +pub fn parse(self: *Dylib, allocator: *Allocator, target: std.Target) !void { log.debug("parsing shared library '{s}'", .{self.name}); - self.library_offset = try fat.getLibraryOffset(self.file.reader(), arch); + self.library_offset = try fat.getLibraryOffset(self.file.reader(), target); try self.file.seekTo(self.library_offset); @@ -237,10 +237,10 @@ pub fn parse(self: *Dylib, allocator: *Allocator, arch: Arch) !void { return error.NotDylib; } - const this_arch: Arch = try fat.decodeArch(self.header.?.cputype, true); + const this_arch: std.Target.Cpu.Arch = try fat.decodeArch(self.header.?.cputype, true); - if (this_arch != arch) { - log.err("mismatched cpu architecture: expected {s}, found {s}", .{ arch, this_arch }); + if (this_arch != target.cpu.arch) { + log.err("mismatched cpu architecture: expected {s}, found {s}", .{ target.cpu.arch, this_arch }); return error.MismatchedCpuArchitecture; } @@ -334,7 +334,16 @@ fn addObjCClassSymbols(self: *Dylib, allocator: *Allocator, sym_name: []const u8 } } -pub fn parseFromStub(self: *Dylib, allocator: *Allocator, arch: Arch, lib_stub: LibStub) !void { +fn targetToAppleString(allocator: *Allocator, target: std.Target) ![]const u8 { + const arch = switch (target.cpu.arch) { + .aarch64 => "arm64", + .x86_64 => "x86_64", + else => unreachable, + }; + return std.fmt.allocPrint(allocator, "{s}-{s}", .{ arch, @tagName(target.os.tag) }); +} + +pub fn parseFromStub(self: *Dylib, allocator: *Allocator, target: std.Target, lib_stub: LibStub) !void { if (lib_stub.inner.len == 0) return error.EmptyStubFile; log.debug("parsing shared library from stub '{s}'", .{self.name}); @@ -350,11 +359,8 @@ pub fn parseFromStub(self: *Dylib, allocator: *Allocator, arch: Arch, lib_stub: } self.id = id; - const target_string: []const u8 = switch (arch) { - .aarch64 => "arm64-macos", - .x86_64 => "x86_64-macos", - else => unreachable, - }; + const target_string = try targetToAppleString(allocator, target); + defer allocator.free(target_string); var umbrella_libs = std.StringHashMap(void).init(allocator); defer umbrella_libs.deinit(); @@ -443,7 +449,7 @@ pub fn parseFromStub(self: *Dylib, allocator: *Allocator, arch: Arch, lib_stub: pub fn parseDependentLibs( self: *Dylib, allocator: *Allocator, - arch: Arch, + target: std.Target, out: *std.ArrayList(Dylib), syslibroot: ?[]const u8, ) !void { @@ -475,7 +481,7 @@ pub fn parseDependentLibs( const dylibs = (try createAndParseFromPath( allocator, - arch, + target, full_path, .{ .id = id, diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index 2e6a20ad4b..c6aa2fb631 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -15,7 +15,6 @@ const segmentName = commands.segmentName; const sectionName = commands.sectionName; const Allocator = mem.Allocator; -const Arch = std.Target.Cpu.Arch; const LoadCommand = commands.LoadCommand; const MachO = @import("../MachO.zig"); const TextBlock = @import("TextBlock.zig"); @@ -154,7 +153,7 @@ pub fn deinit(self: *Object, allocator: *Allocator) void { } } -pub fn createAndParseFromPath(allocator: *Allocator, arch: Arch, path: []const u8) !?Object { +pub fn createAndParseFromPath(allocator: *Allocator, target: std.Target, path: []const u8) !?Object { const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) { error.FileNotFound => return null, else => |e| return e, @@ -169,7 +168,7 @@ pub fn createAndParseFromPath(allocator: *Allocator, arch: Arch, path: []const u .file = file, }; - object.parse(allocator, arch) catch |err| switch (err) { + object.parse(allocator, target) catch |err| switch (err) { error.EndOfStream, error.NotObject => { object.deinit(allocator); return null; @@ -180,7 +179,7 @@ pub fn createAndParseFromPath(allocator: *Allocator, arch: Arch, path: []const u return object; } -pub fn parse(self: *Object, allocator: *Allocator, arch: Arch) !void { +pub fn parse(self: *Object, allocator: *Allocator, target: std.Target) !void { const reader = self.file.reader(); if (self.file_offset) |offset| { try reader.context.seekTo(offset); @@ -195,7 +194,7 @@ pub fn parse(self: *Object, allocator: *Allocator, arch: Arch) !void { return error.NotObject; } - const this_arch: Arch = switch (header.cputype) { + const this_arch: std.Target.Cpu.Arch = switch (header.cputype) { macho.CPU_TYPE_ARM64 => .aarch64, macho.CPU_TYPE_X86_64 => .x86_64, else => |value| { @@ -203,8 +202,8 @@ pub fn parse(self: *Object, allocator: *Allocator, arch: Arch) !void { return error.UnsupportedCpuArchitecture; }, }; - if (this_arch != arch) { - log.err("mismatched cpu architecture: expected {s}, found {s}", .{ arch, this_arch }); + if (this_arch != target.cpu.arch) { + log.err("mismatched cpu architecture: expected {s}, found {s}", .{ target.cpu.arch, this_arch }); return error.MismatchedCpuArchitecture; } diff --git a/src/link/MachO/fat.zig b/src/link/MachO/fat.zig index 61592c88a5..c911d89535 100644 --- a/src/link/MachO/fat.zig +++ b/src/link/MachO/fat.zig @@ -5,10 +5,8 @@ const macho = std.macho; const mem = std.mem; const native_endian = builtin.target.cpu.arch.endian(); -const Arch = std.Target.Cpu.Arch; - pub fn decodeArch(cputype: macho.cpu_type_t, comptime logError: bool) !std.Target.Cpu.Arch { - const arch: Arch = switch (cputype) { + const arch: std.Target.Cpu.Arch = switch (cputype) { macho.CPU_TYPE_ARM64 => .aarch64, macho.CPU_TYPE_X86_64 => .x86_64, else => { @@ -31,7 +29,7 @@ fn readFatStruct(reader: anytype, comptime T: type) !T { return res; } -pub fn getLibraryOffset(reader: anytype, arch: Arch) !u64 { +pub fn getLibraryOffset(reader: anytype, target: std.Target) !u64 { const fat_header = try readFatStruct(reader, macho.fat_header); if (fat_header.magic != macho.FAT_MAGIC) return 0; @@ -44,12 +42,12 @@ pub fn getLibraryOffset(reader: anytype, arch: Arch) !u64 { error.UnsupportedCpuArchitecture => continue, else => |e| return e, }; - if (lib_arch == arch) { + if (lib_arch == target.cpu.arch) { // We have found a matching architecture! return fat_arch.offset; } } else { - log.err("Could not find matching cpu architecture in fat library: expected {s}", .{arch}); + log.err("Could not find matching cpu architecture in fat library: expected {s}", .{target.cpu.arch}); return error.MismatchedCpuArchitecture; } } From 700768498488dbae1c737b484f330b86f116cb62 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 5 Aug 2021 16:57:59 +0200 Subject: [PATCH 3/7] macho: swap out VERSION_MIN for BUILD_VERSION this makes the app successfully run on the iOS simluator! --- lib/std/macho.zig | 15 ++++++ src/link/MachO.zig | 99 +++++++++++++++++++++++-------------- src/link/MachO/Dylib.zig | 3 ++ src/link/MachO/commands.zig | 8 +++ 4 files changed, 87 insertions(+), 38 deletions(-) diff --git a/lib/std/macho.zig b/lib/std/macho.zig index cb030e941e..14be755a70 100644 --- a/lib/std/macho.zig +++ b/lib/std/macho.zig @@ -116,6 +116,21 @@ pub const build_tool_version = extern struct { version: u32, }; +pub const PLATFORM_MACOS: u32 = 0x1; +pub const PLATFORM_IOS: u32 = 0x2; +pub const PLATFORM_TVOS: u32 = 0x3; +pub const PLATFORM_WATCHOS: u32 = 0x4; +pub const PLATFORM_BRIDGEOS: u32 = 0x5; +pub const PLATFORM_MACCATALYST: u32 = 0x6; +pub const PLATFORM_IOSSIMULATOR: u32 = 0x7; +pub const PLATFORM_TVOSSIMULATOR: u32 = 0x8; +pub const PLATFORM_WATCHOSSIMULATOR: u32 = 0x9; +pub const PLATFORM_DRIVERKIT: u32 = 0x10; + +pub const TOOL_CLANG: u32 = 0x1; +pub const TOOL_SWIFT: u32 = 0x2; +pub const TOOL_LD: u32 = 0x3; + /// The entry_point_command is a replacement for thread_command. /// It is used for main executables to specify the location (file offset) /// of main(). If -stack_size was used at link time, the stacksize diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 81f0808dc7..886c3a33e2 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -82,8 +82,8 @@ data_in_code_cmd_index: ?u16 = null, function_starts_cmd_index: ?u16 = null, main_cmd_index: ?u16 = null, dylib_id_cmd_index: ?u16 = null, -version_min_cmd_index: ?u16 = null, source_version_cmd_index: ?u16 = null, +build_version_cmd_index: ?u16 = null, uuid_cmd_index: ?u16 = null, code_signature_cmd_index: ?u16 = null, /// Path to libSystem @@ -2716,27 +2716,6 @@ fn populateMetadata(self: *MachO) !void { try self.load_commands.append(self.base.allocator, .{ .Dylib = dylib_cmd }); } - if (self.version_min_cmd_index == null) { - self.version_min_cmd_index = @intCast(u16, self.load_commands.items.len); - const cmd: u32 = switch (self.base.options.target.os.tag) { - .macos => macho.LC_VERSION_MIN_MACOSX, - .ios => macho.LC_VERSION_MIN_IPHONEOS, - .tvos => macho.LC_VERSION_MIN_TVOS, - .watchos => macho.LC_VERSION_MIN_WATCHOS, - else => unreachable, // wrong OS - }; - const ver = self.base.options.target.os.version_range.semver.min; - const version = ver.major << 16 | ver.minor << 8 | ver.patch; - try self.load_commands.append(self.base.allocator, .{ - .VersionMin = .{ - .cmd = cmd, - .cmdsize = @sizeOf(macho.version_min_command), - .version = version, - .sdk = version, - }, - }); - } - if (self.source_version_cmd_index == null) { self.source_version_cmd_index = @intCast(u16, self.load_commands.items.len); try self.load_commands.append(self.base.allocator, .{ @@ -2748,6 +2727,39 @@ fn populateMetadata(self: *MachO) !void { }); } + if (self.build_version_cmd_index == null) { + self.build_version_cmd_index = @intCast(u16, self.load_commands.items.len); + const cmdsize = @intCast(u32, mem.alignForwardGeneric( + u64, + @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version), + @sizeOf(u64), + )); + const ver = self.base.options.target.os.version_range.semver.min; + const version = ver.major << 16 | ver.minor << 8 | ver.patch; + var cmd = commands.emptyGenericCommandWithData(macho.build_version_command{ + .cmd = macho.LC_BUILD_VERSION, + .cmdsize = cmdsize, + .platform = switch (self.base.options.target.os.tag) { + .macos => macho.PLATFORM_MACOS, + .ios => macho.PLATFORM_IOSSIMULATOR, + .watchos => macho.PLATFORM_WATCHOS, + .tvos => macho.PLATFORM_TVOS, + else => unreachable, + }, + .minos = version, + .sdk = version, + .ntools = 1, + }); + const ld_ver = macho.build_tool_version{ + .tool = macho.TOOL_LD, + .version = 0x0, + }; + cmd.data = try self.base.allocator.alloc(u8, cmdsize - @sizeOf(macho.build_version_command)); + mem.set(u8, cmd.data, 0); + mem.copy(u8, cmd.data, mem.asBytes(&ld_ver)); + try self.load_commands.append(self.base.allocator, .{ .BuildVersion = cmd }); + } + if (self.uuid_cmd_index == null) { self.uuid_cmd_index = @intCast(u16, self.load_commands.items.len); var uuid_cmd: macho.uuid_command = .{ @@ -4334,26 +4346,37 @@ pub fn populateMissingMetadata(self: *MachO) !void { }); self.load_commands_dirty = true; } - if (self.version_min_cmd_index == null) { - self.version_min_cmd_index = @intCast(u16, self.load_commands.items.len); - const cmd: u32 = switch (self.base.options.target.os.tag) { - .macos => macho.LC_VERSION_MIN_MACOSX, - .ios => macho.LC_VERSION_MIN_IPHONEOS, - .tvos => macho.LC_VERSION_MIN_TVOS, - .watchos => macho.LC_VERSION_MIN_WATCHOS, - else => unreachable, // wrong OS - }; + if (self.build_version_cmd_index == null) { + self.build_version_cmd_index = @intCast(u16, self.load_commands.items.len); + const cmdsize = @intCast(u32, mem.alignForwardGeneric( + u64, + @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version), + @sizeOf(u64), + )); const ver = self.base.options.target.os.version_range.semver.min; const version = ver.major << 16 | ver.minor << 8 | ver.patch; - try self.load_commands.append(self.base.allocator, .{ - .VersionMin = .{ - .cmd = cmd, - .cmdsize = @sizeOf(macho.version_min_command), - .version = version, - .sdk = version, + var cmd = commands.emptyGenericCommandWithData(macho.build_version_command{ + .cmd = macho.LC_BUILD_VERSION, + .cmdsize = cmdsize, + .platform = switch (self.base.options.target.os.tag) { + .macos => macho.PLATFORM_MACOS, + .ios => macho.PLATFORM_IOSSIMULATOR, + .watchos => macho.PLATFORM_WATCHOS, + .tvos => macho.PLATFORM_TVOS, + else => unreachable, }, + .minos = version, + .sdk = version, + .ntools = 1, }); - self.load_commands_dirty = true; + const ld_ver = macho.build_tool_version{ + .tool = macho.TOOL_LD, + .version = 0x0, + }; + cmd.data = try self.base.allocator.alloc(u8, cmdsize - @sizeOf(macho.build_version_command)); + mem.set(u8, cmd.data, 0); + mem.copy(u8, cmd.data, mem.asBytes(&ld_ver)); + try self.load_commands.append(self.base.allocator, .{ .BuildVersion = cmd }); } if (self.source_version_cmd_index == null) { self.source_version_cmd_index = @intCast(u16, self.load_commands.items.len); diff --git a/src/link/MachO/Dylib.zig b/src/link/MachO/Dylib.zig index 580159a13c..3dcccf6710 100644 --- a/src/link/MachO/Dylib.zig +++ b/src/link/MachO/Dylib.zig @@ -340,6 +340,9 @@ fn targetToAppleString(allocator: *Allocator, target: std.Target) ![]const u8 { .x86_64 => "x86_64", else => unreachable, }; + if (target.os.tag == .ios) { + return std.fmt.allocPrint(allocator, "{s}-{s}-simulator", .{ arch, @tagName(target.os.tag) }); + } return std.fmt.allocPrint(allocator, "{s}-{s}", .{ arch, @tagName(target.os.tag) }); } diff --git a/src/link/MachO/commands.zig b/src/link/MachO/commands.zig index f7a2fd3eda..b4515da966 100644 --- a/src/link/MachO/commands.zig +++ b/src/link/MachO/commands.zig @@ -43,6 +43,7 @@ pub const LoadCommand = union(enum) { Main: macho.entry_point_command, VersionMin: macho.version_min_command, SourceVersion: macho.source_version_command, + BuildVersion: GenericCommandWithData(macho.build_version_command), Uuid: macho.uuid_command, LinkeditData: macho.linkedit_data_command, Rpath: GenericCommandWithData(macho.rpath_command), @@ -97,6 +98,9 @@ pub const LoadCommand = union(enum) { macho.LC_SOURCE_VERSION => LoadCommand{ .SourceVersion = try stream.reader().readStruct(macho.source_version_command), }, + macho.LC_BUILD_VERSION => LoadCommand{ + .BuildVersion = try GenericCommandWithData(macho.build_version_command).read(allocator, stream.reader()), + }, macho.LC_UUID => LoadCommand{ .Uuid = try stream.reader().readStruct(macho.uuid_command), }, @@ -129,6 +133,7 @@ pub const LoadCommand = union(enum) { .Dylinker => |x| x.write(writer), .Dylib => |x| x.write(writer), .Rpath => |x| x.write(writer), + .BuildVersion => |x| x.write(writer), .Unknown => |x| x.write(writer), }; } @@ -147,6 +152,7 @@ pub const LoadCommand = union(enum) { .Dylinker => |x| x.inner.cmd, .Dylib => |x| x.inner.cmd, .Rpath => |x| x.inner.cmd, + .BuildVersion => |x| x.inner.cmd, .Unknown => |x| x.inner.cmd, }; } @@ -165,6 +171,7 @@ pub const LoadCommand = union(enum) { .Dylinker => |x| x.inner.cmdsize, .Dylib => |x| x.inner.cmdsize, .Rpath => |x| x.inner.cmdsize, + .BuildVersion => |x| x.inner.cmdsize, .Unknown => |x| x.inner.cmdsize, }; } @@ -193,6 +200,7 @@ pub const LoadCommand = union(enum) { .Main => |x| meta.eql(x, other.Main), .VersionMin => |x| meta.eql(x, other.VersionMin), .SourceVersion => |x| meta.eql(x, other.SourceVersion), + .BuildVersion => |x| x.eql(other.BuildVersion), .Uuid => |x| meta.eql(x, other.Uuid), .LinkeditData => |x| meta.eql(x, other.LinkeditData), .Segment => |x| x.eql(other.Segment), From 2371a63bd46cb1af7e3b9857136ea7677f6abdcc Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 6 Aug 2021 09:12:43 +0200 Subject: [PATCH 4/7] macho: allow .simulator ABI when targeting Apple simulator env For example, in order to run a binary on an iPhone Simulator, you need to specify that explicitly as part of the target as `aarch64-ios-simulator` rather than `aarch64-ios-gnu` or `aarch64-ios` for short. --- src/link/MachO.zig | 14 ++++++++------ src/link/MachO/Dylib.zig | 12 +++++++++--- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 886c3a33e2..d23b696cb8 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -2736,14 +2736,15 @@ fn populateMetadata(self: *MachO) !void { )); const ver = self.base.options.target.os.version_range.semver.min; const version = ver.major << 16 | ver.minor << 8 | ver.patch; + const is_simulator_abi = self.base.options.target.abi == .simulator; var cmd = commands.emptyGenericCommandWithData(macho.build_version_command{ .cmd = macho.LC_BUILD_VERSION, .cmdsize = cmdsize, .platform = switch (self.base.options.target.os.tag) { .macos => macho.PLATFORM_MACOS, - .ios => macho.PLATFORM_IOSSIMULATOR, - .watchos => macho.PLATFORM_WATCHOS, - .tvos => macho.PLATFORM_TVOS, + .ios => if (is_simulator_abi) macho.PLATFORM_IOSSIMULATOR else macho.PLATFORM_IOS, + .watchos => if (is_simulator_abi) macho.PLATFORM_WATCHOSSIMULATOR else macho.PLATFORM_WATCHOS, + .tvos => if (is_simulator_abi) macho.PLATFORM_TVOSSIMULATOR else macho.PLATFORM_TVOS, else => unreachable, }, .minos = version, @@ -4355,14 +4356,15 @@ pub fn populateMissingMetadata(self: *MachO) !void { )); const ver = self.base.options.target.os.version_range.semver.min; const version = ver.major << 16 | ver.minor << 8 | ver.patch; + const is_simulator_abi = self.base.options.target.abi == .simulator; var cmd = commands.emptyGenericCommandWithData(macho.build_version_command{ .cmd = macho.LC_BUILD_VERSION, .cmdsize = cmdsize, .platform = switch (self.base.options.target.os.tag) { .macos => macho.PLATFORM_MACOS, - .ios => macho.PLATFORM_IOSSIMULATOR, - .watchos => macho.PLATFORM_WATCHOS, - .tvos => macho.PLATFORM_TVOS, + .ios => if (is_simulator_abi) macho.PLATFORM_IOSSIMULATOR else macho.PLATFORM_IOS, + .watchos => if (is_simulator_abi) macho.PLATFORM_WATCHOSSIMULATOR else macho.PLATFORM_WATCHOS, + .tvos => if (is_simulator_abi) macho.PLATFORM_TVOSSIMULATOR else macho.PLATFORM_TVOS, else => unreachable, }, .minos = version, diff --git a/src/link/MachO/Dylib.zig b/src/link/MachO/Dylib.zig index 3dcccf6710..e425d0fc8e 100644 --- a/src/link/MachO/Dylib.zig +++ b/src/link/MachO/Dylib.zig @@ -340,10 +340,16 @@ fn targetToAppleString(allocator: *Allocator, target: std.Target) ![]const u8 { .x86_64 => "x86_64", else => unreachable, }; - if (target.os.tag == .ios) { - return std.fmt.allocPrint(allocator, "{s}-{s}-simulator", .{ arch, @tagName(target.os.tag) }); + const os = @tagName(target.os.tag); + const abi: ?[]const u8 = switch (target.abi) { + .gnu => null, + .simulator => "simulator", + else => unreachable, + }; + if (abi) |x| { + return std.fmt.allocPrint(allocator, "{s}-{s}-{s}", .{ arch, os, x }); } - return std.fmt.allocPrint(allocator, "{s}-{s}", .{ arch, @tagName(target.os.tag) }); + return std.fmt.allocPrint(allocator, "{s}-{s}", .{ arch, os }); } pub fn parseFromStub(self: *Dylib, allocator: *Allocator, target: std.Target, lib_stub: LibStub) !void { From e9bee08f8869bc97312f37a76cb00b98b8c6ee3d Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 6 Aug 2021 11:36:40 +0200 Subject: [PATCH 5/7] Try audodetecting sysroot when building Darwin on Darwin This is now no longer limited to targeting macOS natively but also tries to detect the sysroot when targeting different Apple platforms from macOS; for instance targeting iPhone Simulator from macOS. In this case, Zig will try detecting the SDK path by invoking `xcrun --sdk iphonesimulator --show-sdk-path`, and if the command fails because the SDK doesn't exist (case when having CLT installed only) or not having either Xcode or CLT installed, we simply return null signaling that the user has to provide the sysroot themselves. --- lib/std/zig/system.zig | 10 +++--- lib/std/zig/system/darwin.zig | 44 +++++++++++++++++++++++ lib/std/zig/system/{ => darwin}/macos.zig | 22 ------------ src/Compilation.zig | 3 +- src/main.zig | 18 ++++++---- 5 files changed, 60 insertions(+), 37 deletions(-) create mode 100644 lib/std/zig/system/darwin.zig rename lib/std/zig/system/{ => darwin}/macos.zig (94%) diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig index 4d671efe94..c5c62911cc 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -13,12 +13,10 @@ const assert = std.debug.assert; const process = std.process; const Target = std.Target; const CrossTarget = std.zig.CrossTarget; -const macos = @import("system/macos.zig"); const native_endian = std.Target.current.cpu.arch.endian(); const linux = @import("system/linux.zig"); pub const windows = @import("system/windows.zig"); - -pub const getSDKPath = macos.getSDKPath; +pub const darwin = @import("system/darwin.zig"); pub const NativePaths = struct { include_dirs: ArrayList([:0]u8), @@ -255,7 +253,7 @@ pub const NativeTargetInfo = struct { os.version_range.windows.min = detected_version; os.version_range.windows.max = detected_version; }, - .macos => try macos.detect(&os), + .macos => try darwin.macos.detect(&os), .freebsd, .netbsd, .dragonfly => { const key = switch (Target.current.os.tag) { .freebsd => "kern.osreldate", @@ -972,7 +970,7 @@ pub const NativeTargetInfo = struct { switch (std.Target.current.os.tag) { .linux => return linux.detectNativeCpuAndFeatures(), - .macos => return macos.detectNativeCpuAndFeatures(), + .macos => return darwin.macos.detectNativeCpuAndFeatures(), else => {}, } @@ -983,6 +981,6 @@ pub const NativeTargetInfo = struct { }; test { - _ = @import("system/macos.zig"); + _ = @import("system/darwin.zig"); _ = @import("system/linux.zig"); } diff --git a/lib/std/zig/system/darwin.zig b/lib/std/zig/system/darwin.zig new file mode 100644 index 0000000000..1e8d9e4b48 --- /dev/null +++ b/lib/std/zig/system/darwin.zig @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2015-2021 Zig Contributors +// This file is part of [zig](https://ziglang.org/), which is MIT licensed. +// The MIT license requires this copyright notice to be included in all copies +// and substantial portions of the software. +const std = @import("std"); +const mem = std.mem; +const Allocator = mem.Allocator; +const Target = std.Target; + +pub const macos = @import("darwin/macos.zig"); + +/// Detect SDK path on Darwin. +/// Calls `xcrun --sdk --show-sdk-path` which result can be used to specify +/// `--sysroot` of the compiler. +/// The caller needs to free the resulting path slice. +pub fn getSDKPath(allocator: *Allocator, target: Target) !?[]u8 { + const is_simulator_abi = target.abi == .simulator; + const sdk = switch (target.os.tag) { + .macos => "macosx", + .ios => if (is_simulator_abi) "iphonesimulator" else "iphoneos", + .watchos => if (is_simulator_abi) "watchsimulator" else "watchos", + .tvos => if (is_simulator_abi) "appletvsimulator" else "appletvos", + else => return null, + }; + + const argv = &[_][]const u8{ "xcrun", "--sdk", sdk, "--show-sdk-path" }; + const result = try std.ChildProcess.exec(.{ .allocator = allocator, .argv = argv }); + defer { + allocator.free(result.stderr); + allocator.free(result.stdout); + } + if (result.stderr.len != 0 or result.term.Exited != 0) { + // We don't actually care if there were errors as this is best-effort check anyhow + // and in the worst case the user can specify the sysroot manually. + return null; + } + const sysroot = try allocator.dupe(u8, mem.trimRight(u8, result.stdout, "\r\n")); + return sysroot; +} + +test "" { + _ = @import("darwin/macos.zig"); +} diff --git a/lib/std/zig/system/macos.zig b/lib/std/zig/system/darwin/macos.zig similarity index 94% rename from lib/std/zig/system/macos.zig rename to lib/std/zig/system/darwin/macos.zig index ae450ecae5..c8f48800c5 100644 --- a/lib/std/zig/system/macos.zig +++ b/lib/std/zig/system/darwin/macos.zig @@ -408,28 +408,6 @@ fn testVersionEquality(expected: std.builtin.Version, got: std.builtin.Version) try testing.expectEqualStrings(s_expected, s_got); } -/// Detect SDK path on Darwin. -/// Calls `xcrun --show-sdk-path` which result can be used to specify -/// `-syslibroot` param of the linker. -/// The caller needs to free the resulting path slice. -pub fn getSDKPath(allocator: *mem.Allocator) ![]u8 { - assert(Target.current.isDarwin()); - const argv = &[_][]const u8{ "xcrun", "--show-sdk-path" }; - const result = try std.ChildProcess.exec(.{ .allocator = allocator, .argv = argv }); - defer { - allocator.free(result.stderr); - allocator.free(result.stdout); - } - if (result.stderr.len != 0) { - std.log.err("unexpected 'xcrun --show-sdk-path' stderr: {s}", .{result.stderr}); - } - if (result.term.Exited != 0) { - return error.ProcessTerminated; - } - const syslibroot = mem.trimRight(u8, result.stdout, "\r\n"); - return mem.dupe(allocator, u8, syslibroot); -} - pub fn detectNativeCpuAndFeatures() ?Target.Cpu { var cpu_family: os.CPUFAMILY = undefined; var len: usize = @sizeOf(os.CPUFAMILY); diff --git a/src/Compilation.zig b/src/Compilation.zig index 24e2d9e67f..7987ed95df 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -944,8 +944,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { if (options.sysroot) |sysroot| { break :blk sysroot; } else if (darwin_can_use_system_sdk) { - const at_least_big_sur = options.target.os.getVersionRange().semver.min.major >= 11; - break :blk if (at_least_big_sur) try std.zig.system.getSDKPath(arena) else null; + break :blk try std.zig.system.darwin.getSDKPath(arena, options.target); } else { break :blk null; } diff --git a/src/main.zig b/src/main.zig index 73c4560ddf..285b6d2316 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1678,7 +1678,9 @@ fn buildOutputType( want_native_include_dirs = true; } - if (sysroot == null and cross_target.isNativeOs() and + const is_darwin_on_darwin = (comptime std.Target.current.isDarwin()) and cross_target.isDarwin(); + + if (sysroot == null and (cross_target.isNativeOs() or is_darwin_on_darwin) and (system_libs.items.len != 0 or want_native_include_dirs)) { const paths = std.zig.system.NativePaths.detect(arena, target_info) catch |err| { @@ -1689,16 +1691,18 @@ fn buildOutputType( } const has_sysroot = if (comptime std.Target.current.isDarwin()) outer: { - const min = target_info.target.os.getVersionRange().semver.min; - const at_least_mojave = min.major >= 11 or (min.major >= 10 and min.minor >= 14); - if (at_least_mojave) { - const sdk_path = try std.zig.system.getSDKPath(arena); + const should_get_sdk_path = if (cross_target.isNativeOs() and target_info.target.os.tag == .macos) inner: { + const min = target_info.target.os.getVersionRange().semver.min; + const at_least_mojave = min.major >= 11 or (min.major >= 10 and min.minor >= 14); + break :inner at_least_mojave; + } else true; + if (!should_get_sdk_path) break :outer false; + if (try std.zig.system.darwin.getSDKPath(arena, target_info.target)) |sdk_path| { try clang_argv.ensureCapacity(clang_argv.items.len + 2); clang_argv.appendAssumeCapacity("-isysroot"); clang_argv.appendAssumeCapacity(sdk_path); break :outer true; - } - break :outer false; + } else break :outer false; } else false; try clang_argv.ensureCapacity(clang_argv.items.len + paths.include_dirs.items.len * 2); From 9ab8d065b6ebc2db40f680d0de4a558297327ee3 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 6 Aug 2021 12:05:57 +0200 Subject: [PATCH 6/7] macho: deinit BuildVersion load command --- src/link/MachO/commands.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/src/link/MachO/commands.zig b/src/link/MachO/commands.zig index b4515da966..6e75af08c4 100644 --- a/src/link/MachO/commands.zig +++ b/src/link/MachO/commands.zig @@ -182,6 +182,7 @@ pub const LoadCommand = union(enum) { .Dylinker => |*x| x.deinit(allocator), .Dylib => |*x| x.deinit(allocator), .Rpath => |*x| x.deinit(allocator), + .BuildVersion => |*x| x.deinit(allocator), .Unknown => |*x| x.deinit(allocator), else => {}, }; From 509fe33d10e4e89a351678f4d466f30a7870ebcf Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 9 Aug 2021 00:05:37 +0200 Subject: [PATCH 7/7] macho: when targeting simulator, match host dylibs too otherwise, linking may fail as some libc functions are provided by the host when simulating a different OS such iPhoneOS. --- src/link/MachO/Dylib.zig | 50 ++++++++++++++++++++++++++++++++++------ 1 file changed, 43 insertions(+), 7 deletions(-) diff --git a/src/link/MachO/Dylib.zig b/src/link/MachO/Dylib.zig index e425d0fc8e..64be1fd2fa 100644 --- a/src/link/MachO/Dylib.zig +++ b/src/link/MachO/Dylib.zig @@ -352,6 +352,42 @@ fn targetToAppleString(allocator: *Allocator, target: std.Target) ![]const u8 { return std.fmt.allocPrint(allocator, "{s}-{s}", .{ arch, os }); } +const TargetMatcher = struct { + allocator: *Allocator, + target_strings: std.ArrayListUnmanaged([]const u8) = .{}, + + fn init(allocator: *Allocator, target: std.Target) !TargetMatcher { + var self = TargetMatcher{ .allocator = allocator }; + try self.target_strings.append(allocator, try targetToAppleString(allocator, target)); + + if (target.abi == .simulator) { + // For Apple simulator targets, linking gets tricky as we need to link against the simulator + // hosts dylibs too. + const host_target = try targetToAppleString(allocator, (std.zig.CrossTarget{ + .cpu_arch = target.cpu.arch, + .os_tag = .macos, + }).toTarget()); + try self.target_strings.append(allocator, host_target); + } + + return self; + } + + fn deinit(self: *TargetMatcher) void { + for (self.target_strings.items) |t| { + self.allocator.free(t); + } + self.target_strings.deinit(self.allocator); + } + + fn matches(self: TargetMatcher, targets: []const []const u8) bool { + for (self.target_strings.items) |t| { + if (hasTarget(targets, t)) return true; + } + return false; + } +}; + pub fn parseFromStub(self: *Dylib, allocator: *Allocator, target: std.Target, lib_stub: LibStub) !void { if (lib_stub.inner.len == 0) return error.EmptyStubFile; @@ -368,14 +404,14 @@ pub fn parseFromStub(self: *Dylib, allocator: *Allocator, target: std.Target, li } self.id = id; - const target_string = try targetToAppleString(allocator, target); - defer allocator.free(target_string); + var matcher = try TargetMatcher.init(allocator, target); + defer matcher.deinit(); var umbrella_libs = std.StringHashMap(void).init(allocator); defer umbrella_libs.deinit(); for (lib_stub.inner) |stub, stub_index| { - if (!hasTarget(stub.targets, target_string)) continue; + if (!matcher.matches(stub.targets)) continue; if (stub_index > 0) { // TODO I thought that we could switch on presence of `parent-umbrella` map; @@ -386,7 +422,7 @@ pub fn parseFromStub(self: *Dylib, allocator: *Allocator, target: std.Target, li if (stub.exports) |exports| { for (exports) |exp| { - if (!hasTarget(exp.targets, target_string)) continue; + if (!matcher.matches(exp.targets)) continue; if (exp.symbols) |symbols| { for (symbols) |sym_name| { @@ -405,7 +441,7 @@ pub fn parseFromStub(self: *Dylib, allocator: *Allocator, target: std.Target, li if (stub.reexports) |reexports| { for (reexports) |reexp| { - if (!hasTarget(reexp.targets, target_string)) continue; + if (!matcher.matches(reexp.targets)) continue; if (reexp.symbols) |symbols| { for (symbols) |sym_name| { @@ -433,11 +469,11 @@ pub fn parseFromStub(self: *Dylib, allocator: *Allocator, target: std.Target, li // TODO track which libs were already parsed in different steps for (lib_stub.inner) |stub| { - if (!hasTarget(stub.targets, target_string)) continue; + if (!matcher.matches(stub.targets)) continue; if (stub.reexported_libraries) |reexports| { for (reexports) |reexp| { - if (!hasTarget(reexp.targets, target_string)) continue; + if (!matcher.matches(reexp.targets)) continue; for (reexp.libraries) |lib| { if (umbrella_libs.contains(lib)) {