From 2d017f379f6dfa5e35944044eaf34347371b8d33 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 8 Feb 2023 11:13:36 +0100 Subject: [PATCH] link-tests: do not hardcode UUID when testing the build with/out DI --- test/link/macho/uuid/build.zig | 147 +++++++++++++++++++++++++++++---- 1 file changed, 132 insertions(+), 15 deletions(-) diff --git a/test/link/macho/uuid/build.zig b/test/link/macho/uuid/build.zig index 6a68263fbf..5a8c14ae37 100644 --- a/test/link/macho/uuid/build.zig +++ b/test/link/macho/uuid/build.zig @@ -1,4 +1,8 @@ const std = @import("std"); +const Builder = std.Build.Builder; +const CompileStep = std.Build.CompileStep; +const FileSource = std.Build.FileSource; +const Step = std.Build.Step; pub fn build(b: *std.Build) void { const test_step = b.step("test", "Test"); @@ -10,18 +14,18 @@ pub fn build(b: *std.Build) void { .os_tag = .macos, }; - testUuid(b, test_step, .ReleaseSafe, aarch64_macos, "675bb6ba8e5d3d3191f7936d7168f0e9"); - testUuid(b, test_step, .ReleaseFast, aarch64_macos, "675bb6ba8e5d3d3191f7936d7168f0e9"); - testUuid(b, test_step, .ReleaseSmall, aarch64_macos, "675bb6ba8e5d3d3191f7936d7168f0e9"); + testUuid(b, test_step, .ReleaseSafe, aarch64_macos); + testUuid(b, test_step, .ReleaseFast, aarch64_macos); + testUuid(b, test_step, .ReleaseSmall, aarch64_macos); const x86_64_macos = std.zig.CrossTarget{ .cpu_arch = .x86_64, .os_tag = .macos, }; - testUuid(b, test_step, .ReleaseSafe, x86_64_macos, "5b7071b4587c3071b0d2352fadce0e48"); - testUuid(b, test_step, .ReleaseFast, x86_64_macos, "5b7071b4587c3071b0d2352fadce0e48"); - testUuid(b, test_step, .ReleaseSmall, x86_64_macos, "4b58f2583c383169bbe3a716bd240048"); + testUuid(b, test_step, .ReleaseSafe, x86_64_macos); + testUuid(b, test_step, .ReleaseFast, x86_64_macos); + testUuid(b, test_step, .ReleaseSmall, x86_64_macos); } fn testUuid( @@ -29,25 +33,23 @@ fn testUuid( test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode, target: std.zig.CrossTarget, - comptime exp: []const u8, ) void { // The calculated UUID value is independent of debug info and so it should // stay the same across builds. { const dylib = simpleDylib(b, optimize, target); - const check_dylib = dylib.checkObject(.macho); - check_dylib.checkStart("cmd UUID"); - check_dylib.checkNext("uuid " ++ exp); - test_step.dependOn(&check_dylib.step); + const install_step = installWithRename(dylib, "test1.dylib"); + install_step.step.dependOn(&dylib.step); } { const dylib = simpleDylib(b, optimize, target); dylib.strip = true; - const check_dylib = dylib.checkObject(.macho); - check_dylib.checkStart("cmd UUID"); - check_dylib.checkNext("uuid " ++ exp); - test_step.dependOn(&check_dylib.step); + const install_step = installWithRename(dylib, "test2.dylib"); + install_step.step.dependOn(&dylib.step); } + + const cmp_step = CompareUuid.create(b, "test1.dylib", "test2.dylib"); + test_step.dependOn(&cmp_step.step); } fn simpleDylib( @@ -65,3 +67,118 @@ fn simpleDylib( dylib.linkLibC(); return dylib; } + +fn installWithRename(cs: *CompileStep, name: []const u8) *InstallWithRename { + const step = InstallWithRename.create(cs.builder, cs.getOutputSource(), name); + cs.builder.getInstallStep().dependOn(&step.step); + return step; +} + +const InstallWithRename = struct { + pub const base_id = .custom; + + step: Step, + builder: *Builder, + source: FileSource, + name: []const u8, + + pub fn create( + builder: *Builder, + source: FileSource, + name: []const u8, + ) *InstallWithRename { + const self = builder.allocator.create(InstallWithRename) catch @panic("OOM"); + self.* = InstallWithRename{ + .builder = builder, + .step = Step.init(.custom, builder.fmt("install and rename: {s} -> {s}", .{ + source.getDisplayName(), + name, + }), builder.allocator, make), + .source = source, + .name = builder.dupe(name), + }; + return self; + } + + fn make(step: *Step) anyerror!void { + const self = @fieldParentPtr(InstallWithRename, "step", step); + const source_path = self.source.getPath(self.builder); + const target_path = self.builder.getInstallPath(.lib, self.name); + self.builder.updateFile(source_path, target_path) catch |err| { + std.log.err("Unable to rename: {s} -> {s}", .{ source_path, target_path }); + return err; + }; + } +}; + +const CompareUuid = struct { + pub const base_id = .custom; + + step: Step, + builder: *Builder, + lhs: []const u8, + rhs: []const u8, + + pub fn create(builder: *Builder, lhs: []const u8, rhs: []const u8) *CompareUuid { + const self = builder.allocator.create(CompareUuid) catch @panic("OOM"); + self.* = CompareUuid{ + .builder = builder, + .step = Step.init( + .custom, + builder.fmt("compare uuid: {s} and {s}", .{ + lhs, + rhs, + }), + builder.allocator, + make, + ), + .lhs = lhs, + .rhs = rhs, + }; + return self; + } + + fn make(step: *Step) anyerror!void { + const self = @fieldParentPtr(CompareUuid, "step", step); + const gpa = self.builder.allocator; + + var lhs_uuid: [16]u8 = undefined; + const lhs_path = self.builder.getInstallPath(.lib, self.lhs); + try parseUuid(gpa, lhs_path, &lhs_uuid); + + var rhs_uuid: [16]u8 = undefined; + const rhs_path = self.builder.getInstallPath(.lib, self.rhs); + try parseUuid(gpa, rhs_path, &rhs_uuid); + + try std.testing.expectEqualStrings(&lhs_uuid, &rhs_uuid); + } + + fn parseUuid(gpa: std.mem.Allocator, path: []const u8, uuid: *[16]u8) anyerror!void { + const max_bytes: usize = 20 * 1024 * 1024; + const data = try std.fs.cwd().readFileAllocOptions( + gpa, + path, + max_bytes, + null, + @alignOf(u64), + null, + ); + var stream = std.io.fixedBufferStream(data); + const reader = stream.reader(); + + const hdr = try reader.readStruct(std.macho.mach_header_64); + if (hdr.magic != std.macho.MH_MAGIC_64) { + return error.InvalidMagicNumber; + } + + var it = std.macho.LoadCommandIterator{ + .ncmds = hdr.ncmds, + .buffer = data[@sizeOf(std.macho.mach_header_64)..][0..hdr.sizeofcmds], + }; + const cmd = while (it.next()) |cmd| switch (cmd.cmd()) { + .UUID => break cmd.cast(std.macho.uuid_command).?, + else => {}, + } else return error.UuidLoadCommandNotFound; + std.mem.copy(u8, uuid, &cmd.uuid); + } +};