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 {...@@ -192,12 +192,30 @@ pub const CRTFile = struct {
192 }192 }
193};193};
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
195/// For passing to a C compiler.211/// For passing to a C compiler.
196pub const CSourceFile = struct {212pub 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};
202220
203const Job = union(enum) {221const 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
26122630
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 }
26172636
...@@ -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 });
39293948 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) {
39353954 .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);
39373965
3938 // When all these flags are true, it means that the entire purpose of3966 // 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 means3967 // 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 the4427 // 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 for4428 // 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}
45904620
4591pub fn hasAsmExt(filename: []const u8) bool {
4592 return mem.endsWith(u8, filename, ".s") or mem.endsWith(u8, filename, ".S");
4593}
4594
4595pub fn hasSharedLibraryExt(filename: []const u8) bool {4621pub fn hasSharedLibraryExt(filename: []const u8) bool {
4596 if (mem.endsWith(u8, filename, ".so") or4622 if (mem.endsWith(u8, filename, ".so") or
4597 mem.endsWith(u8, filename, ".dll") or4623 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},
7173jspd1("u"),7173jspd1("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},
7175joinpd1("y"),7182joinpd1("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 addresses391 \\ -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 pointer397 \\ -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();
915916
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 its920 // 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, // -S1495 .asm_only => c_out_mode = .assembly, // -S
1485 .preprocess_only => c_out_mode = .preprocessor, // -E1496 .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 // -l1533 // -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};
504508
505const blacklisted_options = [_][]const u8{};509const blacklisted_options = [_][]const u8{};