Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 5b78d47

Browse files
committedNov 10, 2023
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 42c37d9 commit 5b78d47

File tree

3 files changed

+58
-26
lines changed

3 files changed

+58
-26
lines changed
 

Diff for: ‎src/Compilation.zig

+24-10
Original file line numberDiff line numberDiff line change
@@ -218,11 +218,11 @@ pub const LangToExt = std.ComptimeStringMap(FileExt, .{
218218
.{ "c", .c },
219219
.{ "c-header", .h },
220220
.{ "c++", .cpp },
221-
.{ "c++-header", .h },
221+
.{ "c++-header", .hpp },
222222
.{ "objective-c", .m },
223-
.{ "objective-c-header", .h },
223+
.{ "objective-c-header", .hm },
224224
.{ "objective-c++", .mm },
225-
.{ "objective-c++-header", .h },
225+
.{ "objective-c++-header", .hmm },
226226
.{ "assembler", .assembly },
227227
.{ "assembler-with-cpp", .assembly_with_cpp },
228228
.{ "cuda", .cu },
@@ -793,6 +793,8 @@ pub const ClangPreprocessorMode = enum {
793793
yes,
794794
/// This means we are doing `zig cc -E`.
795795
stdout,
796+
/// precompiled C header
797+
pch,
796798
};
797799

798800
pub const Framework = link.Framework;
@@ -4655,6 +4657,10 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
46554657
.assembly_with_cpp => "assembler-with-cpp",
46564658
.c => "c",
46574659
.cpp => "c++",
4660+
.h => "c-header",
4661+
.hpp => "c++-header",
4662+
.hm => "objective-c-header",
4663+
.hmm => "objective-c++-header",
46584664
.cu => "cuda",
46594665
.m => "objective-c",
46604666
.mm => "objective-c++",
@@ -4680,10 +4686,11 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
46804686
else
46814687
"/dev/null";
46824688

4683-
try argv.ensureUnusedCapacity(5);
4689+
try argv.ensureUnusedCapacity(6);
46844690
switch (comp.clang_preprocessor_mode) {
4685-
.no => argv.appendSliceAssumeCapacity(&[_][]const u8{ "-c", "-o", out_obj_path }),
4686-
.yes => argv.appendSliceAssumeCapacity(&[_][]const u8{ "-E", "-o", out_obj_path }),
4691+
.no => argv.appendSliceAssumeCapacity(&.{ "-c", "-o", out_obj_path }),
4692+
.yes => argv.appendSliceAssumeCapacity(&.{ "-E", "-o", out_obj_path }),
4693+
.pch => argv.appendSliceAssumeCapacity(&.{ "-Xclang", "-emit-pch", "-o", out_obj_path }),
46874694
.stdout => argv.appendAssumeCapacity("-E"),
46884695
}
46894696

@@ -4718,10 +4725,11 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
47184725
try argv.appendSlice(c_object.src.extra_flags);
47194726
try argv.appendSlice(c_object.src.cache_exempt_flags);
47204727

4721-
try argv.ensureUnusedCapacity(5);
4728+
try argv.ensureUnusedCapacity(6);
47224729
switch (comp.clang_preprocessor_mode) {
47234730
.no => argv.appendSliceAssumeCapacity(&.{ "-c", "-o", out_obj_path }),
47244731
.yes => argv.appendSliceAssumeCapacity(&.{ "-E", "-o", out_obj_path }),
4732+
.pch => argv.appendSliceAssumeCapacity(&.{ "-Xclang", "-emit-pch", "-o", out_obj_path }),
47254733
.stdout => argv.appendAssumeCapacity("-E"),
47264734
}
47274735
if (comp.clang_passthrough_mode) {
@@ -5345,15 +5353,15 @@ pub fn addCCArgs(
53455353
try argv.appendSlice(&[_][]const u8{ "-target", llvm_triple });
53465354

53475355
if (target.os.tag == .windows) switch (ext) {
5348-
.c, .cpp, .m, .mm, .h, .cu, .rc, .assembly, .assembly_with_cpp => {
5356+
.c, .cpp, .m, .mm, .h, .hpp, .hm, .hmm, .cu, .rc, .assembly, .assembly_with_cpp => {
53495357
const minver: u16 = @truncate(@intFromEnum(target.os.getVersionRange().windows.min) >> 16);
53505358
try argv.append(try std.fmt.allocPrint(argv.allocator, "-D_WIN32_WINNT=0x{x:0>4}", .{minver}));
53515359
},
53525360
else => {},
53535361
};
53545362

53555363
switch (ext) {
5356-
.c, .cpp, .m, .mm, .h, .cu, .rc => {
5364+
.c, .cpp, .m, .mm, .h, .hpp, .hm, .hmm, .cu, .rc => {
53575365
try argv.appendSlice(&[_][]const u8{
53585366
"-nostdinc",
53595367
"-fno-spell-checking",
@@ -5976,6 +5984,9 @@ pub const FileExt = enum {
59765984
cpp,
59775985
cu,
59785986
h,
5987+
hpp,
5988+
hm,
5989+
hmm,
59795990
m,
59805991
mm,
59815992
ll,
@@ -5994,7 +6005,7 @@ pub const FileExt = enum {
59946005

59956006
pub fn clangSupportsDepFile(ext: FileExt) bool {
59966007
return switch (ext) {
5997-
.c, .cpp, .h, .m, .mm, .cu => true,
6008+
.c, .cpp, .h, .hpp, .hm, .hmm, .m, .mm, .cu => true,
59986009

59996010
.ll,
60006011
.bc,
@@ -6019,6 +6030,9 @@ pub const FileExt = enum {
60196030
.cpp => ".cpp",
60206031
.cu => ".cu",
60216032
.h => ".h",
6033+
.hpp => ".h",
6034+
.hm => ".h",
6035+
.hmm => ".h",
60226036
.m => ".m",
60236037
.mm => ".mm",
60246038
.ll => ".ll",

Diff for: ‎src/link.zig

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

Diff for: ‎src/main.zig

+33-15
Original file line numberDiff line numberDiff line change
@@ -1638,7 +1638,7 @@ fn buildOutputType(
16381638
fatal("only one manifest file can be specified, found '{s}' after '{s}'", .{ arg, other });
16391639
} else manifest_file = arg;
16401640
},
1641-
.assembly, .assembly_with_cpp, .c, .cpp, .h, .ll, .bc, .m, .mm, .cu => {
1641+
.assembly, .assembly_with_cpp, .c, .cpp, .h, .hpp, .hm, .hmm, .ll, .bc, .m, .mm, .cu => {
16421642
try c_source_files.append(.{
16431643
.src_path = arg,
16441644
.extra_flags = try arena.dupe([]const u8, extra_cflags.items),
@@ -1669,6 +1669,11 @@ fn buildOutputType(
16691669
optimize_mode = std.meta.stringToEnum(std.builtin.OptimizeMode, s) orelse
16701670
fatal("unrecognized optimization mode: '{s}'", .{s});
16711671
}
1672+
1673+
// precompiled header syntax: "zig build-obj -x c-header test.h"
1674+
const emit_pch = ((file_ext == .h or file_ext == .hpp or file_ext == .hm or file_ext == .hmm) and emit_bin != .no);
1675+
if (emit_pch)
1676+
clang_preprocessor_mode = .pch;
16721677
},
16731678
.cc, .cpp => {
16741679
if (build_options.only_c) unreachable;
@@ -1690,7 +1695,7 @@ fn buildOutputType(
16901695
assembly,
16911696
preprocessor,
16921697
};
1693-
var c_out_mode: COutMode = .link;
1698+
var c_out_mode: ?COutMode = null;
16941699
var out_path: ?[]const u8 = null;
16951700
var is_shared_lib = false;
16961701
var linker_args = std.ArrayList([]const u8).init(arena);
@@ -1734,7 +1739,7 @@ fn buildOutputType(
17341739
},
17351740
.positional => switch (file_ext orelse
17361741
Compilation.classifyFileExt(mem.sliceTo(it.only_arg, 0))) {
1737-
.assembly, .assembly_with_cpp, .c, .cpp, .ll, .bc, .h, .m, .mm, .cu => {
1742+
.assembly, .assembly_with_cpp, .c, .cpp, .ll, .bc, .h, .hpp, .hm, .hmm, .m, .mm, .cu => {
17381743
try c_source_files.append(.{
17391744
.src_path = it.only_arg,
17401745
.ext = file_ext, // duped while parsing the args.
@@ -2405,7 +2410,12 @@ fn buildOutputType(
24052410
}
24062411
}
24072412

2408-
switch (c_out_mode) {
2413+
// precompiled header syntax: "zig cc -x c-header test.h -o test.pch"
2414+
const emit_pch = ((file_ext == .h or file_ext == .hpp or file_ext == .hm or file_ext == .hmm) and c_out_mode == null);
2415+
if (emit_pch)
2416+
c_out_mode = .preprocessor;
2417+
2418+
switch (c_out_mode orelse .link) {
24092419
.link => {
24102420
output_mode = if (is_shared_lib) .Lib else .Exe;
24112421
emit_bin = if (out_path) |p| .{ .yes = p } else EmitBin.yes_a_out;
@@ -2454,11 +2464,16 @@ fn buildOutputType(
24542464
// For example `zig cc` and no args should print the "no input files" message.
24552465
return process.exit(try clangMain(arena, all_args));
24562466
}
2457-
if (out_path) |p| {
2458-
emit_bin = .{ .yes = p };
2459-
clang_preprocessor_mode = .yes;
2467+
if (emit_pch) {
2468+
emit_bin = if (out_path) |p| .{ .yes = p } else .yes_default_path;
2469+
clang_preprocessor_mode = .pch;
24602470
} else {
2461-
clang_preprocessor_mode = .stdout;
2471+
if (out_path) |p| {
2472+
emit_bin = .{ .yes = p };
2473+
clang_preprocessor_mode = .yes;
2474+
} else {
2475+
clang_preprocessor_mode = .stdout;
2476+
}
24622477
}
24632478
},
24642479
}
@@ -3137,13 +3152,16 @@ fn buildOutputType(
31373152
},
31383153
}
31393154
},
3140-
.basename = try std.zig.binNameAlloc(arena, .{
3141-
.root_name = root_name,
3142-
.target = target_info.target,
3143-
.output_mode = output_mode,
3144-
.link_mode = link_mode,
3145-
.version = optional_version,
3146-
}),
3155+
.basename = if (clang_preprocessor_mode == .pch)
3156+
try std.fmt.allocPrint(arena, "{s}.pch", .{root_name})
3157+
else
3158+
try std.zig.binNameAlloc(arena, .{
3159+
.root_name = root_name,
3160+
.target = target_info.target,
3161+
.output_mode = output_mode,
3162+
.link_mode = link_mode,
3163+
.version = optional_version,
3164+
}),
31473165
},
31483166
.yes => |full_path| b: {
31493167
const basename = fs.path.basename(full_path);

0 commit comments

Comments
 (0)