Skip to content

Commit 6b3f59c

Browse files
motiejusandrewrk
authored andcommitted
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 d813cef commit 6b3f59c

File tree

5 files changed

+105
-47
lines changed

5 files changed

+105
-47
lines changed

src/Compilation.zig

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -192,12 +192,30 @@ pub const CRTFile = struct {
192192
}
193193
};
194194

195+
// supported languages for "zig clang -x <lang>".
196+
// Loosely based on llvm-project/clang/include/clang/Driver/Types.def
197+
pub const LangToExt = std.ComptimeStringMap(FileExt, .{
198+
.{ "c", .c },
199+
.{ "c-header", .h },
200+
.{ "c++", .cpp },
201+
.{ "c++-header", .h },
202+
.{ "objective-c", .m },
203+
.{ "objective-c-header", .h },
204+
.{ "objective-c++", .mm },
205+
.{ "objective-c++-header", .h },
206+
.{ "assembler", .assembly },
207+
.{ "assembler-with-cpp", .assembly_with_cpp },
208+
.{ "cuda", .cu },
209+
});
210+
195211
/// For passing to a C compiler.
196212
pub const CSourceFile = struct {
197213
src_path: []const u8,
198214
extra_flags: []const []const u8 = &.{},
199215
/// Same as extra_flags except they are not added to the Cache hash.
200216
cache_exempt_flags: []const []const u8 = &.{},
217+
// this field is non-null iff language was explicitly set with "-x lang".
218+
ext: ?FileExt = null,
201219
};
202220

203221
const Job = union(enum) {
@@ -2612,6 +2630,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes
26122630

26132631
for (comp.c_object_table.keys()) |key| {
26142632
_ = try man.addFile(key.src.src_path, null);
2633+
man.hash.addOptional(key.src.ext);
26152634
man.hash.addListOfBytes(key.src.extra_flags);
26162635
}
26172636

@@ -3926,14 +3945,23 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
39263945
break :e o_ext;
39273946
};
39283947
const o_basename = try std.fmt.allocPrint(arena, "{s}{s}", .{ o_basename_noext, out_ext });
3929-
3930-
try argv.appendSlice(&[_][]const u8{
3931-
self_exe_path,
3932-
"clang",
3933-
c_object.src.src_path,
3934-
});
3935-
3936-
const ext = classifyFileExt(c_object.src.src_path);
3948+
const ext = c_object.src.ext orelse classifyFileExt(c_object.src.src_path);
3949+
3950+
try argv.appendSlice(&[_][]const u8{ self_exe_path, "clang" });
3951+
// if "ext" is explicit, add "-x <lang>". Otherwise let clang do its thing.
3952+
if (c_object.src.ext != null) {
3953+
try argv.appendSlice(&[_][]const u8{ "-x", switch (ext) {
3954+
.assembly => "assembler",
3955+
.assembly_with_cpp => "assembler-with-cpp",
3956+
.c => "c",
3957+
.cpp => "c++",
3958+
.cu => "cuda",
3959+
.m => "objective-c",
3960+
.mm => "objective-c++",
3961+
else => fatal("language '{s}' is unsupported in this context", .{@tagName(ext)}),
3962+
} });
3963+
}
3964+
try argv.append(c_object.src.src_path);
39373965

39383966
// When all these flags are true, it means that the entire purpose of
39393967
// this compilation is to perform a single zig cc operation. This means
@@ -4395,7 +4423,7 @@ pub fn addCCArgs(
43954423
}
43964424
},
43974425
.shared_library, .ll, .bc, .unknown, .static_library, .object, .def, .zig => {},
4398-
.assembly => {
4426+
.assembly, .assembly_with_cpp => {
43994427
// The Clang assembler does not accept the list of CPU features like the
44004428
// compiler frontend does. Therefore we must hard-code the -m flags for
44014429
// all CPU features here.
@@ -4535,6 +4563,7 @@ pub const FileExt = enum {
45354563
ll,
45364564
bc,
45374565
assembly,
4566+
assembly_with_cpp,
45384567
shared_library,
45394568
object,
45404569
static_library,
@@ -4549,6 +4578,7 @@ pub const FileExt = enum {
45494578
.ll,
45504579
.bc,
45514580
.assembly,
4581+
.assembly_with_cpp,
45524582
.shared_library,
45534583
.object,
45544584
.static_library,
@@ -4588,10 +4618,6 @@ pub fn hasObjCppExt(filename: []const u8) bool {
45884618
return mem.endsWith(u8, filename, ".mm");
45894619
}
45904620

4591-
pub fn hasAsmExt(filename: []const u8) bool {
4592-
return mem.endsWith(u8, filename, ".s") or mem.endsWith(u8, filename, ".S");
4593-
}
4594-
45954621
pub fn hasSharedLibraryExt(filename: []const u8) bool {
45964622
if (mem.endsWith(u8, filename, ".so") or
45974623
mem.endsWith(u8, filename, ".dll") or
@@ -4632,8 +4658,10 @@ pub fn classifyFileExt(filename: []const u8) FileExt {
46324658
return .ll;
46334659
} else if (mem.endsWith(u8, filename, ".bc")) {
46344660
return .bc;
4635-
} else if (hasAsmExt(filename)) {
4661+
} else if (mem.endsWith(u8, filename, ".s")) {
46364662
return .assembly;
4663+
} else if (mem.endsWith(u8, filename, ".S")) {
4664+
return .assembly_with_cpp;
46374665
} else if (mem.endsWith(u8, filename, ".h")) {
46384666
return .h;
46394667
} 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/libunwind.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ pub fn buildStaticLib(comp: *Compilation) !void {
4848
try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxx", "include" }),
4949
});
5050
},
51-
.assembly => {},
51+
.assembly_with_cpp => {},
5252
else => unreachable, // You can see the entire list of files just above.
5353
}
5454
try cflags.append("-I");

src/main.zig

Lines changed: 50 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,7 @@ const usage_build_generic =
391391
\\ -mcmodel=[default|tiny| Limit range of code and data virtual addresses
392392
\\ small|kernel|
393393
\\ medium|large]
394+
\\ -x language Treat subsequent input files as having type <language>
394395
\\ -mred-zone Force-enable the "red-zone"
395396
\\ -mno-red-zone Force-disable the "red-zone"
396397
\\ -fomit-frame-pointer Omit the stack frame pointer
@@ -913,6 +914,7 @@ fn buildOutputType(
913914
var cssan = ClangSearchSanitizer.init(gpa, &clang_argv);
914915
defer cssan.map.deinit();
915916

917+
var file_ext: ?Compilation.FileExt = null;
916918
args_loop: while (args_iter.next()) |arg| {
917919
if (mem.startsWith(u8, arg, "@")) {
918920
// This is a "compiler response file". We must parse the file and treat its
@@ -1401,29 +1403,37 @@ fn buildOutputType(
14011403
try clang_argv.append(arg);
14021404
} else if (mem.startsWith(u8, arg, "-I")) {
14031405
try cssan.addIncludePath(.I, arg, arg[2..], true);
1406+
} else if (mem.eql(u8, arg, "-x")) {
1407+
const lang = args_iter.nextOrFatal();
1408+
if (mem.eql(u8, lang, "none")) {
1409+
file_ext = null;
1410+
} else if (Compilation.LangToExt.get(lang)) |got_ext| {
1411+
file_ext = got_ext;
1412+
} else {
1413+
fatal("language not recognized: '{s}'", .{lang});
1414+
}
14041415
} else if (mem.startsWith(u8, arg, "-mexec-model=")) {
14051416
wasi_exec_model = std.meta.stringToEnum(std.builtin.WasiExecModel, arg["-mexec-model=".len..]) orelse {
14061417
fatal("expected [command|reactor] for -mexec-mode=[value], found '{s}'", .{arg["-mexec-model=".len..]});
14071418
};
14081419
} else {
14091420
fatal("unrecognized parameter: '{s}'", .{arg});
14101421
}
1411-
} else switch (Compilation.classifyFileExt(arg)) {
1412-
.object, .static_library, .shared_library => {
1413-
try link_objects.append(.{ .path = arg });
1414-
},
1415-
.assembly, .c, .cpp, .h, .ll, .bc, .m, .mm, .cu => {
1422+
} else switch (file_ext orelse
1423+
Compilation.classifyFileExt(arg)) {
1424+
.object, .static_library, .shared_library => try link_objects.append(.{ .path = arg }),
1425+
.assembly, .assembly_with_cpp, .c, .cpp, .h, .ll, .bc, .m, .mm, .cu => {
14161426
try c_source_files.append(.{
14171427
.src_path = arg,
14181428
.extra_flags = try arena.dupe([]const u8, extra_cflags.items),
1429+
// duped when parsing the args.
1430+
.ext = file_ext,
14191431
});
14201432
},
14211433
.zig => {
14221434
if (root_src_file) |other| {
14231435
fatal("found another zig file '{s}' after root source file '{s}'", .{ arg, other });
1424-
} else {
1425-
root_src_file = arg;
1426-
}
1436+
} else root_src_file = arg;
14271437
},
14281438
.def, .unknown => {
14291439
fatal("unrecognized file extension of parameter '{s}'", .{arg});
@@ -1464,6 +1474,7 @@ fn buildOutputType(
14641474
var needed = false;
14651475
var must_link = false;
14661476
var force_static_libs = false;
1477+
var file_ext: ?Compilation.FileExt = null;
14671478
while (it.has_next) {
14681479
it.next() catch |err| {
14691480
fatal("unable to parse command line parameters: {s}", .{@errorName(err)});
@@ -1484,32 +1495,39 @@ fn buildOutputType(
14841495
.asm_only => c_out_mode = .assembly, // -S
14851496
.preprocess_only => c_out_mode = .preprocessor, // -E
14861497
.emit_llvm => emit_llvm = true,
1498+
.x => {
1499+
const lang = mem.sliceTo(it.only_arg, 0);
1500+
if (mem.eql(u8, lang, "none")) {
1501+
file_ext = null;
1502+
} else if (Compilation.LangToExt.get(lang)) |got_ext| {
1503+
file_ext = got_ext;
1504+
} else {
1505+
fatal("language not recognized: '{s}'", .{lang});
1506+
}
1507+
},
14871508
.other => {
14881509
try clang_argv.appendSlice(it.other_args);
14891510
},
1490-
.positional => {
1491-
const file_ext = Compilation.classifyFileExt(mem.sliceTo(it.only_arg, 0));
1492-
switch (file_ext) {
1493-
.assembly, .c, .cpp, .ll, .bc, .h, .m, .mm, .cu => {
1494-
try c_source_files.append(.{ .src_path = it.only_arg });
1495-
},
1496-
.unknown, .shared_library, .object, .static_library => {
1497-
try link_objects.append(.{
1498-
.path = it.only_arg,
1499-
.must_link = must_link,
1500-
});
1501-
},
1502-
.def => {
1503-
linker_module_definition_file = it.only_arg;
1504-
},
1505-
.zig => {
1506-
if (root_src_file) |other| {
1507-
fatal("found another zig file '{s}' after root source file '{s}'", .{ it.only_arg, other });
1508-
} else {
1509-
root_src_file = it.only_arg;
1510-
}
1511-
},
1512-
}
1511+
.positional => switch (file_ext orelse
1512+
Compilation.classifyFileExt(mem.sliceTo(it.only_arg, 0))) {
1513+
.assembly, .assembly_with_cpp, .c, .cpp, .ll, .bc, .h, .m, .mm, .cu => {
1514+
try c_source_files.append(.{
1515+
.src_path = it.only_arg,
1516+
.ext = file_ext, // duped while parsing the args.
1517+
});
1518+
},
1519+
.unknown, .shared_library, .object, .static_library => try link_objects.append(.{
1520+
.path = it.only_arg,
1521+
.must_link = must_link,
1522+
}),
1523+
.def => {
1524+
linker_module_definition_file = it.only_arg;
1525+
},
1526+
.zig => {
1527+
if (root_src_file) |other| {
1528+
fatal("found another zig file '{s}' after root source file '{s}'", .{ it.only_arg, other });
1529+
} else root_src_file = it.only_arg;
1530+
},
15131531
},
15141532
.l => {
15151533
// -l
@@ -4860,6 +4878,7 @@ pub const ClangArgIterator = struct {
48604878
o,
48614879
c,
48624880
m,
4881+
x,
48634882
other,
48644883
positional,
48654884
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)