Skip to content

Commit 1163b3a

Browse files
committed
zig run/cc: recognize "-x language"
This commit adds support for "-x language" for a couple of hand-picked supported languages. There is no reason the list of supported languages to not grow (e.g. add "c-header"), but I'd like to keep it small at the start. Alternative 1 ------------- I first tried to add a new type "Language", and then add that to the `CSourceFile`. But oh boy what a change it turns out to be. So I am keeping myself tied to FileExt and see what you folks think. Alternative 2 ------------- I tried adding `Language: ?[]const u8` to `CSourceFile`. However, the language/ext, whatever we want to call it, still needs to be interpreted in the main loop: one kind of handling for source files, other kind of handling for everything else. Test case --------- *standalone.c* #include <iostream> int main() { std::cout << "elho\n"; } Compile and run: $ ./zig run -x c++ -lc++ standalone.c elho $ ./zig c++ -x c++ standalone.c -o standalone && ./standalone elho Fixes #10915
1 parent b483c79 commit 1163b3a

File tree

4 files changed

+92
-30
lines changed

4 files changed

+92
-30
lines changed

src/Compilation.zig

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -198,12 +198,29 @@ pub const CRTFile = struct {
198198
}
199199
};
200200

201+
// supported languages for "zig clang -x <lang>".
202+
// Loosely based on llvm-project/clang/include/clang/Driver/Types.def
203+
pub const LangToExt = std.ComptimeStringMap(FileExt, .{
204+
.{ "c", .c },
205+
.{ "c-header", .h },
206+
.{ "c++", .cpp },
207+
.{ "c++-header", .h },
208+
.{ "objective-c", .m },
209+
.{ "objective-c-header", .h },
210+
.{ "objective-c++", .mm },
211+
.{ "objective-c++-header", .h },
212+
.{ "assembler", .assembly },
213+
.{ "cuda", .cu },
214+
});
215+
201216
/// For passing to a C compiler.
202217
pub const CSourceFile = struct {
203218
src_path: []const u8,
204219
extra_flags: []const []const u8 = &.{},
205220
/// Same as extra_flags except they are not added to the Cache hash.
206221
cache_exempt_flags: []const []const u8 = &.{},
222+
// this field is non-null iff language was explicitly set with "-x lang".
223+
ext: ?FileExt = null,
207224
};
208225

209226
const Job = union(enum) {
@@ -3880,14 +3897,21 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
38803897
break :e o_ext;
38813898
};
38823899
const o_basename = try std.fmt.allocPrint(arena, "{s}{s}", .{ o_basename_noext, out_ext });
3883-
3884-
try argv.appendSlice(&[_][]const u8{
3885-
self_exe_path,
3886-
"clang",
3887-
c_object.src.src_path,
3888-
});
3889-
3890-
const ext = classifyFileExt(c_object.src.src_path);
3900+
const ext = c_object.src.ext orelse classifyFileExt(c_object.src.src_path);
3901+
3902+
try argv.appendSlice(&[_][]const u8{ self_exe_path, "clang" });
3903+
// if "ext" is explicit, add "-x <lang>". Otherwise let clang do its thing.
3904+
if (c_object.src.ext != null) {
3905+
try argv.appendSlice(&[_][]const u8{ "-x", switch (ext) {
3906+
.c => "c",
3907+
.cpp => "c++",
3908+
.m => "objective-c",
3909+
.mm => "objective-c++",
3910+
.cu => "cuda",
3911+
else => fatal("language '{s}' is unsupported in this context", .{@tagName(ext)}),
3912+
} });
3913+
}
3914+
try argv.append(c_object.src.src_path);
38913915

38923916
// When all these flags are true, it means that the entire purpose of
38933917
// this compilation is to perform a single zig cc operation. This means

src/clang_options_data.zig

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7157,6 +7157,13 @@ joinpd1("d"),
71577157
.psl = true,
71587158
},
71597159
jspd1("u"),
7160-
jspd1("x"),
7160+
.{
7161+
.name = "x",
7162+
.syntax = .joined_or_separate,
7163+
.zig_equivalent = .x,
7164+
.pd1 = true,
7165+
.pd2 = false,
7166+
.psl = false,
7167+
},
71617168
joinpd1("y"),
71627169
};};

src/main.zig

Lines changed: 48 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,7 @@ const usage_build_generic =
381381
\\ -mcmodel=[default|tiny| Limit range of code and data virtual addresses
382382
\\ small|kernel|
383383
\\ medium|large]
384+
\\ -x language Treat subsequent input files as having type <language>
384385
\\ -mred-zone Force-enable the "red-zone"
385386
\\ -mno-red-zone Force-disable the "red-zone"
386387
\\ -fomit-frame-pointer Omit the stack frame pointer
@@ -888,6 +889,7 @@ fn buildOutputType(
888889
var cssan = ClangSearchSanitizer.init(gpa, &clang_argv);
889890
defer cssan.map.deinit();
890891

892+
var file_ext: ?Compilation.FileExt = null;
891893
args_loop: while (args_iter.next()) |arg| {
892894
if (mem.startsWith(u8, arg, "@")) {
893895
// This is a "compiler response file". We must parse the file and treat its
@@ -1368,21 +1370,33 @@ fn buildOutputType(
13681370
try clang_argv.append(arg);
13691371
} else if (mem.startsWith(u8, arg, "-I")) {
13701372
try cssan.addIncludePath(.I, arg, arg[2..], true);
1373+
} else if (mem.eql(u8, arg, "-x")) {
1374+
const lang = args_iter.nextOrFatal();
1375+
if (mem.eql(u8, lang, "none")) {
1376+
file_ext = null;
1377+
} else if (Compilation.LangToExt.get(lang)) |got_ext| {
1378+
file_ext = got_ext;
1379+
} else {
1380+
fatal("language not recognized: '{s}'", .{lang});
1381+
}
13711382
} else if (mem.startsWith(u8, arg, "-mexec-model=")) {
13721383
wasi_exec_model = std.meta.stringToEnum(std.builtin.WasiExecModel, arg["-mexec-model=".len..]) orelse {
13731384
fatal("expected [command|reactor] for -mexec-mode=[value], found '{s}'", .{arg["-mexec-model=".len..]});
13741385
};
13751386
} else {
13761387
fatal("unrecognized parameter: '{s}'", .{arg});
13771388
}
1378-
} else switch (Compilation.classifyFileExt(arg)) {
1389+
} else switch (file_ext orelse
1390+
Compilation.classifyFileExt(arg)) {
13791391
.object, .static_library, .shared_library => {
13801392
try link_objects.append(.{ .path = arg });
13811393
},
13821394
.assembly, .c, .cpp, .h, .ll, .bc, .m, .mm, .cu => {
13831395
try c_source_files.append(.{
13841396
.src_path = arg,
13851397
.extra_flags = try arena.dupe([]const u8, extra_cflags.items),
1398+
// duped when parsing the args.
1399+
.ext = file_ext,
13861400
});
13871401
},
13881402
.zig => {
@@ -1431,6 +1445,7 @@ fn buildOutputType(
14311445
var needed = false;
14321446
var must_link = false;
14331447
var force_static_libs = false;
1448+
var file_ext: ?Compilation.FileExt = null;
14341449
while (it.has_next) {
14351450
it.next() catch |err| {
14361451
fatal("unable to parse command line parameters: {s}", .{@errorName(err)});
@@ -1451,29 +1466,40 @@ fn buildOutputType(
14511466
.asm_only => c_out_mode = .assembly, // -S
14521467
.preprocess_only => c_out_mode = .preprocessor, // -E
14531468
.emit_llvm => emit_llvm = true,
1469+
.x => {
1470+
const lang = mem.sliceTo(it.only_arg, 0);
1471+
if (mem.eql(u8, lang, "none")) {
1472+
file_ext = null;
1473+
} else if (Compilation.LangToExt.get(lang)) |got_ext| {
1474+
file_ext = got_ext;
1475+
} else {
1476+
fatal("language not recognized: '{s}'", .{lang});
1477+
}
1478+
},
14541479
.other => {
14551480
try clang_argv.appendSlice(it.other_args);
14561481
},
1457-
.positional => {
1458-
const file_ext = Compilation.classifyFileExt(mem.sliceTo(it.only_arg, 0));
1459-
switch (file_ext) {
1460-
.assembly, .c, .cpp, .ll, .bc, .h, .m, .mm, .cu => {
1461-
try c_source_files.append(.{ .src_path = it.only_arg });
1462-
},
1463-
.unknown, .shared_library, .object, .static_library => {
1464-
try link_objects.append(.{
1465-
.path = it.only_arg,
1466-
.must_link = must_link,
1467-
});
1468-
},
1469-
.zig => {
1470-
if (root_src_file) |other| {
1471-
fatal("found another zig file '{s}' after root source file '{s}'", .{ it.only_arg, other });
1472-
} else {
1473-
root_src_file = it.only_arg;
1474-
}
1475-
},
1476-
}
1482+
.positional => switch (file_ext orelse
1483+
Compilation.classifyFileExt(mem.sliceTo(it.only_arg, 0))) {
1484+
.assembly, .c, .cpp, .ll, .bc, .h, .m, .mm, .cu => {
1485+
try c_source_files.append(.{
1486+
.src_path = it.only_arg,
1487+
.ext = file_ext, // duped while parsing the args.
1488+
});
1489+
},
1490+
.unknown, .shared_library, .object, .static_library => {
1491+
try link_objects.append(.{
1492+
.path = it.only_arg,
1493+
.must_link = must_link,
1494+
});
1495+
},
1496+
.zig => {
1497+
if (root_src_file) |other| {
1498+
fatal("found another zig file '{s}' after root source file '{s}'", .{ it.only_arg, other });
1499+
} else {
1500+
root_src_file = it.only_arg;
1501+
}
1502+
},
14771503
},
14781504
.l => {
14791505
// -l
@@ -4729,6 +4755,7 @@ pub const ClangArgIterator = struct {
47294755
o,
47304756
c,
47314757
m,
4758+
x,
47324759
other,
47334760
positional,
47344761
l,

tools/update_clang_options.zig

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,10 @@ const known_options = [_]KnownOpt{
492492
.name = "compress-debug-sections=",
493493
.ident = "compress_debug_sections",
494494
},
495+
.{
496+
.name = "x",
497+
.ident = "x",
498+
},
495499
};
496500

497501
const blacklisted_options = [_][]const u8{};

0 commit comments

Comments
 (0)