commit abd131e3367bb10a67bc4c240b87b05d1de4e42f (tree)
parent 54b3484256695381c034aa30f322b1499a4b27c8
Author: Andrew Kelley <andrew@ziglang.org>
Date: Wed, 25 Mar 2026 18:29:19 -0700
zig cc: make --version use the full clang CLI lowering code path
because clang wants to parse the -target argument with clang -target
syntax.
closes #30178
Diffstat:
5 files changed, 47 insertions(+), 15 deletions(-)
diff --git a/lib/std/zig.zig b/lib/std/zig.zig
@@ -1121,6 +1121,7 @@ pub const ClangCliParam = struct {
rtlib,
static,
dynamic,
+ version,
};
pub fn matchEql(self: @This(), arg: []const u8) u2 {
diff --git a/src/Compilation.zig b/src/Compilation.zig
@@ -1471,6 +1471,8 @@ pub const ClangPreprocessorMode = enum {
stdout,
/// precompiled C header
pch,
+ /// `--version`
+ version,
};
pub const Framework = link.File.MachO.Framework;
@@ -2936,16 +2938,16 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) UpdateE
.none => unreachable,
.manifest_create, .manifest_read, .manifest_lock => |e| return comp.setMiscFailure(
.check_whole_cache,
- "failed to check cache: {s} {s}",
- .{ @tagName(man.diagnostic), @errorName(e) },
+ "failed to check cache: {t} {t}",
+ .{ man.diagnostic, e },
),
.file_open, .file_stat, .file_read, .file_hash => |op| {
const pp = man.files.keys()[op.file_index].prefixed_path;
const prefix = man.cache.prefixes()[pp.prefix];
return comp.setMiscFailure(
.check_whole_cache,
- "failed to check cache: '{f}{s}' {s} {s}",
- .{ prefix, pp.sub_path, @tagName(man.diagnostic), @errorName(op.err) },
+ "failed to check cache: '{f}{s}' {t} {t}",
+ .{ prefix, pp.sub_path, man.diagnostic, op.err },
);
},
},
@@ -5739,6 +5741,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr
.yes => argv.appendSliceAssumeCapacity(&.{ "-E", "-o", out_obj_path }),
.pch => argv.appendSliceAssumeCapacity(&.{ "-Xclang", "-emit-pch", "-o", out_obj_path }),
.stdout => argv.appendAssumeCapacity("-E"),
+ .version => argv.appendAssumeCapacity("--version"),
}
if (comp.emit_asm != null) {
@@ -5782,6 +5785,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr
.yes => argv.appendSliceAssumeCapacity(&.{ "-E", "-o", out_obj_path }),
.pch => argv.appendSliceAssumeCapacity(&.{ "-Xclang", "-emit-pch", "-o", out_obj_path }),
.stdout => argv.appendAssumeCapacity("-E"),
+ .version => argv.appendAssumeCapacity("--version"),
}
if (out_diag_path) |diag_file_path| {
argv.appendSliceAssumeCapacity(&.{ "--serialize-diagnostics", diag_file_path });
@@ -5830,8 +5834,10 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr
if (code != 0) {
std.process.exit(code);
}
- if (comp.clang_preprocessor_mode == .stdout)
- std.process.exit(0);
+ switch (comp.clang_preprocessor_mode) {
+ .stdout, .version => std.process.exit(0),
+ else => {},
+ }
},
else => std.process.abort(),
}
@@ -5879,11 +5885,10 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr
return comp.failCObj(c_object, "clang exited with code {d}", .{exit_code});
}
}
- if (comp.clang_passthrough_mode and
- comp.clang_preprocessor_mode == .stdout)
- {
- std.process.exit(0);
- }
+ if (comp.clang_passthrough_mode) switch (comp.clang_preprocessor_mode) {
+ .stdout, .version => std.process.exit(0),
+ else => {},
+ };
}
if (out_dep_path) |dep_file_path| {
diff --git a/src/clang_options.zon b/src/clang_options.zon
@@ -637,6 +637,7 @@
},
.{
.name = "version",
+ .ze = .version,
.pd1 = false,
.pd2 = true,
},
@@ -3378,7 +3379,7 @@
},
.{ .name = "verify-ignore-unexpected" },
.{ .name = "verify-pch" },
-.{ .name = "version" },
+.{ .name = "version", .ze = .version },
.{ .name = "via-file-asm", .pd2 = true },
.{ .name = "w" },
.{
diff --git a/src/main.zig b/src/main.zig
@@ -1889,6 +1889,7 @@ fn buildOutputType(
object,
assembly,
preprocessor,
+ version,
};
var c_out_mode: ?COutMode = null;
var out_path: ?[]const u8 = null;
@@ -1917,6 +1918,10 @@ fn buildOutputType(
.c, .r => c_out_mode = .object, // -c or -r
.asm_only => c_out_mode = .assembly, // -S
.preprocess_only => c_out_mode = .preprocessor, // -E
+ .version => {
+ c_out_mode = .version; // --version
+ disable_c_depfile = true;
+ },
.emit_llvm => emit_llvm = true,
.x => {
const lang = mem.sliceTo(it.only_arg, 0);
@@ -2939,9 +2944,11 @@ fn buildOutputType(
}
// precompiled header syntax: "zig cc -x c-header test.h -o test.pch"
- const emit_pch = ((file_ext == .h or file_ext == .hpp or file_ext == .hm or file_ext == .hmm) and c_out_mode == null);
- if (emit_pch)
- c_out_mode = .preprocessor;
+ const emit_pch = if (file_ext) |fe| switch (fe) {
+ .h, .hpp, .hm, .hmm => c_out_mode == null,
+ else => false,
+ } else false;
+ if (emit_pch) c_out_mode = .preprocessor;
switch (c_out_mode orelse .link) {
.link => {
@@ -3009,6 +3016,20 @@ fn buildOutputType(
}
}
},
+ .version => {
+ // We can't allow control flow to reach the simpler logic
+ // below because the -target argument has to be lowered to
+ // clang syntax in Compilation.
+ create_module.opts.output_mode = .Obj;
+ clang_preprocessor_mode = .version;
+ if (create_module.c_source_files.items.len == 0) {
+ try create_module.c_source_files.append(arena, .{
+ .owner = undefined,
+ .src_path = "a.c", // dummy name
+ .ext = .c,
+ });
+ }
+ },
}
if (create_module.c_source_files.items.len == 0 and
!anyObjectLinkInputs(create_module.cli_link_inputs.items) and
diff --git a/tools/update_clang_options.zig b/tools/update_clang_options.zig
@@ -590,6 +590,10 @@ const known_options = [_]KnownOpt{
.name = "dynamic",
.ident = "dynamic",
},
+ .{
+ .name = "version",
+ .ident = "version",
+ },
};
const blacklisted_options = [_][]const u8{};