| author | |
| committer | |
| log | 6b3f59c3a735ddbda3b3a62a0dfb5d55fa045f57 |
| tree | 457c3ef57718e31cd6297bb3154dcb6177793027 |
| parent | d813cef42af43b499fc5dc465e34f873008aaad0 |
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 #109155 files changed, 105 insertions(+), 47 deletions(-)
src/Compilation.zig+42-14| ... | ... | @@ -192,12 +192,30 @@ pub const CRTFile = struct { |
| 192 | 192 | } |
| 193 | 193 | }; |
| 194 | 194 | |
| 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 | ||
| 195 | 211 | /// For passing to a C compiler. |
| 196 | 212 | pub const CSourceFile = struct { |
| 197 | 213 | src_path: []const u8, |
| 198 | 214 | extra_flags: []const []const u8 = &.{}, |
| 199 | 215 | /// Same as extra_flags except they are not added to the Cache hash. |
| 200 | 216 | cache_exempt_flags: []const []const u8 = &.{}, |
| 217 | // this field is non-null iff language was explicitly set with "-x lang". | |
| 218 | ext: ?FileExt = null, | |
| 201 | 219 | }; |
| 202 | 220 | |
| 203 | 221 | const Job = union(enum) { |
| ... | ... | @@ -2612,6 +2630,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes |
| 2612 | 2630 | |
| 2613 | 2631 | for (comp.c_object_table.keys()) |key| { |
| 2614 | 2632 | _ = try man.addFile(key.src.src_path, null); |
| 2633 | man.hash.addOptional(key.src.ext); | |
| 2615 | 2634 | man.hash.addListOfBytes(key.src.extra_flags); |
| 2616 | 2635 | } |
| 2617 | 2636 | |
| ... | ... | @@ -3926,14 +3945,23 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P |
| 3926 | 3945 | break :e o_ext; |
| 3927 | 3946 | }; |
| 3928 | 3947 | 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); | |
| 3937 | 3965 | |
| 3938 | 3966 | // When all these flags are true, it means that the entire purpose of |
| 3939 | 3967 | // this compilation is to perform a single zig cc operation. This means |
| ... | ... | @@ -4395,7 +4423,7 @@ pub fn addCCArgs( |
| 4395 | 4423 | } |
| 4396 | 4424 | }, |
| 4397 | 4425 | .shared_library, .ll, .bc, .unknown, .static_library, .object, .def, .zig => {}, |
| 4398 | .assembly => { | |
| 4426 | .assembly, .assembly_with_cpp => { | |
| 4399 | 4427 | // The Clang assembler does not accept the list of CPU features like the |
| 4400 | 4428 | // compiler frontend does. Therefore we must hard-code the -m flags for |
| 4401 | 4429 | // all CPU features here. |
| ... | ... | @@ -4535,6 +4563,7 @@ pub const FileExt = enum { |
| 4535 | 4563 | ll, |
| 4536 | 4564 | bc, |
| 4537 | 4565 | assembly, |
| 4566 | assembly_with_cpp, | |
| 4538 | 4567 | shared_library, |
| 4539 | 4568 | object, |
| 4540 | 4569 | static_library, |
| ... | ... | @@ -4549,6 +4578,7 @@ pub const FileExt = enum { |
| 4549 | 4578 | .ll, |
| 4550 | 4579 | .bc, |
| 4551 | 4580 | .assembly, |
| 4581 | .assembly_with_cpp, | |
| 4552 | 4582 | .shared_library, |
| 4553 | 4583 | .object, |
| 4554 | 4584 | .static_library, |
| ... | ... | @@ -4588,10 +4618,6 @@ pub fn hasObjCppExt(filename: []const u8) bool { |
| 4588 | 4618 | return mem.endsWith(u8, filename, ".mm"); |
| 4589 | 4619 | } |
| 4590 | 4620 | |
| 4591 | pub fn hasAsmExt(filename: []const u8) bool { | |
| 4592 | return mem.endsWith(u8, filename, ".s") or mem.endsWith(u8, filename, ".S"); | |
| 4593 | } | |
| 4594 | ||
| 4595 | 4621 | pub fn hasSharedLibraryExt(filename: []const u8) bool { |
| 4596 | 4622 | if (mem.endsWith(u8, filename, ".so") or |
| 4597 | 4623 | mem.endsWith(u8, filename, ".dll") or |
| ... | ... | @@ -4632,8 +4658,10 @@ pub fn classifyFileExt(filename: []const u8) FileExt { |
| 4632 | 4658 | return .ll; |
| 4633 | 4659 | } else if (mem.endsWith(u8, filename, ".bc")) { |
| 4634 | 4660 | return .bc; |
| 4635 | } else if (hasAsmExt(filename)) { | |
| 4661 | } else if (mem.endsWith(u8, filename, ".s")) { | |
| 4636 | 4662 | return .assembly; |
| 4663 | } else if (mem.endsWith(u8, filename, ".S")) { | |
| 4664 | return .assembly_with_cpp; | |
| 4637 | 4665 | } else if (mem.endsWith(u8, filename, ".h")) { |
| 4638 | 4666 | return .h; |
| 4639 | 4667 | } else if (mem.endsWith(u8, filename, ".zig")) { |
src/clang_options_data.zig+8-1| ... | ... | @@ -7171,6 +7171,13 @@ joinpd1("d"), |
| 7171 | 7171 | .psl = true, |
| 7172 | 7172 | }, |
| 7173 | 7173 | 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 | }, | |
| 7175 | 7182 | joinpd1("y"), |
| 7176 | 7183 | };}; |
src/libunwind.zig+1-1| ... | ... | @@ -48,7 +48,7 @@ pub fn buildStaticLib(comp: *Compilation) !void { |
| 48 | 48 | try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxx", "include" }), |
| 49 | 49 | }); |
| 50 | 50 | }, |
| 51 | .assembly => {}, | |
| 51 | .assembly_with_cpp => {}, | |
| 52 | 52 | else => unreachable, // You can see the entire list of files just above. |
| 53 | 53 | } |
| 54 | 54 | try cflags.append("-I"); |
src/main.zig+50-31| ... | ... | @@ -391,6 +391,7 @@ const usage_build_generic = |
| 391 | 391 | \\ -mcmodel=[default|tiny| Limit range of code and data virtual addresses |
| 392 | 392 | \\ small|kernel| |
| 393 | 393 | \\ medium|large] |
| 394 | \\ -x language Treat subsequent input files as having type <language> | |
| 394 | 395 | \\ -mred-zone Force-enable the "red-zone" |
| 395 | 396 | \\ -mno-red-zone Force-disable the "red-zone" |
| 396 | 397 | \\ -fomit-frame-pointer Omit the stack frame pointer |
| ... | ... | @@ -913,6 +914,7 @@ fn buildOutputType( |
| 913 | 914 | var cssan = ClangSearchSanitizer.init(gpa, &clang_argv); |
| 914 | 915 | defer cssan.map.deinit(); |
| 915 | 916 | |
| 917 | var file_ext: ?Compilation.FileExt = null; | |
| 916 | 918 | args_loop: while (args_iter.next()) |arg| { |
| 917 | 919 | if (mem.startsWith(u8, arg, "@")) { |
| 918 | 920 | // This is a "compiler response file". We must parse the file and treat its |
| ... | ... | @@ -1401,6 +1403,15 @@ fn buildOutputType( |
| 1401 | 1403 | try clang_argv.append(arg); |
| 1402 | 1404 | } else if (mem.startsWith(u8, arg, "-I")) { |
| 1403 | 1405 | 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 | } | |
| 1404 | 1415 | } else if (mem.startsWith(u8, arg, "-mexec-model=")) { |
| 1405 | 1416 | wasi_exec_model = std.meta.stringToEnum(std.builtin.WasiExecModel, arg["-mexec-model=".len..]) orelse { |
| 1406 | 1417 | fatal("expected [command|reactor] for -mexec-mode=[value], found '{s}'", .{arg["-mexec-model=".len..]}); |
| ... | ... | @@ -1408,22 +1419,21 @@ fn buildOutputType( |
| 1408 | 1419 | } else { |
| 1409 | 1420 | fatal("unrecognized parameter: '{s}'", .{arg}); |
| 1410 | 1421 | } |
| 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 => { | |
| 1416 | 1426 | try c_source_files.append(.{ |
| 1417 | 1427 | .src_path = arg, |
| 1418 | 1428 | .extra_flags = try arena.dupe([]const u8, extra_cflags.items), |
| 1429 | // duped when parsing the args. | |
| 1430 | .ext = file_ext, | |
| 1419 | 1431 | }); |
| 1420 | 1432 | }, |
| 1421 | 1433 | .zig => { |
| 1422 | 1434 | if (root_src_file) |other| { |
| 1423 | 1435 | 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; | |
| 1427 | 1437 | }, |
| 1428 | 1438 | .def, .unknown => { |
| 1429 | 1439 | fatal("unrecognized file extension of parameter '{s}'", .{arg}); |
| ... | ... | @@ -1464,6 +1474,7 @@ fn buildOutputType( |
| 1464 | 1474 | var needed = false; |
| 1465 | 1475 | var must_link = false; |
| 1466 | 1476 | var force_static_libs = false; |
| 1477 | var file_ext: ?Compilation.FileExt = null; | |
| 1467 | 1478 | while (it.has_next) { |
| 1468 | 1479 | it.next() catch |err| { |
| 1469 | 1480 | fatal("unable to parse command line parameters: {s}", .{@errorName(err)}); |
| ... | ... | @@ -1484,32 +1495,39 @@ fn buildOutputType( |
| 1484 | 1495 | .asm_only => c_out_mode = .assembly, // -S |
| 1485 | 1496 | .preprocess_only => c_out_mode = .preprocessor, // -E |
| 1486 | 1497 | .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 | }, | |
| 1487 | 1508 | .other => { |
| 1488 | 1509 | try clang_argv.appendSlice(it.other_args); |
| 1489 | 1510 | }, |
| 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 | }, | |
| 1513 | 1531 | }, |
| 1514 | 1532 | .l => { |
| 1515 | 1533 | // -l |
| ... | ... | @@ -4860,6 +4878,7 @@ pub const ClangArgIterator = struct { |
| 4860 | 4878 | o, |
| 4861 | 4879 | c, |
| 4862 | 4880 | m, |
| 4881 | x, | |
| 4863 | 4882 | other, |
| 4864 | 4883 | positional, |
| 4865 | 4884 | l, |
tools/update_clang_options.zig+4| ... | ... | @@ -500,6 +500,10 @@ const known_options = [_]KnownOpt{ |
| 500 | 500 | .name = "undefined", |
| 501 | 501 | .ident = "undefined", |
| 502 | 502 | }, |
| 503 | .{ | |
| 504 | .name = "x", | |
| 505 | .ident = "x", | |
| 506 | }, | |
| 503 | 507 | }; |
| 504 | 508 | |
| 505 | 509 | const blacklisted_options = [_][]const u8{}; |