zig

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

blob cfed6c3d (27454B) - Raw


      1 const std = @import("std");
      2 const mem = std.mem;
      3 const io = std.io;
      4 const os = std.os;
      5 const heap = std.heap;
      6 const warn = std.debug.warn;
      7 const assert = std.debug.assert;
      8 const target = @import("target.zig");
      9 const Target = target.Target;
     10 const Module = @import("module.zig").Module;
     11 const ErrColor = Module.ErrColor;
     12 const Emit = Module.Emit;
     13 const builtin = @import("builtin");
     14 const ArrayList = std.ArrayList;
     15 const c = @import("c.zig");
     16 
     17 const default_zig_cache_name = "zig-cache";
     18 
     19 const Cmd = enum {
     20     None,
     21     Build,
     22     Test,
     23     Version,
     24     Zen,
     25     TranslateC,
     26     Targets,
     27 };
     28 
     29 fn badArgs(comptime format: []const u8, args: ...) noreturn {
     30     var stderr = io.getStdErr() catch std.os.exit(1);
     31     var stderr_stream_adapter = io.FileOutStream.init(&stderr);
     32     const stderr_stream = &stderr_stream_adapter.stream;
     33     stderr_stream.print(format ++ "\n\n", args) catch std.os.exit(1);
     34     printUsage(&stderr_stream_adapter.stream) catch std.os.exit(1);
     35     std.os.exit(1);
     36 }
     37 
     38 pub fn main() !void {
     39     const allocator = std.heap.c_allocator;
     40 
     41     const args = try os.argsAlloc(allocator);
     42     defer os.argsFree(allocator, args);
     43 
     44     if (args.len >= 2 and mem.eql(u8, args[1], "fmt")) {
     45         return fmtMain(allocator, args[2..]);
     46     }
     47 
     48     var cmd = Cmd.None;
     49     var build_kind: Module.Kind = undefined;
     50     var build_mode: builtin.Mode = builtin.Mode.Debug;
     51     var color = ErrColor.Auto;
     52     var emit_file_type = Emit.Binary;
     53 
     54     var strip = false;
     55     var is_static = false;
     56     var verbose_tokenize = false;
     57     var verbose_ast_tree = false;
     58     var verbose_ast_fmt = false;
     59     var verbose_link = false;
     60     var verbose_ir = false;
     61     var verbose_llvm_ir = false;
     62     var verbose_cimport = false;
     63     var mwindows = false;
     64     var mconsole = false;
     65     var rdynamic = false;
     66     var each_lib_rpath = false;
     67     var timing_info = false;
     68 
     69     var in_file_arg: ?[]u8 = null;
     70     var out_file: ?[]u8 = null;
     71     var out_file_h: ?[]u8 = null;
     72     var out_name_arg: ?[]u8 = null;
     73     var libc_lib_dir_arg: ?[]u8 = null;
     74     var libc_static_lib_dir_arg: ?[]u8 = null;
     75     var libc_include_dir_arg: ?[]u8 = null;
     76     var msvc_lib_dir_arg: ?[]u8 = null;
     77     var kernel32_lib_dir_arg: ?[]u8 = null;
     78     var zig_install_prefix: ?[]u8 = null;
     79     var dynamic_linker_arg: ?[]u8 = null;
     80     var cache_dir_arg: ?[]const u8 = null;
     81     var target_arch: ?[]u8 = null;
     82     var target_os: ?[]u8 = null;
     83     var target_environ: ?[]u8 = null;
     84     var mmacosx_version_min: ?[]u8 = null;
     85     var mios_version_min: ?[]u8 = null;
     86     var linker_script_arg: ?[]u8 = null;
     87     var test_name_prefix_arg: ?[]u8 = null;
     88 
     89     var test_filters = ArrayList([]const u8).init(allocator);
     90     defer test_filters.deinit();
     91 
     92     var lib_dirs = ArrayList([]const u8).init(allocator);
     93     defer lib_dirs.deinit();
     94 
     95     var clang_argv = ArrayList([]const u8).init(allocator);
     96     defer clang_argv.deinit();
     97 
     98     var llvm_argv = ArrayList([]const u8).init(allocator);
     99     defer llvm_argv.deinit();
    100 
    101     var link_libs = ArrayList([]const u8).init(allocator);
    102     defer link_libs.deinit();
    103 
    104     var frameworks = ArrayList([]const u8).init(allocator);
    105     defer frameworks.deinit();
    106 
    107     var objects = ArrayList([]const u8).init(allocator);
    108     defer objects.deinit();
    109 
    110     var asm_files = ArrayList([]const u8).init(allocator);
    111     defer asm_files.deinit();
    112 
    113     var rpath_list = ArrayList([]const u8).init(allocator);
    114     defer rpath_list.deinit();
    115 
    116     var ver_major: u32 = 0;
    117     var ver_minor: u32 = 0;
    118     var ver_patch: u32 = 0;
    119 
    120     var arg_i: usize = 1;
    121     while (arg_i < args.len) : (arg_i += 1) {
    122         const arg = args[arg_i];
    123 
    124         if (arg.len != 0 and arg[0] == '-') {
    125             if (mem.eql(u8, arg, "--release-fast")) {
    126                 build_mode = builtin.Mode.ReleaseFast;
    127             } else if (mem.eql(u8, arg, "--release-safe")) {
    128                 build_mode = builtin.Mode.ReleaseSafe;
    129             } else if (mem.eql(u8, arg, "--strip")) {
    130                 strip = true;
    131             } else if (mem.eql(u8, arg, "--static")) {
    132                 is_static = true;
    133             } else if (mem.eql(u8, arg, "--verbose-tokenize")) {
    134                 verbose_tokenize = true;
    135             } else if (mem.eql(u8, arg, "--verbose-ast-tree")) {
    136                 verbose_ast_tree = true;
    137             } else if (mem.eql(u8, arg, "--verbose-ast-fmt")) {
    138                 verbose_ast_fmt = true;
    139             } else if (mem.eql(u8, arg, "--verbose-link")) {
    140                 verbose_link = true;
    141             } else if (mem.eql(u8, arg, "--verbose-ir")) {
    142                 verbose_ir = true;
    143             } else if (mem.eql(u8, arg, "--verbose-llvm-ir")) {
    144                 verbose_llvm_ir = true;
    145             } else if (mem.eql(u8, arg, "--verbose-cimport")) {
    146                 verbose_cimport = true;
    147             } else if (mem.eql(u8, arg, "-mwindows")) {
    148                 mwindows = true;
    149             } else if (mem.eql(u8, arg, "-mconsole")) {
    150                 mconsole = true;
    151             } else if (mem.eql(u8, arg, "-rdynamic")) {
    152                 rdynamic = true;
    153             } else if (mem.eql(u8, arg, "--each-lib-rpath")) {
    154                 each_lib_rpath = true;
    155             } else if (mem.eql(u8, arg, "--enable-timing-info")) {
    156                 timing_info = true;
    157             } else if (mem.eql(u8, arg, "--test-cmd-bin")) {
    158                 @panic("TODO --test-cmd-bin");
    159             } else if (arg[1] == 'L' and arg.len > 2) {
    160                 // alias for --library-path
    161                 try lib_dirs.append(arg[1..]);
    162             } else if (mem.eql(u8, arg, "--pkg-begin")) {
    163                 @panic("TODO --pkg-begin");
    164             } else if (mem.eql(u8, arg, "--pkg-end")) {
    165                 @panic("TODO --pkg-end");
    166             } else if (arg_i + 1 >= args.len) {
    167                 badArgs("expected another argument after {}", arg);
    168             } else {
    169                 arg_i += 1;
    170                 if (mem.eql(u8, arg, "--output")) {
    171                     out_file = args[arg_i];
    172                 } else if (mem.eql(u8, arg, "--output-h")) {
    173                     out_file_h = args[arg_i];
    174                 } else if (mem.eql(u8, arg, "--color")) {
    175                     if (mem.eql(u8, args[arg_i], "auto")) {
    176                         color = ErrColor.Auto;
    177                     } else if (mem.eql(u8, args[arg_i], "on")) {
    178                         color = ErrColor.On;
    179                     } else if (mem.eql(u8, args[arg_i], "off")) {
    180                         color = ErrColor.Off;
    181                     } else {
    182                         badArgs("--color options are 'auto', 'on', or 'off'");
    183                     }
    184                 } else if (mem.eql(u8, arg, "--emit")) {
    185                     if (mem.eql(u8, args[arg_i], "asm")) {
    186                         emit_file_type = Emit.Assembly;
    187                     } else if (mem.eql(u8, args[arg_i], "bin")) {
    188                         emit_file_type = Emit.Binary;
    189                     } else if (mem.eql(u8, args[arg_i], "llvm-ir")) {
    190                         emit_file_type = Emit.LlvmIr;
    191                     } else {
    192                         badArgs("--emit options are 'asm', 'bin', or 'llvm-ir'");
    193                     }
    194                 } else if (mem.eql(u8, arg, "--name")) {
    195                     out_name_arg = args[arg_i];
    196                 } else if (mem.eql(u8, arg, "--libc-lib-dir")) {
    197                     libc_lib_dir_arg = args[arg_i];
    198                 } else if (mem.eql(u8, arg, "--libc-static-lib-dir")) {
    199                     libc_static_lib_dir_arg = args[arg_i];
    200                 } else if (mem.eql(u8, arg, "--libc-include-dir")) {
    201                     libc_include_dir_arg = args[arg_i];
    202                 } else if (mem.eql(u8, arg, "--msvc-lib-dir")) {
    203                     msvc_lib_dir_arg = args[arg_i];
    204                 } else if (mem.eql(u8, arg, "--kernel32-lib-dir")) {
    205                     kernel32_lib_dir_arg = args[arg_i];
    206                 } else if (mem.eql(u8, arg, "--zig-install-prefix")) {
    207                     zig_install_prefix = args[arg_i];
    208                 } else if (mem.eql(u8, arg, "--dynamic-linker")) {
    209                     dynamic_linker_arg = args[arg_i];
    210                 } else if (mem.eql(u8, arg, "-isystem")) {
    211                     try clang_argv.append("-isystem");
    212                     try clang_argv.append(args[arg_i]);
    213                 } else if (mem.eql(u8, arg, "-dirafter")) {
    214                     try clang_argv.append("-dirafter");
    215                     try clang_argv.append(args[arg_i]);
    216                 } else if (mem.eql(u8, arg, "-mllvm")) {
    217                     try clang_argv.append("-mllvm");
    218                     try clang_argv.append(args[arg_i]);
    219 
    220                     try llvm_argv.append(args[arg_i]);
    221                 } else if (mem.eql(u8, arg, "--library-path") or mem.eql(u8, arg, "-L")) {
    222                     try lib_dirs.append(args[arg_i]);
    223                 } else if (mem.eql(u8, arg, "--library")) {
    224                     try link_libs.append(args[arg_i]);
    225                 } else if (mem.eql(u8, arg, "--object")) {
    226                     try objects.append(args[arg_i]);
    227                 } else if (mem.eql(u8, arg, "--assembly")) {
    228                     try asm_files.append(args[arg_i]);
    229                 } else if (mem.eql(u8, arg, "--cache-dir")) {
    230                     cache_dir_arg = args[arg_i];
    231                 } else if (mem.eql(u8, arg, "--target-arch")) {
    232                     target_arch = args[arg_i];
    233                 } else if (mem.eql(u8, arg, "--target-os")) {
    234                     target_os = args[arg_i];
    235                 } else if (mem.eql(u8, arg, "--target-environ")) {
    236                     target_environ = args[arg_i];
    237                 } else if (mem.eql(u8, arg, "-mmacosx-version-min")) {
    238                     mmacosx_version_min = args[arg_i];
    239                 } else if (mem.eql(u8, arg, "-mios-version-min")) {
    240                     mios_version_min = args[arg_i];
    241                 } else if (mem.eql(u8, arg, "-framework")) {
    242                     try frameworks.append(args[arg_i]);
    243                 } else if (mem.eql(u8, arg, "--linker-script")) {
    244                     linker_script_arg = args[arg_i];
    245                 } else if (mem.eql(u8, arg, "-rpath")) {
    246                     try rpath_list.append(args[arg_i]);
    247                 } else if (mem.eql(u8, arg, "--test-filter")) {
    248                     try test_filters.append(args[arg_i]);
    249                 } else if (mem.eql(u8, arg, "--test-name-prefix")) {
    250                     test_name_prefix_arg = args[arg_i];
    251                 } else if (mem.eql(u8, arg, "--ver-major")) {
    252                     ver_major = try std.fmt.parseUnsigned(u32, args[arg_i], 10);
    253                 } else if (mem.eql(u8, arg, "--ver-minor")) {
    254                     ver_minor = try std.fmt.parseUnsigned(u32, args[arg_i], 10);
    255                 } else if (mem.eql(u8, arg, "--ver-patch")) {
    256                     ver_patch = try std.fmt.parseUnsigned(u32, args[arg_i], 10);
    257                 } else if (mem.eql(u8, arg, "--test-cmd")) {
    258                     @panic("TODO --test-cmd");
    259                 } else {
    260                     badArgs("invalid argument: {}", arg);
    261                 }
    262             }
    263         } else if (cmd == Cmd.None) {
    264             if (mem.eql(u8, arg, "build-obj")) {
    265                 cmd = Cmd.Build;
    266                 build_kind = Module.Kind.Obj;
    267             } else if (mem.eql(u8, arg, "build-exe")) {
    268                 cmd = Cmd.Build;
    269                 build_kind = Module.Kind.Exe;
    270             } else if (mem.eql(u8, arg, "build-lib")) {
    271                 cmd = Cmd.Build;
    272                 build_kind = Module.Kind.Lib;
    273             } else if (mem.eql(u8, arg, "version")) {
    274                 cmd = Cmd.Version;
    275             } else if (mem.eql(u8, arg, "zen")) {
    276                 cmd = Cmd.Zen;
    277             } else if (mem.eql(u8, arg, "translate-c")) {
    278                 cmd = Cmd.TranslateC;
    279             } else if (mem.eql(u8, arg, "test")) {
    280                 cmd = Cmd.Test;
    281                 build_kind = Module.Kind.Exe;
    282             } else {
    283                 badArgs("unrecognized command: {}", arg);
    284             }
    285         } else switch (cmd) {
    286             Cmd.Build, Cmd.TranslateC, Cmd.Test => {
    287                 if (in_file_arg == null) {
    288                     in_file_arg = arg;
    289                 } else {
    290                     badArgs("unexpected extra parameter: {}", arg);
    291                 }
    292             },
    293             Cmd.Version, Cmd.Zen, Cmd.Targets => {
    294                 badArgs("unexpected extra parameter: {}", arg);
    295             },
    296             Cmd.None => unreachable,
    297         }
    298     }
    299 
    300     target.initializeAll();
    301 
    302     // TODO
    303 //    ZigTarget alloc_target;
    304 //    ZigTarget *target;
    305 //    if (!target_arch && !target_os && !target_environ) {
    306 //        target = nullptr;
    307 //    } else {
    308 //        target = &alloc_target;
    309 //        get_unknown_target(target);
    310 //        if (target_arch) {
    311 //            if (parse_target_arch(target_arch, &target->arch)) {
    312 //                fprintf(stderr, "invalid --target-arch argument\n");
    313 //                return usage(arg0);
    314 //            }
    315 //        }
    316 //        if (target_os) {
    317 //            if (parse_target_os(target_os, &target->os)) {
    318 //                fprintf(stderr, "invalid --target-os argument\n");
    319 //                return usage(arg0);
    320 //            }
    321 //        }
    322 //        if (target_environ) {
    323 //            if (parse_target_environ(target_environ, &target->env_type)) {
    324 //                fprintf(stderr, "invalid --target-environ argument\n");
    325 //                return usage(arg0);
    326 //            }
    327 //        }
    328 //    }
    329 
    330     switch (cmd) {
    331         Cmd.None => badArgs("expected command"),
    332         Cmd.Zen => return printZen(),
    333         Cmd.Build, Cmd.Test, Cmd.TranslateC => {
    334             if (cmd == Cmd.Build and in_file_arg == null and objects.len == 0 and asm_files.len == 0) {
    335                 badArgs("expected source file argument or at least one --object or --assembly argument");
    336             } else if ((cmd == Cmd.TranslateC or cmd == Cmd.Test) and in_file_arg == null) {
    337                 badArgs("expected source file argument");
    338             } else if (cmd == Cmd.Build and build_kind == Module.Kind.Obj and objects.len != 0) {
    339                 badArgs("When building an object file, --object arguments are invalid");
    340             }
    341 
    342             const root_name = switch (cmd) {
    343                 Cmd.Build, Cmd.TranslateC => x: {
    344                     if (out_name_arg) |out_name| {
    345                         break :x out_name;
    346                     } else if (in_file_arg) |in_file_path| {
    347                         const basename = os.path.basename(in_file_path);
    348                         var it = mem.split(basename, ".");
    349                         break :x it.next() ?? badArgs("file name cannot be empty");
    350                     } else {
    351                         badArgs("--name [name] not provided and unable to infer");
    352                     }
    353                 },
    354                 Cmd.Test => "test",
    355                 else => unreachable,
    356             };
    357 
    358             const zig_root_source_file = if (cmd == Cmd.TranslateC) null else in_file_arg;
    359 
    360             const chosen_cache_dir = cache_dir_arg ?? default_zig_cache_name;
    361             const full_cache_dir = try os.path.resolve(allocator, ".", chosen_cache_dir);
    362             defer allocator.free(full_cache_dir);
    363 
    364             const zig_lib_dir = try resolveZigLibDir(allocator, zig_install_prefix);
    365             errdefer allocator.free(zig_lib_dir);
    366 
    367             const module = try Module.create(allocator, root_name, zig_root_source_file,
    368                 Target.Native, build_kind, build_mode, zig_lib_dir, full_cache_dir);
    369             defer module.destroy();
    370 
    371             module.version_major = ver_major;
    372             module.version_minor = ver_minor;
    373             module.version_patch = ver_patch;
    374 
    375             module.is_test = cmd == Cmd.Test;
    376             if (linker_script_arg) |linker_script| {
    377                 module.linker_script = linker_script;
    378             }
    379             module.each_lib_rpath = each_lib_rpath;
    380             module.clang_argv = clang_argv.toSliceConst();
    381             module.llvm_argv = llvm_argv.toSliceConst();
    382             module.strip = strip;
    383             module.is_static = is_static;
    384 
    385             if (libc_lib_dir_arg) |libc_lib_dir| {
    386                 module.libc_lib_dir = libc_lib_dir;
    387             }
    388             if (libc_static_lib_dir_arg) |libc_static_lib_dir| {
    389                 module.libc_static_lib_dir = libc_static_lib_dir;
    390             }
    391             if (libc_include_dir_arg) |libc_include_dir| {
    392                 module.libc_include_dir = libc_include_dir;
    393             }
    394             if (msvc_lib_dir_arg) |msvc_lib_dir| {
    395                 module.msvc_lib_dir = msvc_lib_dir;
    396             }
    397             if (kernel32_lib_dir_arg) |kernel32_lib_dir| {
    398                 module.kernel32_lib_dir = kernel32_lib_dir;
    399             }
    400             if (dynamic_linker_arg) |dynamic_linker| {
    401                 module.dynamic_linker = dynamic_linker;
    402             }
    403             module.verbose_tokenize = verbose_tokenize;
    404             module.verbose_ast_tree = verbose_ast_tree;
    405             module.verbose_ast_fmt = verbose_ast_fmt;
    406             module.verbose_link = verbose_link;
    407             module.verbose_ir = verbose_ir;
    408             module.verbose_llvm_ir = verbose_llvm_ir;
    409             module.verbose_cimport = verbose_cimport;
    410 
    411             module.err_color = color;
    412 
    413             module.lib_dirs = lib_dirs.toSliceConst();
    414             module.darwin_frameworks = frameworks.toSliceConst();
    415             module.rpath_list = rpath_list.toSliceConst();
    416 
    417             for (link_libs.toSliceConst()) |name| {
    418                 _ = try module.addLinkLib(name, true);
    419             }
    420 
    421             module.windows_subsystem_windows = mwindows;
    422             module.windows_subsystem_console = mconsole;
    423             module.linker_rdynamic = rdynamic;
    424 
    425             if (mmacosx_version_min != null and mios_version_min != null) {
    426                 badArgs("-mmacosx-version-min and -mios-version-min options not allowed together");
    427             }
    428 
    429             if (mmacosx_version_min) |ver| {
    430                 module.darwin_version_min = Module.DarwinVersionMin { .MacOS = ver };
    431             } else if (mios_version_min) |ver| {
    432                 module.darwin_version_min = Module.DarwinVersionMin { .Ios = ver };
    433             }
    434 
    435             module.test_filters = test_filters.toSliceConst();
    436             module.test_name_prefix = test_name_prefix_arg;
    437             module.out_h_path = out_file_h;
    438 
    439             // TODO
    440             //add_package(g, cur_pkg, g->root_package);
    441 
    442             switch (cmd) {
    443                 Cmd.Build => {
    444                     module.emit_file_type = emit_file_type;
    445 
    446                     module.link_objects = objects.toSliceConst();
    447                     module.assembly_files = asm_files.toSliceConst();
    448 
    449                     try module.build();
    450                     try module.link(out_file);
    451                 },
    452                 Cmd.TranslateC => @panic("TODO translate-c"),
    453                 Cmd.Test => @panic("TODO test cmd"),
    454                 else => unreachable,
    455             }
    456         },
    457         Cmd.Version => {
    458             var stdout_file = try io.getStdErr();
    459             try stdout_file.write(std.cstr.toSliceConst(c.ZIG_VERSION_STRING));
    460             try stdout_file.write("\n");
    461         },
    462         Cmd.Targets => @panic("TODO zig targets"),
    463     }
    464 }
    465 
    466 fn printUsage(stream: var) !void {
    467     try stream.write(
    468         \\Usage: zig [command] [options]
    469         \\
    470         \\Commands:
    471         \\  build                        build project from build.zig
    472         \\  build-exe [source]           create executable from source or object files
    473         \\  build-lib [source]           create library from source or object files
    474         \\  build-obj [source]           create object from source or assembly
    475         \\  fmt [file]                   parse file and render in canonical zig format
    476         \\  translate-c [source]         convert c code to zig code
    477         \\  targets                      list available compilation targets
    478         \\  test [source]                create and run a test build
    479         \\  version                      print version number and exit
    480         \\  zen                          print zen of zig and exit
    481         \\Compile Options:
    482         \\  --assembly [source]          add assembly file to build
    483         \\  --cache-dir [path]           override the cache directory
    484         \\  --color [auto|off|on]        enable or disable colored error messages
    485         \\  --emit [filetype]            emit a specific file format as compilation output
    486         \\  --enable-timing-info         print timing diagnostics
    487         \\  --libc-include-dir [path]    directory where libc stdlib.h resides
    488         \\  --name [name]                override output name
    489         \\  --output [file]              override destination path
    490         \\  --output-h [file]            override generated header file path
    491         \\  --pkg-begin [name] [path]    make package available to import and push current pkg
    492         \\  --pkg-end                    pop current pkg
    493         \\  --release-fast               build with optimizations on and safety off
    494         \\  --release-safe               build with optimizations on and safety on
    495         \\  --static                     output will be statically linked
    496         \\  --strip                      exclude debug symbols
    497         \\  --target-arch [name]         specify target architecture
    498         \\  --target-environ [name]      specify target environment
    499         \\  --target-os [name]           specify target operating system
    500         \\  --verbose-tokenize           enable compiler debug info: tokenization
    501         \\  --verbose-ast-tree           enable compiler debug info: parsing into an AST (treeview)
    502         \\  --verbose-ast-fmt            enable compiler debug info: parsing into an AST (render source)
    503         \\  --verbose-cimport            enable compiler debug info: C imports
    504         \\  --verbose-ir                 enable compiler debug info: Zig IR
    505         \\  --verbose-llvm-ir            enable compiler debug info: LLVM IR
    506         \\  --verbose-link               enable compiler debug info: linking
    507         \\  --zig-install-prefix [path]  override directory where zig thinks it is installed
    508         \\  -dirafter [dir]              same as -isystem but do it last
    509         \\  -isystem [dir]               add additional search path for other .h files
    510         \\  -mllvm [arg]                 additional arguments to forward to LLVM's option processing
    511         \\Link Options:
    512         \\  --ar-path [path]             set the path to ar
    513         \\  --dynamic-linker [path]      set the path to ld.so
    514         \\  --each-lib-rpath             add rpath for each used dynamic library
    515         \\  --libc-lib-dir [path]        directory where libc crt1.o resides
    516         \\  --libc-static-lib-dir [path] directory where libc crtbegin.o resides
    517         \\  --msvc-lib-dir [path]        (windows) directory where vcruntime.lib resides
    518         \\  --kernel32-lib-dir [path]    (windows) directory where kernel32.lib resides
    519         \\  --library [lib]              link against lib
    520         \\  --library-path [dir]         add a directory to the library search path
    521         \\  --linker-script [path]       use a custom linker script
    522         \\  --object [obj]               add object file to build
    523         \\  -L[dir]                      alias for --library-path
    524         \\  -rdynamic                    add all symbols to the dynamic symbol table
    525         \\  -rpath [path]                add directory to the runtime library search path
    526         \\  -mconsole                    (windows) --subsystem console to the linker
    527         \\  -mwindows                    (windows) --subsystem windows to the linker
    528         \\  -framework [name]            (darwin) link against framework
    529         \\  -mios-version-min [ver]      (darwin) set iOS deployment target
    530         \\  -mmacosx-version-min [ver]   (darwin) set Mac OS X deployment target
    531         \\  --ver-major [ver]            dynamic library semver major version
    532         \\  --ver-minor [ver]            dynamic library semver minor version
    533         \\  --ver-patch [ver]            dynamic library semver patch version
    534         \\Test Options:
    535         \\  --test-filter [text]         skip tests that do not match filter
    536         \\  --test-name-prefix [text]    add prefix to all tests
    537         \\  --test-cmd [arg]             specify test execution command one arg at a time
    538         \\  --test-cmd-bin               appends test binary path to test cmd args
    539         \\
    540     );
    541 }
    542 
    543 fn printZen() !void {
    544     var stdout_file = try io.getStdErr();
    545     try stdout_file.write(
    546         \\
    547         \\ * Communicate intent precisely.
    548         \\ * Edge cases matter.
    549         \\ * Favor reading code over writing code.
    550         \\ * Only one obvious way to do things.
    551         \\ * Runtime crashes are better than bugs.
    552         \\ * Compile errors are better than runtime crashes.
    553         \\ * Incremental improvements.
    554         \\ * Avoid local maximums.
    555         \\ * Reduce the amount one must remember.
    556         \\ * Minimize energy spent on coding style.
    557         \\ * Together we serve end users.
    558         \\
    559         \\
    560     );
    561 }
    562 
    563 fn fmtMain(allocator: &mem.Allocator, file_paths: []const []const u8) !void {
    564     for (file_paths) |file_path| {
    565         var file = try io.File.openRead(allocator, file_path);
    566         defer file.close();
    567 
    568         warn("opened {} (todo tokenize and parse and render)\n", file_path);
    569     }
    570 }
    571 
    572 /// Caller must free result
    573 fn resolveZigLibDir(allocator: &mem.Allocator, zig_install_prefix_arg: ?[]const u8) ![]u8 {
    574     if (zig_install_prefix_arg) |zig_install_prefix| {
    575         return testZigInstallPrefix(allocator, zig_install_prefix) catch |err| {
    576             warn("No Zig installation found at prefix {}: {}\n", zig_install_prefix_arg, @errorName(err));
    577             return error.ZigInstallationNotFound;
    578         };
    579     } else {
    580         return findZigLibDir(allocator) catch |err| {
    581             warn("Unable to find zig lib directory: {}.\nReinstall Zig or use --zig-install-prefix.\n",
    582                 @errorName(err));
    583             return error.ZigLibDirNotFound;
    584         };
    585     }
    586 }
    587 
    588 /// Caller must free result
    589 fn testZigInstallPrefix(allocator: &mem.Allocator, test_path: []const u8) ![]u8 {
    590     const test_zig_dir = try os.path.join(allocator, test_path, "lib", "zig");
    591     errdefer allocator.free(test_zig_dir);
    592 
    593     const test_index_file = try os.path.join(allocator, test_zig_dir, "std", "index.zig");
    594     defer allocator.free(test_index_file);
    595 
    596     var file = try io.File.openRead(allocator, test_index_file);
    597     file.close();
    598 
    599     return test_zig_dir;
    600 }
    601 
    602 /// Caller must free result
    603 fn findZigLibDir(allocator: &mem.Allocator) ![]u8 {
    604     const self_exe_path = try os.selfExeDirPath(allocator);
    605     defer allocator.free(self_exe_path);
    606 
    607     var cur_path: []const u8 = self_exe_path;
    608     while (true) {
    609         const test_dir = os.path.dirname(cur_path);
    610 
    611         if (mem.eql(u8, test_dir, cur_path)) {
    612             break;
    613         }
    614 
    615         return testZigInstallPrefix(allocator, test_dir) catch |err| {
    616             cur_path = test_dir;
    617             continue;
    618         };
    619     }
    620 
    621     // TODO look in hard coded installation path from configuration
    622     //if (ZIG_INSTALL_PREFIX != nullptr) {
    623     //    if (test_zig_install_prefix(buf_create_from_str(ZIG_INSTALL_PREFIX), out_path)) {
    624     //        return 0;
    625     //    }
    626     //}
    627 
    628     return error.FileNotFound;
    629 }