| 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,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 | /// For passing to a C compiler. | 211 | /// For passing to a C compiler. |
| 196 | pub const CSourceFile = struct { | 212 | pub const CSourceFile = struct { |
| 197 | src_path: []const u8, | 213 | src_path: []const u8, |
| 198 | extra_flags: []const []const u8 = &.{}, | 214 | extra_flags: []const []const u8 = &.{}, |
| 199 | /// Same as extra_flags except they are not added to the Cache hash. | 215 | /// Same as extra_flags except they are not added to the Cache hash. |
| 200 | cache_exempt_flags: []const []const u8 = &.{}, | 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 | const Job = union(enum) { | 221 | const Job = union(enum) { |
| ... | @@ -2612,6 +2630,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes | ... | @@ -2612,6 +2630,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes |
| 2612 | 2630 | ||
| 2613 | for (comp.c_object_table.keys()) |key| { | 2631 | for (comp.c_object_table.keys()) |key| { |
| 2614 | _ = try man.addFile(key.src.src_path, null); | 2632 | _ = try man.addFile(key.src.src_path, null); |
| 2633 | man.hash.addOptional(key.src.ext); | ||
| 2615 | man.hash.addListOfBytes(key.src.extra_flags); | 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,14 +3945,23 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P |
| 3926 | break :e o_ext; | 3945 | break :e o_ext; |
| 3927 | }; | 3946 | }; |
| 3928 | const o_basename = try std.fmt.allocPrint(arena, "{s}{s}", .{ o_basename_noext, out_ext }); | 3947 | const o_basename = try std.fmt.allocPrint(arena, "{s}{s}", .{ o_basename_noext, out_ext }); |
| 3929 | 3948 | const ext = c_object.src.ext orelse classifyFileExt(c_object.src.src_path); | |
| 3930 | try argv.appendSlice(&[_][]const u8{ | 3949 | |
| 3931 | self_exe_path, | 3950 | try argv.appendSlice(&[_][]const u8{ self_exe_path, "clang" }); |
| 3932 | "clang", | 3951 | // if "ext" is explicit, add "-x <lang>". Otherwise let clang do its thing. |
| 3933 | c_object.src.src_path, | 3952 | if (c_object.src.ext != null) { |
| 3934 | }); | 3953 | try argv.appendSlice(&[_][]const u8{ "-x", switch (ext) { |
| 3935 | 3954 | .assembly => "assembler", | |
| 3936 | const ext = classifyFileExt(c_object.src.src_path); | 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 | // When all these flags are true, it means that the entire purpose of | 3966 | // When all these flags are true, it means that the entire purpose of |
| 3939 | // this compilation is to perform a single zig cc operation. This means | 3967 | // this compilation is to perform a single zig cc operation. This means |
| ... | @@ -4395,7 +4423,7 @@ pub fn addCCArgs( | ... | @@ -4395,7 +4423,7 @@ pub fn addCCArgs( |
| 4395 | } | 4423 | } |
| 4396 | }, | 4424 | }, |
| 4397 | .shared_library, .ll, .bc, .unknown, .static_library, .object, .def, .zig => {}, | 4425 | .shared_library, .ll, .bc, .unknown, .static_library, .object, .def, .zig => {}, |
| 4398 | .assembly => { | 4426 | .assembly, .assembly_with_cpp => { |
| 4399 | // The Clang assembler does not accept the list of CPU features like the | 4427 | // The Clang assembler does not accept the list of CPU features like the |
| 4400 | // compiler frontend does. Therefore we must hard-code the -m flags for | 4428 | // compiler frontend does. Therefore we must hard-code the -m flags for |
| 4401 | // all CPU features here. | 4429 | // all CPU features here. |
| ... | @@ -4535,6 +4563,7 @@ pub const FileExt = enum { | ... | @@ -4535,6 +4563,7 @@ pub const FileExt = enum { |
| 4535 | ll, | 4563 | ll, |
| 4536 | bc, | 4564 | bc, |
| 4537 | assembly, | 4565 | assembly, |
| 4566 | assembly_with_cpp, | ||
| 4538 | shared_library, | 4567 | shared_library, |
| 4539 | object, | 4568 | object, |
| 4540 | static_library, | 4569 | static_library, |
| ... | @@ -4549,6 +4578,7 @@ pub const FileExt = enum { | ... | @@ -4549,6 +4578,7 @@ pub const FileExt = enum { |
| 4549 | .ll, | 4578 | .ll, |
| 4550 | .bc, | 4579 | .bc, |
| 4551 | .assembly, | 4580 | .assembly, |
| 4581 | .assembly_with_cpp, | ||
| 4552 | .shared_library, | 4582 | .shared_library, |
| 4553 | .object, | 4583 | .object, |
| 4554 | .static_library, | 4584 | .static_library, |
| ... | @@ -4588,10 +4618,6 @@ pub fn hasObjCppExt(filename: []const u8) bool { | ... | @@ -4588,10 +4618,6 @@ pub fn hasObjCppExt(filename: []const u8) bool { |
| 4588 | return mem.endsWith(u8, filename, ".mm"); | 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 | pub fn hasSharedLibraryExt(filename: []const u8) bool { | 4621 | pub fn hasSharedLibraryExt(filename: []const u8) bool { |
| 4596 | if (mem.endsWith(u8, filename, ".so") or | 4622 | if (mem.endsWith(u8, filename, ".so") or |
| 4597 | mem.endsWith(u8, filename, ".dll") or | 4623 | mem.endsWith(u8, filename, ".dll") or |
| ... | @@ -4632,8 +4658,10 @@ pub fn classifyFileExt(filename: []const u8) FileExt { | ... | @@ -4632,8 +4658,10 @@ pub fn classifyFileExt(filename: []const u8) FileExt { |
| 4632 | return .ll; | 4658 | return .ll; |
| 4633 | } else if (mem.endsWith(u8, filename, ".bc")) { | 4659 | } else if (mem.endsWith(u8, filename, ".bc")) { |
| 4634 | return .bc; | 4660 | return .bc; |
| 4635 | } else if (hasAsmExt(filename)) { | 4661 | } else if (mem.endsWith(u8, filename, ".s")) { |
| 4636 | return .assembly; | 4662 | return .assembly; |
| 4663 | } else if (mem.endsWith(u8, filename, ".S")) { | ||
| 4664 | return .assembly_with_cpp; | ||
| 4637 | } else if (mem.endsWith(u8, filename, ".h")) { | 4665 | } else if (mem.endsWith(u8, filename, ".h")) { |
| 4638 | return .h; | 4666 | return .h; |
| 4639 | } else if (mem.endsWith(u8, filename, ".zig")) { | 4667 | } else if (mem.endsWith(u8, filename, ".zig")) { |
src/clang_options_data.zig+8-1| ... | @@ -7171,6 +7171,13 @@ joinpd1("d"), | ... | @@ -7171,6 +7171,13 @@ joinpd1("d"), |
| 7171 | .psl = true, | 7171 | .psl = true, |
| 7172 | }, | 7172 | }, |
| 7173 | jspd1("u"), | 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 | joinpd1("y"), | 7182 | joinpd1("y"), |
| 7176 | };}; | 7183 | };}; |
src/libunwind.zig+1-1| ... | @@ -48,7 +48,7 @@ pub fn buildStaticLib(comp: *Compilation) !void { | ... | @@ -48,7 +48,7 @@ pub fn buildStaticLib(comp: *Compilation) !void { |
| 48 | try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxx", "include" }), | 48 | try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxx", "include" }), |
| 49 | }); | 49 | }); |
| 50 | }, | 50 | }, |
| 51 | .assembly => {}, | 51 | .assembly_with_cpp => {}, |
| 52 | else => unreachable, // You can see the entire list of files just above. | 52 | else => unreachable, // You can see the entire list of files just above. |
| 53 | } | 53 | } |
| 54 | try cflags.append("-I"); | 54 | try cflags.append("-I"); |
src/main.zig+50-31| ... | @@ -391,6 +391,7 @@ const usage_build_generic = | ... | @@ -391,6 +391,7 @@ const usage_build_generic = |
| 391 | \\ -mcmodel=[default|tiny| Limit range of code and data virtual addresses | 391 | \\ -mcmodel=[default|tiny| Limit range of code and data virtual addresses |
| 392 | \\ small|kernel| | 392 | \\ small|kernel| |
| 393 | \\ medium|large] | 393 | \\ medium|large] |
| 394 | \\ -x language Treat subsequent input files as having type <language> | ||
| 394 | \\ -mred-zone Force-enable the "red-zone" | 395 | \\ -mred-zone Force-enable the "red-zone" |
| 395 | \\ -mno-red-zone Force-disable the "red-zone" | 396 | \\ -mno-red-zone Force-disable the "red-zone" |
| 396 | \\ -fomit-frame-pointer Omit the stack frame pointer | 397 | \\ -fomit-frame-pointer Omit the stack frame pointer |
| ... | @@ -913,6 +914,7 @@ fn buildOutputType( | ... | @@ -913,6 +914,7 @@ fn buildOutputType( |
| 913 | var cssan = ClangSearchSanitizer.init(gpa, &clang_argv); | 914 | var cssan = ClangSearchSanitizer.init(gpa, &clang_argv); |
| 914 | defer cssan.map.deinit(); | 915 | defer cssan.map.deinit(); |
| 915 | 916 | ||
| 917 | var file_ext: ?Compilation.FileExt = null; | ||
| 916 | args_loop: while (args_iter.next()) |arg| { | 918 | args_loop: while (args_iter.next()) |arg| { |
| 917 | if (mem.startsWith(u8, arg, "@")) { | 919 | if (mem.startsWith(u8, arg, "@")) { |
| 918 | // This is a "compiler response file". We must parse the file and treat its | 920 | // This is a "compiler response file". We must parse the file and treat its |
| ... | @@ -1401,6 +1403,15 @@ fn buildOutputType( | ... | @@ -1401,6 +1403,15 @@ fn buildOutputType( |
| 1401 | try clang_argv.append(arg); | 1403 | try clang_argv.append(arg); |
| 1402 | } else if (mem.startsWith(u8, arg, "-I")) { | 1404 | } else if (mem.startsWith(u8, arg, "-I")) { |
| 1403 | try cssan.addIncludePath(.I, arg, arg[2..], true); | 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 | } else if (mem.startsWith(u8, arg, "-mexec-model=")) { | 1415 | } else if (mem.startsWith(u8, arg, "-mexec-model=")) { |
| 1405 | wasi_exec_model = std.meta.stringToEnum(std.builtin.WasiExecModel, arg["-mexec-model=".len..]) orelse { | 1416 | wasi_exec_model = std.meta.stringToEnum(std.builtin.WasiExecModel, arg["-mexec-model=".len..]) orelse { |
| 1406 | fatal("expected [command|reactor] for -mexec-mode=[value], found '{s}'", .{arg["-mexec-model=".len..]}); | 1417 | fatal("expected [command|reactor] for -mexec-mode=[value], found '{s}'", .{arg["-mexec-model=".len..]}); |
| ... | @@ -1408,22 +1419,21 @@ fn buildOutputType( | ... | @@ -1408,22 +1419,21 @@ fn buildOutputType( |
| 1408 | } else { | 1419 | } else { |
| 1409 | fatal("unrecognized parameter: '{s}'", .{arg}); | 1420 | fatal("unrecognized parameter: '{s}'", .{arg}); |
| 1410 | } | 1421 | } |
| 1411 | } else switch (Compilation.classifyFileExt(arg)) { | 1422 | } else switch (file_ext orelse |
| 1412 | .object, .static_library, .shared_library => { | 1423 | Compilation.classifyFileExt(arg)) { |
| 1413 | try link_objects.append(.{ .path = arg }); | 1424 | .object, .static_library, .shared_library => try link_objects.append(.{ .path = arg }), |
| 1414 | }, | 1425 | .assembly, .assembly_with_cpp, .c, .cpp, .h, .ll, .bc, .m, .mm, .cu => { |
| 1415 | .assembly, .c, .cpp, .h, .ll, .bc, .m, .mm, .cu => { | ||
| 1416 | try c_source_files.append(.{ | 1426 | try c_source_files.append(.{ |
| 1417 | .src_path = arg, | 1427 | .src_path = arg, |
| 1418 | .extra_flags = try arena.dupe([]const u8, extra_cflags.items), | 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 | .zig => { | 1433 | .zig => { |
| 1422 | if (root_src_file) |other| { | 1434 | if (root_src_file) |other| { |
| 1423 | fatal("found another zig file '{s}' after root source file '{s}'", .{ arg, other }); | 1435 | fatal("found another zig file '{s}' after root source file '{s}'", .{ arg, other }); |
| 1424 | } else { | 1436 | } else root_src_file = arg; |
| 1425 | root_src_file = arg; | ||
| 1426 | } | ||
| 1427 | }, | 1437 | }, |
| 1428 | .def, .unknown => { | 1438 | .def, .unknown => { |
| 1429 | fatal("unrecognized file extension of parameter '{s}'", .{arg}); | 1439 | fatal("unrecognized file extension of parameter '{s}'", .{arg}); |
| ... | @@ -1464,6 +1474,7 @@ fn buildOutputType( | ... | @@ -1464,6 +1474,7 @@ fn buildOutputType( |
| 1464 | var needed = false; | 1474 | var needed = false; |
| 1465 | var must_link = false; | 1475 | var must_link = false; |
| 1466 | var force_static_libs = false; | 1476 | var force_static_libs = false; |
| 1477 | var file_ext: ?Compilation.FileExt = null; | ||
| 1467 | while (it.has_next) { | 1478 | while (it.has_next) { |
| 1468 | it.next() catch |err| { | 1479 | it.next() catch |err| { |
| 1469 | fatal("unable to parse command line parameters: {s}", .{@errorName(err)}); | 1480 | fatal("unable to parse command line parameters: {s}", .{@errorName(err)}); |
| ... | @@ -1484,32 +1495,39 @@ fn buildOutputType( | ... | @@ -1484,32 +1495,39 @@ fn buildOutputType( |
| 1484 | .asm_only => c_out_mode = .assembly, // -S | 1495 | .asm_only => c_out_mode = .assembly, // -S |
| 1485 | .preprocess_only => c_out_mode = .preprocessor, // -E | 1496 | .preprocess_only => c_out_mode = .preprocessor, // -E |
| 1486 | .emit_llvm => emit_llvm = true, | 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 | .other => { | 1508 | .other => { |
| 1488 | try clang_argv.appendSlice(it.other_args); | 1509 | try clang_argv.appendSlice(it.other_args); |
| 1489 | }, | 1510 | }, |
| 1490 | .positional => { | 1511 | .positional => switch (file_ext orelse |
| 1491 | const file_ext = Compilation.classifyFileExt(mem.sliceTo(it.only_arg, 0)); | 1512 | Compilation.classifyFileExt(mem.sliceTo(it.only_arg, 0))) { |
| 1492 | switch (file_ext) { | 1513 | .assembly, .assembly_with_cpp, .c, .cpp, .ll, .bc, .h, .m, .mm, .cu => { |
| 1493 | .assembly, .c, .cpp, .ll, .bc, .h, .m, .mm, .cu => { | 1514 | try c_source_files.append(.{ |
| 1494 | try c_source_files.append(.{ .src_path = it.only_arg }); | 1515 | .src_path = it.only_arg, |
| 1495 | }, | 1516 | .ext = file_ext, // duped while parsing the args. |
| 1496 | .unknown, .shared_library, .object, .static_library => { | 1517 | }); |
| 1497 | try link_objects.append(.{ | 1518 | }, |
| 1498 | .path = it.only_arg, | 1519 | .unknown, .shared_library, .object, .static_library => try link_objects.append(.{ |
| 1499 | .must_link = must_link, | 1520 | .path = it.only_arg, |
| 1500 | }); | 1521 | .must_link = must_link, |
| 1501 | }, | 1522 | }), |
| 1502 | .def => { | 1523 | .def => { |
| 1503 | linker_module_definition_file = it.only_arg; | 1524 | linker_module_definition_file = it.only_arg; |
| 1504 | }, | 1525 | }, |
| 1505 | .zig => { | 1526 | .zig => { |
| 1506 | if (root_src_file) |other| { | 1527 | if (root_src_file) |other| { |
| 1507 | fatal("found another zig file '{s}' after root source file '{s}'", .{ it.only_arg, other }); | 1528 | fatal("found another zig file '{s}' after root source file '{s}'", .{ it.only_arg, other }); |
| 1508 | } else { | 1529 | } else root_src_file = it.only_arg; |
| 1509 | root_src_file = it.only_arg; | 1530 | }, |
| 1510 | } | ||
| 1511 | }, | ||
| 1512 | } | ||
| 1513 | }, | 1531 | }, |
| 1514 | .l => { | 1532 | .l => { |
| 1515 | // -l | 1533 | // -l |
| ... | @@ -4860,6 +4878,7 @@ pub const ClangArgIterator = struct { | ... | @@ -4860,6 +4878,7 @@ pub const ClangArgIterator = struct { |
| 4860 | o, | 4878 | o, |
| 4861 | c, | 4879 | c, |
| 4862 | m, | 4880 | m, |
| 4881 | x, | ||
| 4863 | other, | 4882 | other, |
| 4864 | positional, | 4883 | positional, |
| 4865 | l, | 4884 | l, |
tools/update_clang_options.zig+4| ... | @@ -500,6 +500,10 @@ const known_options = [_]KnownOpt{ | ... | @@ -500,6 +500,10 @@ const known_options = [_]KnownOpt{ |
| 500 | .name = "undefined", | 500 | .name = "undefined", |
| 501 | .ident = "undefined", | 501 | .ident = "undefined", |
| 502 | }, | 502 | }, |
| 503 | .{ | ||
| 504 | .name = "x", | ||
| 505 | .ident = "x", | ||
| 506 | }, | ||
| 503 | }; | 507 | }; |
| 504 | 508 | ||
| 505 | const blacklisted_options = [_][]const u8{}; | 509 | const blacklisted_options = [_][]const u8{}; |