authorgravatar for motiejus@jakstys.ltMotiejus Jakštys <motiejus@jakstys.lt> 2022-11-14 04:15:04+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-01-13 21:38:11-05:00
log6b3f59c3a735ddbda3b3a62a0dfb5d55fa045f57
tree457c3ef57718e31cd6297bb3154dcb6177793027
parentd813cef42af43b499fc5dc465e34f873008aaad0

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

5 files changed, 105 insertions(+), 47 deletions(-)

src/Compilation.zig+42-14
......@@ -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
197pub 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.
196212pub 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
203221const 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
4591pub fn hasAsmExt(filename: []const u8) bool {
4592 return mem.endsWith(u8, filename, ".s") or mem.endsWith(u8, filename, ".S");
4593}
4594
45954621pub 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+8-1
......@@ -7171,6 +7171,13 @@ joinpd1("d"),
71717171 .psl = true,
71727172},
71737173jspd1("u"),
7174jspd1("x"),
7174.{
7175 .name = "x",
7176 .syntax = .joined_or_separate,
7177 .zig_equivalent = .x,
7178 .pd1 = true,
7179 .pd2 = false,
7180 .psl = false,
7181},
71757182joinpd1("y"),
71767183};};
src/libunwind.zig+1-1
......@@ -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+50-31
......@@ -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,6 +1403,15 @@ 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..]});
......@@ -1408,22 +1419,21 @@ fn buildOutputType(
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+4
......@@ -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
505509const blacklisted_options = [_][]const u8{};