zig

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

blob 5c27e5f5 (30378B) - Raw


      1 const std = @import("std");
      2 const builtin = @import("builtin");
      3 
      4 const event = std.event;
      5 const os = std.os;
      6 const io = std.io;
      7 const mem = std.mem;
      8 const Allocator = mem.Allocator;
      9 const ArrayList = std.ArrayList;
     10 const Buffer = std.Buffer;
     11 
     12 const arg = @import("arg.zig");
     13 const c = @import("c.zig");
     14 const introspect = @import("introspect.zig");
     15 const Args = arg.Args;
     16 const Flag = arg.Flag;
     17 const EventLoopLocal = @import("compilation.zig").EventLoopLocal;
     18 const Compilation = @import("compilation.zig").Compilation;
     19 const Target = @import("target.zig").Target;
     20 const errmsg = @import("errmsg.zig");
     21 const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
     22 
     23 var stderr_file: os.File = undefined;
     24 var stderr: *io.OutStream(io.FileOutStream.Error) = undefined;
     25 var stdout: *io.OutStream(io.FileOutStream.Error) = undefined;
     26 
     27 const usage =
     28     \\usage: zig [command] [options]
     29     \\
     30     \\Commands:
     31     \\
     32     \\  build-exe  [source]      Create executable from source or object files
     33     \\  build-lib  [source]      Create library from source or object files
     34     \\  build-obj  [source]      Create object from source or assembly
     35     \\  fmt        [source]      Parse file and render in canonical zig format
     36     \\  libc       [paths_file]  Display native libc paths file or validate one
     37     \\  targets                  List available compilation targets
     38     \\  version                  Print version number and exit
     39     \\  zen                      Print zen of zig and exit
     40     \\
     41     \\
     42 ;
     43 
     44 const Command = struct {
     45     name: []const u8,
     46     exec: fn (*Allocator, []const []const u8) error!void,
     47 };
     48 
     49 pub fn main() !void {
     50     // This allocator needs to be thread-safe because we use it for the event.Loop
     51     // which multiplexes coroutines onto kernel threads.
     52     // libc allocator is guaranteed to have this property.
     53     const allocator = std.heap.c_allocator;
     54 
     55     var stdout_file = try std.io.getStdOut();
     56     var stdout_out_stream = std.io.FileOutStream.init(&stdout_file);
     57     stdout = &stdout_out_stream.stream;
     58 
     59     stderr_file = try std.io.getStdErr();
     60     var stderr_out_stream = std.io.FileOutStream.init(&stderr_file);
     61     stderr = &stderr_out_stream.stream;
     62 
     63     const args = try os.argsAlloc(allocator);
     64     // TODO I'm getting  unreachable code here, which shouldn't happen
     65     //defer os.argsFree(allocator, args);
     66 
     67     if (args.len <= 1) {
     68         try stderr.write("expected command argument\n\n");
     69         try stderr.write(usage);
     70         os.exit(1);
     71     }
     72 
     73     const commands = []Command{
     74         Command{
     75             .name = "build-exe",
     76             .exec = cmdBuildExe,
     77         },
     78         Command{
     79             .name = "build-lib",
     80             .exec = cmdBuildLib,
     81         },
     82         Command{
     83             .name = "build-obj",
     84             .exec = cmdBuildObj,
     85         },
     86         Command{
     87             .name = "fmt",
     88             .exec = cmdFmt,
     89         },
     90         Command{
     91             .name = "libc",
     92             .exec = cmdLibC,
     93         },
     94         Command{
     95             .name = "targets",
     96             .exec = cmdTargets,
     97         },
     98         Command{
     99             .name = "version",
    100             .exec = cmdVersion,
    101         },
    102         Command{
    103             .name = "zen",
    104             .exec = cmdZen,
    105         },
    106 
    107         // undocumented commands
    108         Command{
    109             .name = "help",
    110             .exec = cmdHelp,
    111         },
    112         Command{
    113             .name = "internal",
    114             .exec = cmdInternal,
    115         },
    116     };
    117 
    118     for (commands) |command| {
    119         if (mem.eql(u8, command.name, args[1])) {
    120             return command.exec(allocator, args[2..]);
    121         }
    122     }
    123 
    124     try stderr.print("unknown command: {}\n\n", args[1]);
    125     try stderr.write(usage);
    126     os.exit(1);
    127 }
    128 
    129 const usage_build_generic =
    130     \\usage: zig build-exe <options> [file]
    131     \\       zig build-lib <options> [file]
    132     \\       zig build-obj <options> [file]
    133     \\
    134     \\General Options:
    135     \\  --help                       Print this help and exit
    136     \\  --color [auto|off|on]        Enable or disable colored error messages
    137     \\
    138     \\Compile Options:
    139     \\  --libc [file]                Provide a file which specifies libc paths
    140     \\  --assembly [source]          Add assembly file to build
    141     \\  --cache-dir [path]           Override the cache directory
    142     \\  --emit [filetype]            Emit a specific file format as compilation output
    143     \\  --enable-timing-info         Print timing diagnostics
    144     \\  --name [name]                Override output name
    145     \\  --output [file]              Override destination path
    146     \\  --output-h [file]            Override generated header file path
    147     \\  --pkg-begin [name] [path]    Make package available to import and push current pkg
    148     \\  --pkg-end                    Pop current pkg
    149     \\  --mode [mode]                Set the build mode
    150     \\    debug                      (default) optimizations off, safety on
    151     \\    release-fast               optimizations on, safety off
    152     \\    release-safe               optimizations on, safety on
    153     \\    release-small              optimize for small binary, safety off
    154     \\  --static                     Output will be statically linked
    155     \\  --strip                      Exclude debug symbols
    156     \\  --target-arch [name]         Specify target architecture
    157     \\  --target-environ [name]      Specify target environment
    158     \\  --target-os [name]           Specify target operating system
    159     \\  --verbose-tokenize           Turn on compiler debug output for tokenization
    160     \\  --verbose-ast-tree           Turn on compiler debug output for parsing into an AST (tree view)
    161     \\  --verbose-ast-fmt            Turn on compiler debug output for parsing into an AST (render source)
    162     \\  --verbose-link               Turn on compiler debug output for linking
    163     \\  --verbose-ir                 Turn on compiler debug output for Zig IR
    164     \\  --verbose-llvm-ir            Turn on compiler debug output for LLVM IR
    165     \\  --verbose-cimport            Turn on compiler debug output for C imports
    166     \\  -dirafter [dir]              Same as -isystem but do it last
    167     \\  -isystem [dir]               Add additional search path for other .h files
    168     \\  -mllvm [arg]                 Additional arguments to forward to LLVM's option processing
    169     \\
    170     \\Link Options:
    171     \\  --ar-path [path]             Set the path to ar
    172     \\  --each-lib-rpath             Add rpath for each used dynamic library
    173     \\  --library [lib]              Link against lib
    174     \\  --forbid-library [lib]       Make it an error to link against lib
    175     \\  --library-path [dir]         Add a directory to the library search path
    176     \\  --linker-script [path]       Use a custom linker script
    177     \\  --object [obj]               Add object file to build
    178     \\  -rdynamic                    Add all symbols to the dynamic symbol table
    179     \\  -rpath [path]                Add directory to the runtime library search path
    180     \\  -mconsole                    (windows) --subsystem console to the linker
    181     \\  -mwindows                    (windows) --subsystem windows to the linker
    182     \\  -framework [name]            (darwin) link against framework
    183     \\  -mios-version-min [ver]      (darwin) set iOS deployment target
    184     \\  -mmacosx-version-min [ver]   (darwin) set Mac OS X deployment target
    185     \\  --ver-major [ver]            Dynamic library semver major version
    186     \\  --ver-minor [ver]            Dynamic library semver minor version
    187     \\  --ver-patch [ver]            Dynamic library semver patch version
    188     \\
    189     \\
    190 ;
    191 
    192 const args_build_generic = []Flag{
    193     Flag.Bool("--help"),
    194     Flag.Option("--color", []const []const u8{
    195         "auto",
    196         "off",
    197         "on",
    198     }),
    199     Flag.Option("--mode", []const []const u8{
    200         "debug",
    201         "release-fast",
    202         "release-safe",
    203         "release-small",
    204     }),
    205 
    206     Flag.ArgMergeN("--assembly", 1),
    207     Flag.Arg1("--cache-dir"),
    208     Flag.Option("--emit", []const []const u8{
    209         "asm",
    210         "bin",
    211         "llvm-ir",
    212     }),
    213     Flag.Bool("--enable-timing-info"),
    214     Flag.Arg1("--libc"),
    215     Flag.Arg1("--name"),
    216     Flag.Arg1("--output"),
    217     Flag.Arg1("--output-h"),
    218     // NOTE: Parsed manually after initial check
    219     Flag.ArgN("--pkg-begin", 2),
    220     Flag.Bool("--pkg-end"),
    221     Flag.Bool("--static"),
    222     Flag.Bool("--strip"),
    223     Flag.Arg1("--target-arch"),
    224     Flag.Arg1("--target-environ"),
    225     Flag.Arg1("--target-os"),
    226     Flag.Bool("--verbose-tokenize"),
    227     Flag.Bool("--verbose-ast-tree"),
    228     Flag.Bool("--verbose-ast-fmt"),
    229     Flag.Bool("--verbose-link"),
    230     Flag.Bool("--verbose-ir"),
    231     Flag.Bool("--verbose-llvm-ir"),
    232     Flag.Bool("--verbose-cimport"),
    233     Flag.Arg1("-dirafter"),
    234     Flag.ArgMergeN("-isystem", 1),
    235     Flag.Arg1("-mllvm"),
    236 
    237     Flag.Arg1("--ar-path"),
    238     Flag.Bool("--each-lib-rpath"),
    239     Flag.ArgMergeN("--library", 1),
    240     Flag.ArgMergeN("--forbid-library", 1),
    241     Flag.ArgMergeN("--library-path", 1),
    242     Flag.Arg1("--linker-script"),
    243     Flag.ArgMergeN("--object", 1),
    244     // NOTE: Removed -L since it would need to be special-cased and we have an alias in library-path
    245     Flag.Bool("-rdynamic"),
    246     Flag.Arg1("-rpath"),
    247     Flag.Bool("-mconsole"),
    248     Flag.Bool("-mwindows"),
    249     Flag.ArgMergeN("-framework", 1),
    250     Flag.Arg1("-mios-version-min"),
    251     Flag.Arg1("-mmacosx-version-min"),
    252     Flag.Arg1("--ver-major"),
    253     Flag.Arg1("--ver-minor"),
    254     Flag.Arg1("--ver-patch"),
    255 };
    256 
    257 fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Compilation.Kind) !void {
    258     var flags = try Args.parse(allocator, args_build_generic, args);
    259     defer flags.deinit();
    260 
    261     if (flags.present("help")) {
    262         try stdout.write(usage_build_generic);
    263         os.exit(0);
    264     }
    265 
    266     const build_mode = blk: {
    267         if (flags.single("mode")) |mode_flag| {
    268             if (mem.eql(u8, mode_flag, "debug")) {
    269                 break :blk builtin.Mode.Debug;
    270             } else if (mem.eql(u8, mode_flag, "release-fast")) {
    271                 break :blk builtin.Mode.ReleaseFast;
    272             } else if (mem.eql(u8, mode_flag, "release-safe")) {
    273                 break :blk builtin.Mode.ReleaseSafe;
    274             } else if (mem.eql(u8, mode_flag, "release-small")) {
    275                 break :blk builtin.Mode.ReleaseSmall;
    276             } else unreachable;
    277         } else {
    278             break :blk builtin.Mode.Debug;
    279         }
    280     };
    281 
    282     const color = blk: {
    283         if (flags.single("color")) |color_flag| {
    284             if (mem.eql(u8, color_flag, "auto")) {
    285                 break :blk errmsg.Color.Auto;
    286             } else if (mem.eql(u8, color_flag, "on")) {
    287                 break :blk errmsg.Color.On;
    288             } else if (mem.eql(u8, color_flag, "off")) {
    289                 break :blk errmsg.Color.Off;
    290             } else unreachable;
    291         } else {
    292             break :blk errmsg.Color.Auto;
    293         }
    294     };
    295 
    296     const emit_type = blk: {
    297         if (flags.single("emit")) |emit_flag| {
    298             if (mem.eql(u8, emit_flag, "asm")) {
    299                 break :blk Compilation.Emit.Assembly;
    300             } else if (mem.eql(u8, emit_flag, "bin")) {
    301                 break :blk Compilation.Emit.Binary;
    302             } else if (mem.eql(u8, emit_flag, "llvm-ir")) {
    303                 break :blk Compilation.Emit.LlvmIr;
    304             } else unreachable;
    305         } else {
    306             break :blk Compilation.Emit.Binary;
    307         }
    308     };
    309 
    310     var cur_pkg = try CliPkg.init(allocator, "", "", null);
    311     defer cur_pkg.deinit();
    312 
    313     var i: usize = 0;
    314     while (i < args.len) : (i += 1) {
    315         const arg_name = args[i];
    316         if (mem.eql(u8, "--pkg-begin", arg_name)) {
    317             // following two arguments guaranteed to exist due to arg parsing
    318             i += 1;
    319             const new_pkg_name = args[i];
    320             i += 1;
    321             const new_pkg_path = args[i];
    322 
    323             var new_cur_pkg = try CliPkg.init(allocator, new_pkg_name, new_pkg_path, cur_pkg);
    324             try cur_pkg.children.append(new_cur_pkg);
    325             cur_pkg = new_cur_pkg;
    326         } else if (mem.eql(u8, "--pkg-end", arg_name)) {
    327             if (cur_pkg.parent) |parent| {
    328                 cur_pkg = parent;
    329             } else {
    330                 try stderr.print("encountered --pkg-end with no matching --pkg-begin\n");
    331                 os.exit(1);
    332             }
    333         }
    334     }
    335 
    336     if (cur_pkg.parent != null) {
    337         try stderr.print("unmatched --pkg-begin\n");
    338         os.exit(1);
    339     }
    340 
    341     const provided_name = flags.single("name");
    342     const root_source_file = switch (flags.positionals.len) {
    343         0 => null,
    344         1 => flags.positionals.at(0),
    345         else => {
    346             try stderr.print("unexpected extra parameter: {}\n", flags.positionals.at(1));
    347             os.exit(1);
    348         },
    349     };
    350 
    351     const root_name = if (provided_name) |n| n else blk: {
    352         if (root_source_file) |file| {
    353             const basename = os.path.basename(file);
    354             var it = mem.split(basename, ".");
    355             break :blk it.next() orelse basename;
    356         } else {
    357             try stderr.write("--name [name] not provided and unable to infer\n");
    358             os.exit(1);
    359         }
    360     };
    361 
    362     const is_static = flags.present("static");
    363 
    364     const assembly_files = flags.many("assembly");
    365     const link_objects = flags.many("object");
    366     if (root_source_file == null and link_objects.len == 0 and assembly_files.len == 0) {
    367         try stderr.write("Expected source file argument or at least one --object or --assembly argument\n");
    368         os.exit(1);
    369     }
    370 
    371     if (out_type == Compilation.Kind.Obj and link_objects.len != 0) {
    372         try stderr.write("When building an object file, --object arguments are invalid\n");
    373         os.exit(1);
    374     }
    375 
    376     const rel_cache_dir = flags.single("cache-dir") orelse "zig-cache"[0..];
    377     const full_cache_dir = os.path.resolve(allocator, ".", rel_cache_dir) catch {
    378         try stderr.print("invalid cache dir: {}\n", rel_cache_dir);
    379         os.exit(1);
    380     };
    381     defer allocator.free(full_cache_dir);
    382 
    383     const zig_lib_dir = introspect.resolveZigLibDir(allocator) catch os.exit(1);
    384     defer allocator.free(zig_lib_dir);
    385 
    386     var override_libc: LibCInstallation = undefined;
    387 
    388     var loop: event.Loop = undefined;
    389     try loop.initMultiThreaded(allocator);
    390     defer loop.deinit();
    391 
    392     var event_loop_local = try EventLoopLocal.init(&loop);
    393     defer event_loop_local.deinit();
    394 
    395     var comp = try Compilation.create(
    396         &event_loop_local,
    397         root_name,
    398         root_source_file,
    399         Target.Native,
    400         out_type,
    401         build_mode,
    402         is_static,
    403         zig_lib_dir,
    404         full_cache_dir,
    405     );
    406     defer comp.destroy();
    407 
    408     if (flags.single("libc")) |libc_path| {
    409         parseLibcPaths(loop.allocator, &override_libc, libc_path);
    410         comp.override_libc = &override_libc;
    411     }
    412 
    413     for (flags.many("library")) |lib| {
    414         _ = try comp.addLinkLib(lib, true);
    415     }
    416 
    417     comp.version_major = try std.fmt.parseUnsigned(u32, flags.single("ver-major") orelse "0", 10);
    418     comp.version_minor = try std.fmt.parseUnsigned(u32, flags.single("ver-minor") orelse "0", 10);
    419     comp.version_patch = try std.fmt.parseUnsigned(u32, flags.single("ver-patch") orelse "0", 10);
    420 
    421     comp.is_test = false;
    422 
    423     comp.linker_script = flags.single("linker-script");
    424     comp.each_lib_rpath = flags.present("each-lib-rpath");
    425 
    426     var clang_argv_buf = ArrayList([]const u8).init(allocator);
    427     defer clang_argv_buf.deinit();
    428 
    429     const mllvm_flags = flags.many("mllvm");
    430     for (mllvm_flags) |mllvm| {
    431         try clang_argv_buf.append("-mllvm");
    432         try clang_argv_buf.append(mllvm);
    433     }
    434 
    435     comp.llvm_argv = mllvm_flags;
    436     comp.clang_argv = clang_argv_buf.toSliceConst();
    437 
    438     comp.strip = flags.present("strip");
    439 
    440     comp.verbose_tokenize = flags.present("verbose-tokenize");
    441     comp.verbose_ast_tree = flags.present("verbose-ast-tree");
    442     comp.verbose_ast_fmt = flags.present("verbose-ast-fmt");
    443     comp.verbose_link = flags.present("verbose-link");
    444     comp.verbose_ir = flags.present("verbose-ir");
    445     comp.verbose_llvm_ir = flags.present("verbose-llvm-ir");
    446     comp.verbose_cimport = flags.present("verbose-cimport");
    447 
    448     comp.err_color = color;
    449     comp.lib_dirs = flags.many("library-path");
    450     comp.darwin_frameworks = flags.many("framework");
    451     comp.rpath_list = flags.many("rpath");
    452 
    453     if (flags.single("output-h")) |output_h| {
    454         comp.out_h_path = output_h;
    455     }
    456 
    457     comp.windows_subsystem_windows = flags.present("mwindows");
    458     comp.windows_subsystem_console = flags.present("mconsole");
    459     comp.linker_rdynamic = flags.present("rdynamic");
    460 
    461     if (flags.single("mmacosx-version-min") != null and flags.single("mios-version-min") != null) {
    462         try stderr.write("-mmacosx-version-min and -mios-version-min options not allowed together\n");
    463         os.exit(1);
    464     }
    465 
    466     if (flags.single("mmacosx-version-min")) |ver| {
    467         comp.darwin_version_min = Compilation.DarwinVersionMin{ .MacOS = ver };
    468     }
    469     if (flags.single("mios-version-min")) |ver| {
    470         comp.darwin_version_min = Compilation.DarwinVersionMin{ .Ios = ver };
    471     }
    472 
    473     comp.emit_file_type = emit_type;
    474     comp.assembly_files = assembly_files;
    475     comp.link_out_file = flags.single("out-file");
    476     comp.link_objects = link_objects;
    477 
    478     try comp.build();
    479     const process_build_events_handle = try async<loop.allocator> processBuildEvents(comp, color);
    480     defer cancel process_build_events_handle;
    481     loop.run();
    482 }
    483 
    484 async fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void {
    485     // TODO directly awaiting async should guarantee memory allocation elision
    486     const build_event = await (async comp.events.get() catch unreachable);
    487 
    488     switch (build_event) {
    489         Compilation.Event.Ok => {
    490             return;
    491         },
    492         Compilation.Event.Error => |err| {
    493             std.debug.warn("build failed: {}\n", @errorName(err));
    494             os.exit(1);
    495         },
    496         Compilation.Event.Fail => |msgs| {
    497             for (msgs) |msg| {
    498                 errmsg.printToFile(&stderr_file, msg, color) catch os.exit(1);
    499             }
    500         },
    501     }
    502 }
    503 
    504 fn cmdBuildExe(allocator: *Allocator, args: []const []const u8) !void {
    505     return buildOutputType(allocator, args, Compilation.Kind.Exe);
    506 }
    507 
    508 fn cmdBuildLib(allocator: *Allocator, args: []const []const u8) !void {
    509     return buildOutputType(allocator, args, Compilation.Kind.Lib);
    510 }
    511 
    512 fn cmdBuildObj(allocator: *Allocator, args: []const []const u8) !void {
    513     return buildOutputType(allocator, args, Compilation.Kind.Obj);
    514 }
    515 
    516 const usage_fmt =
    517     \\usage: zig fmt [file]...
    518     \\
    519     \\   Formats the input files and modifies them in-place.
    520     \\
    521     \\Options:
    522     \\   --help                 Print this help and exit
    523     \\   --color [auto|off|on]  Enable or disable colored error messages
    524     \\   --stdin                Format code from stdin
    525     \\
    526     \\
    527 ;
    528 
    529 const args_fmt_spec = []Flag{
    530     Flag.Bool("--help"),
    531     Flag.Option("--color", []const []const u8{
    532         "auto",
    533         "off",
    534         "on",
    535     }),
    536     Flag.Bool("--stdin"),
    537 };
    538 
    539 const Fmt = struct {
    540     seen: std.HashMap([]const u8, void, mem.hash_slice_u8, mem.eql_slice_u8),
    541     queue: std.LinkedList([]const u8),
    542     any_error: bool,
    543 
    544     // file_path must outlive Fmt
    545     fn addToQueue(self: *Fmt, file_path: []const u8) !void {
    546         const new_node = try self.seen.allocator.create(std.LinkedList([]const u8).Node{
    547             .prev = undefined,
    548             .next = undefined,
    549             .data = file_path,
    550         });
    551 
    552         if (try self.seen.put(file_path, {})) |_| return;
    553 
    554         self.queue.append(new_node);
    555     }
    556 
    557     fn addDirToQueue(self: *Fmt, file_path: []const u8) !void {
    558         var dir = try std.os.Dir.open(self.seen.allocator, file_path);
    559         defer dir.close();
    560         while (try dir.next()) |entry| {
    561             if (entry.kind == std.os.Dir.Entry.Kind.Directory or mem.endsWith(u8, entry.name, ".zig")) {
    562                 const full_path = try os.path.join(self.seen.allocator, file_path, entry.name);
    563                 try self.addToQueue(full_path);
    564             }
    565         }
    566     }
    567 };
    568 
    569 fn parseLibcPaths(allocator: *Allocator, libc: *LibCInstallation, libc_paths_file: []const u8) void {
    570     libc.parse(allocator, libc_paths_file, stderr) catch |err| {
    571         stderr.print(
    572             "Unable to parse libc path file '{}': {}.\n" ++
    573                 "Try running `zig libc` to see an example for the native target.\n",
    574             libc_paths_file,
    575             @errorName(err),
    576         ) catch os.exit(1);
    577         os.exit(1);
    578     };
    579 }
    580 
    581 fn cmdLibC(allocator: *Allocator, args: []const []const u8) !void {
    582     switch (args.len) {
    583         0 => {},
    584         1 => {
    585             var libc_installation: LibCInstallation = undefined;
    586             parseLibcPaths(allocator, &libc_installation, args[0]);
    587             return;
    588         },
    589         else => {
    590             try stderr.print("unexpected extra parameter: {}\n", args[1]);
    591             os.exit(1);
    592         },
    593     }
    594 
    595     var loop: event.Loop = undefined;
    596     try loop.initMultiThreaded(allocator);
    597     defer loop.deinit();
    598 
    599     var event_loop_local = try EventLoopLocal.init(&loop);
    600     defer event_loop_local.deinit();
    601 
    602     const handle = try async<loop.allocator> findLibCAsync(&event_loop_local);
    603     defer cancel handle;
    604 
    605     loop.run();
    606 }
    607 
    608 async fn findLibCAsync(event_loop_local: *EventLoopLocal) void {
    609     const libc = (await (async event_loop_local.getNativeLibC() catch unreachable)) catch |err| {
    610         stderr.print("unable to find libc: {}\n", @errorName(err)) catch os.exit(1);
    611         os.exit(1);
    612     };
    613     libc.render(stdout) catch os.exit(1);
    614 }
    615 
    616 fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
    617     var flags = try Args.parse(allocator, args_fmt_spec, args);
    618     defer flags.deinit();
    619 
    620     if (flags.present("help")) {
    621         try stdout.write(usage_fmt);
    622         os.exit(0);
    623     }
    624 
    625     const color = blk: {
    626         if (flags.single("color")) |color_flag| {
    627             if (mem.eql(u8, color_flag, "auto")) {
    628                 break :blk errmsg.Color.Auto;
    629             } else if (mem.eql(u8, color_flag, "on")) {
    630                 break :blk errmsg.Color.On;
    631             } else if (mem.eql(u8, color_flag, "off")) {
    632                 break :blk errmsg.Color.Off;
    633             } else unreachable;
    634         } else {
    635             break :blk errmsg.Color.Auto;
    636         }
    637     };
    638 
    639     if (flags.present("stdin")) {
    640         if (flags.positionals.len != 0) {
    641             try stderr.write("cannot use --stdin with positional arguments\n");
    642             os.exit(1);
    643         }
    644 
    645         var stdin_file = try io.getStdIn();
    646         var stdin = io.FileInStream.init(&stdin_file);
    647 
    648         const source_code = try stdin.stream.readAllAlloc(allocator, @maxValue(usize));
    649         defer allocator.free(source_code);
    650 
    651         var tree = std.zig.parse(allocator, source_code) catch |err| {
    652             try stderr.print("error parsing stdin: {}\n", err);
    653             os.exit(1);
    654         };
    655         defer tree.deinit();
    656 
    657         var error_it = tree.errors.iterator(0);
    658         while (error_it.next()) |parse_error| {
    659             const msg = try errmsg.createFromParseError(allocator, parse_error, &tree, "<stdin>");
    660             defer allocator.destroy(msg);
    661 
    662             try errmsg.printToFile(&stderr_file, msg, color);
    663         }
    664         if (tree.errors.len != 0) {
    665             os.exit(1);
    666         }
    667 
    668         _ = try std.zig.render(allocator, stdout, &tree);
    669         return;
    670     }
    671 
    672     if (flags.positionals.len == 0) {
    673         try stderr.write("expected at least one source file argument\n");
    674         os.exit(1);
    675     }
    676 
    677     var fmt = Fmt{
    678         .seen = std.HashMap([]const u8, void, mem.hash_slice_u8, mem.eql_slice_u8).init(allocator),
    679         .queue = std.LinkedList([]const u8).init(),
    680         .any_error = false,
    681     };
    682 
    683     for (flags.positionals.toSliceConst()) |file_path| {
    684         try fmt.addToQueue(file_path);
    685     }
    686 
    687     while (fmt.queue.popFirst()) |node| {
    688         const file_path = node.data;
    689 
    690         var file = try os.File.openRead(allocator, file_path);
    691         defer file.close();
    692 
    693         const source_code = io.readFileAlloc(allocator, file_path) catch |err| switch (err) {
    694             error.IsDir => {
    695                 try fmt.addDirToQueue(file_path);
    696                 continue;
    697             },
    698             else => {
    699                 try stderr.print("unable to open '{}': {}\n", file_path, err);
    700                 fmt.any_error = true;
    701                 continue;
    702             },
    703         };
    704         defer allocator.free(source_code);
    705 
    706         var tree = std.zig.parse(allocator, source_code) catch |err| {
    707             try stderr.print("error parsing file '{}': {}\n", file_path, err);
    708             fmt.any_error = true;
    709             continue;
    710         };
    711         defer tree.deinit();
    712 
    713         var error_it = tree.errors.iterator(0);
    714         while (error_it.next()) |parse_error| {
    715             const msg = try errmsg.createFromParseError(allocator, parse_error, &tree, file_path);
    716             defer allocator.destroy(msg);
    717 
    718             try errmsg.printToFile(&stderr_file, msg, color);
    719         }
    720         if (tree.errors.len != 0) {
    721             fmt.any_error = true;
    722             continue;
    723         }
    724 
    725         const baf = try io.BufferedAtomicFile.create(allocator, file_path);
    726         defer baf.destroy();
    727 
    728         const anything_changed = try std.zig.render(allocator, baf.stream(), &tree);
    729         if (anything_changed) {
    730             try stderr.print("{}\n", file_path);
    731             try baf.finish();
    732         }
    733     }
    734 
    735     if (fmt.any_error) {
    736         os.exit(1);
    737     }
    738 }
    739 
    740 // cmd:targets /////////////////////////////////////////////////////////////////////////////////////
    741 
    742 fn cmdTargets(allocator: *Allocator, args: []const []const u8) !void {
    743     try stdout.write("Architectures:\n");
    744     {
    745         comptime var i: usize = 0;
    746         inline while (i < @memberCount(builtin.Arch)) : (i += 1) {
    747             comptime const arch_tag = @memberName(builtin.Arch, i);
    748             // NOTE: Cannot use empty string, see #918.
    749             comptime const native_str = if (comptime mem.eql(u8, arch_tag, @tagName(builtin.arch))) " (native)\n" else "\n";
    750 
    751             try stdout.print("  {}{}", arch_tag, native_str);
    752         }
    753     }
    754     try stdout.write("\n");
    755 
    756     try stdout.write("Operating Systems:\n");
    757     {
    758         comptime var i: usize = 0;
    759         inline while (i < @memberCount(builtin.Os)) : (i += 1) {
    760             comptime const os_tag = @memberName(builtin.Os, i);
    761             // NOTE: Cannot use empty string, see #918.
    762             comptime const native_str = if (comptime mem.eql(u8, os_tag, @tagName(builtin.os))) " (native)\n" else "\n";
    763 
    764             try stdout.print("  {}{}", os_tag, native_str);
    765         }
    766     }
    767     try stdout.write("\n");
    768 
    769     try stdout.write("Environments:\n");
    770     {
    771         comptime var i: usize = 0;
    772         inline while (i < @memberCount(builtin.Environ)) : (i += 1) {
    773             comptime const environ_tag = @memberName(builtin.Environ, i);
    774             // NOTE: Cannot use empty string, see #918.
    775             comptime const native_str = if (comptime mem.eql(u8, environ_tag, @tagName(builtin.environ))) " (native)\n" else "\n";
    776 
    777             try stdout.print("  {}{}", environ_tag, native_str);
    778         }
    779     }
    780 }
    781 
    782 fn cmdVersion(allocator: *Allocator, args: []const []const u8) !void {
    783     try stdout.print("{}\n", std.cstr.toSliceConst(c.ZIG_VERSION_STRING));
    784 }
    785 
    786 const args_test_spec = []Flag{Flag.Bool("--help")};
    787 
    788 fn cmdHelp(allocator: *Allocator, args: []const []const u8) !void {
    789     try stdout.write(usage);
    790 }
    791 
    792 const info_zen =
    793     \\
    794     \\ * Communicate intent precisely.
    795     \\ * Edge cases matter.
    796     \\ * Favor reading code over writing code.
    797     \\ * Only one obvious way to do things.
    798     \\ * Runtime crashes are better than bugs.
    799     \\ * Compile errors are better than runtime crashes.
    800     \\ * Incremental improvements.
    801     \\ * Avoid local maximums.
    802     \\ * Reduce the amount one must remember.
    803     \\ * Minimize energy spent on coding style.
    804     \\ * Together we serve end users.
    805     \\
    806     \\
    807 ;
    808 
    809 fn cmdZen(allocator: *Allocator, args: []const []const u8) !void {
    810     try stdout.write(info_zen);
    811 }
    812 
    813 const usage_internal =
    814     \\usage: zig internal [subcommand]
    815     \\
    816     \\Sub-Commands:
    817     \\  build-info                   Print static compiler build-info
    818     \\
    819     \\
    820 ;
    821 
    822 fn cmdInternal(allocator: *Allocator, args: []const []const u8) !void {
    823     if (args.len == 0) {
    824         try stderr.write(usage_internal);
    825         os.exit(1);
    826     }
    827 
    828     const sub_commands = []Command{Command{
    829         .name = "build-info",
    830         .exec = cmdInternalBuildInfo,
    831     }};
    832 
    833     for (sub_commands) |sub_command| {
    834         if (mem.eql(u8, sub_command.name, args[0])) {
    835             try sub_command.exec(allocator, args[1..]);
    836             return;
    837         }
    838     }
    839 
    840     try stderr.print("unknown sub command: {}\n\n", args[0]);
    841     try stderr.write(usage_internal);
    842 }
    843 
    844 fn cmdInternalBuildInfo(allocator: *Allocator, args: []const []const u8) !void {
    845     try stdout.print(
    846         \\ZIG_CMAKE_BINARY_DIR {}
    847         \\ZIG_CXX_COMPILER     {}
    848         \\ZIG_LLVM_CONFIG_EXE  {}
    849         \\ZIG_LLD_INCLUDE_PATH {}
    850         \\ZIG_LLD_LIBRARIES    {}
    851         \\ZIG_STD_FILES        {}
    852         \\ZIG_C_HEADER_FILES   {}
    853         \\ZIG_DIA_GUIDS_LIB    {}
    854         \\
    855     ,
    856         std.cstr.toSliceConst(c.ZIG_CMAKE_BINARY_DIR),
    857         std.cstr.toSliceConst(c.ZIG_CXX_COMPILER),
    858         std.cstr.toSliceConst(c.ZIG_LLVM_CONFIG_EXE),
    859         std.cstr.toSliceConst(c.ZIG_LLD_INCLUDE_PATH),
    860         std.cstr.toSliceConst(c.ZIG_LLD_LIBRARIES),
    861         std.cstr.toSliceConst(c.ZIG_STD_FILES),
    862         std.cstr.toSliceConst(c.ZIG_C_HEADER_FILES),
    863         std.cstr.toSliceConst(c.ZIG_DIA_GUIDS_LIB),
    864     );
    865 }
    866 
    867 const CliPkg = struct {
    868     name: []const u8,
    869     path: []const u8,
    870     children: ArrayList(*CliPkg),
    871     parent: ?*CliPkg,
    872 
    873     pub fn init(allocator: *mem.Allocator, name: []const u8, path: []const u8, parent: ?*CliPkg) !*CliPkg {
    874         var pkg = try allocator.create(CliPkg{
    875             .name = name,
    876             .path = path,
    877             .children = ArrayList(*CliPkg).init(allocator),
    878             .parent = parent,
    879         });
    880         return pkg;
    881     }
    882 
    883     pub fn deinit(self: *CliPkg) void {
    884         for (self.children.toSliceConst()) |child| {
    885             child.deinit();
    886         }
    887         self.children.deinit();
    888     }
    889 };