tweaks to --build-id

* build.zig: the result of b.option() can be assigned directly in many
   cases thanks to the return type being an optional
 * std.Build: make the build system aware of the
   std.Build.Step.Compile.BuildId type when used as an option.
   - remove extraneous newlines in error logs
 * simplify caching logic
 * simplify hexstring parsing tests and use a doc test
 * simplify hashing logic. don't use an optional when the `none` tag
   already provides this meaning.
 * CLI: fix incorrect linker arg parsing
This commit is contained in:
Andrew Kelley 2023-05-16 20:00:47 -07:00
parent df5085bde0
commit 728ce2d7c1
9 changed files with 170 additions and 165 deletions

View file

@ -167,8 +167,11 @@ pub fn build(b: *std.Build) !void {
exe.sanitize_thread = sanitize_thread; exe.sanitize_thread = sanitize_thread;
exe.entitlements = entitlements; exe.entitlements = entitlements;
if (b.option([]const u8, "build-id", "Include a build id note")) |build_id| exe.build_id = b.option(
exe.build_id = try std.Build.CompileStep.BuildId.parse(b.allocator, build_id); std.Build.Step.Compile.BuildId,
"build-id",
"Request creation of '.note.gnu.build-id' section",
);
b.installArtifact(exe); b.installArtifact(exe);

View file

@ -181,6 +181,7 @@ const TypeId = enum {
@"enum", @"enum",
string, string,
list, list,
build_id,
}; };
const TopLevelStep = struct { const TopLevelStep = struct {
@ -832,13 +833,13 @@ pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_
} else if (mem.eql(u8, s, "false")) { } else if (mem.eql(u8, s, "false")) {
return false; return false;
} else { } else {
log.err("Expected -D{s} to be a boolean, but received '{s}'\n", .{ name, s }); log.err("Expected -D{s} to be a boolean, but received '{s}'", .{ name, s });
self.markInvalidUserInput(); self.markInvalidUserInput();
return null; return null;
} }
}, },
.list, .map => { .list, .map => {
log.err("Expected -D{s} to be a boolean, but received a {s}.\n", .{ log.err("Expected -D{s} to be a boolean, but received a {s}.", .{
name, @tagName(option_ptr.value), name, @tagName(option_ptr.value),
}); });
self.markInvalidUserInput(); self.markInvalidUserInput();
@ -847,7 +848,7 @@ pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_
}, },
.int => switch (option_ptr.value) { .int => switch (option_ptr.value) {
.flag, .list, .map => { .flag, .list, .map => {
log.err("Expected -D{s} to be an integer, but received a {s}.\n", .{ log.err("Expected -D{s} to be an integer, but received a {s}.", .{
name, @tagName(option_ptr.value), name, @tagName(option_ptr.value),
}); });
self.markInvalidUserInput(); self.markInvalidUserInput();
@ -856,12 +857,12 @@ pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_
.scalar => |s| { .scalar => |s| {
const n = std.fmt.parseInt(T, s, 10) catch |err| switch (err) { const n = std.fmt.parseInt(T, s, 10) catch |err| switch (err) {
error.Overflow => { error.Overflow => {
log.err("-D{s} value {s} cannot fit into type {s}.\n", .{ name, s, @typeName(T) }); log.err("-D{s} value {s} cannot fit into type {s}.", .{ name, s, @typeName(T) });
self.markInvalidUserInput(); self.markInvalidUserInput();
return null; return null;
}, },
else => { else => {
log.err("Expected -D{s} to be an integer of type {s}.\n", .{ name, @typeName(T) }); log.err("Expected -D{s} to be an integer of type {s}.", .{ name, @typeName(T) });
self.markInvalidUserInput(); self.markInvalidUserInput();
return null; return null;
}, },
@ -871,7 +872,7 @@ pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_
}, },
.float => switch (option_ptr.value) { .float => switch (option_ptr.value) {
.flag, .map, .list => { .flag, .map, .list => {
log.err("Expected -D{s} to be a float, but received a {s}.\n", .{ log.err("Expected -D{s} to be a float, but received a {s}.", .{
name, @tagName(option_ptr.value), name, @tagName(option_ptr.value),
}); });
self.markInvalidUserInput(); self.markInvalidUserInput();
@ -879,7 +880,7 @@ pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_
}, },
.scalar => |s| { .scalar => |s| {
const n = std.fmt.parseFloat(T, s) catch { const n = std.fmt.parseFloat(T, s) catch {
log.err("Expected -D{s} to be a float of type {s}.\n", .{ name, @typeName(T) }); log.err("Expected -D{s} to be a float of type {s}.", .{ name, @typeName(T) });
self.markInvalidUserInput(); self.markInvalidUserInput();
return null; return null;
}; };
@ -888,7 +889,7 @@ pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_
}, },
.@"enum" => switch (option_ptr.value) { .@"enum" => switch (option_ptr.value) {
.flag, .map, .list => { .flag, .map, .list => {
log.err("Expected -D{s} to be an enum, but received a {s}.\n", .{ log.err("Expected -D{s} to be an enum, but received a {s}.", .{
name, @tagName(option_ptr.value), name, @tagName(option_ptr.value),
}); });
self.markInvalidUserInput(); self.markInvalidUserInput();
@ -898,7 +899,7 @@ pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_
if (std.meta.stringToEnum(T, s)) |enum_lit| { if (std.meta.stringToEnum(T, s)) |enum_lit| {
return enum_lit; return enum_lit;
} else { } else {
log.err("Expected -D{s} to be of type {s}.\n", .{ name, @typeName(T) }); log.err("Expected -D{s} to be of type {s}.", .{ name, @typeName(T) });
self.markInvalidUserInput(); self.markInvalidUserInput();
return null; return null;
} }
@ -906,7 +907,7 @@ pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_
}, },
.string => switch (option_ptr.value) { .string => switch (option_ptr.value) {
.flag, .list, .map => { .flag, .list, .map => {
log.err("Expected -D{s} to be a string, but received a {s}.\n", .{ log.err("Expected -D{s} to be a string, but received a {s}.", .{
name, @tagName(option_ptr.value), name, @tagName(option_ptr.value),
}); });
self.markInvalidUserInput(); self.markInvalidUserInput();
@ -914,9 +915,27 @@ pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_
}, },
.scalar => |s| return s, .scalar => |s| return s,
}, },
.build_id => switch (option_ptr.value) {
.flag, .map, .list => {
log.err("Expected -D{s} to be an enum, but received a {s}.", .{
name, @tagName(option_ptr.value),
});
self.markInvalidUserInput();
return null;
},
.scalar => |s| {
if (Step.Compile.BuildId.parse(s)) |build_id| {
return build_id;
} else |err| {
log.err("unable to parse option '-D{s}': {s}", .{ name, @errorName(err) });
self.markInvalidUserInput();
return null;
}
},
},
.list => switch (option_ptr.value) { .list => switch (option_ptr.value) {
.flag, .map => { .flag, .map => {
log.err("Expected -D{s} to be a list, but received a {s}.\n", .{ log.err("Expected -D{s} to be a list, but received a {s}.", .{
name, @tagName(option_ptr.value), name, @tagName(option_ptr.value),
}); });
self.markInvalidUserInput(); self.markInvalidUserInput();
@ -1183,15 +1202,18 @@ pub fn addUserInputFlag(self: *Build, name_raw: []const u8) !bool {
} }
fn typeToEnum(comptime T: type) TypeId { fn typeToEnum(comptime T: type) TypeId {
return switch (@typeInfo(T)) { return switch (T) {
.Int => .int, Step.Compile.BuildId => .build_id,
.Float => .float, else => return switch (@typeInfo(T)) {
.Bool => .bool, .Int => .int,
.Enum => .@"enum", .Float => .float,
else => switch (T) { .Bool => .bool,
[]const u8 => .string, .Enum => .@"enum",
[]const []const u8 => .list, else => switch (T) {
else => @compileError("Unsupported type: " ++ @typeName(T)), []const u8 => .string,
[]const []const u8 => .list,
else => @compileError("Unsupported type: " ++ @typeName(T)),
},
}, },
}; };
} }

View file

@ -235,6 +235,10 @@ pub const HashHelper = struct {
.none => {}, .none => {},
} }
}, },
std.Build.Step.Compile.BuildId => switch (x) {
.none, .fast, .uuid, .sha1, .md5 => hh.add(std.meta.activeTag(x)),
.hexstring => |hex_string| hh.addBytes(hex_string.toSlice()),
},
else => switch (@typeInfo(@TypeOf(x))) { else => switch (@typeInfo(@TypeOf(x))) {
.Bool, .Int, .Enum, .Array => hh.addBytes(mem.asBytes(&x)), .Bool, .Int, .Enum, .Array => hh.addBytes(mem.asBytes(&x)),
else => @compileError("unable to hash type " ++ @typeName(@TypeOf(x))), else => @compileError("unable to hash type " ++ @typeName(@TypeOf(x))),

View file

@ -294,27 +294,41 @@ pub const BuildId = union(enum) {
uuid, uuid,
sha1, sha1,
md5, md5,
hexstring: []const u8, hexstring: HexString,
pub fn hash(self: BuildId, hasher: anytype) void { pub fn eql(a: BuildId, b: BuildId) bool {
switch (self) { const a_tag = std.meta.activeTag(a);
.none, .fast, .uuid, .sha1, .md5 => { const b_tag = std.meta.activeTag(b);
hasher.update(@tagName(self)); if (a_tag != b_tag) return false;
}, return switch (a) {
.hexstring => |str| { .none, .fast, .uuid, .sha1, .md5 => true,
hasher.update("0x"); .hexstring => |a_hexstring| mem.eql(u8, a_hexstring.toSlice(), b.hexstring.toSlice()),
hasher.update(str); };
},
}
} }
// parses the incoming BuildId. If returns a hexstring, it is allocated pub const HexString = struct {
// by the provided allocator. bytes: [32]u8,
pub fn parse(allocator: std.mem.Allocator, text: []const u8) error{ len: u8,
InvalidHexInt,
InvalidBuildId, /// Result is byte values, *not* hex-encoded.
OutOfMemory, pub fn toSlice(hs: *const HexString) []const u8 {
}!BuildId { return hs.bytes[0..hs.len];
}
};
/// Input is byte values, *not* hex-encoded.
/// Asserts `bytes` fits inside `HexString`
pub fn initHexString(bytes: []const u8) BuildId {
var result: BuildId = .{ .hexstring = .{
.bytes = undefined,
.len = @intCast(u8, bytes.len),
} };
@memcpy(result.hexstring.bytes[0..bytes.len], bytes);
return result;
}
/// Converts UTF-8 text to a `BuildId`.
pub fn parse(text: []const u8) !BuildId {
if (mem.eql(u8, text, "none")) { if (mem.eql(u8, text, "none")) {
return .none; return .none;
} else if (mem.eql(u8, text, "fast")) { } else if (mem.eql(u8, text, "fast")) {
@ -326,27 +340,27 @@ pub const BuildId = union(enum) {
} else if (mem.eql(u8, text, "md5")) { } else if (mem.eql(u8, text, "md5")) {
return .md5; return .md5;
} else if (mem.startsWith(u8, text, "0x")) { } else if (mem.startsWith(u8, text, "0x")) {
var clean_hex_string = try allocator.alloc(u8, text.len); var result: BuildId = .{ .hexstring = undefined };
errdefer allocator.free(clean_hex_string); const slice = try std.fmt.hexToBytes(&result.hexstring.bytes, text[2..]);
result.hexstring.len = @intCast(u8, slice.len);
var i: usize = 0; return result;
for (text["0x".len..]) |c| {
if (std.ascii.isHex(c)) {
clean_hex_string[i] = c;
i += 1;
} else if (c == '-' or c == ':') {
continue;
} else {
return error.InvalidHexInt;
}
}
if (i < text.len)
_ = allocator.resize(clean_hex_string, i);
return BuildId{ .hexstring = clean_hex_string[0..i] };
} }
return error.InvalidBuildIdStyle;
}
return error.InvalidBuildId; test parse {
try std.testing.expectEqual(BuildId.md5, try parse("md5"));
try std.testing.expectEqual(BuildId.none, try parse("none"));
try std.testing.expectEqual(BuildId.fast, try parse("fast"));
try std.testing.expectEqual(BuildId.uuid, try parse("uuid"));
try std.testing.expectEqual(BuildId.sha1, try parse("sha1"));
try std.testing.expectEqual(BuildId.sha1, try parse("tree"));
try std.testing.expect(BuildId.initHexString("").eql(try parse("0x")));
try std.testing.expect(BuildId.initHexString("\x12\x34\x56").eql(try parse("0x123456")));
try std.testing.expectError(error.InvalidLength, parse("0x12-34"));
try std.testing.expectError(error.InvalidCharacter, parse("0xfoobbb"));
try std.testing.expectError(error.InvalidBuildIdStyle, parse("yaddaxxx"));
} }
}; };
@ -1872,11 +1886,13 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
try addFlag(&zig_args, "valgrind", self.valgrind_support); try addFlag(&zig_args, "valgrind", self.valgrind_support);
try addFlag(&zig_args, "each-lib-rpath", self.each_lib_rpath); try addFlag(&zig_args, "each-lib-rpath", self.each_lib_rpath);
if (self.build_id) |build_id| { if (self.build_id) |build_id| {
const fmt_str = "--build-id={s}{s}";
try zig_args.append(switch (build_id) { try zig_args.append(switch (build_id) {
.hexstring => |str| try std.fmt.allocPrint(b.allocator, fmt_str, .{ "0x", str }), .hexstring => |hs| b.fmt("--build-id=0x{s}", .{
.none, .fast, .uuid, .sha1, .md5 => try std.fmt.allocPrint(b.allocator, fmt_str, .{ "", @tagName(build_id) }), std.fmt.fmtSliceHexLower(hs.toSlice()),
}),
.none, .fast, .uuid, .sha1, .md5 => b.fmt("--build-id={s}", .{@tagName(build_id)}),
}); });
} }
@ -2243,50 +2259,3 @@ fn checkCompileErrors(self: *Compile) !void {
\\========================================= \\=========================================
, .{ expected_generated.items, actual_stderr }); , .{ expected_generated.items, actual_stderr });
} }
const testing = std.testing;
test "BuildId.parse" {
const tests = &[_]struct {
[]const u8,
?BuildId,
?anyerror,
}{
.{ "0x", BuildId{ .hexstring = "" }, null },
.{ "0x12-34:", BuildId{ .hexstring = "1234" }, null },
.{ "0x123456", BuildId{ .hexstring = "123456" }, null },
.{ "md5", .md5, null },
.{ "none", .none, null },
.{ "fast", .fast, null },
.{ "uuid", .uuid, null },
.{ "sha1", .sha1, null },
.{ "tree", .sha1, null },
.{ "0xfoobbb", null, error.InvalidHexInt },
.{ "yaddaxxx", null, error.InvalidBuildId },
};
for (tests) |tt| {
const input = tt[0];
const expected = tt[1];
const expected_err = tt[2];
_ = (if (expected_err) |err| {
try testing.expectError(err, BuildId.parse(testing.allocator, input));
} else blk: {
const actual = BuildId.parse(testing.allocator, input) catch |e| break :blk e;
switch (expected.?) {
.hexstring => |expected_str| {
try testing.expectEqualStrings(expected_str, actual.hexstring);
testing.allocator.free(actual.hexstring);
},
else => try testing.expectEqual(expected.?, actual),
}
}) catch |e| {
std.log.err(
"BuildId.parse failed on {s}: expected {} got {!}",
.{ input, expected.?, e },
);
return e;
};
}
}

View file

@ -798,6 +798,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
const unwind_tables = options.want_unwind_tables orelse const unwind_tables = options.want_unwind_tables orelse
(link_libunwind or target_util.needUnwindTables(options.target)); (link_libunwind or target_util.needUnwindTables(options.target));
const link_eh_frame_hdr = options.link_eh_frame_hdr or unwind_tables; const link_eh_frame_hdr = options.link_eh_frame_hdr or unwind_tables;
const build_id = options.build_id orelse .none;
// Make a decision on whether to use LLD or our own linker. // Make a decision on whether to use LLD or our own linker.
const use_lld = options.use_lld orelse blk: { const use_lld = options.use_lld orelse blk: {
@ -828,7 +829,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
options.output_mode == .Lib or options.output_mode == .Lib or
options.linker_script != null or options.version_script != null or options.linker_script != null or options.version_script != null or
options.emit_implib != null or options.emit_implib != null or
options.build_id != null or build_id != .none or
options.symbol_wrap_set.count() > 0) options.symbol_wrap_set.count() > 0)
{ {
break :blk true; break :blk true;
@ -1514,7 +1515,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
.skip_linker_dependencies = options.skip_linker_dependencies, .skip_linker_dependencies = options.skip_linker_dependencies,
.parent_compilation_link_libc = options.parent_compilation_link_libc, .parent_compilation_link_libc = options.parent_compilation_link_libc,
.each_lib_rpath = options.each_lib_rpath orelse options.is_native_os, .each_lib_rpath = options.each_lib_rpath orelse options.is_native_os,
.build_id = options.build_id, .build_id = build_id,
.cache_mode = cache_mode, .cache_mode = cache_mode,
.disable_lld_caching = options.disable_lld_caching or cache_mode == .whole, .disable_lld_caching = options.disable_lld_caching or cache_mode == .whole,
.subsystem = options.subsystem, .subsystem = options.subsystem,
@ -2269,9 +2270,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes
man.hash.addListOfBytes(comp.bin_file.options.rpath_list); man.hash.addListOfBytes(comp.bin_file.options.rpath_list);
man.hash.addListOfBytes(comp.bin_file.options.symbol_wrap_set.keys()); man.hash.addListOfBytes(comp.bin_file.options.symbol_wrap_set.keys());
man.hash.add(comp.bin_file.options.each_lib_rpath); man.hash.add(comp.bin_file.options.each_lib_rpath);
if (comp.bin_file.options.build_id) |build_id| { man.hash.add(comp.bin_file.options.build_id);
build_id.hash(&man.hash.hasher);
}
man.hash.add(comp.bin_file.options.skip_linker_dependencies); man.hash.add(comp.bin_file.options.skip_linker_dependencies);
man.hash.add(comp.bin_file.options.z_nodelete); man.hash.add(comp.bin_file.options.z_nodelete);
man.hash.add(comp.bin_file.options.z_notext); man.hash.add(comp.bin_file.options.z_notext);

View file

@ -158,7 +158,7 @@ pub const Options = struct {
skip_linker_dependencies: bool, skip_linker_dependencies: bool,
parent_compilation_link_libc: bool, parent_compilation_link_libc: bool,
each_lib_rpath: bool, each_lib_rpath: bool,
build_id: ?BuildId, build_id: BuildId,
disable_lld_caching: bool, disable_lld_caching: bool,
is_test: bool, is_test: bool,
hash_style: HashStyle, hash_style: HashStyle,

View file

@ -1399,8 +1399,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
man.hash.add(self.base.options.each_lib_rpath); man.hash.add(self.base.options.each_lib_rpath);
if (self.base.options.output_mode == .Exe) { if (self.base.options.output_mode == .Exe) {
man.hash.add(stack_size); man.hash.add(stack_size);
if (self.base.options.build_id) |build_id| man.hash.add(self.base.options.build_id);
build_id.hash(&man.hash.hasher);
} }
man.hash.addListOfBytes(self.base.options.symbol_wrap_set.keys()); man.hash.addListOfBytes(self.base.options.symbol_wrap_set.keys());
man.hash.add(self.base.options.skip_linker_dependencies); man.hash.add(self.base.options.skip_linker_dependencies);
@ -1543,12 +1542,18 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
try argv.append("-z"); try argv.append("-z");
try argv.append(try std.fmt.allocPrint(arena, "stack-size={d}", .{stack_size})); try argv.append(try std.fmt.allocPrint(arena, "stack-size={d}", .{stack_size}));
if (self.base.options.build_id) |build_id| { switch (self.base.options.build_id) {
const fmt_str = "--build-id={s}{s}"; .none => {},
try argv.append(switch (build_id) { .fast, .uuid, .sha1, .md5 => {
.hexstring => |str| try std.fmt.allocPrint(arena, fmt_str, .{ "0x", str }), try argv.append(try std.fmt.allocPrint(arena, "--build-id={s}", .{
.none, .fast, .uuid, .sha1, .md5 => try std.fmt.allocPrint(arena, fmt_str, .{ "", @tagName(build_id) }), @tagName(self.base.options.build_id),
}); }));
},
.hexstring => |hs| {
try argv.append(try std.fmt.allocPrint(arena, "--build-id=0x{s}", .{
std.fmt.fmtSliceHexLower(hs.toSlice()),
}));
},
} }
} }

View file

@ -3163,8 +3163,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l
try man.addOptionalFile(compiler_rt_path); try man.addOptionalFile(compiler_rt_path);
man.hash.addOptionalBytes(options.entry); man.hash.addOptionalBytes(options.entry);
man.hash.addOptional(options.stack_size_override); man.hash.addOptional(options.stack_size_override);
if (wasm.base.options.build_id) |build_id| man.hash.add(wasm.base.options.build_id);
build_id.hash(&man.hash.hasher);
man.hash.add(options.import_memory); man.hash.add(options.import_memory);
man.hash.add(options.import_table); man.hash.add(options.import_table);
man.hash.add(options.export_table); man.hash.add(options.export_table);
@ -3798,27 +3797,29 @@ fn writeToFile(
if (!wasm.base.options.strip) { if (!wasm.base.options.strip) {
// The build id must be computed on the main sections only, // The build id must be computed on the main sections only,
// so we have to do it now, before the debug sections. // so we have to do it now, before the debug sections.
if (wasm.base.options.build_id) |build_id| { switch (wasm.base.options.build_id) {
switch (build_id) { .none => {},
.none => {}, .fast => {
.fast => { var id: [16]u8 = undefined;
var id: [16]u8 = undefined; std.crypto.hash.sha3.TurboShake128(null).hash(binary_bytes.items, &id, .{});
std.crypto.hash.sha3.TurboShake128(null).hash(binary_bytes.items, &id, .{}); var uuid: [36]u8 = undefined;
var uuid: [36]u8 = undefined; _ = try std.fmt.bufPrint(&uuid, "{s}-{s}-{s}-{s}-{s}", .{
_ = try std.fmt.bufPrint(&uuid, "{s}-{s}-{s}-{s}-{s}", .{ std.fmt.fmtSliceHexLower(id[0..4]),
std.fmt.fmtSliceHexLower(id[0..4]), std.fmt.fmtSliceHexLower(id[4..6]),
std.fmt.fmtSliceHexLower(id[4..6]), std.fmt.fmtSliceHexLower(id[6..8]),
std.fmt.fmtSliceHexLower(id[6..8]), std.fmt.fmtSliceHexLower(id[8..10]),
std.fmt.fmtSliceHexLower(id[8..10]), std.fmt.fmtSliceHexLower(id[10..]),
std.fmt.fmtSliceHexLower(id[10..]), });
}); try emitBuildIdSection(&binary_bytes, &uuid);
try emitBuildIdSection(&binary_bytes, &uuid); },
}, .hexstring => |hs| {
.hexstring => |str| { var buffer: [32 * 2]u8 = undefined;
try emitBuildIdSection(&binary_bytes, str); const str = std.fmt.bufPrint(&buffer, "{s}", .{
}, std.fmt.fmtSliceHexLower(hs.toSlice()),
else => |mode| log.err("build-id '{s}' is not supported for WASM", .{@tagName(mode)}), }) catch unreachable;
} try emitBuildIdSection(&binary_bytes, str);
},
else => |mode| log.err("build-id '{s}' is not supported for WASM", .{@tagName(mode)}),
} }
// if (wasm.dwarf) |*dwarf| { // if (wasm.dwarf) |*dwarf| {
@ -4211,8 +4212,7 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !
try man.addOptionalFile(compiler_rt_path); try man.addOptionalFile(compiler_rt_path);
man.hash.addOptionalBytes(wasm.base.options.entry); man.hash.addOptionalBytes(wasm.base.options.entry);
man.hash.addOptional(wasm.base.options.stack_size_override); man.hash.addOptional(wasm.base.options.stack_size_override);
if (wasm.base.options.build_id) |build_id| man.hash.add(wasm.base.options.build_id);
build_id.hash(&man.hash.hasher);
man.hash.add(wasm.base.options.import_memory); man.hash.add(wasm.base.options.import_memory);
man.hash.add(wasm.base.options.import_table); man.hash.add(wasm.base.options.import_table);
man.hash.add(wasm.base.options.export_table); man.hash.add(wasm.base.options.export_table);

View file

@ -494,7 +494,10 @@ const usage_build_generic =
\\ -fno-each-lib-rpath Prevent adding rpath for each used dynamic library \\ -fno-each-lib-rpath Prevent adding rpath for each used dynamic library
\\ -fallow-shlib-undefined Allows undefined symbols in shared libraries \\ -fallow-shlib-undefined Allows undefined symbols in shared libraries
\\ -fno-allow-shlib-undefined Disallows undefined symbols in shared libraries \\ -fno-allow-shlib-undefined Disallows undefined symbols in shared libraries
\\ --build-id[=style] Generate a build ID note \\ --build-id[=style] At a minor link-time expense, coordinates stripped binaries
\\ fast, uuid, sha1, md5 with debug symbols via a '.note.gnu.build-id' section
\\ 0x[hexstring] Maximum 32 bytes
\\ none (default) Disable build-id
\\ --eh-frame-hdr Enable C++ exception handling by passing --eh-frame-hdr to linker \\ --eh-frame-hdr Enable C++ exception handling by passing --eh-frame-hdr to linker
\\ --emit-relocs Enable output of relocation sections for post build tools \\ --emit-relocs Enable output of relocation sections for post build tools
\\ -z [arg] Set linker extension flags \\ -z [arg] Set linker extension flags
@ -1445,11 +1448,11 @@ fn buildOutputType(
} else if (mem.eql(u8, arg, "--build-id")) { } else if (mem.eql(u8, arg, "--build-id")) {
build_id = .fast; build_id = .fast;
} else if (mem.startsWith(u8, arg, "--build-id=")) { } else if (mem.startsWith(u8, arg, "--build-id=")) {
const value = arg["--build-id=".len..]; const style = arg["--build-id=".len..];
build_id = BuildId.parse(arena, value) catch |err| switch (err) { build_id = BuildId.parse(style) catch |err| {
error.InvalidHexInt => fatal("failed to parse hex value {s}", .{value}), fatal("unable to parse --build-id style '{s}': {s}", .{
error.InvalidBuildId => fatal("invalid --build-id={s}", .{value}), style, @errorName(err),
error.OutOfMemory => fatal("OOM", .{}), });
}; };
} else if (mem.eql(u8, arg, "--debug-compile-errors")) { } else if (mem.eql(u8, arg, "--debug-compile-errors")) {
if (!crash_report.is_enabled) { if (!crash_report.is_enabled) {
@ -1689,7 +1692,14 @@ fn buildOutputType(
if (mem.indexOfScalar(u8, linker_arg, '=')) |equals_pos| { if (mem.indexOfScalar(u8, linker_arg, '=')) |equals_pos| {
const key = linker_arg[0..equals_pos]; const key = linker_arg[0..equals_pos];
const value = linker_arg[equals_pos + 1 ..]; const value = linker_arg[equals_pos + 1 ..];
if (mem.eql(u8, key, "--sort-common")) { if (mem.eql(u8, key, "--build-id")) {
build_id = BuildId.parse(value) catch |err| {
fatal("unable to parse --build-id style '{s}': {s}", .{
value, @errorName(err),
});
};
continue;
} else if (mem.eql(u8, key, "--sort-common")) {
// this ignores --sort=common=<anything>; ignoring plain --sort-common // this ignores --sort=common=<anything>; ignoring plain --sort-common
// is done below. // is done below.
continue; continue;
@ -1699,7 +1709,9 @@ fn buildOutputType(
continue; continue;
} }
} }
if (mem.eql(u8, linker_arg, "--as-needed")) { if (mem.eql(u8, linker_arg, "--build-id")) {
build_id = .fast;
} else if (mem.eql(u8, linker_arg, "--as-needed")) {
needed = false; needed = false;
} else if (mem.eql(u8, linker_arg, "--no-as-needed")) { } else if (mem.eql(u8, linker_arg, "--no-as-needed")) {
needed = true; needed = true;
@ -1731,15 +1743,6 @@ fn buildOutputType(
search_strategy = .paths_first; search_strategy = .paths_first;
} else if (mem.eql(u8, linker_arg, "-search_dylibs_first")) { } else if (mem.eql(u8, linker_arg, "-search_dylibs_first")) {
search_strategy = .dylibs_first; search_strategy = .dylibs_first;
} else if (mem.eql(u8, linker_arg, "--build-id")) {
build_id = .fast;
} else if (mem.startsWith(u8, linker_arg, "--build-id=")) {
const value = linker_arg["--build-id=".len..];
build_id = BuildId.parse(arena, value) catch |err| switch (err) {
error.InvalidHexInt => fatal("failed to parse hex value {s}", .{value}),
error.InvalidBuildId => fatal("invalid --build-id={s}", .{value}),
error.OutOfMemory => fatal("OOM", .{}),
};
} else { } else {
try linker_args.append(linker_arg); try linker_args.append(linker_arg);
} }