Skip to content

Commit 83ad70a

Browse files
authored
Merge pull request #180 from kubkon/kubkon/macos-15
Enable macos-15 runner
2 parents 718ff23 + 70c9f20 commit 83ad70a

File tree

4 files changed

+128
-2
lines changed

4 files changed

+128
-2
lines changed

.github/workflows/main.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ jobs:
99
strategy:
1010
fail-fast: false
1111
matrix:
12-
os: [ macos-13, macos-14 ]
12+
os: [ macos-13, macos-14, macos-15 ]
1313

1414
steps:
1515
- uses: actions/checkout@v3

src/Dylib.zig

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1011,6 +1011,25 @@ pub const Id = struct {
10111011

10121012
return out;
10131013
}
1014+
1015+
pub fn eql(id: Id, other: Id) bool {
1016+
return mem.eql(u8, id.name, other.name) and
1017+
id.timestamp == other.timestamp and
1018+
id.current_version == other.current_version and
1019+
id.compatibility_version == other.compatibility_version;
1020+
}
1021+
1022+
/// Hashes the Id.
1023+
/// TODO: we currently do differentiate between dylibs installed at the *same* path but having different
1024+
/// versions. This might be wrong and we should dedup them. Then again, surely this should be an error, right?
1025+
pub fn hash(id: Id) u64 {
1026+
var hasher = std.hash.Wyhash.init(0);
1027+
hasher.update(id.name);
1028+
hasher.update(mem.asBytes(&id.timestamp));
1029+
hasher.update(mem.asBytes(&id.current_version));
1030+
hasher.update(mem.asBytes(&id.compatibility_version));
1031+
return hasher.final();
1032+
}
10141033
};
10151034

10161035
const Export = struct {

src/MachO.zig

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,7 @@ pub fn link(self: *MachO) !void {
249249
}
250250

251251
try self.parseInputFiles();
252+
try self.dedupDylibs(resolved_objects.items);
252253
try self.parseDependentDylibs(arena, lib_dirs.items, framework_dirs.items);
253254

254255
if (!self.options.relocatable) {
@@ -832,6 +833,58 @@ fn isHoisted(self: *MachO, install_name: []const u8) bool {
832833
return false;
833834
}
834835

836+
fn dedupDylibs(self: *MachO, resolved_objects: []const LinkObject) !void {
837+
const tracy = trace(@src());
838+
defer tracy.end();
839+
840+
var map = std.HashMap(Dylib.Id, void, struct {
841+
pub fn hash(ctx: @This(), id: Dylib.Id) u64 {
842+
_ = ctx;
843+
return id.hash();
844+
}
845+
846+
pub fn eql(ctx: @This(), id: Dylib.Id, other: Dylib.Id) bool {
847+
_ = ctx;
848+
return id.eql(other);
849+
}
850+
}, std.hash_map.default_max_load_percentage).init(self.allocator);
851+
defer map.deinit();
852+
try map.ensureTotalCapacity(@intCast(self.dylibs.items.len));
853+
854+
var marked_dylibs = std.ArrayList(bool).init(self.allocator);
855+
defer marked_dylibs.deinit();
856+
try marked_dylibs.ensureTotalCapacityPrecise(self.dylibs.items.len);
857+
marked_dylibs.resize(self.dylibs.items.len) catch unreachable;
858+
@memset(marked_dylibs.items, false);
859+
860+
for (self.dylibs.items, marked_dylibs.items) |index, *marker| {
861+
const dylib = self.getFile(index).dylib;
862+
const cmd_object = resolved_objects[@intFromEnum(index)];
863+
864+
const gop = map.getOrPutAssumeCapacity(dylib.id.?);
865+
866+
if (!gop.found_existing) continue;
867+
868+
if (cmd_object.tag == .lib) {
869+
self.warn("ignoring duplicate libraries: {}", .{cmd_object});
870+
}
871+
872+
marker.* = true;
873+
}
874+
875+
var i: usize = 0;
876+
while (i < self.dylibs.items.len) {
877+
const index = self.dylibs.items[i];
878+
const marker = marked_dylibs.items[i];
879+
if (marker) {
880+
_ = self.dylibs.orderedRemove(i);
881+
_ = marked_dylibs.orderedRemove(i);
882+
self.files.items(.data)[@intFromEnum(index)].dylib.deinit(self.allocator);
883+
self.files.set(@intFromEnum(index), .none);
884+
} else i += 1;
885+
}
886+
}
887+
835888
fn parseDependentDylibs(
836889
self: *MachO,
837890
arena: Allocator,

test/macho.zig

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ pub fn addTests(step: *Step, opts: Options) void {
77
step.dependOn(testBuildVersionIOS(b, opts));
88
step.dependOn(testDeadStrip(b, opts));
99
step.dependOn(testDeadStripDylibs(b, opts));
10+
step.dependOn(testDedupDylibs(b, opts));
1011
step.dependOn(testDylib(b, opts));
1112
step.dependOn(testDylibReexport(b, opts));
1213
step.dependOn(testDylibReexportDeep(b, opts));
@@ -384,6 +385,60 @@ fn testDeadStripDylibs(b: *Build, opts: Options) *Step {
384385
return test_step;
385386
}
386387

388+
fn testDedupDylibs(b: *Build, opts: Options) *Step {
389+
const test_step = b.step("test-macho-dedup-dylibs", "");
390+
391+
const obj = cc(b, "a.o", opts);
392+
obj.addArg("-c");
393+
obj.addCSource(
394+
\\char world[] = "world";
395+
\\char* hello() {
396+
\\ return "Hello";
397+
\\}
398+
);
399+
400+
const dylib = ld(b, "liba.dylib", opts);
401+
dylib.addFileSource(obj.getFile());
402+
dylib.addArgs(&.{
403+
"-dynamic",
404+
"-syslibroot",
405+
opts.macos_sdk,
406+
"-dylib",
407+
"-install_name",
408+
"@rpath/liba.dylib",
409+
"-lSystem",
410+
"-lSystem",
411+
"-lc",
412+
});
413+
414+
const check = dylib.check();
415+
check.checkInHeaders();
416+
// Check that we only have one copy of libSystem present
417+
check.checkContains("libSystem");
418+
check.checkNotPresent("libSystem");
419+
test_step.dependOn(&check.step);
420+
421+
const exe = cc(b, "main", opts);
422+
exe.addCSource(
423+
\\#include<stdio.h>
424+
\\char* hello();
425+
\\extern char world[];
426+
\\int main() {
427+
\\ printf("%s %s", hello(), world);
428+
\\ return 0;
429+
\\}
430+
);
431+
exe.addArg("-la");
432+
exe.addPrefixedDirectorySource("-L", dylib.getDir());
433+
exe.addPrefixedDirectorySource("-Wl,-rpath,", dylib.getDir());
434+
435+
const run = exe.run();
436+
run.expectStdOutEqual("Hello world");
437+
test_step.dependOn(run.step());
438+
439+
return test_step;
440+
}
441+
387442
fn testDylib(b: *Build, opts: Options) *Step {
388443
const test_step = b.step("test-macho-dylib", "");
389444

@@ -2801,7 +2856,6 @@ fn testSearchStrategy(b: *Build, opts: Options) *Step {
28012856
const obj = cc(b, "a.o", opts);
28022857
obj.addArg("-c");
28032858
obj.addCSource(
2804-
\\#include<stdio.h>
28052859
\\char world[] = "world";
28062860
\\char* hello() {
28072861
\\ return "Hello";

0 commit comments

Comments
 (0)