Merge pull request #11950 from ziglang/macho-weak-libs-frameworks
macho: fully implement `-weak-lx` and `-weak_framework x` flags
This commit is contained in:
@@ -1483,7 +1483,7 @@ pub const LibExeObjStep = struct {
|
||||
lib_paths: ArrayList([]const u8),
|
||||
rpaths: ArrayList([]const u8),
|
||||
framework_dirs: ArrayList([]const u8),
|
||||
frameworks: StringHashMap(bool),
|
||||
frameworks: StringHashMap(FrameworkLinkInfo),
|
||||
verbose_link: bool,
|
||||
verbose_cc: bool,
|
||||
emit_analysis: EmitOption = .default,
|
||||
@@ -1643,6 +1643,7 @@ pub const LibExeObjStep = struct {
|
||||
pub const SystemLib = struct {
|
||||
name: []const u8,
|
||||
needed: bool,
|
||||
weak: bool,
|
||||
use_pkg_config: enum {
|
||||
/// Don't use pkg-config, just pass -lfoo where foo is name.
|
||||
no,
|
||||
@@ -1655,6 +1656,11 @@ pub const LibExeObjStep = struct {
|
||||
},
|
||||
};
|
||||
|
||||
const FrameworkLinkInfo = struct {
|
||||
needed: bool = false,
|
||||
weak: bool = false,
|
||||
};
|
||||
|
||||
pub const IncludeDir = union(enum) {
|
||||
raw_path: []const u8,
|
||||
raw_path_system: []const u8,
|
||||
@@ -1744,7 +1750,7 @@ pub const LibExeObjStep = struct {
|
||||
.kind = kind,
|
||||
.root_src = root_src,
|
||||
.name = name,
|
||||
.frameworks = StringHashMap(bool).init(builder.allocator),
|
||||
.frameworks = StringHashMap(FrameworkLinkInfo).init(builder.allocator),
|
||||
.step = Step.init(base_id, name, builder.allocator, make),
|
||||
.version = ver,
|
||||
.out_filename = undefined,
|
||||
@@ -1893,11 +1899,19 @@ pub const LibExeObjStep = struct {
|
||||
}
|
||||
|
||||
pub fn linkFramework(self: *LibExeObjStep, framework_name: []const u8) void {
|
||||
self.frameworks.put(self.builder.dupe(framework_name), false) catch unreachable;
|
||||
self.frameworks.put(self.builder.dupe(framework_name), .{}) catch unreachable;
|
||||
}
|
||||
|
||||
pub fn linkFrameworkNeeded(self: *LibExeObjStep, framework_name: []const u8) void {
|
||||
self.frameworks.put(self.builder.dupe(framework_name), true) catch unreachable;
|
||||
self.frameworks.put(self.builder.dupe(framework_name), .{
|
||||
.needed = true,
|
||||
}) catch unreachable;
|
||||
}
|
||||
|
||||
pub fn linkFrameworkWeak(self: *LibExeObjStep, framework_name: []const u8) void {
|
||||
self.frameworks.put(self.builder.dupe(framework_name), .{
|
||||
.weak = true,
|
||||
}) catch unreachable;
|
||||
}
|
||||
|
||||
/// Returns whether the library, executable, or object depends on a particular system library.
|
||||
@@ -1939,6 +1953,7 @@ pub const LibExeObjStep = struct {
|
||||
.system_lib = .{
|
||||
.name = "c",
|
||||
.needed = false,
|
||||
.weak = false,
|
||||
.use_pkg_config = .no,
|
||||
},
|
||||
}) catch unreachable;
|
||||
@@ -1952,6 +1967,7 @@ pub const LibExeObjStep = struct {
|
||||
.system_lib = .{
|
||||
.name = "c++",
|
||||
.needed = false,
|
||||
.weak = false,
|
||||
.use_pkg_config = .no,
|
||||
},
|
||||
}) catch unreachable;
|
||||
@@ -1977,6 +1993,7 @@ pub const LibExeObjStep = struct {
|
||||
.system_lib = .{
|
||||
.name = self.builder.dupe(name),
|
||||
.needed = false,
|
||||
.weak = false,
|
||||
.use_pkg_config = .no,
|
||||
},
|
||||
}) catch unreachable;
|
||||
@@ -1989,6 +2006,20 @@ pub const LibExeObjStep = struct {
|
||||
.system_lib = .{
|
||||
.name = self.builder.dupe(name),
|
||||
.needed = true,
|
||||
.weak = false,
|
||||
.use_pkg_config = .no,
|
||||
},
|
||||
}) catch unreachable;
|
||||
}
|
||||
|
||||
/// Darwin-only. This one has no integration with anything, it just puts -weak-lname on the
|
||||
/// command line. Prefer to use `linkSystemLibraryWeak` instead.
|
||||
pub fn linkSystemLibraryWeakName(self: *LibExeObjStep, name: []const u8) void {
|
||||
self.link_objects.append(.{
|
||||
.system_lib = .{
|
||||
.name = self.builder.dupe(name),
|
||||
.needed = false,
|
||||
.weak = true,
|
||||
.use_pkg_config = .no,
|
||||
},
|
||||
}) catch unreachable;
|
||||
@@ -2001,6 +2032,7 @@ pub const LibExeObjStep = struct {
|
||||
.system_lib = .{
|
||||
.name = self.builder.dupe(lib_name),
|
||||
.needed = false,
|
||||
.weak = false,
|
||||
.use_pkg_config = .force,
|
||||
},
|
||||
}) catch unreachable;
|
||||
@@ -2013,6 +2045,7 @@ pub const LibExeObjStep = struct {
|
||||
.system_lib = .{
|
||||
.name = self.builder.dupe(lib_name),
|
||||
.needed = true,
|
||||
.weak = false,
|
||||
.use_pkg_config = .force,
|
||||
},
|
||||
}) catch unreachable;
|
||||
@@ -2115,14 +2148,21 @@ pub const LibExeObjStep = struct {
|
||||
}
|
||||
|
||||
pub fn linkSystemLibrary(self: *LibExeObjStep, name: []const u8) void {
|
||||
self.linkSystemLibraryInner(name, false);
|
||||
self.linkSystemLibraryInner(name, .{});
|
||||
}
|
||||
|
||||
pub fn linkSystemLibraryNeeded(self: *LibExeObjStep, name: []const u8) void {
|
||||
self.linkSystemLibraryInner(name, true);
|
||||
self.linkSystemLibraryInner(name, .{ .needed = true });
|
||||
}
|
||||
|
||||
fn linkSystemLibraryInner(self: *LibExeObjStep, name: []const u8, needed: bool) void {
|
||||
pub fn linkSystemLibraryWeak(self: *LibExeObjStep, name: []const u8) void {
|
||||
self.linkSystemLibraryInner(name, .{ .weak = true });
|
||||
}
|
||||
|
||||
fn linkSystemLibraryInner(self: *LibExeObjStep, name: []const u8, opts: struct {
|
||||
needed: bool = false,
|
||||
weak: bool = false,
|
||||
}) void {
|
||||
if (isLibCLibrary(name)) {
|
||||
self.linkLibC();
|
||||
return;
|
||||
@@ -2135,7 +2175,8 @@ pub const LibExeObjStep = struct {
|
||||
self.link_objects.append(.{
|
||||
.system_lib = .{
|
||||
.name = self.builder.dupe(name),
|
||||
.needed = needed,
|
||||
.needed = opts.needed,
|
||||
.weak = opts.weak,
|
||||
.use_pkg_config = .yes,
|
||||
},
|
||||
}) catch unreachable;
|
||||
@@ -2513,7 +2554,14 @@ pub const LibExeObjStep = struct {
|
||||
},
|
||||
|
||||
.system_lib => |system_lib| {
|
||||
const prefix: []const u8 = if (system_lib.needed) "-needed-l" else "-l";
|
||||
const prefix: []const u8 = prefix: {
|
||||
if (system_lib.needed) break :prefix "-needed-l";
|
||||
if (system_lib.weak) {
|
||||
if (self.target.isDarwin()) break :prefix "-weak-l";
|
||||
warn("Weak library import used for a non-darwin target, this will be converted to normally library import `-lname`\n", .{});
|
||||
}
|
||||
break :prefix "-l";
|
||||
};
|
||||
switch (system_lib.use_pkg_config) {
|
||||
.no => try zig_args.append(builder.fmt("{s}{s}", .{ prefix, system_lib.name })),
|
||||
.yes, .force => {
|
||||
@@ -3018,9 +3066,11 @@ pub const LibExeObjStep = struct {
|
||||
var it = self.frameworks.iterator();
|
||||
while (it.next()) |entry| {
|
||||
const name = entry.key_ptr.*;
|
||||
const needed = entry.value_ptr.*;
|
||||
if (needed) {
|
||||
const info = entry.value_ptr.*;
|
||||
if (info.needed) {
|
||||
zig_args.append("-needed_framework") catch unreachable;
|
||||
} else if (info.weak) {
|
||||
zig_args.append("-weak_framework") catch unreachable;
|
||||
} else {
|
||||
zig_args.append("-framework") catch unreachable;
|
||||
}
|
||||
|
||||
@@ -65,6 +65,7 @@ const Action = struct {
|
||||
fn match(act: Action, haystack: []const u8, global_vars: anytype) !bool {
|
||||
assert(act.tag == .match);
|
||||
|
||||
var candidate_var: ?struct { name: []const u8, value: u64 } = null;
|
||||
var hay_it = mem.tokenize(u8, mem.trim(u8, haystack, " "), " ");
|
||||
var needle_it = mem.tokenize(u8, mem.trim(u8, act.phrase, " "), " ");
|
||||
|
||||
@@ -92,12 +93,19 @@ const Action = struct {
|
||||
const name = needle_tok[1..closing_brace];
|
||||
if (name.len == 0) return error.MissingBraceValue;
|
||||
const value = try std.fmt.parseInt(u64, hay_tok, 16);
|
||||
try global_vars.putNoClobber(name, value);
|
||||
candidate_var = .{
|
||||
.name = name,
|
||||
.value = value,
|
||||
};
|
||||
} else {
|
||||
if (!mem.eql(u8, hay_tok, needle_tok)) return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (candidate_var) |v| {
|
||||
try global_vars.putNoClobber(v.name, v.value);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -332,20 +340,43 @@ const MachODumper = struct {
|
||||
var output = std.ArrayList(u8).init(gpa);
|
||||
const writer = output.writer();
|
||||
|
||||
var symtab_cmd: ?macho.symtab_command = null;
|
||||
var load_commands = std.ArrayList(macho.LoadCommand).init(gpa);
|
||||
try load_commands.ensureTotalCapacity(hdr.ncmds);
|
||||
|
||||
var sections = std.ArrayList(struct { seg: u16, sect: u16 }).init(gpa);
|
||||
var imports = std.ArrayList(u16).init(gpa);
|
||||
|
||||
var symtab_cmd: ?u16 = null;
|
||||
var i: u16 = 0;
|
||||
while (i < hdr.ncmds) : (i += 1) {
|
||||
var cmd = try macho.LoadCommand.read(gpa, reader);
|
||||
load_commands.appendAssumeCapacity(cmd);
|
||||
|
||||
if (opts.dump_symtab and cmd.cmd() == .SYMTAB) {
|
||||
symtab_cmd = cmd.symtab;
|
||||
switch (cmd.cmd()) {
|
||||
.SEGMENT_64 => {
|
||||
const seg = cmd.segment;
|
||||
for (seg.sections.items) |_, j| {
|
||||
try sections.append(.{ .seg = i, .sect = @intCast(u16, j) });
|
||||
}
|
||||
},
|
||||
.SYMTAB => {
|
||||
symtab_cmd = i;
|
||||
},
|
||||
.LOAD_DYLIB,
|
||||
.LOAD_WEAK_DYLIB,
|
||||
.REEXPORT_DYLIB,
|
||||
=> {
|
||||
try imports.append(i);
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
|
||||
try dumpLoadCommand(cmd, i, writer);
|
||||
try writer.writeByte('\n');
|
||||
}
|
||||
|
||||
if (symtab_cmd) |cmd| {
|
||||
if (opts.dump_symtab) {
|
||||
const cmd = load_commands.items[symtab_cmd.?].symtab;
|
||||
try writer.writeAll(symtab_label ++ "\n");
|
||||
const strtab = bytes[cmd.stroff..][0..cmd.strsize];
|
||||
const raw_symtab = bytes[cmd.symoff..][0 .. cmd.nsyms * @sizeOf(macho.nlist_64)];
|
||||
@@ -354,7 +385,51 @@ const MachODumper = struct {
|
||||
for (symtab) |sym| {
|
||||
if (sym.stab()) continue;
|
||||
const sym_name = mem.sliceTo(@ptrCast([*:0]const u8, strtab.ptr + sym.n_strx), 0);
|
||||
try writer.print("{s} {x}\n", .{ sym_name, sym.n_value });
|
||||
if (sym.sect()) {
|
||||
const map = sections.items[sym.n_sect - 1];
|
||||
const seg = load_commands.items[map.seg].segment;
|
||||
const sect = seg.sections.items[map.sect];
|
||||
try writer.print("{x} ({s},{s})", .{
|
||||
sym.n_value,
|
||||
sect.segName(),
|
||||
sect.sectName(),
|
||||
});
|
||||
if (sym.ext()) {
|
||||
try writer.writeAll(" external");
|
||||
}
|
||||
try writer.print(" {s}\n", .{sym_name});
|
||||
} else if (sym.undf()) {
|
||||
const ordinal = @divTrunc(@bitCast(i16, sym.n_desc), macho.N_SYMBOL_RESOLVER);
|
||||
const import_name = blk: {
|
||||
if (ordinal <= 0) {
|
||||
if (ordinal == macho.BIND_SPECIAL_DYLIB_SELF)
|
||||
break :blk "self import";
|
||||
if (ordinal == macho.BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE)
|
||||
break :blk "main executable";
|
||||
if (ordinal == macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP)
|
||||
break :blk "flat lookup";
|
||||
unreachable;
|
||||
}
|
||||
const import_id = imports.items[@bitCast(u16, ordinal) - 1];
|
||||
const import = load_commands.items[import_id].dylib;
|
||||
const full_path = mem.sliceTo(import.data, 0);
|
||||
const basename = fs.path.basename(full_path);
|
||||
assert(basename.len > 0);
|
||||
const ext = mem.lastIndexOfScalar(u8, basename, '.') orelse basename.len;
|
||||
break :blk basename[0..ext];
|
||||
};
|
||||
try writer.writeAll("(undefined)");
|
||||
if (sym.weakRef()) {
|
||||
try writer.writeAll(" weak");
|
||||
}
|
||||
if (sym.ext()) {
|
||||
try writer.writeAll(" external");
|
||||
}
|
||||
try writer.print(" {s} (from {s})\n", .{
|
||||
sym_name,
|
||||
import_name,
|
||||
});
|
||||
} else unreachable;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -408,6 +483,8 @@ const MachODumper = struct {
|
||||
|
||||
.ID_DYLIB,
|
||||
.LOAD_DYLIB,
|
||||
.LOAD_WEAK_DYLIB,
|
||||
.REEXPORT_DYLIB,
|
||||
=> {
|
||||
const dylib = lc.dylib.inner.dylib;
|
||||
try writer.writeByte('\n');
|
||||
|
||||
@@ -2085,11 +2085,13 @@ pub fn GenericCommandWithData(comptime Cmd: type) type {
|
||||
|
||||
pub fn createLoadDylibCommand(
|
||||
allocator: Allocator,
|
||||
cmd_id: LC,
|
||||
name: []const u8,
|
||||
timestamp: u32,
|
||||
current_version: u32,
|
||||
compatibility_version: u32,
|
||||
) !GenericCommandWithData(dylib_command) {
|
||||
assert(cmd_id == .LOAD_DYLIB or cmd_id == .LOAD_WEAK_DYLIB or cmd_id == .REEXPORT_DYLIB or cmd_id == .ID_DYLIB);
|
||||
const cmdsize = @intCast(u32, mem.alignForwardGeneric(
|
||||
u64,
|
||||
@sizeOf(dylib_command) + name.len + 1, // +1 for nul
|
||||
@@ -2097,7 +2099,7 @@ pub fn createLoadDylibCommand(
|
||||
));
|
||||
|
||||
var dylib_cmd = emptyGenericCommandWithData(dylib_command{
|
||||
.cmd = .LOAD_DYLIB,
|
||||
.cmd = cmd_id,
|
||||
.cmdsize = cmdsize,
|
||||
.dylib = .{
|
||||
.name = @sizeOf(dylib_command),
|
||||
|
||||
@@ -21,6 +21,7 @@ const TypedValue = @import("TypedValue.zig");
|
||||
|
||||
pub const SystemLib = struct {
|
||||
needed: bool = false,
|
||||
weak: bool = false,
|
||||
};
|
||||
|
||||
pub const CacheMode = enum { incremental, whole };
|
||||
|
||||
@@ -52,6 +52,11 @@ pub const SearchStrategy = enum {
|
||||
dylibs_first,
|
||||
};
|
||||
|
||||
const SystemLib = struct {
|
||||
needed: bool = false,
|
||||
weak: bool = false,
|
||||
};
|
||||
|
||||
base: File,
|
||||
|
||||
/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.
|
||||
@@ -768,7 +773,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
|
||||
}
|
||||
|
||||
// Shared and static libraries passed via `-l` flag.
|
||||
var candidate_libs = std.StringArrayHashMap(Compilation.SystemLib).init(arena);
|
||||
var candidate_libs = std.StringArrayHashMap(SystemLib).init(arena);
|
||||
|
||||
const system_lib_names = self.base.options.system_libs.keys();
|
||||
for (system_lib_names) |system_lib_name| {
|
||||
@@ -781,7 +786,10 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
|
||||
}
|
||||
|
||||
const system_lib_info = self.base.options.system_libs.get(system_lib_name).?;
|
||||
try candidate_libs.put(system_lib_name, system_lib_info);
|
||||
try candidate_libs.put(system_lib_name, .{
|
||||
.needed = system_lib_info.needed,
|
||||
.weak = system_lib_info.weak,
|
||||
});
|
||||
}
|
||||
|
||||
var lib_dirs = std.ArrayList([]const u8).init(arena);
|
||||
@@ -793,7 +801,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
|
||||
}
|
||||
}
|
||||
|
||||
var libs = std.StringArrayHashMap(Compilation.SystemLib).init(arena);
|
||||
var libs = std.StringArrayHashMap(SystemLib).init(arena);
|
||||
|
||||
// Assume ld64 default -search_paths_first if no strategy specified.
|
||||
const search_strategy = self.base.options.search_strategy orelse .paths_first;
|
||||
@@ -890,7 +898,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
|
||||
for (framework_dirs.items) |dir| {
|
||||
for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| {
|
||||
if (try resolveFramework(arena, dir, f_name, ext)) |full_path| {
|
||||
try libs.put(full_path, self.base.options.frameworks.get(f_name).?);
|
||||
const info = self.base.options.frameworks.get(f_name).?;
|
||||
try libs.put(full_path, .{
|
||||
.needed = info.needed,
|
||||
.weak = info.weak,
|
||||
});
|
||||
continue :outer;
|
||||
}
|
||||
}
|
||||
@@ -1026,9 +1038,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
|
||||
try argv.append("-lc");
|
||||
|
||||
for (self.base.options.system_libs.keys()) |l_name| {
|
||||
const needed = self.base.options.system_libs.get(l_name).?.needed;
|
||||
const arg = if (needed)
|
||||
const info = self.base.options.system_libs.get(l_name).?;
|
||||
const arg = if (info.needed)
|
||||
try std.fmt.allocPrint(arena, "-needed-l{s}", .{l_name})
|
||||
else if (info.weak)
|
||||
try std.fmt.allocPrint(arena, "-weak-l{s}", .{l_name})
|
||||
else
|
||||
try std.fmt.allocPrint(arena, "-l{s}", .{l_name});
|
||||
try argv.append(arg);
|
||||
@@ -1039,9 +1053,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
|
||||
}
|
||||
|
||||
for (self.base.options.frameworks.keys()) |framework| {
|
||||
const needed = self.base.options.frameworks.get(framework).?.needed;
|
||||
const arg = if (needed)
|
||||
const info = self.base.options.frameworks.get(framework).?;
|
||||
const arg = if (info.needed)
|
||||
try std.fmt.allocPrint(arena, "-needed_framework {s}", .{framework})
|
||||
else if (info.weak)
|
||||
try std.fmt.allocPrint(arena, "-weak_framework {s}", .{framework})
|
||||
else
|
||||
try std.fmt.allocPrint(arena, "-framework {s}", .{framework});
|
||||
try argv.append(arg);
|
||||
@@ -1063,7 +1079,10 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
|
||||
Compilation.dump_argv(argv.items);
|
||||
}
|
||||
|
||||
var dependent_libs = std.fifo.LinearFifo(Dylib.Id, .Dynamic).init(self.base.allocator);
|
||||
var dependent_libs = std.fifo.LinearFifo(struct {
|
||||
id: Dylib.Id,
|
||||
parent: u16,
|
||||
}, .Dynamic).init(self.base.allocator);
|
||||
defer dependent_libs.deinit();
|
||||
try self.parseInputFiles(positionals.items, self.base.options.sysroot, &dependent_libs);
|
||||
try self.parseAndForceLoadStaticArchives(must_link_archives.keys());
|
||||
@@ -1389,13 +1408,18 @@ const ParseDylibError = error{
|
||||
|
||||
const DylibCreateOpts = struct {
|
||||
syslibroot: ?[]const u8,
|
||||
dependent_libs: *std.fifo.LinearFifo(Dylib.Id, .Dynamic),
|
||||
id: ?Dylib.Id = null,
|
||||
is_dependent: bool = false,
|
||||
is_needed: bool = false,
|
||||
dependent: bool = false,
|
||||
needed: bool = false,
|
||||
weak: bool = false,
|
||||
};
|
||||
|
||||
pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDylibError!bool {
|
||||
pub fn parseDylib(
|
||||
self: *MachO,
|
||||
path: []const u8,
|
||||
dependent_libs: anytype,
|
||||
opts: DylibCreateOpts,
|
||||
) ParseDylibError!bool {
|
||||
const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) {
|
||||
error.FileNotFound => return false,
|
||||
else => |e| return e,
|
||||
@@ -1405,12 +1429,19 @@ pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDy
|
||||
const name = try self.base.allocator.dupe(u8, path);
|
||||
errdefer self.base.allocator.free(name);
|
||||
|
||||
const dylib_id = @intCast(u16, self.dylibs.items.len);
|
||||
var dylib = Dylib{
|
||||
.name = name,
|
||||
.file = file,
|
||||
.weak = opts.weak,
|
||||
};
|
||||
|
||||
dylib.parse(self.base.allocator, self.base.options.target, opts.dependent_libs) catch |err| switch (err) {
|
||||
dylib.parse(
|
||||
self.base.allocator,
|
||||
self.base.options.target,
|
||||
dylib_id,
|
||||
dependent_libs,
|
||||
) catch |err| switch (err) {
|
||||
error.EndOfStream, error.NotDylib => {
|
||||
try file.seekTo(0);
|
||||
|
||||
@@ -1420,7 +1451,13 @@ pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDy
|
||||
};
|
||||
defer lib_stub.deinit();
|
||||
|
||||
try dylib.parseFromStub(self.base.allocator, self.base.options.target, lib_stub, opts.dependent_libs);
|
||||
try dylib.parseFromStub(
|
||||
self.base.allocator,
|
||||
self.base.options.target,
|
||||
lib_stub,
|
||||
dylib_id,
|
||||
dependent_libs,
|
||||
);
|
||||
},
|
||||
else => |e| return e,
|
||||
};
|
||||
@@ -1438,13 +1475,12 @@ pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDy
|
||||
}
|
||||
}
|
||||
|
||||
const dylib_id = @intCast(u16, self.dylibs.items.len);
|
||||
try self.dylibs.append(self.base.allocator, dylib);
|
||||
try self.dylibs_map.putNoClobber(self.base.allocator, dylib.id.?.name, dylib_id);
|
||||
|
||||
const should_link_dylib_even_if_unreachable = blk: {
|
||||
if (self.base.options.dead_strip_dylibs and !opts.is_needed) break :blk false;
|
||||
break :blk !(opts.is_dependent or self.referenced_dylibs.contains(dylib_id));
|
||||
if (self.base.options.dead_strip_dylibs and !opts.needed) break :blk false;
|
||||
break :blk !(opts.dependent or self.referenced_dylibs.contains(dylib_id));
|
||||
};
|
||||
|
||||
if (should_link_dylib_even_if_unreachable) {
|
||||
@@ -1467,9 +1503,8 @@ fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const
|
||||
|
||||
if (try self.parseObject(full_path)) continue;
|
||||
if (try self.parseArchive(full_path, false)) continue;
|
||||
if (try self.parseDylib(full_path, .{
|
||||
if (try self.parseDylib(full_path, dependent_libs, .{
|
||||
.syslibroot = syslibroot,
|
||||
.dependent_libs = dependent_libs,
|
||||
})) continue;
|
||||
|
||||
log.warn("unknown filetype for positional input file: '{s}'", .{file_name});
|
||||
@@ -1494,17 +1529,17 @@ fn parseAndForceLoadStaticArchives(self: *MachO, files: []const []const u8) !voi
|
||||
fn parseLibs(
|
||||
self: *MachO,
|
||||
lib_names: []const []const u8,
|
||||
lib_infos: []const Compilation.SystemLib,
|
||||
lib_infos: []const SystemLib,
|
||||
syslibroot: ?[]const u8,
|
||||
dependent_libs: anytype,
|
||||
) !void {
|
||||
for (lib_names) |lib, i| {
|
||||
const lib_info = lib_infos[i];
|
||||
log.debug("parsing lib path '{s}'", .{lib});
|
||||
if (try self.parseDylib(lib, .{
|
||||
if (try self.parseDylib(lib, dependent_libs, .{
|
||||
.syslibroot = syslibroot,
|
||||
.dependent_libs = dependent_libs,
|
||||
.is_needed = lib_info.needed,
|
||||
.needed = lib_info.needed,
|
||||
.weak = lib_info.weak,
|
||||
})) continue;
|
||||
if (try self.parseArchive(lib, false)) continue;
|
||||
|
||||
@@ -1522,20 +1557,21 @@ fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs: any
|
||||
const arena = arena_alloc.allocator();
|
||||
defer arena_alloc.deinit();
|
||||
|
||||
while (dependent_libs.readItem()) |*id| {
|
||||
defer id.deinit(self.base.allocator);
|
||||
while (dependent_libs.readItem()) |*dep_id| {
|
||||
defer dep_id.id.deinit(self.base.allocator);
|
||||
|
||||
if (self.dylibs_map.contains(id.name)) continue;
|
||||
if (self.dylibs_map.contains(dep_id.id.name)) continue;
|
||||
|
||||
const weak = self.dylibs.items[dep_id.parent].weak;
|
||||
const has_ext = blk: {
|
||||
const basename = fs.path.basename(id.name);
|
||||
const basename = fs.path.basename(dep_id.id.name);
|
||||
break :blk mem.lastIndexOfScalar(u8, basename, '.') != null;
|
||||
};
|
||||
const extension = if (has_ext) fs.path.extension(id.name) else "";
|
||||
const extension = if (has_ext) fs.path.extension(dep_id.id.name) else "";
|
||||
const without_ext = if (has_ext) blk: {
|
||||
const index = mem.lastIndexOfScalar(u8, id.name, '.') orelse unreachable;
|
||||
break :blk id.name[0..index];
|
||||
} else id.name;
|
||||
const index = mem.lastIndexOfScalar(u8, dep_id.id.name, '.') orelse unreachable;
|
||||
break :blk dep_id.id.name[0..index];
|
||||
} else dep_id.id.name;
|
||||
|
||||
for (&[_][]const u8{ extension, ".tbd" }) |ext| {
|
||||
const with_ext = try std.fmt.allocPrint(arena, "{s}{s}", .{ without_ext, ext });
|
||||
@@ -1543,15 +1579,15 @@ fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs: any
|
||||
|
||||
log.debug("trying dependency at fully resolved path {s}", .{full_path});
|
||||
|
||||
const did_parse_successfully = try self.parseDylib(full_path, .{
|
||||
.id = id.*,
|
||||
const did_parse_successfully = try self.parseDylib(full_path, dependent_libs, .{
|
||||
.id = dep_id.id,
|
||||
.syslibroot = syslibroot,
|
||||
.is_dependent = true,
|
||||
.dependent_libs = dependent_libs,
|
||||
.dependent = true,
|
||||
.weak = weak,
|
||||
});
|
||||
if (did_parse_successfully) break;
|
||||
} else {
|
||||
log.warn("unable to resolve dependency {s}", .{id.name});
|
||||
log.warn("unable to resolve dependency {s}", .{dep_id.id.name});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3081,6 +3117,10 @@ fn resolveSymbolsInDylibs(self: *MachO) !void {
|
||||
undef.n_type |= macho.N_EXT;
|
||||
undef.n_desc = @intCast(u16, ordinal + 1) * macho.N_SYMBOL_RESOLVER;
|
||||
|
||||
if (dylib.weak) {
|
||||
undef.n_desc |= macho.N_WEAK_REF;
|
||||
}
|
||||
|
||||
if (self.unresolved.fetchSwapRemove(resolv.where_index)) |entry| outer_blk: {
|
||||
switch (entry.value) {
|
||||
.none => {},
|
||||
@@ -3441,6 +3481,7 @@ fn addLoadDylibLC(self: *MachO, id: u16) !void {
|
||||
const dylib_id = dylib.id orelse unreachable;
|
||||
var dylib_cmd = try macho.createLoadDylibCommand(
|
||||
self.base.allocator,
|
||||
if (dylib.weak) .LOAD_WEAK_DYLIB else .LOAD_DYLIB,
|
||||
dylib_id.name,
|
||||
dylib_id.timestamp,
|
||||
dylib_id.current_version,
|
||||
@@ -4885,13 +4926,13 @@ fn populateMissingMetadata(self: *MachO) !void {
|
||||
std.builtin.Version{ .major = 1, .minor = 0, .patch = 0 };
|
||||
var dylib_cmd = try macho.createLoadDylibCommand(
|
||||
self.base.allocator,
|
||||
.ID_DYLIB,
|
||||
install_name,
|
||||
2,
|
||||
current_version.major << 16 | current_version.minor << 8 | current_version.patch,
|
||||
compat_version.major << 16 | compat_version.minor << 8 | compat_version.patch,
|
||||
);
|
||||
errdefer dylib_cmd.deinit(self.base.allocator);
|
||||
dylib_cmd.inner.cmd = .ID_DYLIB;
|
||||
try self.load_commands.append(self.base.allocator, .{ .dylib = dylib_cmd });
|
||||
self.load_commands_dirty = true;
|
||||
}
|
||||
@@ -5769,11 +5810,16 @@ fn writeDyldInfoData(self: *MachO) !void {
|
||||
},
|
||||
.undef => {
|
||||
const bind_sym = self.undefs.items[resolv.where_index];
|
||||
var flags: u4 = 0;
|
||||
if (bind_sym.weakRef()) {
|
||||
flags |= @truncate(u4, macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT);
|
||||
}
|
||||
try bind_pointers.append(.{
|
||||
.offset = binding.offset + base_offset,
|
||||
.segment_id = match.seg,
|
||||
.dylib_ordinal = @divExact(@bitCast(i16, bind_sym.n_desc), macho.N_SYMBOL_RESOLVER),
|
||||
.dylib_ordinal = @divTrunc(@bitCast(i16, bind_sym.n_desc), macho.N_SYMBOL_RESOLVER),
|
||||
.name = self.getString(bind_sym.n_strx),
|
||||
.bind_flags = flags,
|
||||
});
|
||||
},
|
||||
}
|
||||
@@ -5791,11 +5837,16 @@ fn writeDyldInfoData(self: *MachO) !void {
|
||||
},
|
||||
.undef => {
|
||||
const bind_sym = self.undefs.items[resolv.where_index];
|
||||
var flags: u4 = 0;
|
||||
if (bind_sym.weakRef()) {
|
||||
flags |= @truncate(u4, macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT);
|
||||
}
|
||||
try lazy_bind_pointers.append(.{
|
||||
.offset = binding.offset + base_offset,
|
||||
.segment_id = match.seg,
|
||||
.dylib_ordinal = @divExact(@bitCast(i16, bind_sym.n_desc), macho.N_SYMBOL_RESOLVER),
|
||||
.dylib_ordinal = @divTrunc(@bitCast(i16, bind_sym.n_desc), macho.N_SYMBOL_RESOLVER),
|
||||
.name = self.getString(bind_sym.n_strx),
|
||||
.bind_flags = flags,
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ dysymtab_cmd_index: ?u16 = null,
|
||||
id_cmd_index: ?u16 = null,
|
||||
|
||||
id: ?Id = null,
|
||||
weak: bool = false,
|
||||
|
||||
/// Parsed symbol table represented as hash map of symbols'
|
||||
/// names. We can and should defer creating *Symbols until
|
||||
@@ -141,7 +142,13 @@ pub fn deinit(self: *Dylib, allocator: Allocator) void {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse(self: *Dylib, allocator: Allocator, target: std.Target, dependent_libs: anytype) !void {
|
||||
pub fn parse(
|
||||
self: *Dylib,
|
||||
allocator: Allocator,
|
||||
target: std.Target,
|
||||
dylib_id: u16,
|
||||
dependent_libs: anytype,
|
||||
) !void {
|
||||
log.debug("parsing shared library '{s}'", .{self.name});
|
||||
|
||||
self.library_offset = try fat.getLibraryOffset(self.file.reader(), target);
|
||||
@@ -163,12 +170,18 @@ pub fn parse(self: *Dylib, allocator: Allocator, target: std.Target, dependent_l
|
||||
return error.MismatchedCpuArchitecture;
|
||||
}
|
||||
|
||||
try self.readLoadCommands(allocator, reader, dependent_libs);
|
||||
try self.readLoadCommands(allocator, reader, dylib_id, dependent_libs);
|
||||
try self.parseId(allocator);
|
||||
try self.parseSymbols(allocator);
|
||||
}
|
||||
|
||||
fn readLoadCommands(self: *Dylib, allocator: Allocator, reader: anytype, dependent_libs: anytype) !void {
|
||||
fn readLoadCommands(
|
||||
self: *Dylib,
|
||||
allocator: Allocator,
|
||||
reader: anytype,
|
||||
dylib_id: u16,
|
||||
dependent_libs: anytype,
|
||||
) !void {
|
||||
const should_lookup_reexports = self.header.?.flags & macho.MH_NO_REEXPORTED_DYLIBS == 0;
|
||||
|
||||
try self.load_commands.ensureUnusedCapacity(allocator, self.header.?.ncmds);
|
||||
@@ -190,7 +203,7 @@ fn readLoadCommands(self: *Dylib, allocator: Allocator, reader: anytype, depende
|
||||
if (should_lookup_reexports) {
|
||||
// Parse install_name to dependent dylib.
|
||||
var id = try Id.fromLoadCommand(allocator, cmd.dylib);
|
||||
try dependent_libs.writeItem(id);
|
||||
try dependent_libs.writeItem(.{ .id = id, .parent = dylib_id });
|
||||
}
|
||||
},
|
||||
else => {
|
||||
@@ -338,6 +351,7 @@ pub fn parseFromStub(
|
||||
allocator: Allocator,
|
||||
target: std.Target,
|
||||
lib_stub: LibStub,
|
||||
dylib_id: u16,
|
||||
dependent_libs: anytype,
|
||||
) !void {
|
||||
if (lib_stub.inner.len == 0) return error.EmptyStubFile;
|
||||
@@ -417,7 +431,7 @@ pub fn parseFromStub(
|
||||
log.debug(" (found re-export '{s}')", .{lib});
|
||||
|
||||
var dep_id = try Id.default(allocator, lib);
|
||||
try dependent_libs.writeItem(dep_id);
|
||||
try dependent_libs.writeItem(.{ .id = dep_id, .parent = dylib_id });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -522,7 +536,7 @@ pub fn parseFromStub(
|
||||
log.debug(" (found re-export '{s}')", .{lib});
|
||||
|
||||
var dep_id = try Id.default(allocator, lib);
|
||||
try dependent_libs.writeItem(dep_id);
|
||||
try dependent_libs.writeItem(.{ .id = dep_id, .parent = dylib_id });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ pub const Pointer = struct {
|
||||
segment_id: u16,
|
||||
dylib_ordinal: ?i64 = null,
|
||||
name: ?[]const u8 = null,
|
||||
bind_flags: u4 = 0,
|
||||
};
|
||||
|
||||
pub fn rebaseInfoSize(pointers: []const Pointer) !u64 {
|
||||
@@ -73,7 +74,7 @@ pub fn writeBindInfo(pointers: []const Pointer, writer: anytype) !void {
|
||||
}
|
||||
try writer.writeByte(macho.BIND_OPCODE_SET_TYPE_IMM | @truncate(u4, macho.BIND_TYPE_POINTER));
|
||||
|
||||
try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM); // TODO Sometimes we might want to add flags.
|
||||
try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM | pointer.bind_flags);
|
||||
try writer.writeAll(pointer.name.?);
|
||||
try writer.writeByte(0);
|
||||
|
||||
@@ -127,7 +128,7 @@ pub fn writeLazyBindInfo(pointers: []const Pointer, writer: anytype) !void {
|
||||
try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | @truncate(u4, @bitCast(u64, pointer.dylib_ordinal.?)));
|
||||
}
|
||||
|
||||
try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM); // TODO Sometimes we might want to add flags.
|
||||
try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM | pointer.bind_flags);
|
||||
try writer.writeAll(pointer.name.?);
|
||||
try writer.writeByte(0);
|
||||
|
||||
|
||||
41
src/main.zig
41
src/main.zig
@@ -443,9 +443,12 @@ const usage_build_generic =
|
||||
\\ --subsystem [subsystem] (Windows) /SUBSYSTEM:<subsystem> to the linker
|
||||
\\ --stack [size] Override default stack size
|
||||
\\ --image-base [addr] Set base address for executable image
|
||||
\\ -weak-l[lib] (Darwin) link against system library and mark it and all referenced symbols as weak
|
||||
\\ -weak_library [lib]
|
||||
\\ -framework [name] (Darwin) link against framework
|
||||
\\ -needed_framework [name] (Darwin) link against framework (even if unused)
|
||||
\\ -needed_library [lib] (Darwin) link against system library (even if unused)
|
||||
\\ -weak_framework [name] (Darwin) link against framework and mark it and all referenced symbols as weak
|
||||
\\ -F[dir] (Darwin) add search path for frameworks
|
||||
\\ -install_name=[value] (Darwin) add dylib's install name
|
||||
\\ --entitlements [path] (Darwin) add path to entitlements file for embedding in code signature
|
||||
@@ -916,7 +919,12 @@ fn buildOutputType(
|
||||
const path = args_iter.next() orelse {
|
||||
fatal("expected parameter after {s}", .{arg});
|
||||
};
|
||||
try frameworks.put(gpa, path, .{ .needed = false });
|
||||
try frameworks.put(gpa, path, .{});
|
||||
} else if (mem.eql(u8, arg, "-weak_framework")) {
|
||||
const path = args_iter.next() orelse {
|
||||
fatal("expected parameter after {s}", .{arg});
|
||||
};
|
||||
try frameworks.put(gpa, path, .{ .weak = true });
|
||||
} else if (mem.eql(u8, arg, "-needed_framework")) {
|
||||
const path = args_iter.next() orelse {
|
||||
fatal("expected parameter after {s}", .{arg});
|
||||
@@ -962,7 +970,7 @@ fn buildOutputType(
|
||||
};
|
||||
// We don't know whether this library is part of libc or libc++ until
|
||||
// we resolve the target, so we simply append to the list for now.
|
||||
try system_libs.put(next_arg, .{ .needed = false });
|
||||
try system_libs.put(next_arg, .{});
|
||||
} else if (mem.eql(u8, arg, "--needed-library") or
|
||||
mem.eql(u8, arg, "-needed-l") or
|
||||
mem.eql(u8, arg, "-needed_library"))
|
||||
@@ -971,6 +979,11 @@ fn buildOutputType(
|
||||
fatal("expected parameter after {s}", .{arg});
|
||||
};
|
||||
try system_libs.put(next_arg, .{ .needed = true });
|
||||
} else if (mem.eql(u8, arg, "-weak_library") or mem.eql(u8, arg, "-weak-l")) {
|
||||
const next_arg = args_iter.next() orelse {
|
||||
fatal("expected parameter after {s}", .{arg});
|
||||
};
|
||||
try system_libs.put(next_arg, .{ .weak = true });
|
||||
} else if (mem.eql(u8, arg, "-D") or
|
||||
mem.eql(u8, arg, "-isystem") or
|
||||
mem.eql(u8, arg, "-I") or
|
||||
@@ -1300,9 +1313,11 @@ fn buildOutputType(
|
||||
} else if (mem.startsWith(u8, arg, "-l")) {
|
||||
// We don't know whether this library is part of libc or libc++ until
|
||||
// we resolve the target, so we simply append to the list for now.
|
||||
try system_libs.put(arg["-l".len..], .{ .needed = false });
|
||||
try system_libs.put(arg["-l".len..], .{});
|
||||
} else if (mem.startsWith(u8, arg, "-needed-l")) {
|
||||
try system_libs.put(arg["-needed-l".len..], .{ .needed = true });
|
||||
} else if (mem.startsWith(u8, arg, "-weak-l")) {
|
||||
try system_libs.put(arg["-weak-l".len..], .{ .weak = true });
|
||||
} else if (mem.startsWith(u8, arg, "-D") or
|
||||
mem.startsWith(u8, arg, "-I"))
|
||||
{
|
||||
@@ -1596,7 +1611,7 @@ fn buildOutputType(
|
||||
try clang_argv.appendSlice(it.other_args);
|
||||
},
|
||||
.framework_dir => try framework_dirs.append(it.only_arg),
|
||||
.framework => try frameworks.put(gpa, it.only_arg, .{ .needed = false }),
|
||||
.framework => try frameworks.put(gpa, it.only_arg, .{}),
|
||||
.nostdlibinc => want_native_include_dirs = false,
|
||||
.strip => strip = true,
|
||||
.exec_model => {
|
||||
@@ -1879,12 +1894,18 @@ fn buildOutputType(
|
||||
) catch |err| {
|
||||
fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) });
|
||||
};
|
||||
} else if (mem.eql(u8, arg, "-framework") or mem.eql(u8, arg, "-weak_framework")) {
|
||||
} else if (mem.eql(u8, arg, "-framework")) {
|
||||
i += 1;
|
||||
if (i >= linker_args.items.len) {
|
||||
fatal("expected linker arg after '{s}'", .{arg});
|
||||
}
|
||||
try frameworks.put(gpa, linker_args.items[i], .{ .needed = false });
|
||||
try frameworks.put(gpa, linker_args.items[i], .{});
|
||||
} else if (mem.eql(u8, arg, "-weak_framework")) {
|
||||
i += 1;
|
||||
if (i >= linker_args.items.len) {
|
||||
fatal("expected linker arg after '{s}'", .{arg});
|
||||
}
|
||||
try frameworks.put(gpa, linker_args.items[i], .{ .weak = true });
|
||||
} else if (mem.eql(u8, arg, "-needed_framework")) {
|
||||
i += 1;
|
||||
if (i >= linker_args.items.len) {
|
||||
@@ -1897,6 +1918,14 @@ fn buildOutputType(
|
||||
fatal("expected linker arg after '{s}'", .{arg});
|
||||
}
|
||||
try system_libs.put(linker_args.items[i], .{ .needed = true });
|
||||
} else if (mem.startsWith(u8, arg, "-weak-l")) {
|
||||
try system_libs.put(arg["-weak-l".len..], .{ .weak = true });
|
||||
} else if (mem.eql(u8, arg, "-weak_library")) {
|
||||
i += 1;
|
||||
if (i >= linker_args.items.len) {
|
||||
fatal("expected linker arg after '{s}'", .{arg});
|
||||
}
|
||||
try system_libs.put(linker_args.items[i], .{ .weak = true });
|
||||
} else if (mem.eql(u8, arg, "-compatibility_version")) {
|
||||
i += 1;
|
||||
if (i >= linker_args.items.len) {
|
||||
|
||||
@@ -45,7 +45,11 @@ pub fn addCases(cases: *tests.StandaloneContext) void {
|
||||
.requires_macos_sdk = true,
|
||||
});
|
||||
|
||||
cases.addBuildFile("test/link/macho/needed_l/build.zig", .{
|
||||
cases.addBuildFile("test/link/macho/needed_library/build.zig", .{
|
||||
.build_modes = true,
|
||||
});
|
||||
|
||||
cases.addBuildFile("test/link/macho/weak_library/build.zig", .{
|
||||
.build_modes = true,
|
||||
});
|
||||
|
||||
@@ -54,6 +58,11 @@ pub fn addCases(cases: *tests.StandaloneContext) void {
|
||||
.requires_macos_sdk = true,
|
||||
});
|
||||
|
||||
cases.addBuildFile("test/link/macho/weak_framework/build.zig", .{
|
||||
.build_modes = true,
|
||||
.requires_macos_sdk = true,
|
||||
});
|
||||
|
||||
// Try to build and run an Objective-C executable.
|
||||
cases.addBuildFile("test/link/macho/objc/build.zig", .{
|
||||
.build_modes = true,
|
||||
|
||||
@@ -22,7 +22,7 @@ pub fn build(b: *Builder) void {
|
||||
check_exe.checkNext("entryoff {entryoff}");
|
||||
|
||||
check_exe.checkInSymtab();
|
||||
check_exe.checkNext("_non_main {n_value}");
|
||||
check_exe.checkNext("{n_value} (__TEXT,__text) external _non_main");
|
||||
|
||||
check_exe.checkComputeCompare("vmaddr entryoff +", .{ .op = .eq, .value = .{ .variable = "n_value" } });
|
||||
|
||||
|
||||
24
test/link/macho/weak_framework/build.zig
Normal file
24
test/link/macho/weak_framework/build.zig
Normal file
@@ -0,0 +1,24 @@
|
||||
const std = @import("std");
|
||||
const Builder = std.build.Builder;
|
||||
const LibExeObjectStep = std.build.LibExeObjStep;
|
||||
|
||||
pub fn build(b: *Builder) void {
|
||||
const mode = b.standardReleaseOptions();
|
||||
|
||||
const test_step = b.step("test", "Test the program");
|
||||
test_step.dependOn(b.getInstallStep());
|
||||
|
||||
const exe = b.addExecutable("test", null);
|
||||
exe.addCSourceFile("main.c", &[0][]const u8{});
|
||||
exe.setBuildMode(mode);
|
||||
exe.linkLibC();
|
||||
exe.linkFrameworkWeak("Cocoa");
|
||||
|
||||
const check = exe.checkObject(.macho);
|
||||
check.checkStart("cmd LOAD_WEAK_DYLIB");
|
||||
check.checkNext("name {*}Cocoa");
|
||||
test_step.dependOn(&check.step);
|
||||
|
||||
const run_cmd = exe.run();
|
||||
test_step.dependOn(&run_cmd.step);
|
||||
}
|
||||
3
test/link/macho/weak_framework/main.c
Normal file
3
test/link/macho/weak_framework/main.c
Normal file
@@ -0,0 +1,3 @@
|
||||
int main(int argc, char* argv[]) {
|
||||
return 0;
|
||||
}
|
||||
9
test/link/macho/weak_library/a.c
Normal file
9
test/link/macho/weak_library/a.c
Normal file
@@ -0,0 +1,9 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int a = 42;
|
||||
|
||||
const char* asStr() {
|
||||
static char str[3];
|
||||
sprintf(str, "%d", 42);
|
||||
return str;
|
||||
}
|
||||
38
test/link/macho/weak_library/build.zig
Normal file
38
test/link/macho/weak_library/build.zig
Normal file
@@ -0,0 +1,38 @@
|
||||
const std = @import("std");
|
||||
const Builder = std.build.Builder;
|
||||
const LibExeObjectStep = std.build.LibExeObjStep;
|
||||
|
||||
pub fn build(b: *Builder) void {
|
||||
const mode = b.standardReleaseOptions();
|
||||
|
||||
const test_step = b.step("test", "Test the program");
|
||||
test_step.dependOn(b.getInstallStep());
|
||||
|
||||
const dylib = b.addSharedLibrary("a", null, b.version(1, 0, 0));
|
||||
dylib.setBuildMode(mode);
|
||||
dylib.addCSourceFile("a.c", &.{});
|
||||
dylib.linkLibC();
|
||||
dylib.install();
|
||||
|
||||
const exe = b.addExecutable("test", null);
|
||||
exe.addCSourceFile("main.c", &[0][]const u8{});
|
||||
exe.setBuildMode(mode);
|
||||
exe.linkLibC();
|
||||
exe.linkSystemLibraryWeak("a");
|
||||
exe.addLibraryPath(b.pathFromRoot("zig-out/lib"));
|
||||
exe.addRPath(b.pathFromRoot("zig-out/lib"));
|
||||
|
||||
const check = exe.checkObject(.macho);
|
||||
check.checkStart("cmd LOAD_WEAK_DYLIB");
|
||||
check.checkNext("name @rpath/liba.dylib");
|
||||
|
||||
check.checkInSymtab();
|
||||
check.checkNext("(undefined) weak external _a (from liba)");
|
||||
check.checkNext("(undefined) weak external _asStr (from liba)");
|
||||
|
||||
test_step.dependOn(&check.step);
|
||||
|
||||
const run_cmd = exe.run();
|
||||
run_cmd.expectStdOutEqual("42 42");
|
||||
test_step.dependOn(&run_cmd.step);
|
||||
}
|
||||
9
test/link/macho/weak_library/main.c
Normal file
9
test/link/macho/weak_library/main.c
Normal file
@@ -0,0 +1,9 @@
|
||||
#include <stdio.h>
|
||||
|
||||
extern int a;
|
||||
extern const char* asStr();
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
printf("%d %s", a, asStr());
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user