diff --git a/src/Compilation.zig b/src/Compilation.zig index cd0761315aa9..b0af925e2936 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -192,12 +192,30 @@ pub const CRTFile = struct { } }; +// supported languages for "zig clang -x ". +// Loosely based on llvm-project/clang/include/clang/Driver/Types.def +pub const LangToExt = std.ComptimeStringMap(FileExt, .{ + .{ "c", .c }, + .{ "c-header", .h }, + .{ "c++", .cpp }, + .{ "c++-header", .h }, + .{ "objective-c", .m }, + .{ "objective-c-header", .h }, + .{ "objective-c++", .mm }, + .{ "objective-c++-header", .h }, + .{ "assembler", .assembly }, + .{ "assembler-with-cpp", .assembly_with_cpp }, + .{ "cuda", .cu }, +}); + /// For passing to a C compiler. pub const CSourceFile = struct { src_path: []const u8, extra_flags: []const []const u8 = &.{}, /// Same as extra_flags except they are not added to the Cache hash. cache_exempt_flags: []const []const u8 = &.{}, + // this field is non-null iff language was explicitly set with "-x lang". + ext: ?FileExt = null, }; const Job = union(enum) { @@ -2612,6 +2630,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes for (comp.c_object_table.keys()) |key| { _ = try man.addFile(key.src.src_path, null); + man.hash.addOptional(key.src.ext); man.hash.addListOfBytes(key.src.extra_flags); } @@ -3926,14 +3945,23 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P break :e o_ext; }; const o_basename = try std.fmt.allocPrint(arena, "{s}{s}", .{ o_basename_noext, out_ext }); - - try argv.appendSlice(&[_][]const u8{ - self_exe_path, - "clang", - c_object.src.src_path, - }); - - const ext = classifyFileExt(c_object.src.src_path); + const ext = c_object.src.ext orelse classifyFileExt(c_object.src.src_path); + + try argv.appendSlice(&[_][]const u8{ self_exe_path, "clang" }); + // if "ext" is explicit, add "-x ". Otherwise let clang do its thing. + if (c_object.src.ext != null) { + try argv.appendSlice(&[_][]const u8{ "-x", switch (ext) { + .assembly => "assembler", + .assembly_with_cpp => "assembler-with-cpp", + .c => "c", + .cpp => "c++", + .cu => "cuda", + .m => "objective-c", + .mm => "objective-c++", + else => fatal("language '{s}' is unsupported in this context", .{@tagName(ext)}), + } }); + } + try argv.append(c_object.src.src_path); // When all these flags are true, it means that the entire purpose of // this compilation is to perform a single zig cc operation. This means @@ -4395,7 +4423,7 @@ pub fn addCCArgs( } }, .shared_library, .ll, .bc, .unknown, .static_library, .object, .def, .zig => {}, - .assembly => { + .assembly, .assembly_with_cpp => { // The Clang assembler does not accept the list of CPU features like the // compiler frontend does. Therefore we must hard-code the -m flags for // all CPU features here. @@ -4535,6 +4563,7 @@ pub const FileExt = enum { ll, bc, assembly, + assembly_with_cpp, shared_library, object, static_library, @@ -4549,6 +4578,7 @@ pub const FileExt = enum { .ll, .bc, .assembly, + .assembly_with_cpp, .shared_library, .object, .static_library, @@ -4588,10 +4618,6 @@ pub fn hasObjCppExt(filename: []const u8) bool { return mem.endsWith(u8, filename, ".mm"); } -pub fn hasAsmExt(filename: []const u8) bool { - return mem.endsWith(u8, filename, ".s") or mem.endsWith(u8, filename, ".S"); -} - pub fn hasSharedLibraryExt(filename: []const u8) bool { if (mem.endsWith(u8, filename, ".so") or mem.endsWith(u8, filename, ".dll") or @@ -4632,8 +4658,10 @@ pub fn classifyFileExt(filename: []const u8) FileExt { return .ll; } else if (mem.endsWith(u8, filename, ".bc")) { return .bc; - } else if (hasAsmExt(filename)) { + } else if (mem.endsWith(u8, filename, ".s")) { return .assembly; + } else if (mem.endsWith(u8, filename, ".S")) { + return .assembly_with_cpp; } else if (mem.endsWith(u8, filename, ".h")) { return .h; } else if (mem.endsWith(u8, filename, ".zig")) { diff --git a/src/clang_options_data.zig b/src/clang_options_data.zig index 6db3489a47b8..7cee0327bff9 100644 --- a/src/clang_options_data.zig +++ b/src/clang_options_data.zig @@ -7171,6 +7171,13 @@ joinpd1("d"), .psl = true, }, jspd1("u"), -jspd1("x"), +.{ + .name = "x", + .syntax = .joined_or_separate, + .zig_equivalent = .x, + .pd1 = true, + .pd2 = false, + .psl = false, +}, joinpd1("y"), };}; diff --git a/src/libunwind.zig b/src/libunwind.zig index 11666eec3c91..56113d135518 100644 --- a/src/libunwind.zig +++ b/src/libunwind.zig @@ -48,7 +48,7 @@ pub fn buildStaticLib(comp: *Compilation) !void { try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxx", "include" }), }); }, - .assembly => {}, + .assembly_with_cpp => {}, else => unreachable, // You can see the entire list of files just above. } try cflags.append("-I"); diff --git a/src/main.zig b/src/main.zig index f203bad96881..2e97cc02149e 100644 --- a/src/main.zig +++ b/src/main.zig @@ -391,6 +391,7 @@ const usage_build_generic = \\ -mcmodel=[default|tiny| Limit range of code and data virtual addresses \\ small|kernel| \\ medium|large] + \\ -x language Treat subsequent input files as having type \\ -mred-zone Force-enable the "red-zone" \\ -mno-red-zone Force-disable the "red-zone" \\ -fomit-frame-pointer Omit the stack frame pointer @@ -913,6 +914,7 @@ fn buildOutputType( var cssan = ClangSearchSanitizer.init(gpa, &clang_argv); defer cssan.map.deinit(); + var file_ext: ?Compilation.FileExt = null; args_loop: while (args_iter.next()) |arg| { if (mem.startsWith(u8, arg, "@")) { // This is a "compiler response file". We must parse the file and treat its @@ -1401,6 +1403,15 @@ fn buildOutputType( try clang_argv.append(arg); } else if (mem.startsWith(u8, arg, "-I")) { try cssan.addIncludePath(.I, arg, arg[2..], true); + } else if (mem.eql(u8, arg, "-x")) { + const lang = args_iter.nextOrFatal(); + if (mem.eql(u8, lang, "none")) { + file_ext = null; + } else if (Compilation.LangToExt.get(lang)) |got_ext| { + file_ext = got_ext; + } else { + fatal("language not recognized: '{s}'", .{lang}); + } } else if (mem.startsWith(u8, arg, "-mexec-model=")) { wasi_exec_model = std.meta.stringToEnum(std.builtin.WasiExecModel, arg["-mexec-model=".len..]) orelse { fatal("expected [command|reactor] for -mexec-mode=[value], found '{s}'", .{arg["-mexec-model=".len..]}); @@ -1408,22 +1419,21 @@ fn buildOutputType( } else { fatal("unrecognized parameter: '{s}'", .{arg}); } - } else switch (Compilation.classifyFileExt(arg)) { - .object, .static_library, .shared_library => { - try link_objects.append(.{ .path = arg }); - }, - .assembly, .c, .cpp, .h, .ll, .bc, .m, .mm, .cu => { + } else switch (file_ext orelse + Compilation.classifyFileExt(arg)) { + .object, .static_library, .shared_library => try link_objects.append(.{ .path = arg }), + .assembly, .assembly_with_cpp, .c, .cpp, .h, .ll, .bc, .m, .mm, .cu => { try c_source_files.append(.{ .src_path = arg, .extra_flags = try arena.dupe([]const u8, extra_cflags.items), + // duped when parsing the args. + .ext = file_ext, }); }, .zig => { if (root_src_file) |other| { fatal("found another zig file '{s}' after root source file '{s}'", .{ arg, other }); - } else { - root_src_file = arg; - } + } else root_src_file = arg; }, .def, .unknown => { fatal("unrecognized file extension of parameter '{s}'", .{arg}); @@ -1464,6 +1474,7 @@ fn buildOutputType( var needed = false; var must_link = false; var force_static_libs = false; + var file_ext: ?Compilation.FileExt = null; while (it.has_next) { it.next() catch |err| { fatal("unable to parse command line parameters: {s}", .{@errorName(err)}); @@ -1484,32 +1495,39 @@ fn buildOutputType( .asm_only => c_out_mode = .assembly, // -S .preprocess_only => c_out_mode = .preprocessor, // -E .emit_llvm => emit_llvm = true, + .x => { + const lang = mem.sliceTo(it.only_arg, 0); + if (mem.eql(u8, lang, "none")) { + file_ext = null; + } else if (Compilation.LangToExt.get(lang)) |got_ext| { + file_ext = got_ext; + } else { + fatal("language not recognized: '{s}'", .{lang}); + } + }, .other => { try clang_argv.appendSlice(it.other_args); }, - .positional => { - const file_ext = Compilation.classifyFileExt(mem.sliceTo(it.only_arg, 0)); - switch (file_ext) { - .assembly, .c, .cpp, .ll, .bc, .h, .m, .mm, .cu => { - try c_source_files.append(.{ .src_path = it.only_arg }); - }, - .unknown, .shared_library, .object, .static_library => { - try link_objects.append(.{ - .path = it.only_arg, - .must_link = must_link, - }); - }, - .def => { - linker_module_definition_file = it.only_arg; - }, - .zig => { - if (root_src_file) |other| { - fatal("found another zig file '{s}' after root source file '{s}'", .{ it.only_arg, other }); - } else { - root_src_file = it.only_arg; - } - }, - } + .positional => switch (file_ext orelse + Compilation.classifyFileExt(mem.sliceTo(it.only_arg, 0))) { + .assembly, .assembly_with_cpp, .c, .cpp, .ll, .bc, .h, .m, .mm, .cu => { + try c_source_files.append(.{ + .src_path = it.only_arg, + .ext = file_ext, // duped while parsing the args. + }); + }, + .unknown, .shared_library, .object, .static_library => try link_objects.append(.{ + .path = it.only_arg, + .must_link = must_link, + }), + .def => { + linker_module_definition_file = it.only_arg; + }, + .zig => { + if (root_src_file) |other| { + fatal("found another zig file '{s}' after root source file '{s}'", .{ it.only_arg, other }); + } else root_src_file = it.only_arg; + }, }, .l => { // -l @@ -4860,6 +4878,7 @@ pub const ClangArgIterator = struct { o, c, m, + x, other, positional, l, diff --git a/tools/update_clang_options.zig b/tools/update_clang_options.zig index 889e5bcbfef2..85558576dfbf 100644 --- a/tools/update_clang_options.zig +++ b/tools/update_clang_options.zig @@ -500,6 +500,10 @@ const known_options = [_]KnownOpt{ .name = "undefined", .ident = "undefined", }, + .{ + .name = "x", + .ident = "x", + }, }; const blacklisted_options = [_][]const u8{};