Skip to content

Commit f8e181a

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`
1 parent b80cad2 commit f8e181a

File tree

3 files changed

+53
-26
lines changed

3 files changed

+53
-26
lines changed

src/Compilation.zig

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -270,11 +270,11 @@ pub const LangToExt = std.ComptimeStringMap(FileExt, .{
270270
.{ "c", .c },
271271
.{ "c-header", .h },
272272
.{ "c++", .cpp },
273-
.{ "c++-header", .h },
273+
.{ "c++-header", .hpp },
274274
.{ "objective-c", .m },
275-
.{ "objective-c-header", .h },
275+
.{ "objective-c-header", .hm },
276276
.{ "objective-c++", .mm },
277-
.{ "objective-c++-header", .h },
277+
.{ "objective-c++-header", .hmm },
278278
.{ "assembler", .assembly },
279279
.{ "assembler-with-cpp", .assembly_with_cpp },
280280
.{ "cuda", .cu },
@@ -887,6 +887,8 @@ pub const ClangPreprocessorMode = enum {
887887
yes,
888888
/// This means we are doing `zig cc -E`.
889889
stdout,
890+
/// precompiled C header
891+
pch,
890892
};
891893

892894
pub const Framework = link.File.MachO.Framework;
@@ -4393,6 +4395,10 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
43934395
.assembly_with_cpp => "assembler-with-cpp",
43944396
.c => "c",
43954397
.cpp => "c++",
4398+
.h => "c-header",
4399+
.hpp => "c++-header",
4400+
.hm => "objective-c-header",
4401+
.hmm => "objective-c++-header",
43964402
.cu => "cuda",
43974403
.m => "objective-c",
43984404
.mm => "objective-c++",
@@ -4418,10 +4424,11 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
44184424
else
44194425
"/dev/null";
44204426

4421-
try argv.ensureUnusedCapacity(5);
4427+
try argv.ensureUnusedCapacity(6);
44224428
switch (comp.clang_preprocessor_mode) {
4423-
.no => argv.appendSliceAssumeCapacity(&[_][]const u8{ "-c", "-o", out_obj_path }),
4424-
.yes => argv.appendSliceAssumeCapacity(&[_][]const u8{ "-E", "-o", out_obj_path }),
4429+
.no => argv.appendSliceAssumeCapacity(&.{ "-c", "-o", out_obj_path }),
4430+
.yes => argv.appendSliceAssumeCapacity(&.{ "-E", "-o", out_obj_path }),
4431+
.pch => argv.appendSliceAssumeCapacity(&.{ "-Xclang", "-emit-pch", "-o", out_obj_path }),
44254432
.stdout => argv.appendAssumeCapacity("-E"),
44264433
}
44274434

@@ -4456,10 +4463,11 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
44564463
try argv.appendSlice(c_object.src.extra_flags);
44574464
try argv.appendSlice(c_object.src.cache_exempt_flags);
44584465

4459-
try argv.ensureUnusedCapacity(5);
4466+
try argv.ensureUnusedCapacity(6);
44604467
switch (comp.clang_preprocessor_mode) {
44614468
.no => argv.appendSliceAssumeCapacity(&.{ "-c", "-o", out_obj_path }),
44624469
.yes => argv.appendSliceAssumeCapacity(&.{ "-E", "-o", out_obj_path }),
4470+
.pch => argv.appendSliceAssumeCapacity(&.{ "-Xclang", "-emit-pch", "-o", out_obj_path }),
44634471
.stdout => argv.appendAssumeCapacity("-E"),
44644472
}
44654473
if (comp.clang_passthrough_mode) {
@@ -5101,7 +5109,7 @@ pub fn addCCArgs(
51015109
try argv.appendSlice(&[_][]const u8{ "-target", llvm_triple });
51025110

51035111
if (target.os.tag == .windows) switch (ext) {
5104-
.c, .cpp, .m, .mm, .h, .cu, .rc, .assembly, .assembly_with_cpp => {
5112+
.c, .cpp, .m, .mm, .h, .hpp, .hm, .hmm, .cu, .rc, .assembly, .assembly_with_cpp => {
51055113
const minver: u16 = @truncate(@intFromEnum(target.os.getVersionRange().windows.min) >> 16);
51065114
try argv.append(
51075115
try std.fmt.allocPrint(arena, "-D_WIN32_WINNT=0x{x:0>4}", .{minver}),
@@ -5111,7 +5119,7 @@ pub fn addCCArgs(
51115119
};
51125120

51135121
switch (ext) {
5114-
.c, .cpp, .m, .mm, .h, .cu, .rc => {
5122+
.c, .cpp, .m, .mm, .h, .hpp, .hm, .hmm, .cu, .rc => {
51155123
try argv.appendSlice(&[_][]const u8{
51165124
"-nostdinc",
51175125
"-fno-spell-checking",
@@ -5679,6 +5687,9 @@ pub const FileExt = enum {
56795687
cpp,
56805688
cu,
56815689
h,
5690+
hpp,
5691+
hm,
5692+
hmm,
56825693
m,
56835694
mm,
56845695
ll,
@@ -5697,7 +5708,7 @@ pub const FileExt = enum {
56975708

56985709
pub fn clangSupportsDepFile(ext: FileExt) bool {
56995710
return switch (ext) {
5700-
.c, .cpp, .h, .m, .mm, .cu => true,
5711+
.c, .cpp, .h, .hpp, .hm, .hmm, .m, .mm, .cu => true,
57015712

57025713
.ll,
57035714
.bc,
@@ -5722,6 +5733,9 @@ pub const FileExt = enum {
57225733
.cpp => ".cpp",
57235734
.cu => ".cu",
57245735
.h => ".h",
5736+
.hpp => ".h",
5737+
.hm => ".h",
5738+
.hmm => ".h",
57255739
.m => ".m",
57265740
.mm => ".mm",
57275741
.ll => ".ll",

src/link.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -554,7 +554,7 @@ pub const File = struct {
554554
return @fieldParentPtr(C, "base", base).flush(arena, prog_node);
555555
}
556556
const comp = base.comp;
557-
if (comp.clang_preprocessor_mode == .yes) {
557+
if (comp.clang_preprocessor_mode == .yes or comp.clang_preprocessor_mode == .pch) {
558558
const gpa = comp.gpa;
559559
const emit = base.emit;
560560
// TODO: avoid extra link step when it's just 1 object file (the `zig cc -c` case)

src/main.zig

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1694,7 +1694,7 @@ fn buildOutputType(
16941694
fatal("only one manifest file can be specified, found '{s}' after '{s}'", .{ arg, other });
16951695
} else manifest_file = arg;
16961696
},
1697-
.assembly, .assembly_with_cpp, .c, .cpp, .h, .ll, .bc, .m, .mm, .cu => {
1697+
.assembly, .assembly_with_cpp, .c, .cpp, .h, .hpp, .hm, .hmm, .ll, .bc, .m, .mm, .cu => {
16981698
try create_module.c_source_files.append(arena, .{
16991699
// Populated after module creation.
17001700
.owner = undefined,
@@ -1746,7 +1746,7 @@ fn buildOutputType(
17461746
assembly,
17471747
preprocessor,
17481748
};
1749-
var c_out_mode: COutMode = .link;
1749+
var c_out_mode: ?COutMode = null;
17501750
var out_path: ?[]const u8 = null;
17511751
var is_shared_lib = false;
17521752
var linker_args = std.ArrayList([]const u8).init(arena);
@@ -1789,7 +1789,7 @@ fn buildOutputType(
17891789
try cc_argv.appendSlice(arena, it.other_args);
17901790
},
17911791
.positional => switch (file_ext orelse Compilation.classifyFileExt(mem.sliceTo(it.only_arg, 0))) {
1792-
.assembly, .assembly_with_cpp, .c, .cpp, .ll, .bc, .h, .m, .mm, .cu => {
1792+
.assembly, .assembly_with_cpp, .c, .cpp, .ll, .bc, .h, .hpp, .hm, .hmm, .m, .mm, .cu => {
17931793
try create_module.c_source_files.append(arena, .{
17941794
// Populated after module creation.
17951795
.owner = undefined,
@@ -2462,7 +2462,12 @@ fn buildOutputType(
24622462
}
24632463
}
24642464

2465-
switch (c_out_mode) {
2465+
// precompiled header syntax: "zig cc -x c-header test.h -o test.pch"
2466+
const emit_pch = ((file_ext == .h or file_ext == .hpp or file_ext == .hm or file_ext == .hmm) and c_out_mode == null);
2467+
if (emit_pch)
2468+
c_out_mode = .preprocessor;
2469+
2470+
switch (c_out_mode orelse .link) {
24662471
.link => {
24672472
create_module.opts.output_mode = if (is_shared_lib) .Lib else .Exe;
24682473
emit_bin = if (out_path) |p| .{ .yes = p } else EmitBin.yes_a_out;
@@ -2511,11 +2516,16 @@ fn buildOutputType(
25112516
// For example `zig cc` and no args should print the "no input files" message.
25122517
return process.exit(try clangMain(arena, all_args));
25132518
}
2514-
if (out_path) |p| {
2515-
emit_bin = .{ .yes = p };
2516-
clang_preprocessor_mode = .yes;
2519+
if (emit_pch) {
2520+
emit_bin = if (out_path) |p| .{ .yes = p } else .yes_default_path;
2521+
clang_preprocessor_mode = .pch;
25172522
} else {
2518-
clang_preprocessor_mode = .stdout;
2523+
if (out_path) |p| {
2524+
emit_bin = .{ .yes = p };
2525+
clang_preprocessor_mode = .yes;
2526+
} else {
2527+
clang_preprocessor_mode = .stdout;
2528+
}
25192529
}
25202530
},
25212531
}
@@ -2882,13 +2892,16 @@ fn buildOutputType(
28822892
},
28832893
}
28842894
},
2885-
.basename = try std.zig.binNameAlloc(arena, .{
2886-
.root_name = root_name,
2887-
.target = target,
2888-
.output_mode = create_module.resolved_options.output_mode,
2889-
.link_mode = create_module.resolved_options.link_mode,
2890-
.version = optional_version,
2891-
}),
2895+
.basename = if (clang_preprocessor_mode == .pch)
2896+
try std.fmt.allocPrint(arena, "{s}.pch", .{root_name})
2897+
else
2898+
try std.zig.binNameAlloc(arena, .{
2899+
.root_name = root_name,
2900+
.target = target,
2901+
.output_mode = create_module.resolved_options.output_mode,
2902+
.link_mode = create_module.resolved_options.link_mode,
2903+
.version = optional_version,
2904+
}),
28922905
},
28932906
.yes => |full_path| b: {
28942907
const basename = fs.path.basename(full_path);

0 commit comments

Comments
 (0)