Skip to content

Commit f888a3a

Browse files
committed
zig cc: expose clang precompiled C header support
see https://releases.llvm.org/17.0.1/tools/clang/docs/UsersManual.html#generating-a-pch-file syntax examples: `zig cc -x c-header test.h -o test.pch` `zig cc -include-pch test.pch main.c` `zig c++ -x c++-header test.h -o test.pch` `zig c++ -include-pch test.pch main.cpp` `zig build-obj -lc++ -x c++-header test.h` `zig run -lc++ -cflags -include-pch test.pch -- main.cpp`
1 parent 6702284 commit f888a3a

File tree

3 files changed

+58
-26
lines changed

3 files changed

+58
-26
lines changed

src/Compilation.zig

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -215,11 +215,11 @@ pub const LangToExt = std.ComptimeStringMap(FileExt, .{
215215
.{ "c", .c },
216216
.{ "c-header", .h },
217217
.{ "c++", .cpp },
218-
.{ "c++-header", .h },
218+
.{ "c++-header", .hpp },
219219
.{ "objective-c", .m },
220-
.{ "objective-c-header", .h },
220+
.{ "objective-c-header", .hm },
221221
.{ "objective-c++", .mm },
222-
.{ "objective-c++-header", .h },
222+
.{ "objective-c++-header", .hmm },
223223
.{ "assembler", .assembly },
224224
.{ "assembler-with-cpp", .assembly_with_cpp },
225225
.{ "cuda", .cu },
@@ -788,6 +788,8 @@ pub const ClangPreprocessorMode = enum {
788788
yes,
789789
/// This means we are doing `zig cc -E`.
790790
stdout,
791+
/// precompiled C header
792+
pch,
791793
};
792794

793795
pub const Framework = link.Framework;
@@ -4610,6 +4612,10 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
46104612
.assembly_with_cpp => "assembler-with-cpp",
46114613
.c => "c",
46124614
.cpp => "c++",
4615+
.h => "c-header",
4616+
.hpp => "c++-header",
4617+
.hm => "objective-c-header",
4618+
.hmm => "objective-c++-header",
46134619
.cu => "cuda",
46144620
.m => "objective-c",
46154621
.mm => "objective-c++",
@@ -4635,10 +4641,11 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
46354641
else
46364642
"/dev/null";
46374643

4638-
try argv.ensureUnusedCapacity(5);
4644+
try argv.ensureUnusedCapacity(6);
46394645
switch (comp.clang_preprocessor_mode) {
4640-
.no => argv.appendSliceAssumeCapacity(&[_][]const u8{ "-c", "-o", out_obj_path }),
4641-
.yes => argv.appendSliceAssumeCapacity(&[_][]const u8{ "-E", "-o", out_obj_path }),
4646+
.no => argv.appendSliceAssumeCapacity(&.{ "-c", "-o", out_obj_path }),
4647+
.yes => argv.appendSliceAssumeCapacity(&.{ "-E", "-o", out_obj_path }),
4648+
.pch => argv.appendSliceAssumeCapacity(&.{ "-Xclang", "-emit-pch", "-o", out_obj_path }),
46424649
.stdout => argv.appendAssumeCapacity("-E"),
46434650
}
46444651

@@ -4673,10 +4680,11 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
46734680
try argv.appendSlice(c_object.src.extra_flags);
46744681
try argv.appendSlice(c_object.src.cache_exempt_flags);
46754682

4676-
try argv.ensureUnusedCapacity(5);
4683+
try argv.ensureUnusedCapacity(6);
46774684
switch (comp.clang_preprocessor_mode) {
46784685
.no => argv.appendSliceAssumeCapacity(&.{ "-c", "-o", out_obj_path }),
46794686
.yes => argv.appendSliceAssumeCapacity(&.{ "-E", "-o", out_obj_path }),
4687+
.pch => argv.appendSliceAssumeCapacity(&.{ "-Xclang", "-emit-pch", "-o", out_obj_path }),
46804688
.stdout => argv.appendAssumeCapacity("-E"),
46814689
}
46824690
if (comp.clang_passthrough_mode) {
@@ -5300,15 +5308,15 @@ pub fn addCCArgs(
53005308
try argv.appendSlice(&[_][]const u8{ "-target", llvm_triple });
53015309

53025310
if (target.os.tag == .windows) switch (ext) {
5303-
.c, .cpp, .m, .mm, .h, .cu, .rc, .assembly, .assembly_with_cpp => {
5311+
.c, .cpp, .m, .mm, .h, .hpp, .hm, .hmm, .cu, .rc, .assembly, .assembly_with_cpp => {
53045312
const minver: u16 = @truncate(@intFromEnum(target.os.getVersionRange().windows.min) >> 16);
53055313
try argv.append(try std.fmt.allocPrint(argv.allocator, "-D_WIN32_WINNT=0x{x:0>4}", .{minver}));
53065314
},
53075315
else => {},
53085316
};
53095317

53105318
switch (ext) {
5311-
.c, .cpp, .m, .mm, .h, .cu, .rc => {
5319+
.c, .cpp, .m, .mm, .h, .hpp, .hm, .hmm, .cu, .rc => {
53125320
try argv.appendSlice(&[_][]const u8{
53135321
"-nostdinc",
53145322
"-fno-spell-checking",
@@ -5931,6 +5939,9 @@ pub const FileExt = enum {
59315939
cpp,
59325940
cu,
59335941
h,
5942+
hpp,
5943+
hm,
5944+
hmm,
59345945
m,
59355946
mm,
59365947
ll,
@@ -5949,7 +5960,7 @@ pub const FileExt = enum {
59495960

59505961
pub fn clangSupportsDepFile(ext: FileExt) bool {
59515962
return switch (ext) {
5952-
.c, .cpp, .h, .m, .mm, .cu => true,
5963+
.c, .cpp, .h, .hpp, .hm, .hmm, .m, .mm, .cu => true,
59535964

59545965
.ll,
59555966
.bc,
@@ -5974,6 +5985,9 @@ pub const FileExt = enum {
59745985
.cpp => ".cpp",
59755986
.cu => ".cu",
59765987
.h => ".h",
5988+
.hpp => ".h",
5989+
.hm => ".h",
5990+
.hmm => ".h",
59775991
.m => ".m",
59785992
.mm => ".mm",
59795993
.ll => ".ll",

src/link.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -798,7 +798,7 @@ pub const File = struct {
798798
assert(base.tag == .c);
799799
return @fieldParentPtr(C, "base", base).flush(comp, prog_node);
800800
}
801-
if (comp.clang_preprocessor_mode == .yes) {
801+
if (comp.clang_preprocessor_mode == .yes or comp.clang_preprocessor_mode == .pch) {
802802
const emit = base.options.emit orelse return; // -fno-emit-bin
803803
// TODO: avoid extra link step when it's just 1 object file (the `zig cc -c` case)
804804
// Until then, we do `lld -r -o output.o input.o` even though the output is the same

src/main.zig

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1641,7 +1641,7 @@ fn buildOutputType(
16411641
fatal("only one manifest file can be specified, found '{s}' after '{s}'", .{ arg, other });
16421642
} else manifest_file = arg;
16431643
},
1644-
.assembly, .assembly_with_cpp, .c, .cpp, .h, .ll, .bc, .m, .mm, .cu => {
1644+
.assembly, .assembly_with_cpp, .c, .cpp, .h, .hpp, .hm, .hmm, .ll, .bc, .m, .mm, .cu => {
16451645
try c_source_files.append(.{
16461646
.src_path = arg,
16471647
.extra_flags = try arena.dupe([]const u8, extra_cflags.items),
@@ -1672,6 +1672,11 @@ fn buildOutputType(
16721672
optimize_mode = std.meta.stringToEnum(std.builtin.OptimizeMode, s) orelse
16731673
fatal("unrecognized optimization mode: '{s}'", .{s});
16741674
}
1675+
1676+
// precompiled header syntax: "zig build-obj -x c-header test.h"
1677+
const emit_pch = ((file_ext == .h or file_ext == .hpp or file_ext == .hm or file_ext == .hmm) and emit_bin != .no);
1678+
if (emit_pch)
1679+
clang_preprocessor_mode = .pch;
16751680
},
16761681
.cc, .cpp => {
16771682
if (build_options.only_c) unreachable;
@@ -1693,7 +1698,7 @@ fn buildOutputType(
16931698
assembly,
16941699
preprocessor,
16951700
};
1696-
var c_out_mode: COutMode = .link;
1701+
var c_out_mode: ?COutMode = null;
16971702
var out_path: ?[]const u8 = null;
16981703
var is_shared_lib = false;
16991704
var linker_args = std.ArrayList([]const u8).init(arena);
@@ -1737,7 +1742,7 @@ fn buildOutputType(
17371742
},
17381743
.positional => switch (file_ext orelse
17391744
Compilation.classifyFileExt(mem.sliceTo(it.only_arg, 0))) {
1740-
.assembly, .assembly_with_cpp, .c, .cpp, .ll, .bc, .h, .m, .mm, .cu => {
1745+
.assembly, .assembly_with_cpp, .c, .cpp, .ll, .bc, .h, .hpp, .hm, .hmm, .m, .mm, .cu => {
17411746
try c_source_files.append(.{
17421747
.src_path = it.only_arg,
17431748
.ext = file_ext, // duped while parsing the args.
@@ -2408,7 +2413,12 @@ fn buildOutputType(
24082413
}
24092414
}
24102415

2411-
switch (c_out_mode) {
2416+
// precompiled header syntax: "zig cc -x c-header test.h -o test.pch"
2417+
const emit_pch = ((file_ext == .h or file_ext == .hpp or file_ext == .hm or file_ext == .hmm) and c_out_mode == null);
2418+
if (emit_pch)
2419+
c_out_mode = .preprocessor;
2420+
2421+
switch (c_out_mode orelse .link) {
24122422
.link => {
24132423
output_mode = if (is_shared_lib) .Lib else .Exe;
24142424
emit_bin = if (out_path) |p| .{ .yes = p } else EmitBin.yes_a_out;
@@ -2457,11 +2467,16 @@ fn buildOutputType(
24572467
// For example `zig cc` and no args should print the "no input files" message.
24582468
return process.exit(try clangMain(arena, all_args));
24592469
}
2460-
if (out_path) |p| {
2461-
emit_bin = .{ .yes = p };
2462-
clang_preprocessor_mode = .yes;
2470+
if (emit_pch) {
2471+
emit_bin = if (out_path) |p| .{ .yes = p } else .yes_default_path;
2472+
clang_preprocessor_mode = .pch;
24632473
} else {
2464-
clang_preprocessor_mode = .stdout;
2474+
if (out_path) |p| {
2475+
emit_bin = .{ .yes = p };
2476+
clang_preprocessor_mode = .yes;
2477+
} else {
2478+
clang_preprocessor_mode = .stdout;
2479+
}
24652480
}
24662481
},
24672482
}
@@ -3140,13 +3155,16 @@ fn buildOutputType(
31403155
},
31413156
}
31423157
},
3143-
.basename = try std.zig.binNameAlloc(arena, .{
3144-
.root_name = root_name,
3145-
.target = target_info.target,
3146-
.output_mode = output_mode,
3147-
.link_mode = link_mode,
3148-
.version = optional_version,
3149-
}),
3158+
.basename = if (clang_preprocessor_mode == .pch)
3159+
try std.fmt.allocPrint(arena, "{s}.pch", .{root_name})
3160+
else
3161+
try std.zig.binNameAlloc(arena, .{
3162+
.root_name = root_name,
3163+
.target = target_info.target,
3164+
.output_mode = output_mode,
3165+
.link_mode = link_mode,
3166+
.version = optional_version,
3167+
}),
31503168
},
31513169
.yes => |full_path| b: {
31523170
const basename = fs.path.basename(full_path);

0 commit comments

Comments
 (0)