authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-05-16 20:00:47-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-05-16 20:39:01-07:00
log728ce2d7c18e23ca6c36d86f4ee1ea4ce3ac81e2
treeb84ffae450a01a7fa2cf22d87176fb633125b359
parentdf5085bde012773b974f58e8ee28ed90ff686468

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

9 files changed, 169 insertions(+), 164 deletions(-)

build.zig+5-2
...@@ -167,8 +167,11 @@ pub fn build(b: *std.Build) !void {...@@ -167,8 +167,11 @@ pub fn build(b: *std.Build) !void {
167 exe.sanitize_thread = sanitize_thread;167 exe.sanitize_thread = sanitize_thread;
168 exe.entitlements = entitlements;168 exe.entitlements = entitlements;
169169
170 if (b.option([]const u8, "build-id", "Include a build id note")) |build_id|170 exe.build_id = b.option(
171 exe.build_id = try std.Build.CompileStep.BuildId.parse(b.allocator, build_id);171 std.Build.Step.Compile.BuildId,
172 "build-id",
173 "Request creation of '.note.gnu.build-id' section",
174 );
172175
173 b.installArtifact(exe);176 b.installArtifact(exe);
174177
lib/std/Build.zig+42-20
...@@ -181,6 +181,7 @@ const TypeId = enum {...@@ -181,6 +181,7 @@ const TypeId = enum {
181 @"enum",181 @"enum",
182 string,182 string,
183 list,183 list,
184 build_id,
184};185};
185186
186const TopLevelStep = struct {187const TopLevelStep = struct {
...@@ -832,13 +833,13 @@ pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_...@@ -832,13 +833,13 @@ pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_
832 } else if (mem.eql(u8, s, "false")) {833 } else if (mem.eql(u8, s, "false")) {
833 return false;834 return false;
834 } else {835 } else {
835 log.err("Expected -D{s} to be a boolean, but received '{s}'\n", .{ name, s });836 log.err("Expected -D{s} to be a boolean, but received '{s}'", .{ name, s });
836 self.markInvalidUserInput();837 self.markInvalidUserInput();
837 return null;838 return null;
838 }839 }
839 },840 },
840 .list, .map => {841 .list, .map => {
841 log.err("Expected -D{s} to be a boolean, but received a {s}.\n", .{842 log.err("Expected -D{s} to be a boolean, but received a {s}.", .{
842 name, @tagName(option_ptr.value),843 name, @tagName(option_ptr.value),
843 });844 });
844 self.markInvalidUserInput();845 self.markInvalidUserInput();
...@@ -847,7 +848,7 @@ pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_...@@ -847,7 +848,7 @@ pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_
847 },848 },
848 .int => switch (option_ptr.value) {849 .int => switch (option_ptr.value) {
849 .flag, .list, .map => {850 .flag, .list, .map => {
850 log.err("Expected -D{s} to be an integer, but received a {s}.\n", .{851 log.err("Expected -D{s} to be an integer, but received a {s}.", .{
851 name, @tagName(option_ptr.value),852 name, @tagName(option_ptr.value),
852 });853 });
853 self.markInvalidUserInput();854 self.markInvalidUserInput();
...@@ -856,12 +857,12 @@ pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_...@@ -856,12 +857,12 @@ pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_
856 .scalar => |s| {857 .scalar => |s| {
857 const n = std.fmt.parseInt(T, s, 10) catch |err| switch (err) {858 const n = std.fmt.parseInt(T, s, 10) catch |err| switch (err) {
858 error.Overflow => {859 error.Overflow => {
859 log.err("-D{s} value {s} cannot fit into type {s}.\n", .{ name, s, @typeName(T) });860 log.err("-D{s} value {s} cannot fit into type {s}.", .{ name, s, @typeName(T) });
860 self.markInvalidUserInput();861 self.markInvalidUserInput();
861 return null;862 return null;
862 },863 },
863 else => {864 else => {
864 log.err("Expected -D{s} to be an integer of type {s}.\n", .{ name, @typeName(T) });865 log.err("Expected -D{s} to be an integer of type {s}.", .{ name, @typeName(T) });
865 self.markInvalidUserInput();866 self.markInvalidUserInput();
866 return null;867 return null;
867 },868 },
...@@ -871,7 +872,7 @@ pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_...@@ -871,7 +872,7 @@ pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_
871 },872 },
872 .float => switch (option_ptr.value) {873 .float => switch (option_ptr.value) {
873 .flag, .map, .list => {874 .flag, .map, .list => {
874 log.err("Expected -D{s} to be a float, but received a {s}.\n", .{875 log.err("Expected -D{s} to be a float, but received a {s}.", .{
875 name, @tagName(option_ptr.value),876 name, @tagName(option_ptr.value),
876 });877 });
877 self.markInvalidUserInput();878 self.markInvalidUserInput();
...@@ -879,7 +880,7 @@ pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_...@@ -879,7 +880,7 @@ pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_
879 },880 },
880 .scalar => |s| {881 .scalar => |s| {
881 const n = std.fmt.parseFloat(T, s) catch {882 const n = std.fmt.parseFloat(T, s) catch {
882 log.err("Expected -D{s} to be a float of type {s}.\n", .{ name, @typeName(T) });883 log.err("Expected -D{s} to be a float of type {s}.", .{ name, @typeName(T) });
883 self.markInvalidUserInput();884 self.markInvalidUserInput();
884 return null;885 return null;
885 };886 };
...@@ -888,7 +889,7 @@ pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_...@@ -888,7 +889,7 @@ pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_
888 },889 },
889 .@"enum" => switch (option_ptr.value) {890 .@"enum" => switch (option_ptr.value) {
890 .flag, .map, .list => {891 .flag, .map, .list => {
891 log.err("Expected -D{s} to be an enum, but received a {s}.\n", .{892 log.err("Expected -D{s} to be an enum, but received a {s}.", .{
892 name, @tagName(option_ptr.value),893 name, @tagName(option_ptr.value),
893 });894 });
894 self.markInvalidUserInput();895 self.markInvalidUserInput();
...@@ -898,7 +899,7 @@ pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_...@@ -898,7 +899,7 @@ pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_
898 if (std.meta.stringToEnum(T, s)) |enum_lit| {899 if (std.meta.stringToEnum(T, s)) |enum_lit| {
899 return enum_lit;900 return enum_lit;
900 } else {901 } else {
901 log.err("Expected -D{s} to be of type {s}.\n", .{ name, @typeName(T) });902 log.err("Expected -D{s} to be of type {s}.", .{ name, @typeName(T) });
902 self.markInvalidUserInput();903 self.markInvalidUserInput();
903 return null;904 return null;
904 }905 }
...@@ -906,7 +907,7 @@ pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_...@@ -906,7 +907,7 @@ pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_
906 },907 },
907 .string => switch (option_ptr.value) {908 .string => switch (option_ptr.value) {
908 .flag, .list, .map => {909 .flag, .list, .map => {
909 log.err("Expected -D{s} to be a string, but received a {s}.\n", .{910 log.err("Expected -D{s} to be a string, but received a {s}.", .{
910 name, @tagName(option_ptr.value),911 name, @tagName(option_ptr.value),
911 });912 });
912 self.markInvalidUserInput();913 self.markInvalidUserInput();
...@@ -914,9 +915,27 @@ pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_...@@ -914,9 +915,27 @@ pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_
914 },915 },
915 .scalar => |s| return s,916 .scalar => |s| return s,
916 },917 },
918 .build_id => switch (option_ptr.value) {
919 .flag, .map, .list => {
920 log.err("Expected -D{s} to be an enum, but received a {s}.", .{
921 name, @tagName(option_ptr.value),
922 });
923 self.markInvalidUserInput();
924 return null;
925 },
926 .scalar => |s| {
927 if (Step.Compile.BuildId.parse(s)) |build_id| {
928 return build_id;
929 } else |err| {
930 log.err("unable to parse option '-D{s}': {s}", .{ name, @errorName(err) });
931 self.markInvalidUserInput();
932 return null;
933 }
934 },
935 },
917 .list => switch (option_ptr.value) {936 .list => switch (option_ptr.value) {
918 .flag, .map => {937 .flag, .map => {
919 log.err("Expected -D{s} to be a list, but received a {s}.\n", .{938 log.err("Expected -D{s} to be a list, but received a {s}.", .{
920 name, @tagName(option_ptr.value),939 name, @tagName(option_ptr.value),
921 });940 });
922 self.markInvalidUserInput();941 self.markInvalidUserInput();
...@@ -1183,15 +1202,18 @@ pub fn addUserInputFlag(self: *Build, name_raw: []const u8) !bool {...@@ -1183,15 +1202,18 @@ pub fn addUserInputFlag(self: *Build, name_raw: []const u8) !bool {
1183}1202}
11841203
1185fn typeToEnum(comptime T: type) TypeId {1204fn typeToEnum(comptime T: type) TypeId {
1186 return switch (@typeInfo(T)) {1205 return switch (T) {
1187 .Int => .int,1206 Step.Compile.BuildId => .build_id,
1188 .Float => .float,1207 else => return switch (@typeInfo(T)) {
1189 .Bool => .bool,1208 .Int => .int,
1190 .Enum => .@"enum",1209 .Float => .float,
1191 else => switch (T) {1210 .Bool => .bool,
1192 []const u8 => .string,1211 .Enum => .@"enum",
1193 []const []const u8 => .list,1212 else => switch (T) {
1194 else => @compileError("Unsupported type: " ++ @typeName(T)),1213 []const u8 => .string,
1214 []const []const u8 => .list,
1215 else => @compileError("Unsupported type: " ++ @typeName(T)),
1216 },
1195 },1217 },
1196 };1218 };
1197}1219}
lib/std/Build/Cache.zig+4
...@@ -235,6 +235,10 @@ pub const HashHelper = struct {...@@ -235,6 +235,10 @@ pub const HashHelper = struct {
235 .none => {},235 .none => {},
236 }236 }
237 },237 },
238 std.Build.Step.Compile.BuildId => switch (x) {
239 .none, .fast, .uuid, .sha1, .md5 => hh.add(std.meta.activeTag(x)),
240 .hexstring => |hex_string| hh.addBytes(hex_string.toSlice()),
241 },
238 else => switch (@typeInfo(@TypeOf(x))) {242 else => switch (@typeInfo(@TypeOf(x))) {
239 .Bool, .Int, .Enum, .Array => hh.addBytes(mem.asBytes(&x)),243 .Bool, .Int, .Enum, .Array => hh.addBytes(mem.asBytes(&x)),
240 else => @compileError("unable to hash type " ++ @typeName(@TypeOf(x))),244 else => @compileError("unable to hash type " ++ @typeName(@TypeOf(x))),
lib/std/Build/Step/Compile.zig+55-86
...@@ -294,27 +294,41 @@ pub const BuildId = union(enum) {...@@ -294,27 +294,41 @@ pub const BuildId = union(enum) {
294 uuid,294 uuid,
295 sha1,295 sha1,
296 md5,296 md5,
297 hexstring: []const u8,297 hexstring: HexString,
298
299 pub fn eql(a: BuildId, b: BuildId) bool {
300 const a_tag = std.meta.activeTag(a);
301 const b_tag = std.meta.activeTag(b);
302 if (a_tag != b_tag) return false;
303 return switch (a) {
304 .none, .fast, .uuid, .sha1, .md5 => true,
305 .hexstring => |a_hexstring| mem.eql(u8, a_hexstring.toSlice(), b.hexstring.toSlice()),
306 };
307 }
298308
299 pub fn hash(self: BuildId, hasher: anytype) void {309 pub const HexString = struct {
300 switch (self) {310 bytes: [32]u8,
301 .none, .fast, .uuid, .sha1, .md5 => {311 len: u8,
302 hasher.update(@tagName(self));312
303 },313 /// Result is byte values, *not* hex-encoded.
304 .hexstring => |str| {314 pub fn toSlice(hs: *const HexString) []const u8 {
305 hasher.update("0x");315 return hs.bytes[0..hs.len];
306 hasher.update(str);
307 },
308 }316 }
317 };
318
319 /// Input is byte values, *not* hex-encoded.
320 /// Asserts `bytes` fits inside `HexString`
321 pub fn initHexString(bytes: []const u8) BuildId {
322 var result: BuildId = .{ .hexstring = .{
323 .bytes = undefined,
324 .len = @intCast(u8, bytes.len),
325 } };
326 @memcpy(result.hexstring.bytes[0..bytes.len], bytes);
327 return result;
309 }328 }
310329
311 // parses the incoming BuildId. If returns a hexstring, it is allocated330 /// Converts UTF-8 text to a `BuildId`.
312 // by the provided allocator.331 pub fn parse(text: []const u8) !BuildId {
313 pub fn parse(allocator: std.mem.Allocator, text: []const u8) error{
314 InvalidHexInt,
315 InvalidBuildId,
316 OutOfMemory,
317 }!BuildId {
318 if (mem.eql(u8, text, "none")) {332 if (mem.eql(u8, text, "none")) {
319 return .none;333 return .none;
320 } else if (mem.eql(u8, text, "fast")) {334 } else if (mem.eql(u8, text, "fast")) {
...@@ -326,27 +340,27 @@ pub const BuildId = union(enum) {...@@ -326,27 +340,27 @@ pub const BuildId = union(enum) {
326 } else if (mem.eql(u8, text, "md5")) {340 } else if (mem.eql(u8, text, "md5")) {
327 return .md5;341 return .md5;
328 } else if (mem.startsWith(u8, text, "0x")) {342 } else if (mem.startsWith(u8, text, "0x")) {
329 var clean_hex_string = try allocator.alloc(u8, text.len);343 var result: BuildId = .{ .hexstring = undefined };
330 errdefer allocator.free(clean_hex_string);344 const slice = try std.fmt.hexToBytes(&result.hexstring.bytes, text[2..]);
331345 result.hexstring.len = @intCast(u8, slice.len);
332 var i: usize = 0;346 return result;
333 for (text["0x".len..]) |c| {
334 if (std.ascii.isHex(c)) {
335 clean_hex_string[i] = c;
336 i += 1;
337 } else if (c == '-' or c == ':') {
338 continue;
339 } else {
340 return error.InvalidHexInt;
341 }
342 }
343 if (i < text.len)
344 _ = allocator.resize(clean_hex_string, i);
345
346 return BuildId{ .hexstring = clean_hex_string[0..i] };
347 }347 }
348 return error.InvalidBuildIdStyle;
349 }
350
351 test parse {
352 try std.testing.expectEqual(BuildId.md5, try parse("md5"));
353 try std.testing.expectEqual(BuildId.none, try parse("none"));
354 try std.testing.expectEqual(BuildId.fast, try parse("fast"));
355 try std.testing.expectEqual(BuildId.uuid, try parse("uuid"));
356 try std.testing.expectEqual(BuildId.sha1, try parse("sha1"));
357 try std.testing.expectEqual(BuildId.sha1, try parse("tree"));
348358
349 return error.InvalidBuildId;359 try std.testing.expect(BuildId.initHexString("").eql(try parse("0x")));
360 try std.testing.expect(BuildId.initHexString("\x12\x34\x56").eql(try parse("0x123456")));
361 try std.testing.expectError(error.InvalidLength, parse("0x12-34"));
362 try std.testing.expectError(error.InvalidCharacter, parse("0xfoobbb"));
363 try std.testing.expectError(error.InvalidBuildIdStyle, parse("yaddaxxx"));
350 }364 }
351};365};
352366
...@@ -1872,11 +1886,13 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -1872,11 +1886,13 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
18721886
1873 try addFlag(&zig_args, "valgrind", self.valgrind_support);1887 try addFlag(&zig_args, "valgrind", self.valgrind_support);
1874 try addFlag(&zig_args, "each-lib-rpath", self.each_lib_rpath);1888 try addFlag(&zig_args, "each-lib-rpath", self.each_lib_rpath);
1889
1875 if (self.build_id) |build_id| {1890 if (self.build_id) |build_id| {
1876 const fmt_str = "--build-id={s}{s}";
1877 try zig_args.append(switch (build_id) {1891 try zig_args.append(switch (build_id) {
1878 .hexstring => |str| try std.fmt.allocPrint(b.allocator, fmt_str, .{ "0x", str }),1892 .hexstring => |hs| b.fmt("--build-id=0x{s}", .{
1879 .none, .fast, .uuid, .sha1, .md5 => try std.fmt.allocPrint(b.allocator, fmt_str, .{ "", @tagName(build_id) }),1893 std.fmt.fmtSliceHexLower(hs.toSlice()),
1894 }),
1895 .none, .fast, .uuid, .sha1, .md5 => b.fmt("--build-id={s}", .{@tagName(build_id)}),
1880 });1896 });
1881 }1897 }
18821898
...@@ -2243,50 +2259,3 @@ fn checkCompileErrors(self: *Compile) !void {...@@ -2243,50 +2259,3 @@ fn checkCompileErrors(self: *Compile) !void {
2243 \\=========================================2259 \\=========================================
2244 , .{ expected_generated.items, actual_stderr });2260 , .{ expected_generated.items, actual_stderr });
2245}2261}
2246
2247const testing = std.testing;
2248
2249test "BuildId.parse" {
2250 const tests = &[_]struct {
2251 []const u8,
2252 ?BuildId,
2253 ?anyerror,
2254 }{
2255 .{ "0x", BuildId{ .hexstring = "" }, null },
2256 .{ "0x12-34:", BuildId{ .hexstring = "1234" }, null },
2257 .{ "0x123456", BuildId{ .hexstring = "123456" }, null },
2258 .{ "md5", .md5, null },
2259 .{ "none", .none, null },
2260 .{ "fast", .fast, null },
2261 .{ "uuid", .uuid, null },
2262 .{ "sha1", .sha1, null },
2263 .{ "tree", .sha1, null },
2264 .{ "0xfoobbb", null, error.InvalidHexInt },
2265 .{ "yaddaxxx", null, error.InvalidBuildId },
2266 };
2267
2268 for (tests) |tt| {
2269 const input = tt[0];
2270 const expected = tt[1];
2271 const expected_err = tt[2];
2272
2273 _ = (if (expected_err) |err| {
2274 try testing.expectError(err, BuildId.parse(testing.allocator, input));
2275 } else blk: {
2276 const actual = BuildId.parse(testing.allocator, input) catch |e| break :blk e;
2277 switch (expected.?) {
2278 .hexstring => |expected_str| {
2279 try testing.expectEqualStrings(expected_str, actual.hexstring);
2280 testing.allocator.free(actual.hexstring);
2281 },
2282 else => try testing.expectEqual(expected.?, actual),
2283 }
2284 }) catch |e| {
2285 std.log.err(
2286 "BuildId.parse failed on {s}: expected {} got {!}",
2287 .{ input, expected.?, e },
2288 );
2289 return e;
2290 };
2291 }
2292}
src/Compilation.zig+4-5
...@@ -798,6 +798,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {...@@ -798,6 +798,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
798 const unwind_tables = options.want_unwind_tables orelse798 const unwind_tables = options.want_unwind_tables orelse
799 (link_libunwind or target_util.needUnwindTables(options.target));799 (link_libunwind or target_util.needUnwindTables(options.target));
800 const link_eh_frame_hdr = options.link_eh_frame_hdr or unwind_tables;800 const link_eh_frame_hdr = options.link_eh_frame_hdr or unwind_tables;
801 const build_id = options.build_id orelse .none;
801802
802 // Make a decision on whether to use LLD or our own linker.803 // Make a decision on whether to use LLD or our own linker.
803 const use_lld = options.use_lld orelse blk: {804 const use_lld = options.use_lld orelse blk: {
...@@ -828,7 +829,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {...@@ -828,7 +829,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
828 options.output_mode == .Lib or829 options.output_mode == .Lib or
829 options.linker_script != null or options.version_script != null or830 options.linker_script != null or options.version_script != null or
830 options.emit_implib != null or831 options.emit_implib != null or
831 options.build_id != null or832 build_id != .none or
832 options.symbol_wrap_set.count() > 0)833 options.symbol_wrap_set.count() > 0)
833 {834 {
834 break :blk true;835 break :blk true;
...@@ -1514,7 +1515,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {...@@ -1514,7 +1515,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
1514 .skip_linker_dependencies = options.skip_linker_dependencies,1515 .skip_linker_dependencies = options.skip_linker_dependencies,
1515 .parent_compilation_link_libc = options.parent_compilation_link_libc,1516 .parent_compilation_link_libc = options.parent_compilation_link_libc,
1516 .each_lib_rpath = options.each_lib_rpath orelse options.is_native_os,1517 .each_lib_rpath = options.each_lib_rpath orelse options.is_native_os,
1517 .build_id = options.build_id,1518 .build_id = build_id,
1518 .cache_mode = cache_mode,1519 .cache_mode = cache_mode,
1519 .disable_lld_caching = options.disable_lld_caching or cache_mode == .whole,1520 .disable_lld_caching = options.disable_lld_caching or cache_mode == .whole,
1520 .subsystem = options.subsystem,1521 .subsystem = options.subsystem,
...@@ -2269,9 +2270,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes...@@ -2269,9 +2270,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes
2269 man.hash.addListOfBytes(comp.bin_file.options.rpath_list);2270 man.hash.addListOfBytes(comp.bin_file.options.rpath_list);
2270 man.hash.addListOfBytes(comp.bin_file.options.symbol_wrap_set.keys());2271 man.hash.addListOfBytes(comp.bin_file.options.symbol_wrap_set.keys());
2271 man.hash.add(comp.bin_file.options.each_lib_rpath);2272 man.hash.add(comp.bin_file.options.each_lib_rpath);
2272 if (comp.bin_file.options.build_id) |build_id| {2273 man.hash.add(comp.bin_file.options.build_id);
2273 build_id.hash(&man.hash.hasher);
2274 }
2275 man.hash.add(comp.bin_file.options.skip_linker_dependencies);2274 man.hash.add(comp.bin_file.options.skip_linker_dependencies);
2276 man.hash.add(comp.bin_file.options.z_nodelete);2275 man.hash.add(comp.bin_file.options.z_nodelete);
2277 man.hash.add(comp.bin_file.options.z_notext);2276 man.hash.add(comp.bin_file.options.z_notext);
src/link.zig+1-1
...@@ -158,7 +158,7 @@ pub const Options = struct {...@@ -158,7 +158,7 @@ pub const Options = struct {
158 skip_linker_dependencies: bool,158 skip_linker_dependencies: bool,
159 parent_compilation_link_libc: bool,159 parent_compilation_link_libc: bool,
160 each_lib_rpath: bool,160 each_lib_rpath: bool,
161 build_id: ?BuildId,161 build_id: BuildId,
162 disable_lld_caching: bool,162 disable_lld_caching: bool,
163 is_test: bool,163 is_test: bool,
164 hash_style: HashStyle,164 hash_style: HashStyle,
src/link/Elf.zig+13-8
...@@ -1399,8 +1399,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v...@@ -1399,8 +1399,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
1399 man.hash.add(self.base.options.each_lib_rpath);1399 man.hash.add(self.base.options.each_lib_rpath);
1400 if (self.base.options.output_mode == .Exe) {1400 if (self.base.options.output_mode == .Exe) {
1401 man.hash.add(stack_size);1401 man.hash.add(stack_size);
1402 if (self.base.options.build_id) |build_id|1402 man.hash.add(self.base.options.build_id);
1403 build_id.hash(&man.hash.hasher);
1404 }1403 }
1405 man.hash.addListOfBytes(self.base.options.symbol_wrap_set.keys());1404 man.hash.addListOfBytes(self.base.options.symbol_wrap_set.keys());
1406 man.hash.add(self.base.options.skip_linker_dependencies);1405 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...@@ -1543,12 +1542,18 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
1543 try argv.append("-z");1542 try argv.append("-z");
1544 try argv.append(try std.fmt.allocPrint(arena, "stack-size={d}", .{stack_size}));1543 try argv.append(try std.fmt.allocPrint(arena, "stack-size={d}", .{stack_size}));
15451544
1546 if (self.base.options.build_id) |build_id| {1545 switch (self.base.options.build_id) {
1547 const fmt_str = "--build-id={s}{s}";1546 .none => {},
1548 try argv.append(switch (build_id) {1547 .fast, .uuid, .sha1, .md5 => {
1549 .hexstring => |str| try std.fmt.allocPrint(arena, fmt_str, .{ "0x", str }),1548 try argv.append(try std.fmt.allocPrint(arena, "--build-id={s}", .{
1550 .none, .fast, .uuid, .sha1, .md5 => try std.fmt.allocPrint(arena, fmt_str, .{ "", @tagName(build_id) }),1549 @tagName(self.base.options.build_id),
1551 });1550 }));
1551 },
1552 .hexstring => |hs| {
1553 try argv.append(try std.fmt.allocPrint(arena, "--build-id=0x{s}", .{
1554 std.fmt.fmtSliceHexLower(hs.toSlice()),
1555 }));
1556 },
1552 }1557 }
1553 }1558 }
15541559
src/link/Wasm.zig+25-25
...@@ -3163,8 +3163,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l...@@ -3163,8 +3163,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l
3163 try man.addOptionalFile(compiler_rt_path);3163 try man.addOptionalFile(compiler_rt_path);
3164 man.hash.addOptionalBytes(options.entry);3164 man.hash.addOptionalBytes(options.entry);
3165 man.hash.addOptional(options.stack_size_override);3165 man.hash.addOptional(options.stack_size_override);
3166 if (wasm.base.options.build_id) |build_id|3166 man.hash.add(wasm.base.options.build_id);
3167 build_id.hash(&man.hash.hasher);
3168 man.hash.add(options.import_memory);3167 man.hash.add(options.import_memory);
3169 man.hash.add(options.import_table);3168 man.hash.add(options.import_table);
3170 man.hash.add(options.export_table);3169 man.hash.add(options.export_table);
...@@ -3798,27 +3797,29 @@ fn writeToFile(...@@ -3798,27 +3797,29 @@ fn writeToFile(
3798 if (!wasm.base.options.strip) {3797 if (!wasm.base.options.strip) {
3799 // The build id must be computed on the main sections only,3798 // The build id must be computed on the main sections only,
3800 // so we have to do it now, before the debug sections.3799 // so we have to do it now, before the debug sections.
3801 if (wasm.base.options.build_id) |build_id| {3800 switch (wasm.base.options.build_id) {
3802 switch (build_id) {3801 .none => {},
3803 .none => {},3802 .fast => {
3804 .fast => {3803 var id: [16]u8 = undefined;
3805 var id: [16]u8 = undefined;3804 std.crypto.hash.sha3.TurboShake128(null).hash(binary_bytes.items, &id, .{});
3806 std.crypto.hash.sha3.TurboShake128(null).hash(binary_bytes.items, &id, .{});3805 var uuid: [36]u8 = undefined;
3807 var uuid: [36]u8 = undefined;3806 _ = try std.fmt.bufPrint(&uuid, "{s}-{s}-{s}-{s}-{s}", .{
3808 _ = try std.fmt.bufPrint(&uuid, "{s}-{s}-{s}-{s}-{s}", .{3807 std.fmt.fmtSliceHexLower(id[0..4]),
3809 std.fmt.fmtSliceHexLower(id[0..4]),3808 std.fmt.fmtSliceHexLower(id[4..6]),
3810 std.fmt.fmtSliceHexLower(id[4..6]),3809 std.fmt.fmtSliceHexLower(id[6..8]),
3811 std.fmt.fmtSliceHexLower(id[6..8]),3810 std.fmt.fmtSliceHexLower(id[8..10]),
3812 std.fmt.fmtSliceHexLower(id[8..10]),3811 std.fmt.fmtSliceHexLower(id[10..]),
3813 std.fmt.fmtSliceHexLower(id[10..]),3812 });
3814 });3813 try emitBuildIdSection(&binary_bytes, &uuid);
3815 try emitBuildIdSection(&binary_bytes, &uuid);3814 },
3816 },3815 .hexstring => |hs| {
3817 .hexstring => |str| {3816 var buffer: [32 * 2]u8 = undefined;
3818 try emitBuildIdSection(&binary_bytes, str);3817 const str = std.fmt.bufPrint(&buffer, "{s}", .{
3819 },3818 std.fmt.fmtSliceHexLower(hs.toSlice()),
3820 else => |mode| log.err("build-id '{s}' is not supported for WASM", .{@tagName(mode)}),3819 }) catch unreachable;
3821 }3820 try emitBuildIdSection(&binary_bytes, str);
3821 },
3822 else => |mode| log.err("build-id '{s}' is not supported for WASM", .{@tagName(mode)}),
3822 }3823 }
38233824
3824 // if (wasm.dwarf) |*dwarf| {3825 // if (wasm.dwarf) |*dwarf| {
...@@ -4211,8 +4212,7 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !...@@ -4211,8 +4212,7 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !
4211 try man.addOptionalFile(compiler_rt_path);4212 try man.addOptionalFile(compiler_rt_path);
4212 man.hash.addOptionalBytes(wasm.base.options.entry);4213 man.hash.addOptionalBytes(wasm.base.options.entry);
4213 man.hash.addOptional(wasm.base.options.stack_size_override);4214 man.hash.addOptional(wasm.base.options.stack_size_override);
4214 if (wasm.base.options.build_id) |build_id|4215 man.hash.add(wasm.base.options.build_id);
4215 build_id.hash(&man.hash.hasher);
4216 man.hash.add(wasm.base.options.import_memory);4216 man.hash.add(wasm.base.options.import_memory);
4217 man.hash.add(wasm.base.options.import_table);4217 man.hash.add(wasm.base.options.import_table);
4218 man.hash.add(wasm.base.options.export_table);4218 man.hash.add(wasm.base.options.export_table);
src/main.zig+20-17
...@@ -494,7 +494,10 @@ const usage_build_generic =...@@ -494,7 +494,10 @@ const usage_build_generic =
494 \\ -fno-each-lib-rpath Prevent adding rpath for each used dynamic library494 \\ -fno-each-lib-rpath Prevent adding rpath for each used dynamic library
495 \\ -fallow-shlib-undefined Allows undefined symbols in shared libraries495 \\ -fallow-shlib-undefined Allows undefined symbols in shared libraries
496 \\ -fno-allow-shlib-undefined Disallows undefined symbols in shared libraries496 \\ -fno-allow-shlib-undefined Disallows undefined symbols in shared libraries
497 \\ --build-id[=style] Generate a build ID note497 \\ --build-id[=style] At a minor link-time expense, coordinates stripped binaries
498 \\ fast, uuid, sha1, md5 with debug symbols via a '.note.gnu.build-id' section
499 \\ 0x[hexstring] Maximum 32 bytes
500 \\ none (default) Disable build-id
498 \\ --eh-frame-hdr Enable C++ exception handling by passing --eh-frame-hdr to linker501 \\ --eh-frame-hdr Enable C++ exception handling by passing --eh-frame-hdr to linker
499 \\ --emit-relocs Enable output of relocation sections for post build tools502 \\ --emit-relocs Enable output of relocation sections for post build tools
500 \\ -z [arg] Set linker extension flags503 \\ -z [arg] Set linker extension flags
...@@ -1445,11 +1448,11 @@ fn buildOutputType(...@@ -1445,11 +1448,11 @@ fn buildOutputType(
1445 } else if (mem.eql(u8, arg, "--build-id")) {1448 } else if (mem.eql(u8, arg, "--build-id")) {
1446 build_id = .fast;1449 build_id = .fast;
1447 } else if (mem.startsWith(u8, arg, "--build-id=")) {1450 } else if (mem.startsWith(u8, arg, "--build-id=")) {
1448 const value = arg["--build-id=".len..];1451 const style = arg["--build-id=".len..];
1449 build_id = BuildId.parse(arena, value) catch |err| switch (err) {1452 build_id = BuildId.parse(style) catch |err| {
1450 error.InvalidHexInt => fatal("failed to parse hex value {s}", .{value}),1453 fatal("unable to parse --build-id style '{s}': {s}", .{
1451 error.InvalidBuildId => fatal("invalid --build-id={s}", .{value}),1454 style, @errorName(err),
1452 error.OutOfMemory => fatal("OOM", .{}),1455 });
1453 };1456 };
1454 } else if (mem.eql(u8, arg, "--debug-compile-errors")) {1457 } else if (mem.eql(u8, arg, "--debug-compile-errors")) {
1455 if (!crash_report.is_enabled) {1458 if (!crash_report.is_enabled) {
...@@ -1689,7 +1692,14 @@ fn buildOutputType(...@@ -1689,7 +1692,14 @@ fn buildOutputType(
1689 if (mem.indexOfScalar(u8, linker_arg, '=')) |equals_pos| {1692 if (mem.indexOfScalar(u8, linker_arg, '=')) |equals_pos| {
1690 const key = linker_arg[0..equals_pos];1693 const key = linker_arg[0..equals_pos];
1691 const value = linker_arg[equals_pos + 1 ..];1694 const value = linker_arg[equals_pos + 1 ..];
1692 if (mem.eql(u8, key, "--sort-common")) {1695 if (mem.eql(u8, key, "--build-id")) {
1696 build_id = BuildId.parse(value) catch |err| {
1697 fatal("unable to parse --build-id style '{s}': {s}", .{
1698 value, @errorName(err),
1699 });
1700 };
1701 continue;
1702 } else if (mem.eql(u8, key, "--sort-common")) {
1693 // this ignores --sort=common=<anything>; ignoring plain --sort-common1703 // this ignores --sort=common=<anything>; ignoring plain --sort-common
1694 // is done below.1704 // is done below.
1695 continue;1705 continue;
...@@ -1699,7 +1709,9 @@ fn buildOutputType(...@@ -1699,7 +1709,9 @@ fn buildOutputType(
1699 continue;1709 continue;
1700 }1710 }
1701 }1711 }
1702 if (mem.eql(u8, linker_arg, "--as-needed")) {1712 if (mem.eql(u8, linker_arg, "--build-id")) {
1713 build_id = .fast;
1714 } else if (mem.eql(u8, linker_arg, "--as-needed")) {
1703 needed = false;1715 needed = false;
1704 } else if (mem.eql(u8, linker_arg, "--no-as-needed")) {1716 } else if (mem.eql(u8, linker_arg, "--no-as-needed")) {
1705 needed = true;1717 needed = true;
...@@ -1731,15 +1743,6 @@ fn buildOutputType(...@@ -1731,15 +1743,6 @@ fn buildOutputType(
1731 search_strategy = .paths_first;1743 search_strategy = .paths_first;
1732 } else if (mem.eql(u8, linker_arg, "-search_dylibs_first")) {1744 } else if (mem.eql(u8, linker_arg, "-search_dylibs_first")) {
1733 search_strategy = .dylibs_first;1745 search_strategy = .dylibs_first;
1734 } else if (mem.eql(u8, linker_arg, "--build-id")) {
1735 build_id = .fast;
1736 } else if (mem.startsWith(u8, linker_arg, "--build-id=")) {
1737 const value = linker_arg["--build-id=".len..];
1738 build_id = BuildId.parse(arena, value) catch |err| switch (err) {
1739 error.InvalidHexInt => fatal("failed to parse hex value {s}", .{value}),
1740 error.InvalidBuildId => fatal("invalid --build-id={s}", .{value}),
1741 error.OutOfMemory => fatal("OOM", .{}),
1742 };
1743 } else {1746 } else {
1744 try linker_args.append(linker_arg);1747 try linker_args.append(linker_arg);
1745 }1748 }