authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-10-01 03:05:09+02:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-10-01 03:05:09+02:00
logf0b51497baa5d6fe1fd5f5d5ece19364f71489bf
tree2b0cad73ea76433d589d00a7ed93f55e65339afd
parent398f4cada30d4a73a95536ac526cad15e3769bc5
parentf81b328fb960557adc2c49dbcfac1002b98b621a

Merge pull request 'Compilation: fix bad C compilation error reporting' (#37030) from mlugg/windows-fuckery into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/37030

2 files changed, 167 insertions(+), 59 deletions(-)

src/Compilation.zig+153-49
......@@ -1319,7 +1319,7 @@ pub const cache_helpers = struct {
13191319 }
13201320 }
13211321
1322 pub fn hashCSource(man: *Cache.Manifest, c_source: CSourceFile) !void {
1322 pub fn hashCSource(man: *Cache.Manifest, c_source: CSourceFile) Allocator.Error!void {
13231323 _ = try man.addInputPath(.initCwd(c_source.src_path), .{});
13241324 // Hash the extra flags, with special care to call addFile for file parameters.
13251325 // TODO this logic can likely be improved by utilizing clang_options_data.zig.
......@@ -5156,16 +5156,16 @@ fn workerUpdateCObject(
51565156 comp: *Compilation,
51575157 c_object: *CObject,
51585158 progress_node: std.Progress.Node,
5159) void {
5159) Io.Cancelable!void {
51605160 comp.updateCObject(c_object, progress_node) catch |err| switch (err) {
5161 error.AlreadyReported => return,
5162 else => {
5163 comp.reportRetryableCObjectError(c_object, err) catch |oom| switch (oom) {
5164 // Swallowing this error is OK because it's implied to be OOM when
5165 // there is a missing failed_c_objects error message.
5166 error.OutOfMemory => {},
5167 };
5161 error.OutOfMemory => switch (comp.failCObjRetryable(c_object, "failed to update C object: out of memory", .{})) {
5162 // Swallowing this error is OK because it's implied to be OOM when
5163 // there is a missing failed_c_objects error message.
5164 error.OutOfMemory => return,
5165 error.AlreadyReported => return,
51685166 },
5167 error.Canceled => |e| return e,
5168 error.AlreadyReported => return,
51695169 };
51705170}
51715171
......@@ -5428,15 +5428,6 @@ fn buildLibZigC(comp: *Compilation, prog_node: std.Progress.Node) void {
54285428 };
54295429}
54305430
5431fn reportRetryableCObjectError(comp: *Compilation, c_object: *CObject, err: anyerror) error{OutOfMemory}!void {
5432 c_object.status = .failure_retryable;
5433
5434 switch (comp.failCObj(c_object, "{t}", .{err})) {
5435 error.AlreadyReported => return,
5436 else => |e| return e,
5437 }
5438}
5439
54405431fn reportRetryableWin32ResourceError(
54415432 comp: *Compilation,
54425433 win32_resource: *Win32Resource,
......@@ -5470,7 +5461,11 @@ fn reportRetryableWin32ResourceError(
54705461 }
54715462}
54725463
5473fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Progress.Node) !void {
5464fn updateCObject(
5465 comp: *Compilation,
5466 c_object: *CObject,
5467 c_obj_prog_node: std.Progress.Node,
5468) (Allocator.Error || Io.Cancelable || error{AlreadyReported})!void {
54745469 if (comp.config.c_frontend == .aro) {
54755470 return comp.failCObj(c_object, "aro does not support compiling C objects yet", .{});
54765471 }
......@@ -5624,7 +5619,14 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr
56245619 // We can't know the digest until we do the C compiler invocation,
56255620 // so we need a temporary filename.
56265621 const out_obj_path = try comp.tmpFilePath(arena, o_basename);
5627 var zig_cache_tmp_dir = try comp.dirs.local_cache.handle.createDirPathOpen(io, "tmp", .{});
5622 var zig_cache_tmp_dir = comp.dirs.local_cache.handle.createDirPathOpen(io, "tmp", .{}) catch |err| switch (err) {
5623 error.Canceled => |e| return e,
5624 else => |e| return comp.failCObjRetryable(
5625 c_object,
5626 "failed to open '{f}{c}tmp': {t}",
5627 .{ comp.dirs.local_cache, fs.path.sep, e },
5628 ),
5629 };
56285630 defer zig_cache_tmp_dir.close(io);
56295631
56305632 const out_diag_path = if (comp.clang_passthrough_mode or !ext.clangSupportsDiagnostics())
......@@ -5703,18 +5705,45 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr
57035705 else => std.process.abort(),
57045706 }
57055707 } else {
5706 var child = try std.process.spawn(io, .{
5708 var child = std.process.spawn(io, .{
57075709 .argv = argv.items,
57085710 .stdin = .ignore,
57095711 .stdout = .ignore,
57105712 .stderr = .pipe,
5711 });
5713 }) catch |err| switch (err) {
5714 error.Canceled,
5715 error.OutOfMemory,
5716 => |e| return e,
5717 else => |e| return comp.failCObjRetryable(
5718 c_object,
5719 "failed to spawn zig clang '{s}': {t}",
5720 .{ argv.items[0], e },
5721 ),
5722 };
57125723
57135724 var stderr_reader = child.stderr.?.readerStreaming(io, &.{});
5714 const stderr = try stderr_reader.interface.allocRemaining(arena, .limited(std.math.maxInt(u32)));
5725 const stderr = stderr_reader.interface.allocRemaining(arena, .limited(std.math.maxInt(u32))) catch |err| switch (err) {
5726 error.OutOfMemory => |e| return e,
5727 error.StreamTooLong => return comp.failCObjRetryable(
5728 c_object,
5729 "failed to read stderr of zig clang '{s}': exceeded maximum size",
5730 .{argv.items[0]},
5731 ),
5732 error.ReadFailed => return comp.failCObjRetryable(
5733 c_object,
5734 "failed to read stderr of zig clang '{s}': {t}",
5735 .{ argv.items[0], stderr_reader.err.? },
5736 ),
5737 };
57155738
5716 const term = child.wait(io) catch |err|
5717 return comp.failCObj(c_object, "failed to spawn zig clang {s}: {t}", .{ argv.items[0], err });
5739 const term = child.wait(io) catch |err| switch (err) {
5740 error.Canceled => |e| return e,
5741 else => |e| return comp.failCObj(
5742 c_object,
5743 "failed to spawn zig clang '{s}': {t}",
5744 .{ argv.items[0], e },
5745 ),
5746 };
57185747
57195748 switch (term) {
57205749 .exited => |code| if (code != 0) if (out_diag_path) |diag_file_path| {
......@@ -5756,14 +5785,27 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr
57565785 };
57575786 }
57585787
5759 if (out_dep_path) |dep_file_path| {
5760 const dep_basename = fs.path.basename(dep_file_path);
5788 if (out_dep_path) |raw_dep_file_path| {
5789 const dep_file_path: std.Build.Cache.Path = .{
5790 .root_dir = .{
5791 .handle = zig_cache_tmp_dir,
5792 .path = "tmp",
5793 },
5794 .sub_path = fs.path.basename(raw_dep_file_path),
5795 };
57615796
57625797 if (comp.file_system_inputs != null) {
57635798 // TODO instead of this rely on passing the manifest contents directly
57645799
57655800 // Use the same file size limit as the cache code does for dependency files.
5766 const dep_file_contents = try zig_cache_tmp_dir.readFileAlloc(io, dep_basename, gpa, .unlimited);
5801 const dep_file_contents = dep_file_path.root_dir.handle.readFileAlloc(io, dep_file_path.sub_path, gpa, .unlimited) catch |err| switch (err) {
5802 error.OutOfMemory, error.Canceled => |e| return e,
5803 else => |e| return comp.failCObjRetryable(
5804 c_object,
5805 "failed to read '{f}': {t}",
5806 .{ dep_file_path, e },
5807 ),
5808 };
57675809 defer gpa.free(dep_file_contents);
57685810
57695811 var str_buf: std.ArrayList(u8) = .empty;
......@@ -5778,36 +5820,65 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr
57785820 try token.resolve(gpa, &str_buf);
57795821 break :p try .fromUnresolved(arena, comp.dirs, &.{str_buf.items});
57805822 },
5781 else => |err_token| {
5782 log.err("failed parsing {s}: {f}", .{ dep_basename, err_token });
5783 return error.InvalidDepFile;
5784 },
5823 else => |err_token| return comp.failCObjRetryable(
5824 c_object,
5825 "failed parsing '{f}': {f}",
5826 .{ dep_file_path, err_token },
5827 ),
57855828 };
57865829 try comp.appendFileSystemInput(input_path);
57875830 }
57885831 }
57895832
57905833 // Add the files depended on to the cache system.
5791 var diagnostic: Cache.Manifest.AddDiscoveredDepFileDiagnostic = undefined;
5792 try man.addDiscoveredDepFile(.{
5793 .root_dir = .{
5794 .handle = zig_cache_tmp_dir,
5795 .path = "tmp",
5796 },
5797 .sub_path = dep_basename,
5798 }, &diagnostic);
5834 var diags: Cache.Manifest.AddDiscoveredDepFileDiagnostic = undefined;
5835 man.addDiscoveredDepFile(dep_file_path, &diags) catch |err| switch (err) {
5836 error.OutOfMemory,
5837 error.Canceled,
5838 => |e| return e,
5839
5840 error.InvalidDepFile => return comp.failCObjRetryable(
5841 c_object,
5842 "failed to parse '{f}': {f}",
5843 .{ dep_file_path, diags.dep_tokenizer },
5844 ),
5845 error.FileSystemFailure => return comp.failCObjRetryable(
5846 c_object,
5847 "failed to parse '{f}': {f}",
5848 .{ dep_file_path, diags.add_discovered_path },
5849 ),
5850 else => |e| return comp.failCObjRetryable(
5851 c_object,
5852 "failed to read '{f}': {t}",
5853 .{ dep_file_path, e },
5854 ),
5855 };
57995856 switch (comp.cache_use) {
58005857 .whole => |whole| {
58015858 if (whole.cache_manifest) |whole_cache_manifest| {
58025859 try whole.cache_manifest_mutex.lock(io);
58035860 defer whole.cache_manifest_mutex.unlock(io);
5804 try whole_cache_manifest.addDiscoveredDepFile(.{
5805 .root_dir = .{
5806 .handle = zig_cache_tmp_dir,
5807 .path = "tmp",
5808 },
5809 .sub_path = dep_basename,
5810 }, &diagnostic);
5861 whole_cache_manifest.addDiscoveredDepFile(dep_file_path, &diags) catch |err| switch (err) {
5862 error.OutOfMemory,
5863 error.Canceled,
5864 => |e| return e,
5865
5866 error.InvalidDepFile => return comp.failCObjRetryable(
5867 c_object,
5868 "failed to parse '{f}': {f}",
5869 .{ dep_file_path, diags.dep_tokenizer },
5870 ),
5871 error.FileSystemFailure => return comp.failCObjRetryable(
5872 c_object,
5873 "failed to parse '{f}': {f}",
5874 .{ dep_file_path, diags.add_discovered_path },
5875 ),
5876 else => |e| return comp.failCObjRetryable(
5877 c_object,
5878 "failed to read '{f}': {t}",
5879 .{ dep_file_path, e },
5880 ),
5881 };
58115882 }
58125883 },
58135884 .incremental, .none => {},
......@@ -5832,10 +5903,33 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr
58325903
58335904 // Rename into place.
58345905 const o_sub_path = try fs.path.join(arena, &.{ "o", &digest });
5835 var o_dir = try comp.dirs.local_cache.handle.createDirPathOpen(io, o_sub_path, .{});
5906 var o_dir = comp.dirs.local_cache.handle.createDirPathOpen(io, o_sub_path, .{}) catch |err| switch (err) {
5907 error.Canceled => |e| return e,
5908 else => |e| return comp.failCObjRetryable(
5909 c_object,
5910 "failed to create '{f}{c}{s}': {t}",
5911 .{ comp.dirs.local_cache, fs.path.sep, o_sub_path, e },
5912 ),
5913 };
58365914 defer o_dir.close(io);
58375915 const tmp_basename = fs.path.basename(out_obj_path);
5838 try Io.Dir.rename(zig_cache_tmp_dir, tmp_basename, o_dir, o_basename, io);
5916 Io.Dir.rename(zig_cache_tmp_dir, tmp_basename, o_dir, o_basename, io) catch |err| switch (err) {
5917 error.Canceled => |e| return e,
5918 else => |e| return comp.failCObjRetryable(
5919 c_object,
5920 "failed to rename 'tmp{c}{s}' into '{f}{c}{s}{c}{s}': {t}",
5921 .{
5922 fs.path.sep,
5923 tmp_basename,
5924 comp.dirs.local_cache,
5925 fs.path.sep,
5926 o_sub_path,
5927 fs.path.sep,
5928 o_basename,
5929 e,
5930 },
5931 ),
5932 };
58395933 break :d digest;
58405934 };
58415935
......@@ -6785,6 +6879,16 @@ pub fn addCCArgs(
67856879 try argv.appendSlice(mod.cc_argv);
67866880}
67876881
6882fn failCObjRetryable(
6883 comp: *Compilation,
6884 c_object: *CObject,
6885 comptime format: []const u8,
6886 args: anytype,
6887) error{ OutOfMemory, AlreadyReported } {
6888 defer c_object.status = .failure_retryable;
6889 return comp.failCObj(c_object, format, args);
6890}
6891
67886892fn failCObj(
67896893 comp: *Compilation,
67906894 c_object: *CObject,
test/standalone/elf2/build.zig+14-10
......@@ -3,21 +3,24 @@ pub fn build(b: *Build) void {
33 b.default_step = test_step;
44
55 if (b.graph.host.result.cpu.arch == .x86_64 and b.graph.host.result.os.tag == .linux) {
6 addOne(b, test_step, b.graph.host, false, .static, false, "elf2-hello-native-selfhosted-static");
7 addOne(b, test_step, b.graph.host, false, .dynamic, false, "elf2-hello-native-selfhosted-dynamic");
8 addOne(b, test_step, b.graph.host, false, .static, true, "elf2-hello-native-selfhosted-static-pie");
9 addOne(b, test_step, b.graph.host, false, .dynamic, true, "elf2-hello-native-selfhosted-dynamic-pie");
10 addOne(b, test_step, b.graph.host, true, .static, false, "elf2-hello-native-llvm-static");
11 addOne(b, test_step, b.graph.host, true, .dynamic, false, "elf2-hello-native-llvm-dynamic");
6 addOne(b, test_step, b.graph.host, false, .static, false, false, "elf2-hello-native-selfhosted-static");
7 addOne(b, test_step, b.graph.host, false, .dynamic, false, false, "elf2-hello-native-selfhosted-dynamic");
8 addOne(b, test_step, b.graph.host, false, .static, true, false, "elf2-hello-native-selfhosted-static-pie");
9 addOne(b, test_step, b.graph.host, false, .dynamic, true, false, "elf2-hello-native-selfhosted-dynamic-pie");
10 addOne(b, test_step, b.graph.host, true, .static, false, false, "elf2-hello-native-llvm-static");
11 addOne(b, test_step, b.graph.host, true, .dynamic, false, false, "elf2-hello-native-llvm-dynamic");
1212 }
1313
1414 const x86_64_linux_target: Build.ResolvedTarget = b.resolveTargetQuery(.{
1515 .cpu_arch = .x86_64,
1616 .os_tag = .linux,
1717 });
18 addOne(b, test_step, x86_64_linux_target, false, .static, false, "elf2-hello-selfhosted-static");
19 addOne(b, test_step, x86_64_linux_target, false, .static, true, "elf2-hello-selfhosted-static-pie");
20 addOne(b, test_step, x86_64_linux_target, true, .static, false, "elf2-hello-llvm-static");
18 addOne(b, test_step, x86_64_linux_target, false, .static, false, false, "elf2-hello-selfhosted-static");
19 addOne(b, test_step, x86_64_linux_target, false, .static, true, false, "elf2-hello-selfhosted-static-pie");
20 addOne(b, test_step, x86_64_linux_target, true, .static, false, false, "elf2-hello-llvm-static");
21 addOne(b, test_step, x86_64_linux_target, false, .static, false, true, "elf2-hello-selfhosted-static-libc");
22 addOne(b, test_step, x86_64_linux_target, false, .static, true, true, "elf2-hello-selfhosted-static-libc-pie");
23 addOne(b, test_step, x86_64_linux_target, true, .static, false, true, "elf2-hello-llvm-static-libc");
2124}
2225
2326fn addOne(
......@@ -27,13 +30,14 @@ fn addOne(
2730 use_llvm: bool,
2831 link_mode: std.lang.LinkMode,
2932 pie: bool,
33 force_link_libc: bool,
3034 name: []const u8,
3135) void {
3236 const mod = b.createModule(.{
3337 .root_source_file = b.path("hello.zig"),
3438 .target = target,
3539 .optimize = .debug,
36 .link_libc = link_mode == .dynamic,
40 .link_libc = link_mode == .dynamic or force_link_libc,
3741 });
3842 const exe = b.addExecutable(.{
3943 .name = name,