Skip to content

Commit cc96aab

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 3bfae2a commit cc96aab

File tree

4 files changed

+101
-46
lines changed

4 files changed

+101
-46
lines changed

src/Compilation.zig

Lines changed: 41 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -199,12 +199,30 @@ pub const CRTFile = struct {
199199
}
200200
};
201201

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

210228
const Job = union(enum) {
@@ -3898,14 +3916,23 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
38983916
break :e o_ext;
38993917
};
39003918
const o_basename = try std.fmt.allocPrint(arena, "{s}{s}", .{ o_basename_noext, out_ext });
3901-
3902-
try argv.appendSlice(&[_][]const u8{
3903-
self_exe_path,
3904-
"clang",
3905-
c_object.src.src_path,
3906-
});
3907-
3908-
const ext = classifyFileExt(c_object.src.src_path);
3919+
const ext = c_object.src.ext orelse classifyFileExt(c_object.src.src_path);
3920+
3921+
try argv.appendSlice(&[_][]const u8{ self_exe_path, "clang" });
3922+
// if "ext" is explicit, add "-x <lang>". Otherwise let clang do its thing.
3923+
if (c_object.src.ext != null) {
3924+
try argv.appendSlice(&[_][]const u8{ "-x", switch (ext) {
3925+
.assembly => "assembler",
3926+
.assembly_with_cpp => "assembler-with-cpp",
3927+
.c => "c",
3928+
.cpp => "c++",
3929+
.cu => "cuda",
3930+
.m => "objective-c",
3931+
.mm => "objective-c++",
3932+
else => fatal("language '{s}' is unsupported in this context", .{@tagName(ext)}),
3933+
} });
3934+
}
3935+
try argv.append(c_object.src.src_path);
39093936

39103937
// When all these flags are true, it means that the entire purpose of
39113938
// this compilation is to perform a single zig cc operation. This means
@@ -4429,7 +4456,7 @@ pub fn addCCArgs(
44294456
}
44304457
},
44314458
.shared_library, .ll, .bc, .unknown, .static_library, .object, .zig => {},
4432-
.assembly => {
4459+
.assembly, .assembly_with_cpp => {
44334460
// The Clang assembler does not accept the list of CPU features like the
44344461
// compiler frontend does. Therefore we must hard-code the -m flags for
44354462
// all CPU features here.
@@ -4569,6 +4596,7 @@ pub const FileExt = enum {
45694596
ll,
45704597
bc,
45714598
assembly,
4599+
assembly_with_cpp,
45724600
shared_library,
45734601
object,
45744602
static_library,
@@ -4582,6 +4610,7 @@ pub const FileExt = enum {
45824610
.ll,
45834611
.bc,
45844612
.assembly,
4613+
.assembly_with_cpp,
45854614
.shared_library,
45864615
.object,
45874616
.static_library,
@@ -4620,10 +4649,6 @@ pub fn hasObjCppExt(filename: []const u8) bool {
46204649
return mem.endsWith(u8, filename, ".mm");
46214650
}
46224651

4623-
pub fn hasAsmExt(filename: []const u8) bool {
4624-
return mem.endsWith(u8, filename, ".s") or mem.endsWith(u8, filename, ".S");
4625-
}
4626-
46274652
pub fn hasSharedLibraryExt(filename: []const u8) bool {
46284653
if (mem.endsWith(u8, filename, ".so") or
46294654
mem.endsWith(u8, filename, ".dll") or
@@ -4664,8 +4689,10 @@ pub fn classifyFileExt(filename: []const u8) FileExt {
46644689
return .ll;
46654690
} else if (mem.endsWith(u8, filename, ".bc")) {
46664691
return .bc;
4667-
} else if (hasAsmExt(filename)) {
4692+
} else if (mem.endsWith(u8, filename, ".s")) {
46684693
return .assembly;
4694+
} else if (mem.endsWith(u8, filename, ".S")) {
4695+
return .assembly_with_cpp;
46694696
} else if (mem.endsWith(u8, filename, ".h")) {
46704697
return .h;
46714698
} else if (mem.endsWith(u8, filename, ".zig")) {

src/clang_options_data.zig

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7171,6 +7171,13 @@ joinpd1("d"),
71717171
.psl = true,
71727172
},
71737173
jspd1("u"),
7174-
jspd1("x"),
7174+
.{
7175+
.name = "x",
7176+
.syntax = .joined_or_separate,
7177+
.zig_equivalent = .x,
7178+
.pd1 = true,
7179+
.pd2 = false,
7180+
.psl = false,
7181+
},
71757182
joinpd1("y"),
71767183
};};

src/main.zig

Lines changed: 48 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,7 @@ const usage_build_generic =
385385
\\ -mcmodel=[default|tiny| Limit range of code and data virtual addresses
386386
\\ small|kernel|
387387
\\ medium|large]
388+
\\ -x language Treat subsequent input files as having type <language>
388389
\\ -mred-zone Force-enable the "red-zone"
389390
\\ -mno-red-zone Force-disable the "red-zone"
390391
\\ -fomit-frame-pointer Omit the stack frame pointer
@@ -892,6 +893,7 @@ fn buildOutputType(
892893
var cssan = ClangSearchSanitizer.init(gpa, &clang_argv);
893894
defer cssan.map.deinit();
894895

896+
var file_ext: ?Compilation.FileExt = null;
895897
args_loop: while (args_iter.next()) |arg| {
896898
if (mem.startsWith(u8, arg, "@")) {
897899
// This is a "compiler response file". We must parse the file and treat its
@@ -1372,33 +1374,39 @@ fn buildOutputType(
13721374
try clang_argv.append(arg);
13731375
} else if (mem.startsWith(u8, arg, "-I")) {
13741376
try cssan.addIncludePath(.I, arg, arg[2..], true);
1377+
} else if (mem.eql(u8, arg, "-x")) {
1378+
const lang = args_iter.nextOrFatal();
1379+
if (mem.eql(u8, lang, "none")) {
1380+
file_ext = null;
1381+
} else if (Compilation.LangToExt.get(lang)) |got_ext| {
1382+
file_ext = got_ext;
1383+
} else {
1384+
fatal("language not recognized: '{s}'", .{lang});
1385+
}
13751386
} else if (mem.startsWith(u8, arg, "-mexec-model=")) {
13761387
wasi_exec_model = std.meta.stringToEnum(std.builtin.WasiExecModel, arg["-mexec-model=".len..]) orelse {
13771388
fatal("expected [command|reactor] for -mexec-mode=[value], found '{s}'", .{arg["-mexec-model=".len..]});
13781389
};
13791390
} else {
13801391
fatal("unrecognized parameter: '{s}'", .{arg});
13811392
}
1382-
} else switch (Compilation.classifyFileExt(arg)) {
1383-
.object, .static_library, .shared_library => {
1384-
try link_objects.append(.{ .path = arg });
1385-
},
1386-
.assembly, .c, .cpp, .h, .ll, .bc, .m, .mm, .cu => {
1393+
} else switch (file_ext orelse
1394+
Compilation.classifyFileExt(arg)) {
1395+
.object, .static_library, .shared_library => try link_objects.append(.{ .path = arg }),
1396+
.assembly, .assembly_with_cpp, .c, .cpp, .h, .ll, .bc, .m, .mm, .cu => {
13871397
try c_source_files.append(.{
13881398
.src_path = arg,
13891399
.extra_flags = try arena.dupe([]const u8, extra_cflags.items),
1400+
// duped when parsing the args.
1401+
.ext = file_ext,
13901402
});
13911403
},
13921404
.zig => {
13931405
if (root_src_file) |other| {
13941406
fatal("found another zig file '{s}' after root source file '{s}'", .{ arg, other });
1395-
} else {
1396-
root_src_file = arg;
1397-
}
1398-
},
1399-
.unknown => {
1400-
fatal("unrecognized file extension of parameter '{s}'", .{arg});
1407+
} else root_src_file = arg;
14011408
},
1409+
.unknown => fatal("unrecognized file extension of parameter '{s}'", .{arg}),
14021410
}
14031411
}
14041412
if (optimize_mode_string) |s| {
@@ -1435,6 +1443,7 @@ fn buildOutputType(
14351443
var needed = false;
14361444
var must_link = false;
14371445
var force_static_libs = false;
1446+
var file_ext: ?Compilation.FileExt = null;
14381447
while (it.has_next) {
14391448
it.next() catch |err| {
14401449
fatal("unable to parse command line parameters: {s}", .{@errorName(err)});
@@ -1455,29 +1464,36 @@ fn buildOutputType(
14551464
.asm_only => c_out_mode = .assembly, // -S
14561465
.preprocess_only => c_out_mode = .preprocessor, // -E
14571466
.emit_llvm => emit_llvm = true,
1467+
.x => {
1468+
const lang = mem.sliceTo(it.only_arg, 0);
1469+
if (mem.eql(u8, lang, "none")) {
1470+
file_ext = null;
1471+
} else if (Compilation.LangToExt.get(lang)) |got_ext| {
1472+
file_ext = got_ext;
1473+
} else {
1474+
fatal("language not recognized: '{s}'", .{lang});
1475+
}
1476+
},
14581477
.other => {
14591478
try clang_argv.appendSlice(it.other_args);
14601479
},
1461-
.positional => {
1462-
const file_ext = Compilation.classifyFileExt(mem.sliceTo(it.only_arg, 0));
1463-
switch (file_ext) {
1464-
.assembly, .c, .cpp, .ll, .bc, .h, .m, .mm, .cu => {
1465-
try c_source_files.append(.{ .src_path = it.only_arg });
1466-
},
1467-
.unknown, .shared_library, .object, .static_library => {
1468-
try link_objects.append(.{
1469-
.path = it.only_arg,
1470-
.must_link = must_link,
1471-
});
1472-
},
1473-
.zig => {
1474-
if (root_src_file) |other| {
1475-
fatal("found another zig file '{s}' after root source file '{s}'", .{ it.only_arg, other });
1476-
} else {
1477-
root_src_file = it.only_arg;
1478-
}
1479-
},
1480-
}
1480+
.positional => switch (file_ext orelse
1481+
Compilation.classifyFileExt(mem.sliceTo(it.only_arg, 0))) {
1482+
.assembly, .assembly_with_cpp, .c, .cpp, .ll, .bc, .h, .m, .mm, .cu => {
1483+
try c_source_files.append(.{
1484+
.src_path = it.only_arg,
1485+
.ext = file_ext, // duped while parsing the args.
1486+
});
1487+
},
1488+
.unknown, .shared_library, .object, .static_library => try link_objects.append(.{
1489+
.path = it.only_arg,
1490+
.must_link = must_link,
1491+
}),
1492+
.zig => {
1493+
if (root_src_file) |other| {
1494+
fatal("found another zig file '{s}' after root source file '{s}'", .{ it.only_arg, other });
1495+
} else root_src_file = it.only_arg;
1496+
},
14811497
},
14821498
.l => {
14831499
// -l
@@ -4743,6 +4759,7 @@ pub const ClangArgIterator = struct {
47434759
o,
47444760
c,
47454761
m,
4762+
x,
47464763
other,
47474764
positional,
47484765
l,

tools/update_clang_options.zig

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,10 @@ const known_options = [_]KnownOpt{
500500
.name = "undefined",
501501
.ident = "undefined",
502502
},
503+
.{
504+
.name = "x",
505+
.ident = "x",
506+
},
503507
};
504508

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

0 commit comments

Comments
 (0)