zig/src-self-hosted/main.zig

806 lines
33 KiB
Zig

const std = @import("std");
const mem = std.mem;
const io = std.io;
const os = std.os;
const heap = std.heap;
const warn = std.debug.warn;
const assert = std.debug.assert;
const target = @import("target.zig");
const Target = target.Target;
const Module = @import("module.zig").Module;
const ErrColor = Module.ErrColor;
const Emit = Module.Emit;
const builtin = @import("builtin");
const ArrayList = std.ArrayList;
const c = @import("c.zig");
const default_zig_cache_name = "zig-cache";
const Cmd = enum {
None,
Build,
Test,
Version,
Zen,
TranslateC,
Targets,
};
fn badArgs(comptime format: []const u8, args: ...) noreturn {
var stderr = io.getStdErr() catch std.os.exit(1);
var stderr_stream_adapter = io.FileOutStream.init(&stderr);
const stderr_stream = &stderr_stream_adapter.stream;
stderr_stream.print(format ++ "\n\n", args) catch std.os.exit(1);
printUsage(&stderr_stream_adapter.stream) catch std.os.exit(1);
std.os.exit(1);
}
pub fn main() !void {
const allocator = std.heap.c_allocator;
const args = try os.argsAlloc(allocator);
defer os.argsFree(allocator, args);
if (args.len >= 2 and mem.eql(u8, args[1], "build")) {
return buildMain(allocator, args[2..]);
}
if (args.len >= 2 and mem.eql(u8, args[1], "fmt")) {
return fmtMain(allocator, args[2..]);
}
var cmd = Cmd.None;
var build_kind: Module.Kind = undefined;
var build_mode: builtin.Mode = builtin.Mode.Debug;
var color = ErrColor.Auto;
var emit_file_type = Emit.Binary;
var strip = false;
var is_static = false;
var verbose_tokenize = false;
var verbose_ast_tree = false;
var verbose_ast_fmt = false;
var verbose_link = false;
var verbose_ir = false;
var verbose_llvm_ir = false;
var verbose_cimport = false;
var mwindows = false;
var mconsole = false;
var rdynamic = false;
var each_lib_rpath = false;
var timing_info = false;
var in_file_arg: ?[]u8 = null;
var out_file: ?[]u8 = null;
var out_file_h: ?[]u8 = null;
var out_name_arg: ?[]u8 = null;
var libc_lib_dir_arg: ?[]u8 = null;
var libc_static_lib_dir_arg: ?[]u8 = null;
var libc_include_dir_arg: ?[]u8 = null;
var msvc_lib_dir_arg: ?[]u8 = null;
var kernel32_lib_dir_arg: ?[]u8 = null;
var zig_install_prefix: ?[]u8 = null;
var dynamic_linker_arg: ?[]u8 = null;
var cache_dir_arg: ?[]const u8 = null;
var target_arch: ?[]u8 = null;
var target_os: ?[]u8 = null;
var target_environ: ?[]u8 = null;
var mmacosx_version_min: ?[]u8 = null;
var mios_version_min: ?[]u8 = null;
var linker_script_arg: ?[]u8 = null;
var test_name_prefix_arg: ?[]u8 = null;
var test_filters = ArrayList([]const u8).init(allocator);
defer test_filters.deinit();
var lib_dirs = ArrayList([]const u8).init(allocator);
defer lib_dirs.deinit();
var clang_argv = ArrayList([]const u8).init(allocator);
defer clang_argv.deinit();
var llvm_argv = ArrayList([]const u8).init(allocator);
defer llvm_argv.deinit();
var link_libs = ArrayList([]const u8).init(allocator);
defer link_libs.deinit();
var frameworks = ArrayList([]const u8).init(allocator);
defer frameworks.deinit();
var objects = ArrayList([]const u8).init(allocator);
defer objects.deinit();
var asm_files = ArrayList([]const u8).init(allocator);
defer asm_files.deinit();
var rpath_list = ArrayList([]const u8).init(allocator);
defer rpath_list.deinit();
var ver_major: u32 = 0;
var ver_minor: u32 = 0;
var ver_patch: u32 = 0;
var arg_i: usize = 1;
while (arg_i < args.len) : (arg_i += 1) {
const arg = args[arg_i];
if (arg.len != 0 and arg[0] == '-') {
if (mem.eql(u8, arg, "--release-fast")) {
build_mode = builtin.Mode.ReleaseFast;
} else if (mem.eql(u8, arg, "--release-safe")) {
build_mode = builtin.Mode.ReleaseSafe;
} else if (mem.eql(u8, arg, "--strip")) {
strip = true;
} else if (mem.eql(u8, arg, "--static")) {
is_static = true;
} else if (mem.eql(u8, arg, "--verbose-tokenize")) {
verbose_tokenize = true;
} else if (mem.eql(u8, arg, "--verbose-ast-tree")) {
verbose_ast_tree = true;
} else if (mem.eql(u8, arg, "--verbose-ast-fmt")) {
verbose_ast_fmt = true;
} else if (mem.eql(u8, arg, "--verbose-link")) {
verbose_link = true;
} else if (mem.eql(u8, arg, "--verbose-ir")) {
verbose_ir = true;
} else if (mem.eql(u8, arg, "--verbose-llvm-ir")) {
verbose_llvm_ir = true;
} else if (mem.eql(u8, arg, "--verbose-cimport")) {
verbose_cimport = true;
} else if (mem.eql(u8, arg, "-mwindows")) {
mwindows = true;
} else if (mem.eql(u8, arg, "-mconsole")) {
mconsole = true;
} else if (mem.eql(u8, arg, "-rdynamic")) {
rdynamic = true;
} else if (mem.eql(u8, arg, "--each-lib-rpath")) {
each_lib_rpath = true;
} else if (mem.eql(u8, arg, "--enable-timing-info")) {
timing_info = true;
} else if (mem.eql(u8, arg, "--test-cmd-bin")) {
@panic("TODO --test-cmd-bin");
} else if (arg[1] == 'L' and arg.len > 2) {
// alias for --library-path
try lib_dirs.append(arg[1..]);
} else if (mem.eql(u8, arg, "--pkg-begin")) {
@panic("TODO --pkg-begin");
} else if (mem.eql(u8, arg, "--pkg-end")) {
@panic("TODO --pkg-end");
} else if (arg_i + 1 >= args.len) {
badArgs("expected another argument after {}", arg);
} else {
arg_i += 1;
if (mem.eql(u8, arg, "--output")) {
out_file = args[arg_i];
} else if (mem.eql(u8, arg, "--output-h")) {
out_file_h = args[arg_i];
} else if (mem.eql(u8, arg, "--color")) {
if (mem.eql(u8, args[arg_i], "auto")) {
color = ErrColor.Auto;
} else if (mem.eql(u8, args[arg_i], "on")) {
color = ErrColor.On;
} else if (mem.eql(u8, args[arg_i], "off")) {
color = ErrColor.Off;
} else {
badArgs("--color options are 'auto', 'on', or 'off'");
}
} else if (mem.eql(u8, arg, "--emit")) {
if (mem.eql(u8, args[arg_i], "asm")) {
emit_file_type = Emit.Assembly;
} else if (mem.eql(u8, args[arg_i], "bin")) {
emit_file_type = Emit.Binary;
} else if (mem.eql(u8, args[arg_i], "llvm-ir")) {
emit_file_type = Emit.LlvmIr;
} else {
badArgs("--emit options are 'asm', 'bin', or 'llvm-ir'");
}
} else if (mem.eql(u8, arg, "--name")) {
out_name_arg = args[arg_i];
} else if (mem.eql(u8, arg, "--libc-lib-dir")) {
libc_lib_dir_arg = args[arg_i];
} else if (mem.eql(u8, arg, "--libc-static-lib-dir")) {
libc_static_lib_dir_arg = args[arg_i];
} else if (mem.eql(u8, arg, "--libc-include-dir")) {
libc_include_dir_arg = args[arg_i];
} else if (mem.eql(u8, arg, "--msvc-lib-dir")) {
msvc_lib_dir_arg = args[arg_i];
} else if (mem.eql(u8, arg, "--kernel32-lib-dir")) {
kernel32_lib_dir_arg = args[arg_i];
} else if (mem.eql(u8, arg, "--zig-install-prefix")) {
zig_install_prefix = args[arg_i];
} else if (mem.eql(u8, arg, "--dynamic-linker")) {
dynamic_linker_arg = args[arg_i];
} else if (mem.eql(u8, arg, "-isystem")) {
try clang_argv.append("-isystem");
try clang_argv.append(args[arg_i]);
} else if (mem.eql(u8, arg, "-dirafter")) {
try clang_argv.append("-dirafter");
try clang_argv.append(args[arg_i]);
} else if (mem.eql(u8, arg, "-mllvm")) {
try clang_argv.append("-mllvm");
try clang_argv.append(args[arg_i]);
try llvm_argv.append(args[arg_i]);
} else if (mem.eql(u8, arg, "--library-path") or mem.eql(u8, arg, "-L")) {
try lib_dirs.append(args[arg_i]);
} else if (mem.eql(u8, arg, "--library")) {
try link_libs.append(args[arg_i]);
} else if (mem.eql(u8, arg, "--object")) {
try objects.append(args[arg_i]);
} else if (mem.eql(u8, arg, "--assembly")) {
try asm_files.append(args[arg_i]);
} else if (mem.eql(u8, arg, "--cache-dir")) {
cache_dir_arg = args[arg_i];
} else if (mem.eql(u8, arg, "--target-arch")) {
target_arch = args[arg_i];
} else if (mem.eql(u8, arg, "--target-os")) {
target_os = args[arg_i];
} else if (mem.eql(u8, arg, "--target-environ")) {
target_environ = args[arg_i];
} else if (mem.eql(u8, arg, "-mmacosx-version-min")) {
mmacosx_version_min = args[arg_i];
} else if (mem.eql(u8, arg, "-mios-version-min")) {
mios_version_min = args[arg_i];
} else if (mem.eql(u8, arg, "-framework")) {
try frameworks.append(args[arg_i]);
} else if (mem.eql(u8, arg, "--linker-script")) {
linker_script_arg = args[arg_i];
} else if (mem.eql(u8, arg, "-rpath")) {
try rpath_list.append(args[arg_i]);
} else if (mem.eql(u8, arg, "--test-filter")) {
try test_filters.append(args[arg_i]);
} else if (mem.eql(u8, arg, "--test-name-prefix")) {
test_name_prefix_arg = args[arg_i];
} else if (mem.eql(u8, arg, "--ver-major")) {
ver_major = try std.fmt.parseUnsigned(u32, args[arg_i], 10);
} else if (mem.eql(u8, arg, "--ver-minor")) {
ver_minor = try std.fmt.parseUnsigned(u32, args[arg_i], 10);
} else if (mem.eql(u8, arg, "--ver-patch")) {
ver_patch = try std.fmt.parseUnsigned(u32, args[arg_i], 10);
} else if (mem.eql(u8, arg, "--test-cmd")) {
@panic("TODO --test-cmd");
} else {
badArgs("invalid argument: {}", arg);
}
}
} else if (cmd == Cmd.None) {
if (mem.eql(u8, arg, "build-obj")) {
cmd = Cmd.Build;
build_kind = Module.Kind.Obj;
} else if (mem.eql(u8, arg, "build-exe")) {
cmd = Cmd.Build;
build_kind = Module.Kind.Exe;
} else if (mem.eql(u8, arg, "build-lib")) {
cmd = Cmd.Build;
build_kind = Module.Kind.Lib;
} else if (mem.eql(u8, arg, "version")) {
cmd = Cmd.Version;
} else if (mem.eql(u8, arg, "zen")) {
cmd = Cmd.Zen;
} else if (mem.eql(u8, arg, "translate-c")) {
cmd = Cmd.TranslateC;
} else if (mem.eql(u8, arg, "test")) {
cmd = Cmd.Test;
build_kind = Module.Kind.Exe;
} else {
badArgs("unrecognized command: {}", arg);
}
} else switch (cmd) {
Cmd.Build, Cmd.TranslateC, Cmd.Test => {
if (in_file_arg == null) {
in_file_arg = arg;
} else {
badArgs("unexpected extra parameter: {}", arg);
}
},
Cmd.Version, Cmd.Zen, Cmd.Targets => {
badArgs("unexpected extra parameter: {}", arg);
},
Cmd.None => unreachable,
}
}
target.initializeAll();
// TODO
// ZigTarget alloc_target;
// ZigTarget *target;
// if (!target_arch && !target_os && !target_environ) {
// target = nullptr;
// } else {
// target = &alloc_target;
// get_unknown_target(target);
// if (target_arch) {
// if (parse_target_arch(target_arch, &target->arch)) {
// fprintf(stderr, "invalid --target-arch argument\n");
// return usage(arg0);
// }
// }
// if (target_os) {
// if (parse_target_os(target_os, &target->os)) {
// fprintf(stderr, "invalid --target-os argument\n");
// return usage(arg0);
// }
// }
// if (target_environ) {
// if (parse_target_environ(target_environ, &target->env_type)) {
// fprintf(stderr, "invalid --target-environ argument\n");
// return usage(arg0);
// }
// }
// }
switch (cmd) {
Cmd.None => badArgs("expected command"),
Cmd.Zen => return printZen(),
Cmd.Build, Cmd.Test, Cmd.TranslateC => {
if (cmd == Cmd.Build and in_file_arg == null and objects.len == 0 and asm_files.len == 0) {
badArgs("expected source file argument or at least one --object or --assembly argument");
} else if ((cmd == Cmd.TranslateC or cmd == Cmd.Test) and in_file_arg == null) {
badArgs("expected source file argument");
} else if (cmd == Cmd.Build and build_kind == Module.Kind.Obj and objects.len != 0) {
badArgs("When building an object file, --object arguments are invalid");
}
const root_name = switch (cmd) {
Cmd.Build, Cmd.TranslateC => x: {
if (out_name_arg) |out_name| {
break :x out_name;
} else if (in_file_arg) |in_file_path| {
const basename = os.path.basename(in_file_path);
var it = mem.split(basename, ".");
break :x it.next() ?? badArgs("file name cannot be empty");
} else {
badArgs("--name [name] not provided and unable to infer");
}
},
Cmd.Test => "test",
else => unreachable,
};
const zig_root_source_file = if (cmd == Cmd.TranslateC) null else in_file_arg;
const chosen_cache_dir = cache_dir_arg ?? default_zig_cache_name;
const full_cache_dir = try os.path.resolve(allocator, ".", chosen_cache_dir);
defer allocator.free(full_cache_dir);
const zig_lib_dir = try resolveZigLibDir(allocator, zig_install_prefix);
errdefer allocator.free(zig_lib_dir);
const module = try Module.create(allocator, root_name, zig_root_source_file,
Target.Native, build_kind, build_mode, zig_lib_dir, full_cache_dir);
defer module.destroy();
module.version_major = ver_major;
module.version_minor = ver_minor;
module.version_patch = ver_patch;
module.is_test = cmd == Cmd.Test;
if (linker_script_arg) |linker_script| {
module.linker_script = linker_script;
}
module.each_lib_rpath = each_lib_rpath;
module.clang_argv = clang_argv.toSliceConst();
module.llvm_argv = llvm_argv.toSliceConst();
module.strip = strip;
module.is_static = is_static;
if (libc_lib_dir_arg) |libc_lib_dir| {
module.libc_lib_dir = libc_lib_dir;
}
if (libc_static_lib_dir_arg) |libc_static_lib_dir| {
module.libc_static_lib_dir = libc_static_lib_dir;
}
if (libc_include_dir_arg) |libc_include_dir| {
module.libc_include_dir = libc_include_dir;
}
if (msvc_lib_dir_arg) |msvc_lib_dir| {
module.msvc_lib_dir = msvc_lib_dir;
}
if (kernel32_lib_dir_arg) |kernel32_lib_dir| {
module.kernel32_lib_dir = kernel32_lib_dir;
}
if (dynamic_linker_arg) |dynamic_linker| {
module.dynamic_linker = dynamic_linker;
}
module.verbose_tokenize = verbose_tokenize;
module.verbose_ast_tree = verbose_ast_tree;
module.verbose_ast_fmt = verbose_ast_fmt;
module.verbose_link = verbose_link;
module.verbose_ir = verbose_ir;
module.verbose_llvm_ir = verbose_llvm_ir;
module.verbose_cimport = verbose_cimport;
module.err_color = color;
module.lib_dirs = lib_dirs.toSliceConst();
module.darwin_frameworks = frameworks.toSliceConst();
module.rpath_list = rpath_list.toSliceConst();
for (link_libs.toSliceConst()) |name| {
_ = try module.addLinkLib(name, true);
}
module.windows_subsystem_windows = mwindows;
module.windows_subsystem_console = mconsole;
module.linker_rdynamic = rdynamic;
if (mmacosx_version_min != null and mios_version_min != null) {
badArgs("-mmacosx-version-min and -mios-version-min options not allowed together");
}
if (mmacosx_version_min) |ver| {
module.darwin_version_min = Module.DarwinVersionMin { .MacOS = ver };
} else if (mios_version_min) |ver| {
module.darwin_version_min = Module.DarwinVersionMin { .Ios = ver };
}
module.test_filters = test_filters.toSliceConst();
module.test_name_prefix = test_name_prefix_arg;
module.out_h_path = out_file_h;
// TODO
//add_package(g, cur_pkg, g->root_package);
switch (cmd) {
Cmd.Build => {
module.emit_file_type = emit_file_type;
module.link_objects = objects.toSliceConst();
module.assembly_files = asm_files.toSliceConst();
try module.build();
try module.link(out_file);
},
Cmd.TranslateC => @panic("TODO translate-c"),
Cmd.Test => @panic("TODO test cmd"),
else => unreachable,
}
},
Cmd.Version => {
var stdout_file = try io.getStdErr();
try stdout_file.write(std.cstr.toSliceConst(c.ZIG_VERSION_STRING));
try stdout_file.write("\n");
},
Cmd.Targets => @panic("TODO zig targets"),
}
}
fn printUsage(stream: var) !void {
try stream.write(
\\Usage: zig [command] [options]
\\
\\Commands:
\\ build build project from build.zig
\\ build-exe [source] create executable from source or object files
\\ build-lib [source] create library from source or object files
\\ build-obj [source] create object from source or assembly
\\ fmt [file] parse file and render in canonical zig format
\\ translate-c [source] convert c code to zig code
\\ targets list available compilation targets
\\ test [source] create and run a test build
\\ version print version number and exit
\\ zen print zen of zig and exit
\\Compile Options:
\\ --assembly [source] add assembly file to build
\\ --cache-dir [path] override the cache directory
\\ --color [auto|off|on] enable or disable colored error messages
\\ --emit [filetype] emit a specific file format as compilation output
\\ --enable-timing-info print timing diagnostics
\\ --libc-include-dir [path] directory where libc stdlib.h resides
\\ --name [name] override output name
\\ --output [file] override destination path
\\ --output-h [file] override generated header file path
\\ --pkg-begin [name] [path] make package available to import and push current pkg
\\ --pkg-end pop current pkg
\\ --release-fast build with optimizations on and safety off
\\ --release-safe build with optimizations on and safety on
\\ --static output will be statically linked
\\ --strip exclude debug symbols
\\ --target-arch [name] specify target architecture
\\ --target-environ [name] specify target environment
\\ --target-os [name] specify target operating system
\\ --verbose-tokenize enable compiler debug info: tokenization
\\ --verbose-ast-tree enable compiler debug info: parsing into an AST (treeview)
\\ --verbose-ast-fmt enable compiler debug info: parsing into an AST (render source)
\\ --verbose-cimport enable compiler debug info: C imports
\\ --verbose-ir enable compiler debug info: Zig IR
\\ --verbose-llvm-ir enable compiler debug info: LLVM IR
\\ --verbose-link enable compiler debug info: linking
\\ --zig-install-prefix [path] override directory where zig thinks it is installed
\\ -dirafter [dir] same as -isystem but do it last
\\ -isystem [dir] add additional search path for other .h files
\\ -mllvm [arg] additional arguments to forward to LLVM's option processing
\\Link Options:
\\ --ar-path [path] set the path to ar
\\ --dynamic-linker [path] set the path to ld.so
\\ --each-lib-rpath add rpath for each used dynamic library
\\ --libc-lib-dir [path] directory where libc crt1.o resides
\\ --libc-static-lib-dir [path] directory where libc crtbegin.o resides
\\ --msvc-lib-dir [path] (windows) directory where vcruntime.lib resides
\\ --kernel32-lib-dir [path] (windows) directory where kernel32.lib resides
\\ --library [lib] link against lib
\\ --library-path [dir] add a directory to the library search path
\\ --linker-script [path] use a custom linker script
\\ --object [obj] add object file to build
\\ -L[dir] alias for --library-path
\\ -rdynamic add all symbols to the dynamic symbol table
\\ -rpath [path] add directory to the runtime library search path
\\ -mconsole (windows) --subsystem console to the linker
\\ -mwindows (windows) --subsystem windows to the linker
\\ -framework [name] (darwin) link against framework
\\ -mios-version-min [ver] (darwin) set iOS deployment target
\\ -mmacosx-version-min [ver] (darwin) set Mac OS X deployment target
\\ --ver-major [ver] dynamic library semver major version
\\ --ver-minor [ver] dynamic library semver minor version
\\ --ver-patch [ver] dynamic library semver patch version
\\Test Options:
\\ --test-filter [text] skip tests that do not match filter
\\ --test-name-prefix [text] add prefix to all tests
\\ --test-cmd [arg] specify test execution command one arg at a time
\\ --test-cmd-bin appends test binary path to test cmd args
\\
);
}
fn printZen() !void {
var stdout_file = try io.getStdErr();
try stdout_file.write(
\\
\\ * Communicate intent precisely.
\\ * Edge cases matter.
\\ * Favor reading code over writing code.
\\ * Only one obvious way to do things.
\\ * Runtime crashes are better than bugs.
\\ * Compile errors are better than runtime crashes.
\\ * Incremental improvements.
\\ * Avoid local maximums.
\\ * Reduce the amount one must remember.
\\ * Minimize energy spent on coding style.
\\ * Together we serve end users.
\\
\\
);
}
fn buildMain(allocator: &mem.Allocator, argv: []const []const u8) !void {
var build_file: [] const u8 = "build.zig";
var cache_dir: ?[] const u8 = null;
var zig_install_prefix: ?[] const u8 = null;
var asked_for_help = false;
var asked_for_init = false;
var args = ArrayList([] const u8).init(allocator);
defer args.deinit();
var zig_exe_path = try os.selfExePath(allocator);
defer allocator.free(zig_exe_path);
try args.append(""); // Placeholder for zig-cache/build
try args.append(""); // Placeholder for zig_exe_path
try args.append(""); // Placeholder for build_file_dirname
try args.append(""); // Placeholder for full_cache_dir
var i: usize = 0;
while (i < argv.len) : (i += 1) {
var arg = argv[i];
if (mem.eql(u8, arg, "--help")) {
asked_for_help = true;
try args.append(argv[i]);
} else if (mem.eql(u8, arg, "--init")) {
asked_for_init = true;
try args.append(argv[i]);
} else if (i + 1 < argv.len and mem.eql(u8, arg, "--build-file")) {
build_file = argv[i + 1];
i += 1;
} else if (i + 1 < argv.len and mem.eql(u8, arg, "--cache-dir")) {
cache_dir = argv[i + 1];
i += 1;
} else if (i + 1 < argv.len and mem.eql(u8, arg, "--zig-install-prefix")) {
try args.append(arg);
i += 1;
zig_install_prefix = argv[i];
try args.append(argv[i]);
} else {
try args.append(arg);
}
}
const zig_lib_dir = try resolveZigLibDir(allocator, zig_install_prefix);
defer allocator.free(zig_lib_dir);
const zig_std_dir = try os.path.join(allocator, zig_lib_dir, "std");
defer allocator.free(zig_std_dir);
const special_dir = try os.path.join(allocator, zig_std_dir, "special");
defer allocator.free(special_dir);
const build_runner_path = try os.path.join(allocator, special_dir, "build_runner.zig");
defer allocator.free(build_runner_path);
// g = codegen_create(build_runner_path, ...)
// codegen_set_out_name(g, "build")
const build_file_abs = try os.path.resolve(allocator, ".", build_file);
defer allocator.free(build_file_abs);
const build_file_basename = os.path.basename(build_file_abs);
const build_file_dirname = os.path.dirname(build_file_abs);
var full_cache_dir: []u8 = undefined;
if (cache_dir == null) {
full_cache_dir = try os.path.join(allocator, build_file_dirname, "zig-cache");
} else {
full_cache_dir = try os.path.resolve(allocator, ".", ??cache_dir, full_cache_dir);
}
defer allocator.free(full_cache_dir);
const path_to_build_exe = try os.path.join(allocator, full_cache_dir, "build");
defer allocator.free(path_to_build_exe);
// codegen_set_cache_dir(g, full_cache_dir)
args.items[0] = path_to_build_exe;
args.items[1] = zig_exe_path;
args.items[2] = build_file_dirname;
args.items[3] = full_cache_dir;
var build_file_exists: bool = undefined;
if (os.File.openRead(allocator, build_file_abs)) |*file| {
file.close();
build_file_exists = true;
} else |_| {
build_file_exists = false;
}
if (!build_file_exists and asked_for_help) {
// TODO(bnoordhuis) Print help message from std/special/build_runner.zig
return;
}
if (!build_file_exists and asked_for_init) {
const build_template_path = try os.path.join(allocator, special_dir, "build_file_template.zig");
defer allocator.free(build_template_path);
var srcfile = try os.File.openRead(allocator, build_template_path);
defer srcfile.close();
var dstfile = try os.File.openWrite(allocator, build_file_abs);
defer dstfile.close();
while (true) {
var buffer: [4096]u8 = undefined;
const n = try srcfile.read(buffer[0..]);
if (n == 0) break;
try dstfile.write(buffer[0..n]);
}
return;
}
if (!build_file_exists) {
warn(
\\No 'build.zig' file found.
\\Initialize a 'build.zig' template file with `zig build --init`,
\\or build an executable directly with `zig build-exe $FILENAME.zig`.
\\See: `zig build --help` or `zig help` for more options.
\\
);
os.exit(1);
}
// codegen_build(g)
// codegen_link(g, path_to_build_exe)
// codegen_destroy(g)
var proc = try os.ChildProcess.init(args.toSliceConst(), allocator);
defer proc.deinit();
var term = try proc.spawnAndWait();
switch (term) {
os.ChildProcess.Term.Exited => |status| {
if (status != 0) {
warn("{} exited with status {}\n", args.at(0), status);
os.exit(1);
}
},
os.ChildProcess.Term.Signal => |signal| {
warn("{} killed by signal {}\n", args.at(0), signal);
os.exit(1);
},
os.ChildProcess.Term.Stopped => |signal| {
warn("{} stopped by signal {}\n", args.at(0), signal);
os.exit(1);
},
os.ChildProcess.Term.Unknown => |status| {
warn("{} encountered unknown failure {}\n", args.at(0), status);
os.exit(1);
},
}
}
fn fmtMain(allocator: &mem.Allocator, file_paths: []const []const u8) !void {
for (file_paths) |file_path| {
var file = try os.File.openRead(allocator, file_path);
defer file.close();
const source_code = io.readFileAlloc(allocator, file_path) catch |err| {
warn("unable to open '{}': {}", file_path, err);
continue;
};
defer allocator.free(source_code);
var tokenizer = std.zig.Tokenizer.init(source_code);
var parser = std.zig.Parser.init(&tokenizer, allocator, file_path);
defer parser.deinit();
var tree = try parser.parse();
defer tree.deinit();
const baf = try io.BufferedAtomicFile.create(allocator, file_path);
defer baf.destroy();
try parser.renderSource(baf.stream(), tree.root_node);
try baf.finish();
}
}
/// Caller must free result
fn resolveZigLibDir(allocator: &mem.Allocator, zig_install_prefix_arg: ?[]const u8) ![]u8 {
if (zig_install_prefix_arg) |zig_install_prefix| {
return testZigInstallPrefix(allocator, zig_install_prefix) catch |err| {
warn("No Zig installation found at prefix {}: {}\n", zig_install_prefix_arg, @errorName(err));
return error.ZigInstallationNotFound;
};
} else {
return findZigLibDir(allocator) catch |err| {
warn("Unable to find zig lib directory: {}.\nReinstall Zig or use --zig-install-prefix.\n",
@errorName(err));
return error.ZigLibDirNotFound;
};
}
}
/// Caller must free result
fn testZigInstallPrefix(allocator: &mem.Allocator, test_path: []const u8) ![]u8 {
const test_zig_dir = try os.path.join(allocator, test_path, "lib", "zig");
errdefer allocator.free(test_zig_dir);
const test_index_file = try os.path.join(allocator, test_zig_dir, "std", "index.zig");
defer allocator.free(test_index_file);
var file = try os.File.openRead(allocator, test_index_file);
file.close();
return test_zig_dir;
}
/// Caller must free result
fn findZigLibDir(allocator: &mem.Allocator) ![]u8 {
const self_exe_path = try os.selfExeDirPath(allocator);
defer allocator.free(self_exe_path);
var cur_path: []const u8 = self_exe_path;
while (true) {
const test_dir = os.path.dirname(cur_path);
if (mem.eql(u8, test_dir, cur_path)) {
break;
}
return testZigInstallPrefix(allocator, test_dir) catch |err| {
cur_path = test_dir;
continue;
};
}
// TODO look in hard coded installation path from configuration
//if (ZIG_INSTALL_PREFIX != nullptr) {
// if (test_zig_install_prefix(buf_create_from_str(ZIG_INSTALL_PREFIX), out_path)) {
// return 0;
// }
//}
return error.FileNotFound;
}