authorgravatar for xavierb@gmail.comXavier Bouchoux <xavierb@gmail.com> 2023-11-10 13:59:56+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-01-19 22:05:09-08:00
log1a98fcd00abb7266418532a103214467497d58c6
tree39254a968f9777852e246ec5c803983656c479e4
parent314533c28bdb57f4cdc159fe376a10037e72d5f3

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`

3 files changed, 53 insertions(+), 26 deletions(-)

src/Compilation.zig+24-10
...@@ -270,11 +270,11 @@ pub const LangToExt = std.ComptimeStringMap(FileExt, .{...@@ -270,11 +270,11 @@ pub const LangToExt = std.ComptimeStringMap(FileExt, .{
270 .{ "c", .c },270 .{ "c", .c },
271 .{ "c-header", .h },271 .{ "c-header", .h },
272 .{ "c++", .cpp },272 .{ "c++", .cpp },
273 .{ "c++-header", .h },273 .{ "c++-header", .hpp },
274 .{ "objective-c", .m },274 .{ "objective-c", .m },
275 .{ "objective-c-header", .h },275 .{ "objective-c-header", .hm },
276 .{ "objective-c++", .mm },276 .{ "objective-c++", .mm },
277 .{ "objective-c++-header", .h },277 .{ "objective-c++-header", .hmm },
278 .{ "assembler", .assembly },278 .{ "assembler", .assembly },
279 .{ "assembler-with-cpp", .assembly_with_cpp },279 .{ "assembler-with-cpp", .assembly_with_cpp },
280 .{ "cuda", .cu },280 .{ "cuda", .cu },
...@@ -887,6 +887,8 @@ pub const ClangPreprocessorMode = enum {...@@ -887,6 +887,8 @@ pub const ClangPreprocessorMode = enum {
887 yes,887 yes,
888 /// This means we are doing `zig cc -E`.888 /// This means we are doing `zig cc -E`.
889 stdout,889 stdout,
890 /// precompiled C header
891 pch,
890};892};
891893
892pub const Framework = link.File.MachO.Framework;894pub const Framework = link.File.MachO.Framework;
...@@ -4393,6 +4395,10 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P...@@ -4393,6 +4395,10 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
4393 .assembly_with_cpp => "assembler-with-cpp",4395 .assembly_with_cpp => "assembler-with-cpp",
4394 .c => "c",4396 .c => "c",
4395 .cpp => "c++",4397 .cpp => "c++",
4398 .h => "c-header",
4399 .hpp => "c++-header",
4400 .hm => "objective-c-header",
4401 .hmm => "objective-c++-header",
4396 .cu => "cuda",4402 .cu => "cuda",
4397 .m => "objective-c",4403 .m => "objective-c",
4398 .mm => "objective-c++",4404 .mm => "objective-c++",
...@@ -4418,10 +4424,11 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P...@@ -4418,10 +4424,11 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
4418 else4424 else
4419 "/dev/null";4425 "/dev/null";
44204426
4421 try argv.ensureUnusedCapacity(5);4427 try argv.ensureUnusedCapacity(6);
4422 switch (comp.clang_preprocessor_mode) {4428 switch (comp.clang_preprocessor_mode) {
4423 .no => argv.appendSliceAssumeCapacity(&[_][]const u8{ "-c", "-o", out_obj_path }),4429 .no => argv.appendSliceAssumeCapacity(&.{ "-c", "-o", out_obj_path }),
4424 .yes => argv.appendSliceAssumeCapacity(&[_][]const u8{ "-E", "-o", out_obj_path }),4430 .yes => argv.appendSliceAssumeCapacity(&.{ "-E", "-o", out_obj_path }),
4431 .pch => argv.appendSliceAssumeCapacity(&.{ "-Xclang", "-emit-pch", "-o", out_obj_path }),
4425 .stdout => argv.appendAssumeCapacity("-E"),4432 .stdout => argv.appendAssumeCapacity("-E"),
4426 }4433 }
44274434
...@@ -4456,10 +4463,11 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P...@@ -4456,10 +4463,11 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
4456 try argv.appendSlice(c_object.src.extra_flags);4463 try argv.appendSlice(c_object.src.extra_flags);
4457 try argv.appendSlice(c_object.src.cache_exempt_flags);4464 try argv.appendSlice(c_object.src.cache_exempt_flags);
44584465
4459 try argv.ensureUnusedCapacity(5);4466 try argv.ensureUnusedCapacity(6);
4460 switch (comp.clang_preprocessor_mode) {4467 switch (comp.clang_preprocessor_mode) {
4461 .no => argv.appendSliceAssumeCapacity(&.{ "-c", "-o", out_obj_path }),4468 .no => argv.appendSliceAssumeCapacity(&.{ "-c", "-o", out_obj_path }),
4462 .yes => argv.appendSliceAssumeCapacity(&.{ "-E", "-o", out_obj_path }),4469 .yes => argv.appendSliceAssumeCapacity(&.{ "-E", "-o", out_obj_path }),
4470 .pch => argv.appendSliceAssumeCapacity(&.{ "-Xclang", "-emit-pch", "-o", out_obj_path }),
4463 .stdout => argv.appendAssumeCapacity("-E"),4471 .stdout => argv.appendAssumeCapacity("-E"),
4464 }4472 }
4465 if (comp.clang_passthrough_mode) {4473 if (comp.clang_passthrough_mode) {
...@@ -5101,7 +5109,7 @@ pub fn addCCArgs(...@@ -5101,7 +5109,7 @@ pub fn addCCArgs(
5101 try argv.appendSlice(&[_][]const u8{ "-target", llvm_triple });5109 try argv.appendSlice(&[_][]const u8{ "-target", llvm_triple });
51025110
5103 if (target.os.tag == .windows) switch (ext) {5111 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 => {
5105 const minver: u16 = @truncate(@intFromEnum(target.os.getVersionRange().windows.min) >> 16);5113 const minver: u16 = @truncate(@intFromEnum(target.os.getVersionRange().windows.min) >> 16);
5106 try argv.append(5114 try argv.append(
5107 try std.fmt.allocPrint(arena, "-D_WIN32_WINNT=0x{x:0>4}", .{minver}),5115 try std.fmt.allocPrint(arena, "-D_WIN32_WINNT=0x{x:0>4}", .{minver}),
...@@ -5111,7 +5119,7 @@ pub fn addCCArgs(...@@ -5111,7 +5119,7 @@ pub fn addCCArgs(
5111 };5119 };
51125120
5113 switch (ext) {5121 switch (ext) {
5114 .c, .cpp, .m, .mm, .h, .cu, .rc => {5122 .c, .cpp, .m, .mm, .h, .hpp, .hm, .hmm, .cu, .rc => {
5115 try argv.appendSlice(&[_][]const u8{5123 try argv.appendSlice(&[_][]const u8{
5116 "-nostdinc",5124 "-nostdinc",
5117 "-fno-spell-checking",5125 "-fno-spell-checking",
...@@ -5679,6 +5687,9 @@ pub const FileExt = enum {...@@ -5679,6 +5687,9 @@ pub const FileExt = enum {
5679 cpp,5687 cpp,
5680 cu,5688 cu,
5681 h,5689 h,
5690 hpp,
5691 hm,
5692 hmm,
5682 m,5693 m,
5683 mm,5694 mm,
5684 ll,5695 ll,
...@@ -5697,7 +5708,7 @@ pub const FileExt = enum {...@@ -5697,7 +5708,7 @@ pub const FileExt = enum {
56975708
5698 pub fn clangSupportsDepFile(ext: FileExt) bool {5709 pub fn clangSupportsDepFile(ext: FileExt) bool {
5699 return switch (ext) {5710 return switch (ext) {
5700 .c, .cpp, .h, .m, .mm, .cu => true,5711 .c, .cpp, .h, .hpp, .hm, .hmm, .m, .mm, .cu => true,
57015712
5702 .ll,5713 .ll,
5703 .bc,5714 .bc,
...@@ -5722,6 +5733,9 @@ pub const FileExt = enum {...@@ -5722,6 +5733,9 @@ pub const FileExt = enum {
5722 .cpp => ".cpp",5733 .cpp => ".cpp",
5723 .cu => ".cu",5734 .cu => ".cu",
5724 .h => ".h",5735 .h => ".h",
5736 .hpp => ".h",
5737 .hm => ".h",
5738 .hmm => ".h",
5725 .m => ".m",5739 .m => ".m",
5726 .mm => ".mm",5740 .mm => ".mm",
5727 .ll => ".ll",5741 .ll => ".ll",
src/link.zig+1-1
...@@ -554,7 +554,7 @@ pub const File = struct {...@@ -554,7 +554,7 @@ pub const File = struct {
554 return @fieldParentPtr(C, "base", base).flush(arena, prog_node);554 return @fieldParentPtr(C, "base", base).flush(arena, prog_node);
555 }555 }
556 const comp = base.comp;556 const comp = base.comp;
557 if (comp.clang_preprocessor_mode == .yes) {557 if (comp.clang_preprocessor_mode == .yes or comp.clang_preprocessor_mode == .pch) {
558 const gpa = comp.gpa;558 const gpa = comp.gpa;
559 const emit = base.emit;559 const emit = base.emit;
560 // TODO: avoid extra link step when it's just 1 object file (the `zig cc -c` case)560 // TODO: avoid extra link step when it's just 1 object file (the `zig cc -c` case)
src/main.zig+28-15
...@@ -1694,7 +1694,7 @@ fn buildOutputType(...@@ -1694,7 +1694,7 @@ fn buildOutputType(
1694 fatal("only one manifest file can be specified, found '{s}' after '{s}'", .{ arg, other });1694 fatal("only one manifest file can be specified, found '{s}' after '{s}'", .{ arg, other });
1695 } else manifest_file = arg;1695 } else manifest_file = arg;
1696 },1696 },
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 => {
1698 try create_module.c_source_files.append(arena, .{1698 try create_module.c_source_files.append(arena, .{
1699 // Populated after module creation.1699 // Populated after module creation.
1700 .owner = undefined,1700 .owner = undefined,
...@@ -1746,7 +1746,7 @@ fn buildOutputType(...@@ -1746,7 +1746,7 @@ fn buildOutputType(
1746 assembly,1746 assembly,
1747 preprocessor,1747 preprocessor,
1748 };1748 };
1749 var c_out_mode: COutMode = .link;1749 var c_out_mode: ?COutMode = null;
1750 var out_path: ?[]const u8 = null;1750 var out_path: ?[]const u8 = null;
1751 var is_shared_lib = false;1751 var is_shared_lib = false;
1752 var linker_args = std.ArrayList([]const u8).init(arena);1752 var linker_args = std.ArrayList([]const u8).init(arena);
...@@ -1789,7 +1789,7 @@ fn buildOutputType(...@@ -1789,7 +1789,7 @@ fn buildOutputType(
1789 try cc_argv.appendSlice(arena, it.other_args);1789 try cc_argv.appendSlice(arena, it.other_args);
1790 },1790 },
1791 .positional => switch (file_ext orelse Compilation.classifyFileExt(mem.sliceTo(it.only_arg, 0))) {1791 .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 => {
1793 try create_module.c_source_files.append(arena, .{1793 try create_module.c_source_files.append(arena, .{
1794 // Populated after module creation.1794 // Populated after module creation.
1795 .owner = undefined,1795 .owner = undefined,
...@@ -2462,7 +2462,12 @@ fn buildOutputType(...@@ -2462,7 +2462,12 @@ fn buildOutputType(
2462 }2462 }
2463 }2463 }
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) {
2466 .link => {2471 .link => {
2467 create_module.opts.output_mode = if (is_shared_lib) .Lib else .Exe;2472 create_module.opts.output_mode = if (is_shared_lib) .Lib else .Exe;
2468 emit_bin = if (out_path) |p| .{ .yes = p } else EmitBin.yes_a_out;2473 emit_bin = if (out_path) |p| .{ .yes = p } else EmitBin.yes_a_out;
...@@ -2511,11 +2516,16 @@ fn buildOutputType(...@@ -2511,11 +2516,16 @@ fn buildOutputType(
2511 // For example `zig cc` and no args should print the "no input files" message.2516 // For example `zig cc` and no args should print the "no input files" message.
2512 return process.exit(try clangMain(arena, all_args));2517 return process.exit(try clangMain(arena, all_args));
2513 }2518 }
2514 if (out_path) |p| {2519 if (emit_pch) {
2515 emit_bin = .{ .yes = p };2520 emit_bin = if (out_path) |p| .{ .yes = p } else .yes_default_path;
2516 clang_preprocessor_mode = .yes;2521 clang_preprocessor_mode = .pch;
2517 } else {2522 } 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 }
2519 }2529 }
2520 },2530 },
2521 }2531 }
...@@ -2882,13 +2892,16 @@ fn buildOutputType(...@@ -2882,13 +2892,16 @@ fn buildOutputType(
2882 },2892 },
2883 }2893 }
2884 },2894 },
2885 .basename = try std.zig.binNameAlloc(arena, .{2895 .basename = if (clang_preprocessor_mode == .pch)
2886 .root_name = root_name,2896 try std.fmt.allocPrint(arena, "{s}.pch", .{root_name})
2887 .target = target,2897 else
2888 .output_mode = create_module.resolved_options.output_mode,2898 try std.zig.binNameAlloc(arena, .{
2889 .link_mode = create_module.resolved_options.link_mode,2899 .root_name = root_name,
2890 .version = optional_version,2900 .target = target,
2891 }),2901 .output_mode = create_module.resolved_options.output_mode,
2902 .link_mode = create_module.resolved_options.link_mode,
2903 .version = optional_version,
2904 }),
2892 },2905 },
2893 .yes => |full_path| b: {2906 .yes => |full_path| b: {
2894 const basename = fs.path.basename(full_path);2907 const basename = fs.path.basename(full_path);