Skip to content

Commit

Permalink
zig cc: expose clang precompiled C header support
Browse files Browse the repository at this point in the history
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`
  • Loading branch information
xxxbxxx committed Nov 10, 2023
1 parent 42c37d9 commit 5b78d47
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 26 deletions.
34 changes: 24 additions & 10 deletions src/Compilation.zig
Original file line number Diff line number Diff line change
Expand Up @@ -218,11 +218,11 @@ pub const LangToExt = std.ComptimeStringMap(FileExt, .{
.{ "c", .c },
.{ "c-header", .h },
.{ "c++", .cpp },
.{ "c++-header", .h },
.{ "c++-header", .hpp },
.{ "objective-c", .m },
.{ "objective-c-header", .h },
.{ "objective-c-header", .hm },
.{ "objective-c++", .mm },
.{ "objective-c++-header", .h },
.{ "objective-c++-header", .hmm },
.{ "assembler", .assembly },
.{ "assembler-with-cpp", .assembly_with_cpp },
.{ "cuda", .cu },
Expand Down Expand Up @@ -793,6 +793,8 @@ pub const ClangPreprocessorMode = enum {
yes,
/// This means we are doing `zig cc -E`.
stdout,
/// precompiled C header
pch,
};

pub const Framework = link.Framework;
Expand Down Expand Up @@ -4655,6 +4657,10 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
.assembly_with_cpp => "assembler-with-cpp",
.c => "c",
.cpp => "c++",
.h => "c-header",
.hpp => "c++-header",
.hm => "objective-c-header",
.hmm => "objective-c++-header",
.cu => "cuda",
.m => "objective-c",
.mm => "objective-c++",
Expand All @@ -4680,10 +4686,11 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
else
"/dev/null";

try argv.ensureUnusedCapacity(5);
try argv.ensureUnusedCapacity(6);
switch (comp.clang_preprocessor_mode) {
.no => argv.appendSliceAssumeCapacity(&[_][]const u8{ "-c", "-o", out_obj_path }),
.yes => argv.appendSliceAssumeCapacity(&[_][]const u8{ "-E", "-o", out_obj_path }),
.no => argv.appendSliceAssumeCapacity(&.{ "-c", "-o", out_obj_path }),
.yes => argv.appendSliceAssumeCapacity(&.{ "-E", "-o", out_obj_path }),
.pch => argv.appendSliceAssumeCapacity(&.{ "-Xclang", "-emit-pch", "-o", out_obj_path }),
.stdout => argv.appendAssumeCapacity("-E"),
}

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

try argv.ensureUnusedCapacity(5);
try argv.ensureUnusedCapacity(6);
switch (comp.clang_preprocessor_mode) {
.no => argv.appendSliceAssumeCapacity(&.{ "-c", "-o", out_obj_path }),
.yes => argv.appendSliceAssumeCapacity(&.{ "-E", "-o", out_obj_path }),
.pch => argv.appendSliceAssumeCapacity(&.{ "-Xclang", "-emit-pch", "-o", out_obj_path }),
.stdout => argv.appendAssumeCapacity("-E"),
}
if (comp.clang_passthrough_mode) {
Expand Down Expand Up @@ -5345,15 +5353,15 @@ pub fn addCCArgs(
try argv.appendSlice(&[_][]const u8{ "-target", llvm_triple });

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

switch (ext) {
.c, .cpp, .m, .mm, .h, .cu, .rc => {
.c, .cpp, .m, .mm, .h, .hpp, .hm, .hmm, .cu, .rc => {
try argv.appendSlice(&[_][]const u8{
"-nostdinc",
"-fno-spell-checking",
Expand Down Expand Up @@ -5976,6 +5984,9 @@ pub const FileExt = enum {
cpp,
cu,
h,
hpp,
hm,
hmm,
m,
mm,
ll,
Expand All @@ -5994,7 +6005,7 @@ pub const FileExt = enum {

pub fn clangSupportsDepFile(ext: FileExt) bool {
return switch (ext) {
.c, .cpp, .h, .m, .mm, .cu => true,
.c, .cpp, .h, .hpp, .hm, .hmm, .m, .mm, .cu => true,

.ll,
.bc,
Expand All @@ -6019,6 +6030,9 @@ pub const FileExt = enum {
.cpp => ".cpp",
.cu => ".cu",
.h => ".h",
.hpp => ".h",
.hm => ".h",
.hmm => ".h",
.m => ".m",
.mm => ".mm",
.ll => ".ll",
Expand Down
2 changes: 1 addition & 1 deletion src/link.zig
Original file line number Diff line number Diff line change
Expand Up @@ -795,7 +795,7 @@ pub const File = struct {
assert(base.tag == .c);
return @fieldParentPtr(C, "base", base).flush(comp, prog_node);
}
if (comp.clang_preprocessor_mode == .yes) {
if (comp.clang_preprocessor_mode == .yes or comp.clang_preprocessor_mode == .pch) {
const emit = base.options.emit orelse return; // -fno-emit-bin
// TODO: avoid extra link step when it's just 1 object file (the `zig cc -c` case)
// Until then, we do `lld -r -o output.o input.o` even though the output is the same
Expand Down
48 changes: 33 additions & 15 deletions src/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1638,7 +1638,7 @@ fn buildOutputType(
fatal("only one manifest file can be specified, found '{s}' after '{s}'", .{ arg, other });
} else manifest_file = arg;
},
.assembly, .assembly_with_cpp, .c, .cpp, .h, .ll, .bc, .m, .mm, .cu => {
.assembly, .assembly_with_cpp, .c, .cpp, .h, .hpp, .hm, .hmm, .ll, .bc, .m, .mm, .cu => {
try c_source_files.append(.{
.src_path = arg,
.extra_flags = try arena.dupe([]const u8, extra_cflags.items),
Expand Down Expand Up @@ -1669,6 +1669,11 @@ fn buildOutputType(
optimize_mode = std.meta.stringToEnum(std.builtin.OptimizeMode, s) orelse
fatal("unrecognized optimization mode: '{s}'", .{s});
}

// precompiled header syntax: "zig build-obj -x c-header test.h"
const emit_pch = ((file_ext == .h or file_ext == .hpp or file_ext == .hm or file_ext == .hmm) and emit_bin != .no);
if (emit_pch)
clang_preprocessor_mode = .pch;
},
.cc, .cpp => {
if (build_options.only_c) unreachable;
Expand All @@ -1690,7 +1695,7 @@ fn buildOutputType(
assembly,
preprocessor,
};
var c_out_mode: COutMode = .link;
var c_out_mode: ?COutMode = null;
var out_path: ?[]const u8 = null;
var is_shared_lib = false;
var linker_args = std.ArrayList([]const u8).init(arena);
Expand Down Expand Up @@ -1734,7 +1739,7 @@ fn buildOutputType(
},
.positional => switch (file_ext orelse
Compilation.classifyFileExt(mem.sliceTo(it.only_arg, 0))) {
.assembly, .assembly_with_cpp, .c, .cpp, .ll, .bc, .h, .m, .mm, .cu => {
.assembly, .assembly_with_cpp, .c, .cpp, .ll, .bc, .h, .hpp, .hm, .hmm, .m, .mm, .cu => {
try c_source_files.append(.{
.src_path = it.only_arg,
.ext = file_ext, // duped while parsing the args.
Expand Down Expand Up @@ -2405,7 +2410,12 @@ fn buildOutputType(
}
}

switch (c_out_mode) {
// 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;

switch (c_out_mode orelse .link) {
.link => {
output_mode = if (is_shared_lib) .Lib else .Exe;
emit_bin = if (out_path) |p| .{ .yes = p } else EmitBin.yes_a_out;
Expand Down Expand Up @@ -2454,11 +2464,16 @@ fn buildOutputType(
// For example `zig cc` and no args should print the "no input files" message.
return process.exit(try clangMain(arena, all_args));
}
if (out_path) |p| {
emit_bin = .{ .yes = p };
clang_preprocessor_mode = .yes;
if (emit_pch) {
emit_bin = if (out_path) |p| .{ .yes = p } else .yes_default_path;
clang_preprocessor_mode = .pch;
} else {
clang_preprocessor_mode = .stdout;
if (out_path) |p| {
emit_bin = .{ .yes = p };
clang_preprocessor_mode = .yes;
} else {
clang_preprocessor_mode = .stdout;
}
}
},
}
Expand Down Expand Up @@ -3137,13 +3152,16 @@ fn buildOutputType(
},
}
},
.basename = try std.zig.binNameAlloc(arena, .{
.root_name = root_name,
.target = target_info.target,
.output_mode = output_mode,
.link_mode = link_mode,
.version = optional_version,
}),
.basename = if (clang_preprocessor_mode == .pch)
try std.fmt.allocPrint(arena, "{s}.pch", .{root_name})
else
try std.zig.binNameAlloc(arena, .{
.root_name = root_name,
.target = target_info.target,
.output_mode = output_mode,
.link_mode = link_mode,
.version = optional_version,
}),
},
.yes => |full_path| b: {
const basename = fs.path.basename(full_path);
Expand Down

0 comments on commit 5b78d47

Please sign in to comment.