zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

blob f3ebde58 (29853B) - Raw


      1 const std = @import("std");
      2 const mem = std.mem;
      3 const Allocator = mem.Allocator;
      4 const process = std.process;
      5 const Codegen = @import("Codegen_legacy.zig");
      6 const Compilation = @import("Compilation.zig");
      7 const LangOpts = @import("LangOpts.zig");
      8 const Preprocessor = @import("Preprocessor.zig");
      9 const Parser = @import("Parser.zig");
     10 const Source = @import("Source.zig");
     11 const Toolchain = @import("Toolchain.zig");
     12 const util = @import("util.zig");
     13 const target_util = @import("target.zig");
     14 
     15 const Driver = @This();
     16 
     17 pub const Linker = enum {
     18     ld,
     19     bfd,
     20     gold,
     21     lld,
     22     mold,
     23 };
     24 
     25 comp: *Compilation,
     26 inputs: std.ArrayListUnmanaged(Source) = .{},
     27 link_objects: std.ArrayListUnmanaged([]const u8) = .{},
     28 output_name: ?[]const u8 = null,
     29 sysroot: ?[]const u8 = null,
     30 temp_file_count: u32 = 0,
     31 only_preprocess: bool = false,
     32 only_syntax: bool = false,
     33 only_compile: bool = false,
     34 only_preprocess_and_compile: bool = false,
     35 verbose_ast: bool = false,
     36 verbose_pp: bool = false,
     37 verbose_ir: bool = false,
     38 verbose_linker_args: bool = false,
     39 
     40 /// Full path to the aro executable
     41 aro_name: []const u8 = "",
     42 
     43 /// Value of --triple= passed via CLI
     44 raw_target_triple: ?[]const u8 = null,
     45 
     46 // linker options
     47 use_linker: ?[]const u8 = null,
     48 linker_path: ?[]const u8 = null,
     49 nodefaultlibs: bool = false,
     50 nolibc: bool = false,
     51 nostartfiles: bool = false,
     52 nostdlib: bool = false,
     53 pie: ?bool = null,
     54 rdynamic: bool = false,
     55 relocatable: bool = false,
     56 rtlib: ?[]const u8 = null,
     57 shared: bool = false,
     58 shared_libgcc: bool = false,
     59 static: bool = false,
     60 static_libgcc: bool = false,
     61 static_pie: bool = false,
     62 strip: bool = false,
     63 unwindlib: ?[]const u8 = null,
     64 
     65 pub fn deinit(d: *Driver) void {
     66     for (d.link_objects.items[d.link_objects.items.len - d.temp_file_count ..]) |obj| {
     67         std.fs.deleteFileAbsolute(obj) catch {};
     68         d.comp.gpa.free(obj);
     69     }
     70     d.inputs.deinit(d.comp.gpa);
     71     d.link_objects.deinit(d.comp.gpa);
     72     d.* = undefined;
     73 }
     74 
     75 pub const usage =
     76     \\Usage {s}: [options] file..
     77     \\
     78     \\General options:
     79     \\  -h, --help      Print this message.
     80     \\  -v, --version   Print aro version.
     81     \\
     82     \\Compile options:
     83     \\  -c, --compile           Only run preprocess, compile, and assemble steps
     84     \\  -D <macro>=<value>      Define <macro> to <value> (defaults to 1)
     85     \\  -E                      Only run the preprocessor
     86     \\  -fchar8_t               Enable char8_t (enabled by default in C2X and later)
     87     \\  -fno-char8_t            Disable char8_t (disabled by default for pre-C2X)
     88     \\  -fcolor-diagnostics     Enable colors in diagnostics
     89     \\  -fno-color-diagnostics  Disable colors in diagnostics
     90     \\  -fdeclspec              Enable support for __declspec attributes
     91     \\  -fno-declspec           Disable support for __declspec attributes
     92     \\  -ffp-eval-method=[source|double|extended]
     93     \\                          Evaluation method to use for floating-point arithmetic
     94     \\  -fgnu-inline-asm        Enable GNU style inline asm (default: enabled)
     95     \\  -fno-gnu-inline-asm     Disable GNU style inline asm
     96     \\  -fms-extensions         Enable support for Microsoft extensions
     97     \\  -fno-ms-extensions      Disable support for Microsoft extensions
     98     \\  -fdollars-in-identifiers        
     99     \\                          Allow '$' in identifiers
    100     \\  -fno-dollars-in-identifiers     
    101     \\                          Disallow '$' in identifiers
    102     \\  -fmacro-backtrace-limit=<limit>
    103     \\                          Set limit on how many macro expansion traces are shown in errors (default 6)
    104     \\  -fnative-half-type      Use the native half type for __fp16 instead of promoting to float
    105     \\  -fnative-half-arguments-and-returns
    106     \\                          Allow half-precision function arguments and return values
    107     \\  -fshort-enums           Use the narrowest possible integer type for enums
    108     \\  -fno-short-enums        Use "int" as the tag type for enums
    109     \\  -fsigned-char           "char" is signed
    110     \\  -fno-signed-char        "char" is unsigned
    111     \\  -fsyntax-only           Only run the preprocessor, parser, and semantic analysis stages
    112     \\  -funsigned-char         "char" is unsigned
    113     \\  -fno-unsigned-char      "char" is signed
    114     \\  -I <dir>                Add directory to include search path
    115     \\  -isystem                Add directory to SYSTEM include search path
    116     \\  --emulate=[clang|gcc|msvc]
    117     \\                          Select which C compiler to emulate (default clang)
    118     \\  -o <file>               Write output to <file>
    119     \\  -pedantic               Warn on language extensions
    120     \\  --rtlib=<arg>           Compiler runtime library to use (libgcc or compiler-rt)
    121     \\  -std=<standard>         Specify language standard
    122     \\  -S, --assemble          Only run preprocess and compilation steps
    123     \\  --sysroot=<dir>         Use dir as the logical root directory for headers and libraries (not fully implemented)
    124     \\  --target=<value>        Generate code for the given target
    125     \\  -U <macro>              Undefine <macro>
    126     \\  -Werror                 Treat all warnings as errors
    127     \\  -Werror=<warning>       Treat warning as error
    128     \\  -W<warning>             Enable the specified warning
    129     \\  -Wno-<warning>          Disable the specified warning
    130     \\
    131     \\Link options:
    132     \\  -fuse-ld=[bfd|gold|lld|mold]
    133     \\                          Use specific linker
    134     \\  -nodefaultlibs          Do not use the standard system libraries when linking.
    135     \\  -nolibc                 Do not use the C library or system libraries tightly coupled with it when linking.
    136     \\  -nostdlib               Do not use the standard system startup files or libraries when linking
    137     \\  -nostartfiles           Do not use the standard system startup files when linking.
    138     \\  -pie                    Produce a dynamically linked position independent executable on targets that support it.
    139     \\  --ld-path=<path>        Use linker specified by <path>
    140     \\  -r                      Produce a relocatable object as output.
    141     \\  -rdynamic               Pass the flag -export-dynamic to the ELF linker, on targets that support it.
    142     \\  -s                      Remove all symbol table and relocation information from the executable.
    143     \\  -shared                 Produce a shared object which can then be linked with other objects to form an executable.
    144     \\  -shared-libgcc          On systems that provide libgcc as a shared library, force the use of the shared version
    145     \\  -static                 On systems that support dynamic linking, this overrides -pie and prevents linking with the shared libraries.
    146     \\  -static-libgcc          On systems that provide libgcc as a shared library, force the use of the static version
    147     \\  -static-pie             Produce a static position independent executable on targets that support it.
    148     \\  --unwindlib=<arg>       Unwind library to use ("none", "libgcc", or "libunwind") If not specified, will match runtime library
    149     \\
    150     \\Debug options:
    151     \\  --verbose-ast           Dump produced AST to stdout
    152     \\  --verbose-pp            Dump preprocessor state
    153     \\  --verbose-ir            Dump ir to stdout
    154     \\  --verbose-linker-args   Dump linker args to stdout
    155     \\
    156     \\
    157 ;
    158 
    159 /// Process command line arguments, returns true if something was written to std_out.
    160 pub fn parseArgs(
    161     d: *Driver,
    162     std_out: anytype,
    163     macro_buf: anytype,
    164     args: []const []const u8,
    165 ) !bool {
    166     var i: usize = 1;
    167     var color_setting: enum {
    168         on,
    169         off,
    170         unset,
    171     } = .unset;
    172     while (i < args.len) : (i += 1) {
    173         const arg = args[i];
    174         if (mem.startsWith(u8, arg, "-") and arg.len > 1) {
    175             if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
    176                 std_out.print(usage, .{args[0]}) catch |er| {
    177                     return d.fatal("unable to print usage: {s}", .{util.errorDescription(er)});
    178                 };
    179                 return true;
    180             } else if (mem.eql(u8, arg, "-v") or mem.eql(u8, arg, "--version")) {
    181                 std_out.writeAll(@import("lib.zig").version_str ++ "\n") catch |er| {
    182                     return d.fatal("unable to print version: {s}", .{util.errorDescription(er)});
    183                 };
    184                 return true;
    185             } else if (mem.startsWith(u8, arg, "-D")) {
    186                 var macro = arg["-D".len..];
    187                 if (macro.len == 0) {
    188                     i += 1;
    189                     if (i >= args.len) {
    190                         try d.err("expected argument after -I");
    191                         continue;
    192                     }
    193                     macro = args[i];
    194                 }
    195                 var value: []const u8 = "1";
    196                 if (mem.indexOfScalar(u8, macro, '=')) |some| {
    197                     value = macro[some + 1 ..];
    198                     macro = macro[0..some];
    199                 }
    200                 try macro_buf.print("#define {s} {s}\n", .{ macro, value });
    201             } else if (mem.startsWith(u8, arg, "-U")) {
    202                 var macro = arg["-U".len..];
    203                 if (macro.len == 0) {
    204                     i += 1;
    205                     if (i >= args.len) {
    206                         try d.err("expected argument after -I");
    207                         continue;
    208                     }
    209                     macro = args[i];
    210                 }
    211                 try macro_buf.print("#undef {s}\n", .{macro});
    212             } else if (mem.eql(u8, arg, "-c") or mem.eql(u8, arg, "--compile")) {
    213                 d.only_compile = true;
    214             } else if (mem.eql(u8, arg, "-E")) {
    215                 d.only_preprocess = true;
    216             } else if (mem.eql(u8, arg, "-fchar8_t")) {
    217                 d.comp.langopts.has_char8_t_override = true;
    218             } else if (mem.eql(u8, arg, "-fno-char8_t")) {
    219                 d.comp.langopts.has_char8_t_override = false;
    220             } else if (mem.eql(u8, arg, "-fcolor-diagnostics")) {
    221                 color_setting = .on;
    222             } else if (mem.eql(u8, arg, "-fno-color-diagnostics")) {
    223                 color_setting = .off;
    224             } else if (mem.eql(u8, arg, "-fdollars-in-identifiers")) {
    225                 d.comp.langopts.dollars_in_identifiers = true;
    226             } else if (mem.eql(u8, arg, "-fno-dollars-in-identifiers")) {
    227                 d.comp.langopts.dollars_in_identifiers = false;
    228             } else if (mem.eql(u8, arg, "-fdigraphs")) {
    229                 d.comp.langopts.digraphs = true;
    230             } else if (mem.eql(u8, arg, "-fgnu-inline-asm")) {
    231                 d.comp.langopts.gnu_asm = true;
    232             } else if (mem.eql(u8, arg, "-fno-gnu-inline-asm")) {
    233                 d.comp.langopts.gnu_asm = false;
    234             } else if (mem.eql(u8, arg, "-fno-digraphs")) {
    235                 d.comp.langopts.digraphs = false;
    236             } else if (option(arg, "-fmacro-backtrace-limit=")) |limit_str| {
    237                 var limit = std.fmt.parseInt(u32, limit_str, 10) catch {
    238                     try d.err("-fmacro-backtrace-limit takes a number argument");
    239                     continue;
    240                 };
    241 
    242                 if (limit == 0) limit = std.math.maxInt(u32);
    243                 d.comp.diag.macro_backtrace_limit = limit;
    244             } else if (mem.eql(u8, arg, "-fnative-half-type")) {
    245                 d.comp.langopts.use_native_half_type = true;
    246             } else if (mem.eql(u8, arg, "-fnative-half-arguments-and-returns")) {
    247                 d.comp.langopts.allow_half_args_and_returns = true;
    248             } else if (mem.eql(u8, arg, "-fshort-enums")) {
    249                 d.comp.langopts.short_enums = true;
    250             } else if (mem.eql(u8, arg, "-fno-short-enums")) {
    251                 d.comp.langopts.short_enums = false;
    252             } else if (mem.eql(u8, arg, "-fsigned-char")) {
    253                 d.comp.langopts.setCharSignedness(.signed);
    254             } else if (mem.eql(u8, arg, "-fno-signed-char")) {
    255                 d.comp.langopts.setCharSignedness(.unsigned);
    256             } else if (mem.eql(u8, arg, "-funsigned-char")) {
    257                 d.comp.langopts.setCharSignedness(.unsigned);
    258             } else if (mem.eql(u8, arg, "-fno-unsigned-char")) {
    259                 d.comp.langopts.setCharSignedness(.signed);
    260             } else if (mem.eql(u8, arg, "-fdeclspec")) {
    261                 d.comp.langopts.declspec_attrs = true;
    262             } else if (mem.eql(u8, arg, "-fno-declspec")) {
    263                 d.comp.langopts.declspec_attrs = false;
    264             } else if (mem.eql(u8, arg, "-fms-extensions")) {
    265                 d.comp.langopts.enableMSExtensions();
    266             } else if (mem.eql(u8, arg, "-fno-ms-extensions")) {
    267                 d.comp.langopts.disableMSExtensions();
    268             } else if (mem.startsWith(u8, arg, "-I")) {
    269                 var path = arg["-I".len..];
    270                 if (path.len == 0) {
    271                     i += 1;
    272                     if (i >= args.len) {
    273                         try d.err("expected argument after -I");
    274                         continue;
    275                     }
    276                     path = args[i];
    277                 }
    278                 try d.comp.include_dirs.append(path);
    279             } else if (mem.startsWith(u8, arg, "-fsyntax-only")) {
    280                 d.only_syntax = true;
    281             } else if (mem.startsWith(u8, arg, "-fno-syntax-only")) {
    282                 d.only_syntax = false;
    283             } else if (mem.startsWith(u8, arg, "-isystem")) {
    284                 var path = arg["-isystem".len..];
    285                 if (path.len == 0) {
    286                     i += 1;
    287                     if (i >= args.len) {
    288                         try d.err("expected argument after -isystem");
    289                         continue;
    290                     }
    291                     path = args[i];
    292                 }
    293                 const duped = try d.comp.gpa.dupe(u8, path);
    294                 errdefer d.comp.gpa.free(duped);
    295                 try d.comp.system_include_dirs.append(duped);
    296             } else if (option(arg, "--emulate=")) |compiler_str| {
    297                 const compiler = std.meta.stringToEnum(LangOpts.Compiler, compiler_str) orelse {
    298                     try d.comp.diag.add(.{ .tag = .cli_invalid_emulate, .extra = .{ .str = arg } }, &.{});
    299                     continue;
    300                 };
    301                 d.comp.langopts.setEmulatedCompiler(compiler);
    302             } else if (option(arg, "-ffp-eval-method=")) |fp_method_str| {
    303                 const fp_eval_method = std.meta.stringToEnum(LangOpts.FPEvalMethod, fp_method_str) orelse .indeterminate;
    304                 if (fp_eval_method == .indeterminate) {
    305                     try d.comp.diag.add(.{ .tag = .cli_invalid_fp_eval_method, .extra = .{ .str = fp_method_str } }, &.{});
    306                     continue;
    307                 }
    308                 d.comp.langopts.setFpEvalMethod(fp_eval_method);
    309             } else if (mem.startsWith(u8, arg, "-o")) {
    310                 var file = arg["-o".len..];
    311                 if (file.len == 0) {
    312                     i += 1;
    313                     if (i >= args.len) {
    314                         try d.err("expected argument after -o");
    315                         continue;
    316                     }
    317                     file = args[i];
    318                 }
    319                 d.output_name = file;
    320             } else if (option(arg, "--sysroot=")) |sysroot| {
    321                 d.sysroot = sysroot;
    322             } else if (mem.eql(u8, arg, "-pedantic")) {
    323                 d.comp.diag.options.pedantic = .warning;
    324             } else if (option(arg, "--rtlib=")) |rtlib| {
    325                 if (mem.eql(u8, rtlib, "compiler-rt") or mem.eql(u8, rtlib, "libgcc") or mem.eql(u8, rtlib, "platform")) {
    326                     d.rtlib = rtlib;
    327                 } else {
    328                     try d.comp.diag.add(.{ .tag = .invalid_rtlib, .extra = .{ .str = rtlib } }, &.{});
    329                 }
    330             } else if (option(arg, "-Werror=")) |err_name| {
    331                 try d.comp.diag.set(err_name, .@"error");
    332             } else if (mem.eql(u8, arg, "-Wno-fatal-errors")) {
    333                 d.comp.diag.fatal_errors = false;
    334             } else if (option(arg, "-Wno-")) |err_name| {
    335                 try d.comp.diag.set(err_name, .off);
    336             } else if (mem.eql(u8, arg, "-Wfatal-errors")) {
    337                 d.comp.diag.fatal_errors = true;
    338             } else if (option(arg, "-W")) |err_name| {
    339                 try d.comp.diag.set(err_name, .warning);
    340             } else if (option(arg, "-std=")) |standard| {
    341                 d.comp.langopts.setStandard(standard) catch
    342                     try d.comp.diag.add(.{ .tag = .cli_invalid_standard, .extra = .{ .str = arg } }, &.{});
    343             } else if (mem.eql(u8, arg, "-S") or mem.eql(u8, arg, "--assemble")) {
    344                 d.only_preprocess_and_compile = true;
    345             } else if (option(arg, "--target=")) |triple| {
    346                 const cross = std.zig.CrossTarget.parse(.{ .arch_os_abi = triple }) catch {
    347                     try d.comp.diag.add(.{ .tag = .cli_invalid_target, .extra = .{ .str = arg } }, &.{});
    348                     continue;
    349                 };
    350                 d.comp.target = cross.toTarget(); // TODO deprecated
    351                 d.comp.langopts.setEmulatedCompiler(target_util.systemCompiler(d.comp.target));
    352                 d.raw_target_triple = triple;
    353             } else if (mem.eql(u8, arg, "--verbose-ast")) {
    354                 d.verbose_ast = true;
    355             } else if (mem.eql(u8, arg, "--verbose-pp")) {
    356                 d.verbose_pp = true;
    357             } else if (mem.eql(u8, arg, "--verbose-ir")) {
    358                 d.verbose_ir = true;
    359             } else if (mem.eql(u8, arg, "--verbose-linker-args")) {
    360                 d.verbose_linker_args = true;
    361             } else if (option(arg, "-fuse-ld=")) |linker_name| {
    362                 d.use_linker = linker_name;
    363             } else if (mem.eql(u8, arg, "-fuse-ld=")) {
    364                 d.use_linker = null;
    365             } else if (option(arg, "--ld-path=")) |linker_path| {
    366                 d.linker_path = linker_path;
    367             } else if (mem.eql(u8, arg, "-r")) {
    368                 d.relocatable = true;
    369             } else if (mem.eql(u8, arg, "-shared")) {
    370                 d.shared = true;
    371             } else if (mem.eql(u8, arg, "-shared-libgcc")) {
    372                 d.shared_libgcc = true;
    373             } else if (mem.eql(u8, arg, "-static")) {
    374                 d.static = true;
    375             } else if (mem.eql(u8, arg, "-static-libgcc")) {
    376                 d.static_libgcc = true;
    377             } else if (mem.eql(u8, arg, "-static-pie")) {
    378                 d.static_pie = true;
    379             } else if (mem.eql(u8, arg, "-pie")) {
    380                 d.pie = true;
    381             } else if (mem.eql(u8, arg, "-no-pie") or mem.eql(u8, arg, "-nopie")) {
    382                 d.pie = false;
    383             } else if (mem.eql(u8, arg, "-rdynamic")) {
    384                 d.rdynamic = true;
    385             } else if (mem.eql(u8, arg, "-s")) {
    386                 d.strip = true;
    387             } else if (mem.eql(u8, arg, "-nodefaultlibs")) {
    388                 d.nodefaultlibs = true;
    389             } else if (mem.eql(u8, arg, "-nolibc")) {
    390                 d.nolibc = true;
    391             } else if (mem.eql(u8, arg, "-nostdlib")) {
    392                 d.nostdlib = true;
    393             } else if (mem.eql(u8, arg, "-nostartfiles")) {
    394                 d.nostartfiles = true;
    395             } else if (option(arg, "--unwindlib=")) |unwindlib| {
    396                 const valid_unwindlibs: [5][]const u8 = .{ "", "none", "platform", "libunwind", "libgcc" };
    397                 for (valid_unwindlibs) |name| {
    398                     if (mem.eql(u8, name, unwindlib)) {
    399                         d.unwindlib = unwindlib;
    400                         break;
    401                     }
    402                 } else {
    403                     try d.comp.diag.add(.{ .tag = .invalid_unwindlib, .extra = .{ .str = unwindlib } }, &.{});
    404                 }
    405             } else {
    406                 try d.comp.diag.add(.{ .tag = .cli_unknown_arg, .extra = .{ .str = arg } }, &.{});
    407             }
    408         } else if (std.mem.endsWith(u8, arg, ".o") or std.mem.endsWith(u8, arg, ".obj")) {
    409             try d.link_objects.append(d.comp.gpa, arg);
    410         } else {
    411             const source = d.addSource(arg) catch |er| {
    412                 return d.fatal("unable to add source file '{s}': {s}", .{ arg, util.errorDescription(er) });
    413             };
    414             try d.inputs.append(d.comp.gpa, source);
    415         }
    416     }
    417     d.comp.diag.color = switch (color_setting) {
    418         .on => true,
    419         .off => false,
    420         .unset => util.fileSupportsColor(std.io.getStdErr()) and !std.process.hasEnvVarConstant("NO_COLOR"),
    421     };
    422     return false;
    423 }
    424 
    425 fn option(arg: []const u8, name: []const u8) ?[]const u8 {
    426     if (std.mem.startsWith(u8, arg, name) and arg.len > name.len) {
    427         return arg[name.len..];
    428     }
    429     return null;
    430 }
    431 
    432 fn addSource(d: *Driver, path: []const u8) !Source {
    433     if (mem.eql(u8, "-", path)) {
    434         const stdin = std.io.getStdIn().reader();
    435         const input = try stdin.readAllAlloc(d.comp.gpa, std.math.maxInt(u32));
    436         defer d.comp.gpa.free(input);
    437         return d.comp.addSourceFromBuffer("<stdin>", input);
    438     }
    439     return d.comp.addSourceFromPath(path);
    440 }
    441 
    442 pub fn err(d: *Driver, msg: []const u8) !void {
    443     try d.comp.diag.add(.{ .tag = .cli_error, .extra = .{ .str = msg } }, &.{});
    444 }
    445 
    446 pub fn fatal(d: *Driver, comptime fmt: []const u8, args: anytype) error{FatalError} {
    447     d.comp.renderErrors();
    448     return d.comp.diag.fatalNoSrc(fmt, args);
    449 }
    450 
    451 pub fn main(d: *Driver, tc: *Toolchain, args: []const []const u8) !void {
    452     var macro_buf = std.ArrayList(u8).init(d.comp.gpa);
    453     defer macro_buf.deinit();
    454 
    455     const std_out = std.io.getStdOut().writer();
    456     if (try parseArgs(d, std_out, macro_buf.writer(), args)) return;
    457 
    458     const linking = !(d.only_preprocess or d.only_syntax or d.only_compile or d.only_preprocess_and_compile);
    459 
    460     if (d.inputs.items.len == 0) {
    461         return d.fatal("no input files", .{});
    462     } else if (d.inputs.items.len != 1 and d.output_name != null and !linking) {
    463         return d.fatal("cannot specify -o when generating multiple output files", .{});
    464     }
    465 
    466     if (!linking) for (d.link_objects.items) |obj| {
    467         try d.comp.diag.add(.{ .tag = .cli_unused_link_object, .extra = .{ .str = obj } }, &.{});
    468     };
    469 
    470     d.comp.defineSystemIncludes(d.aro_name) catch |er| switch (er) {
    471         error.OutOfMemory => return error.OutOfMemory,
    472         error.AroIncludeNotFound => return d.fatal("unable to find Aro builtin headers", .{}),
    473     };
    474 
    475     const builtin = try d.comp.generateBuiltinMacros();
    476     const user_macros = try d.comp.addSourceFromBuffer("<command line>", macro_buf.items);
    477 
    478     const fast_exit = @import("builtin").mode != .Debug;
    479 
    480     if (fast_exit and d.inputs.items.len == 1) {
    481         d.processSource(tc, d.inputs.items[0], builtin, user_macros, fast_exit) catch |e| switch (e) {
    482             error.FatalError => {
    483                 d.comp.renderErrors();
    484                 d.exitWithCleanup(1);
    485             },
    486             else => |er| return er,
    487         };
    488         unreachable;
    489     }
    490 
    491     for (d.inputs.items) |source| {
    492         d.processSource(tc, source, builtin, user_macros, fast_exit) catch |e| switch (e) {
    493             error.FatalError => {
    494                 d.comp.renderErrors();
    495             },
    496             else => |er| return er,
    497         };
    498     }
    499     if (d.comp.diag.errors != 0) {
    500         if (fast_exit) d.exitWithCleanup(1);
    501         return;
    502     }
    503     if (linking) {
    504         try d.invokeLinker(tc, fast_exit);
    505     }
    506     if (fast_exit) std.process.exit(0);
    507 }
    508 
    509 fn processSource(
    510     d: *Driver,
    511     tc: *Toolchain,
    512     source: Source,
    513     builtin: Source,
    514     user_macros: Source,
    515     comptime fast_exit: bool,
    516 ) !void {
    517     d.comp.generated_buf.items.len = 0;
    518     var pp = Preprocessor.init(d.comp);
    519     defer pp.deinit();
    520 
    521     if (d.verbose_pp) pp.verbose = true;
    522     if (d.only_preprocess) pp.preserve_whitespace = true;
    523     try pp.addBuiltinMacros();
    524 
    525     _ = try pp.preprocess(builtin);
    526     _ = try pp.preprocess(user_macros);
    527     const eof = try pp.preprocess(source);
    528     try pp.tokens.append(pp.comp.gpa, eof);
    529 
    530     if (d.only_preprocess) {
    531         d.comp.renderErrors();
    532 
    533         const file = if (d.output_name) |some|
    534             std.fs.cwd().createFile(some, .{}) catch |er|
    535                 return d.fatal("unable to create output file '{s}': {s}", .{ some, util.errorDescription(er) })
    536         else
    537             std.io.getStdOut();
    538         defer if (d.output_name != null) file.close();
    539 
    540         var buf_w = std.io.bufferedWriter(file.writer());
    541         pp.prettyPrintTokens(buf_w.writer()) catch |er|
    542             return d.fatal("unable to write result: {s}", .{util.errorDescription(er)});
    543 
    544         buf_w.flush() catch |er|
    545             return d.fatal("unable to write result: {s}", .{util.errorDescription(er)});
    546         if (fast_exit) std.process.exit(0); // Not linking, no need for cleanup.
    547         return;
    548     }
    549 
    550     var tree = try Parser.parse(&pp);
    551     defer tree.deinit();
    552 
    553     if (d.verbose_ast) {
    554         const stdout = std.io.getStdOut();
    555         var buf_writer = std.io.bufferedWriter(stdout.writer());
    556         const color = d.comp.diag.color and util.fileSupportsColor(stdout);
    557         tree.dump(color, buf_writer.writer()) catch {};
    558         buf_writer.flush() catch {};
    559     }
    560 
    561     const prev_errors = d.comp.diag.errors;
    562     d.comp.renderErrors();
    563 
    564     if (d.comp.diag.errors != prev_errors) {
    565         if (fast_exit) d.exitWithCleanup(1);
    566         return; // do not compile if there were errors
    567     }
    568 
    569     if (d.only_syntax) {
    570         if (fast_exit) std.process.exit(0); // Not linking, no need for cleanup.
    571         return;
    572     }
    573 
    574     if (d.comp.target.ofmt != .elf or d.comp.target.cpu.arch != .x86_64) {
    575         return d.fatal(
    576             "unsupported target {s}-{s}-{s}, currently only x86-64 elf is supported",
    577             .{ @tagName(d.comp.target.cpu.arch), @tagName(d.comp.target.os.tag), @tagName(d.comp.target.abi) },
    578         );
    579     }
    580 
    581     if (d.verbose_ir) {
    582         try @import("CodeGen.zig").generateTree(d.comp, tree);
    583     }
    584 
    585     const obj = try Codegen.generateTree(d.comp, tree);
    586     defer obj.deinit();
    587 
    588     // If it's used, name_buf will either hold a filename or `/tmp/<12 random bytes with base-64 encoding>.<extension>`
    589     // both of which should fit into MAX_NAME_BYTES for all systems
    590     var name_buf: [std.fs.MAX_NAME_BYTES]u8 = undefined;
    591 
    592     const out_file_name = if (d.only_compile) blk: {
    593         const fmt_template = "{s}{s}";
    594         const fmt_args = .{
    595             std.fs.path.stem(source.path),
    596             d.comp.target.ofmt.fileExt(d.comp.target.cpu.arch),
    597         };
    598         break :blk d.output_name orelse
    599             std.fmt.bufPrint(&name_buf, fmt_template, fmt_args) catch return d.fatal("Filename too long for filesystem: " ++ fmt_template, fmt_args);
    600     } else blk: {
    601         const random_bytes_count = 12;
    602         const sub_path_len = comptime std.fs.base64_encoder.calcSize(random_bytes_count);
    603 
    604         var random_bytes: [random_bytes_count]u8 = undefined;
    605         std.crypto.random.bytes(&random_bytes);
    606         var random_name: [sub_path_len]u8 = undefined;
    607         _ = std.fs.base64_encoder.encode(&random_name, &random_bytes);
    608 
    609         const fmt_template = "/tmp/{s}{s}";
    610         const fmt_args = .{
    611             random_name,
    612             d.comp.target.ofmt.fileExt(d.comp.target.cpu.arch),
    613         };
    614         break :blk std.fmt.bufPrint(&name_buf, fmt_template, fmt_args) catch return d.fatal("Filename too long for filesystem: " ++ fmt_template, fmt_args);
    615     };
    616 
    617     const out_file = std.fs.cwd().createFile(out_file_name, .{}) catch |er|
    618         return d.fatal("unable to create output file '{s}': {s}", .{ out_file_name, util.errorDescription(er) });
    619     defer out_file.close();
    620 
    621     obj.finish(out_file) catch |er|
    622         return d.fatal("could not output to object file '{s}': {s}", .{ out_file_name, util.errorDescription(er) });
    623 
    624     if (d.only_compile) {
    625         if (fast_exit) std.process.exit(0); // Not linking, no need for cleanup.
    626         return;
    627     }
    628     try d.link_objects.ensureUnusedCapacity(d.comp.gpa, 1);
    629     d.link_objects.appendAssumeCapacity(try d.comp.gpa.dupe(u8, out_file_name));
    630     d.temp_file_count += 1;
    631     if (fast_exit) {
    632         try d.invokeLinker(tc, fast_exit);
    633     }
    634 }
    635 
    636 fn dumpLinkerArgs(items: []const []const u8) !void {
    637     const stdout = std.io.getStdOut().writer();
    638     for (items, 0..) |item, i| {
    639         if (i > 0) try stdout.writeByte(' ');
    640         try stdout.print("\"{}\"", .{std.zig.fmtEscapes(item)});
    641     }
    642     try stdout.writeByte('\n');
    643 }
    644 
    645 pub fn invokeLinker(d: *Driver, tc: *Toolchain, comptime fast_exit: bool) !void {
    646     try tc.discover();
    647 
    648     var argv = std.ArrayList([]const u8).init(d.comp.gpa);
    649     defer argv.deinit();
    650 
    651     var linker_path_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
    652     const linker_path = try tc.getLinkerPath(&linker_path_buf);
    653     try argv.append(linker_path);
    654 
    655     try tc.buildLinkerArgs(&argv);
    656 
    657     if (d.verbose_linker_args) {
    658         dumpLinkerArgs(argv.items) catch |er| {
    659             return d.fatal("unable to dump linker args: {s}", .{util.errorDescription(er)});
    660         };
    661     }
    662     var child = std.ChildProcess.init(argv.items, d.comp.gpa);
    663     // TODO handle better
    664     child.stdin_behavior = .Inherit;
    665     child.stdout_behavior = .Inherit;
    666     child.stderr_behavior = .Inherit;
    667 
    668     const term = child.spawnAndWait() catch |er| {
    669         return d.fatal("unable to spawn linker: {s}", .{util.errorDescription(er)});
    670     };
    671     switch (term) {
    672         .Exited => |code| if (code != 0) d.exitWithCleanup(code),
    673         else => std.process.abort(),
    674     }
    675     if (fast_exit) d.exitWithCleanup(0);
    676 }
    677 
    678 fn exitWithCleanup(d: *Driver, code: u8) noreturn {
    679     for (d.link_objects.items[d.link_objects.items.len - d.temp_file_count ..]) |obj| {
    680         std.fs.deleteFileAbsolute(obj) catch {};
    681     }
    682     std.process.exit(code);
    683 }