zig/src-self-hosted/main.zig

997 lines
34 KiB
Zig
Raw Normal View History

const std = @import("std");
const builtin = @import("builtin");
const os = std.os;
const io = std.io;
const mem = std.mem;
const Allocator = mem.Allocator;
const ArrayList = std.ArrayList;
const Buffer = std.Buffer;
2017-12-04 19:05:27 -08:00
const arg = @import("arg.zig");
const c = @import("c.zig");
const introspect = @import("introspect.zig");
const Args = arg.Args;
const Flag = arg.Flag;
const Module = @import("module.zig").Module;
const Target = @import("target.zig").Target;
var stderr: &io.OutStream(io.FileOutStream.Error) = undefined;
var stdout: &io.OutStream(io.FileOutStream.Error) = undefined;
const usage =
\\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 [source] Parse file and render in canonical zig format
\\ run [source] Create executable and run immediately
\\ targets List available compilation targets
\\ test [source] Create and run a test build
\\ translate-c [source] Convert c code to zig code
\\ version Print version number and exit
\\ zen Print zen of zig and exit
\\
\\
;
const Command = struct {
name: []const u8,
exec: fn(&Allocator, []const []const u8) error!void,
};
2017-10-21 14:31:06 -07:00
pub fn main() !void {
var allocator = std.heap.c_allocator;
var stdout_file = try std.io.getStdOut();
var stdout_out_stream = std.io.FileOutStream.init(&stdout_file);
stdout = &stdout_out_stream.stream;
var stderr_file = try std.io.getStdErr();
var stderr_out_stream = std.io.FileOutStream.init(&stderr_file);
stderr = &stderr_out_stream.stream;
2017-10-24 07:08:20 -07:00
const args = try os.argsAlloc(allocator);
2017-12-06 15:22:52 -08:00
defer os.argsFree(allocator, args);
if (args.len <= 1) {
try stderr.write(usage);
os.exit(1);
}
const commands = []Command {
Command { .name = "build", .exec = cmdBuild },
Command { .name = "build-exe", .exec = cmdBuildExe },
Command { .name = "build-lib", .exec = cmdBuildLib },
Command { .name = "build-obj", .exec = cmdBuildObj },
Command { .name = "fmt", .exec = cmdFmt },
Command { .name = "run", .exec = cmdRun },
Command { .name = "targets", .exec = cmdTargets },
Command { .name = "test", .exec = cmdTest },
Command { .name = "translate-c", .exec = cmdTranslateC },
Command { .name = "version", .exec = cmdVersion },
Command { .name = "zen", .exec = cmdZen },
// undocumented commands
Command { .name = "help", .exec = cmdHelp },
Command { .name = "internal", .exec = cmdInternal },
};
for (commands) |command| {
if (mem.eql(u8, command.name, args[1])) {
try command.exec(allocator, args[2..]);
return;
2017-12-04 20:09:03 -08:00
}
}
2017-12-04 21:20:23 -08:00
try stderr.print("unknown command: {}\n\n", args[1]);
try stderr.write(usage);
}
// cmd:build ///////////////////////////////////////////////////////////////////////////////////////
const usage_build =
\\usage: zig build <options>
\\
\\General Options:
\\ --help Print this help and exit
\\ --init Generate a build.zig template
\\ --build-file [file] Override path to build.zig
\\ --cache-dir [path] Override path to cache directory
\\ --verbose Print commands before executing them
\\ --prefix [path] Override default install prefix
\\
\\Project-Specific Options:
\\
\\ Project-specific options become available when the build file is found.
\\
\\Advanced Options:
\\ --build-file [file] Override path to build.zig
\\ --cache-dir [path] Override path to cache directory
\\ --verbose-tokenize Enable compiler debug output for tokenization
\\ --verbose-ast Enable compiler debug output for parsing into an AST
\\ --verbose-link Enable compiler debug output for linking
\\ --verbose-ir Enable compiler debug output for Zig IR
\\ --verbose-llvm-ir Enable compiler debug output for LLVM IR
\\ --verbose-cimport Enable compiler debug output for C imports
\\
\\
;
const args_build_spec = []Flag {
Flag.Bool("--help"),
Flag.Bool("--init"),
Flag.Arg1("--build-file"),
Flag.Arg1("--cache-dir"),
Flag.Bool("--verbose"),
Flag.Arg1("--prefix"),
Flag.Arg1("--build-file"),
Flag.Arg1("--cache-dir"),
Flag.Bool("--verbose-tokenize"),
Flag.Bool("--verbose-ast"),
Flag.Bool("--verbose-link"),
Flag.Bool("--verbose-ir"),
Flag.Bool("--verbose-llvm-ir"),
Flag.Bool("--verbose-cimport"),
};
const missing_build_file =
\\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.
\\
;
fn cmdBuild(allocator: &Allocator, args: []const []const u8) !void {
var flags = try Args.parse(allocator, args_build_spec, args);
defer flags.deinit();
if (flags.present("help")) {
try stderr.write(usage_build);
os.exit(0);
}
const zig_lib_dir = try introspect.resolveZigLibDir(allocator);
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);
const build_file = flags.single("build-file") ?? "build.zig";
const build_file_abs = try os.path.resolve(allocator, ".", build_file);
defer allocator.free(build_file_abs);
2018-04-13 02:27:09 -07:00
const build_file_exists = os.File.access(allocator, build_file_abs, os.default_file_mode) catch false;
if (flags.present("init")) {
if (build_file_exists) {
try stderr.print("build.zig already exists\n");
os.exit(1);
}
// need a new scope for proper defer scope finalization on exit
{
const build_template_path = try os.path.join(allocator, special_dir, "build_file_template.zig");
defer allocator.free(build_template_path);
try os.copyFile(allocator, build_template_path, build_file_abs);
try stderr.print("wrote build.zig template\n");
}
os.exit(0);
}
if (!build_file_exists) {
try stderr.write(missing_build_file);
os.exit(1);
}
// TODO: Invoke build.zig entrypoint directly?
var zig_exe_path = try os.selfExePath(allocator);
defer allocator.free(zig_exe_path);
var build_args = ArrayList([]const u8).init(allocator);
defer build_args.deinit();
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 (flags.single("cache-dir")) |cache_dir| {
full_cache_dir = try os.path.resolve(allocator, ".", cache_dir, full_cache_dir);
} else {
full_cache_dir = try os.path.join(allocator, build_file_dirname, "zig-cache");
}
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);
try build_args.append(path_to_build_exe);
try build_args.append(zig_exe_path);
try build_args.append(build_file_dirname);
try build_args.append(full_cache_dir);
var proc = try os.ChildProcess.init(build_args.toSliceConst(), allocator);
defer proc.deinit();
var term = try proc.spawnAndWait();
switch (term) {
os.ChildProcess.Term.Exited => |status| {
if (status != 0) {
try stderr.print("{} exited with status {}\n", build_args.at(0), status);
os.exit(1);
}
},
os.ChildProcess.Term.Signal => |signal| {
try stderr.print("{} killed by signal {}\n", build_args.at(0), signal);
os.exit(1);
},
os.ChildProcess.Term.Stopped => |signal| {
try stderr.print("{} stopped by signal {}\n", build_args.at(0), signal);
os.exit(1);
},
os.ChildProcess.Term.Unknown => |status| {
try stderr.print("{} encountered unknown failure {}\n", build_args.at(0), status);
os.exit(1);
},
}
}
// cmd:build-exe ///////////////////////////////////////////////////////////////////////////////////
const usage_build_generic =
\\usage: zig build-exe <options> [file]
\\ zig build-lib <options> [file]
\\ zig build-obj <options> [file]
\\
\\General Options:
\\ --help Print this help and exit
\\ --color [auto|off|on] Enable or disable colored error messages
\\
\\Compile Options:
\\ --assembly [source] Add assembly file to build
\\ --cache-dir [path] Override the cache directory
\\ --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 Turn on compiler debug output for tokenization
\\ --verbose-ast-tree Turn on compiler debug output for parsing into an AST (tree view)
\\ --verbose-ast-fmt Turn on compiler debug output for parsing into an AST (render source)
\\ --verbose-link Turn on compiler debug output for linking
\\ --verbose-ir Turn on compiler debug output for Zig IR
\\ --verbose-llvm-ir Turn on compiler debug output for LLVM IR
\\ --verbose-cimport Turn on compiler debug output for C imports
\\ -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
\\ --forbid-library [lib] Make it an error to 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
\\ -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
\\
\\
;
const args_build_generic = []Flag {
Flag.Bool("--help"),
Flag.Option("--color", []const []const u8 { "auto", "off", "on" }),
Flag.ArgMergeN("--assembly", 1),
Flag.Arg1("--cache-dir"),
Flag.Option("--emit", []const []const u8 { "asm", "bin", "llvm-ir" }),
Flag.Bool("--enable-timing-info"),
Flag.Arg1("--libc-include-dir"),
Flag.Arg1("--name"),
Flag.Arg1("--output"),
Flag.Arg1("--output-h"),
// NOTE: Parsed manually after initial check
Flag.ArgN("--pkg-begin", 2),
Flag.Bool("--pkg-end"),
Flag.Bool("--release-fast"),
Flag.Bool("--release-safe"),
Flag.Bool("--static"),
Flag.Bool("--strip"),
Flag.Arg1("--target-arch"),
Flag.Arg1("--target-environ"),
Flag.Arg1("--target-os"),
Flag.Bool("--verbose-tokenize"),
Flag.Bool("--verbose-ast-tree"),
Flag.Bool("--verbose-ast-fmt"),
Flag.Bool("--verbose-link"),
Flag.Bool("--verbose-ir"),
Flag.Bool("--verbose-llvm-ir"),
Flag.Bool("--verbose-cimport"),
Flag.Arg1("-dirafter"),
Flag.ArgMergeN("-isystem", 1),
Flag.Arg1("-mllvm"),
Flag.Arg1("--ar-path"),
Flag.Arg1("--dynamic-linker"),
Flag.Bool("--each-lib-rpath"),
Flag.Arg1("--libc-lib-dir"),
Flag.Arg1("--libc-static-lib-dir"),
Flag.Arg1("--msvc-lib-dir"),
Flag.Arg1("--kernel32-lib-dir"),
Flag.ArgMergeN("--library", 1),
Flag.ArgMergeN("--forbid-library", 1),
Flag.ArgMergeN("--library-path", 1),
Flag.Arg1("--linker-script"),
Flag.ArgMergeN("--object", 1),
// NOTE: Removed -L since it would need to be special-cased and we have an alias in library-path
Flag.Bool("-rdynamic"),
Flag.Arg1("-rpath"),
Flag.Bool("-mconsole"),
Flag.Bool("-mwindows"),
Flag.ArgMergeN("-framework", 1),
Flag.Arg1("-mios-version-min"),
Flag.Arg1("-mmacosx-version-min"),
Flag.Arg1("--ver-major"),
Flag.Arg1("--ver-minor"),
Flag.Arg1("--ver-patch"),
};
2017-12-08 20:15:43 -08:00
fn buildOutputType(allocator: &Allocator, args: []const []const u8, out_type: Module.Kind) !void {
var flags = try Args.parse(allocator, args_build_generic, args);
defer flags.deinit();
if (flags.present("help")) {
try stderr.write(usage_build_generic);
os.exit(0);
}
var build_mode = builtin.Mode.Debug;
if (flags.present("release-fast")) {
build_mode = builtin.Mode.ReleaseFast;
} else if (flags.present("release-safe")) {
build_mode = builtin.Mode.ReleaseSafe;
}
var color = Module.ErrColor.Auto;
if (flags.single("color")) |color_flag| {
if (mem.eql(u8, color_flag, "auto")) {
color = Module.ErrColor.Auto;
} else if (mem.eql(u8, color_flag, "on")) {
color = Module.ErrColor.On;
} else if (mem.eql(u8, color_flag, "off")) {
color = Module.ErrColor.Off;
} else {
unreachable;
}
}
var emit_type = Module.Emit.Binary;
if (flags.single("emit")) |emit_flag| {
if (mem.eql(u8, emit_flag, "asm")) {
emit_type = Module.Emit.Assembly;
} else if (mem.eql(u8, emit_flag, "bin")) {
emit_type = Module.Emit.Binary;
} else if (mem.eql(u8, emit_flag, "llvm-ir")) {
emit_type = Module.Emit.LlvmIr;
} else {
unreachable;
}
}
var cur_pkg = try Module.CliPkg.init(allocator, "", "", null); // TODO: Need a path, name?
defer cur_pkg.deinit();
var i: usize = 0;
while (i < args.len) : (i += 1) {
const arg_name = args[i];
if (mem.eql(u8, "--pkg-begin", arg_name)) {
// following two arguments guaranteed to exist due to arg parsing
i += 1;
const new_pkg_name = args[i];
i += 1;
const new_pkg_path = args[i];
var new_cur_pkg = try Module.CliPkg.init(allocator, new_pkg_name, new_pkg_path, cur_pkg);
try cur_pkg.children.append(new_cur_pkg);
cur_pkg = new_cur_pkg;
} else if (mem.eql(u8, "--pkg-end", arg_name)) {
if (cur_pkg.parent == null) {
try stderr.print("encountered --pkg-end with no matching --pkg-begin\n");
os.exit(1);
}
cur_pkg = ??cur_pkg.parent;
}
}
if (cur_pkg.parent != null) {
try stderr.print("unmatched --pkg-begin\n");
os.exit(1);
}
var in_file: ?[]const u8 = undefined;
switch (flags.positionals.len) {
0 => {
try stderr.write("--name [name] not provided and unable to infer\n");
os.exit(1);
},
1 => {
in_file = flags.positionals.at(0);
},
else => {
try stderr.write("only one zig input file is accepted during build\n");
os.exit(1);
},
}
const basename = os.path.basename(??in_file);
var it = mem.split(basename, ".");
const root_name = it.next() ?? {
try stderr.write("file name cannot be empty\n");
os.exit(1);
};
const asm_a= flags.many("assembly");
const obj_a = flags.many("object");
if (in_file == null and (obj_a == null or (??obj_a).len == 0) and (asm_a == null or (??asm_a).len == 0)) {
try stderr.write("Expected source file argument or at least one --object or --assembly argument\n");
os.exit(1);
}
if (out_type == Module.Kind.Obj and (obj_a != null and (??obj_a).len != 0)) {
try stderr.write("When building an object file, --object arguments are invalid\n");
os.exit(1);
}
const zig_root_source_file = in_file;
const full_cache_dir = os.path.resolve(allocator, ".", flags.single("cache-dir") ?? "zig-cache"[0..]) catch {
os.exit(1);
};
defer allocator.free(full_cache_dir);
const zig_lib_dir = introspect.resolveZigLibDir(allocator) catch os.exit(1);
defer allocator.free(zig_lib_dir);
var module =
try Module.create(
allocator,
root_name,
zig_root_source_file,
Target.Native,
out_type,
build_mode,
zig_lib_dir,
full_cache_dir
);
defer module.destroy();
module.version_major = try std.fmt.parseUnsigned(u32, flags.single("ver-major") ?? "0", 10);
module.version_minor = try std.fmt.parseUnsigned(u32, flags.single("ver-minor") ?? "0", 10);
module.version_patch = try std.fmt.parseUnsigned(u32, flags.single("ver-patch") ?? "0", 10);
module.is_test = false;
if (flags.single("linker-script")) |linker_script| {
module.linker_script = linker_script;
}
module.each_lib_rpath = flags.present("each-lib-rpath");
var clang_argv_buf = ArrayList([]const u8).init(allocator);
defer clang_argv_buf.deinit();
if (flags.many("mllvm")) |mllvm_flags| {
for (mllvm_flags) |mllvm| {
try clang_argv_buf.append("-mllvm");
try clang_argv_buf.append(mllvm);
}
module.llvm_argv = mllvm_flags;
module.clang_argv = clang_argv_buf.toSliceConst();
}
module.strip = flags.present("strip");
module.is_static = flags.present("static");
if (flags.single("libc-lib-dir")) |libc_lib_dir| {
module.libc_lib_dir = libc_lib_dir;
}
if (flags.single("libc-static-lib-dir")) |libc_static_lib_dir| {
module.libc_static_lib_dir = libc_static_lib_dir;
}
if (flags.single("libc-include-dir")) |libc_include_dir| {
module.libc_include_dir = libc_include_dir;
}
if (flags.single("msvc-lib-dir")) |msvc_lib_dir| {
module.msvc_lib_dir = msvc_lib_dir;
}
if (flags.single("kernel32-lib-dir")) |kernel32_lib_dir| {
module.kernel32_lib_dir = kernel32_lib_dir;
}
if (flags.single("dynamic-linker")) |dynamic_linker| {
module.dynamic_linker = dynamic_linker;
}
module.verbose_tokenize = flags.present("verbose-tokenize");
module.verbose_ast_tree = flags.present("verbose-ast-tree");
module.verbose_ast_fmt = flags.present("verbose-ast-fmt");
module.verbose_link = flags.present("verbose-link");
module.verbose_ir = flags.present("verbose-ir");
module.verbose_llvm_ir = flags.present("verbose-llvm-ir");
module.verbose_cimport = flags.present("verbose-cimport");
module.err_color = color;
if (flags.many("library-path")) |lib_dirs| {
module.lib_dirs = lib_dirs;
}
if (flags.many("framework")) |frameworks| {
module.darwin_frameworks = frameworks;
}
if (flags.many("rpath")) |rpath_list| {
module.rpath_list = rpath_list;
}
if (flags.single("output-h")) |output_h| {
module.out_h_path = output_h;
}
module.windows_subsystem_windows = flags.present("mwindows");
module.windows_subsystem_console = flags.present("mconsole");
module.linker_rdynamic = flags.present("rdynamic");
if (flags.single("mmacosx-version-min") != null and flags.single("mios-version-min") != null) {
try stderr.write("-mmacosx-version-min and -mios-version-min options not allowed together\n");
os.exit(1);
}
if (flags.single("mmacosx-version-min")) |ver| {
module.darwin_version_min = Module.DarwinVersionMin { .MacOS = ver };
}
if (flags.single("mios-version-min")) |ver| {
module.darwin_version_min = Module.DarwinVersionMin { .Ios = ver };
}
module.emit_file_type = emit_type;
if (flags.many("object")) |objects| {
module.link_objects = objects;
}
if (flags.many("assembly")) |assembly_files| {
module.assembly_files = assembly_files;
}
try module.build();
try module.link(flags.single("out-file") ?? null);
if (flags.present("print-timing-info")) {
// codegen_print_timing_info(g, stderr);
}
try stderr.print("building {}: {}\n", @tagName(out_type), in_file);
}
fn cmdBuildExe(allocator: &Allocator, args: []const []const u8) !void {
try buildOutputType(allocator, args, Module.Kind.Exe);
}
// cmd:build-lib ///////////////////////////////////////////////////////////////////////////////////
fn cmdBuildLib(allocator: &Allocator, args: []const []const u8) !void {
try buildOutputType(allocator, args, Module.Kind.Lib);
}
// cmd:build-obj ///////////////////////////////////////////////////////////////////////////////////
fn cmdBuildObj(allocator: &Allocator, args: []const []const u8) !void {
try buildOutputType(allocator, args, Module.Kind.Obj);
}
// cmd:fmt /////////////////////////////////////////////////////////////////////////////////////////
const usage_fmt =
\\usage: zig fmt [file]...
\\
\\ Formats the input files and modifies them in-place.
\\
\\Options:
\\ --help Print this help and exit
\\
\\
;
const args_fmt_spec = []Flag {
Flag.Bool("--help"),
};
fn cmdFmt(allocator: &Allocator, args: []const []const u8) !void {
var flags = try Args.parse(allocator, args_fmt_spec, args);
defer flags.deinit();
if (flags.present("help")) {
try stderr.write(usage_fmt);
os.exit(0);
}
if (flags.positionals.len == 0) {
try stderr.write("expected at least one source file argument\n");
os.exit(1);
}
for (flags.positionals.toSliceConst()) |file_path| {
var file = try os.File.openRead(allocator, file_path);
defer file.close();
const source_code = io.readFileAlloc(allocator, file_path) catch |err| {
try stderr.print("unable to open '{}': {}", file_path, err);
continue;
};
defer allocator.free(source_code);
2018-05-09 18:45:29 -07:00
var tree = std.zig.parse(allocator, source_code) catch |err| {
try stderr.print("error parsing file '{}': {}\n", file_path, err);
continue;
};
defer tree.deinit();
2018-05-09 18:45:29 -07:00
var error_it = tree.errors.iterator(0);
while (error_it.next()) |parse_error| {
const token = tree.tokens.at(parse_error.loc());
const loc = tree.tokenLocation(0, parse_error.loc());
try stderr.print("{}:{}:{}: error: ", file_path, loc.line + 1, loc.column + 1);
try tree.renderError(parse_error, stderr);
try stderr.print("\n{}\n", source_code[loc.line_start..loc.line_end]);
{
var i: usize = 0;
while (i < loc.column) : (i += 1) {
try stderr.write(" ");
}
}
{
const caret_count = token.end - token.start;
var i: usize = 0;
while (i < caret_count) : (i += 1) {
try stderr.write("~");
}
}
try stderr.write("\n");
}
if (tree.errors.len != 0) {
continue;
}
try stderr.print("{}\n", file_path);
2018-05-09 18:45:29 -07:00
const baf = try io.BufferedAtomicFile.create(allocator, file_path);
defer baf.destroy();
2018-05-09 18:45:29 -07:00
try std.zig.render(allocator, baf.stream(), &tree);
2018-05-09 20:48:50 -07:00
try baf.finish();
}
}
// cmd:targets /////////////////////////////////////////////////////////////////////////////////////
fn cmdTargets(allocator: &Allocator, args: []const []const u8) !void {
try stdout.write("Architectures:\n");
2018-04-13 01:12:30 -07:00
{
comptime var i: usize = 0;
inline while (i < @memberCount(builtin.Arch)) : (i += 1) {
comptime const arch_tag = @memberName(builtin.Arch, i);
// NOTE: Cannot use empty string, see #918.
comptime const native_str =
if (comptime mem.eql(u8, arch_tag, @tagName(builtin.arch))) " (native)\n" else "\n";
try stdout.print(" {}{}", arch_tag, native_str);
}
}
try stdout.write("\n");
try stdout.write("Operating Systems:\n");
2018-04-13 01:12:30 -07:00
{
comptime var i: usize = 0;
inline while (i < @memberCount(builtin.Os)) : (i += 1) {
comptime const os_tag = @memberName(builtin.Os, i);
// NOTE: Cannot use empty string, see #918.
comptime const native_str =
if (comptime mem.eql(u8, os_tag, @tagName(builtin.os))) " (native)\n" else "\n";
try stdout.print(" {}{}", os_tag, native_str);
}
}
try stdout.write("\n");
try stdout.write("Environments:\n");
2018-04-13 01:12:30 -07:00
{
comptime var i: usize = 0;
inline while (i < @memberCount(builtin.Environ)) : (i += 1) {
comptime const environ_tag = @memberName(builtin.Environ, i);
// NOTE: Cannot use empty string, see #918.
comptime const native_str =
if (comptime mem.eql(u8, environ_tag, @tagName(builtin.environ))) " (native)\n" else "\n";
try stdout.print(" {}{}", environ_tag, native_str);
}
}
}
// cmd:version /////////////////////////////////////////////////////////////////////////////////////
fn cmdVersion(allocator: &Allocator, args: []const []const u8) !void {
try stdout.print("{}\n", std.cstr.toSliceConst(c.ZIG_VERSION_STRING));
}
2017-12-08 20:15:43 -08:00
// cmd:test ////////////////////////////////////////////////////////////////////////////////////////
const usage_test =
\\usage: zig test [file]...
\\
\\Options:
\\ --help Print this help and exit
\\
\\
;
const args_test_spec = []Flag {
Flag.Bool("--help"),
};
2017-12-08 20:15:43 -08:00
fn cmdTest(allocator: &Allocator, args: []const []const u8) !void {
var flags = try Args.parse(allocator, args_build_spec, args);
defer flags.deinit();
if (flags.present("help")) {
try stderr.write(usage_test);
os.exit(0);
}
if (flags.positionals.len != 1) {
try stderr.write("expected exactly one zig source file\n");
os.exit(1);
}
// compile the test program into the cache and run
// NOTE: May be overlap with buildOutput, take the shared part out.
try stderr.print("testing file {}\n", flags.positionals.at(0));
2017-10-21 14:31:06 -07:00
}
2017-12-09 17:01:13 -08:00
// cmd:run /////////////////////////////////////////////////////////////////////////////////////////
// Run should be simple and not expose the full set of arguments provided by build-exe. If specific
// build requirements are need, the user should `build-exe` then `run` manually.
const usage_run =
\\usage: zig run [file] -- <runtime args>
\\
\\Options:
\\ --help Print this help and exit
\\
\\
;
const args_run_spec = []Flag {
Flag.Bool("--help"),
};
fn cmdRun(allocator: &Allocator, args: []const []const u8) !void {
var compile_args = args;
var runtime_args: []const []const u8 = []const []const u8 {};
for (args) |argv, i| {
if (mem.eql(u8, argv, "--")) {
compile_args = args[0..i];
runtime_args = args[i+1..];
break;
}
}
var flags = try Args.parse(allocator, args_run_spec, compile_args);
defer flags.deinit();
if (flags.present("help")) {
try stderr.write(usage_run);
os.exit(0);
}
if (flags.positionals.len != 1) {
try stderr.write("expected exactly one zig source file\n");
os.exit(1);
}
try stderr.print("runtime args:\n");
for (runtime_args) |cargs| {
try stderr.print("{}\n", cargs);
}
}
// cmd:translate-c /////////////////////////////////////////////////////////////////////////////////
const usage_translate_c =
\\usage: zig translate-c [file]
\\
\\Options:
\\ --help Print this help and exit
\\ --enable-timing-info Print timing diagnostics
\\ --output [path] Output file to write generated zig file (default: stdout)
\\
\\
;
const args_translate_c_spec = []Flag {
Flag.Bool("--help"),
Flag.Bool("--enable-timing-info"),
Flag.Arg1("--libc-include-dir"),
Flag.Arg1("--output"),
};
fn cmdTranslateC(allocator: &Allocator, args: []const []const u8) !void {
var flags = try Args.parse(allocator, args_translate_c_spec, args);
defer flags.deinit();
if (flags.present("help")) {
try stderr.write(usage_translate_c);
os.exit(0);
}
if (flags.positionals.len != 1) {
try stderr.write("expected exactly one c source file\n");
os.exit(1);
}
// set up codegen
const zig_root_source_file = null;
// NOTE: translate-c shouldn't require setting up the full codegen instance as it does in
// the C++ compiler.
// codegen_create(g);
// codegen_set_out_name(g, null);
// codegen_translate_c(g, flags.positional.at(0))
var output_stream = stdout;
if (flags.single("output")) |output_file| {
var file = try os.File.openWrite(allocator, output_file);
defer file.close();
var file_stream = io.FileOutStream.init(&file);
// TODO: Not being set correctly, still stdout
output_stream = &file_stream.stream;
}
// ast_render(g, output_stream, g->root_import->root, 4);
try output_stream.write("pub const example = 10;\n");
if (flags.present("enable-timing-info")) {
// codegen_print_timing_info(g, stdout);
try stderr.write("printing timing info for translate-c\n");
}
}
// cmd:help ////////////////////////////////////////////////////////////////////////////////////////
fn cmdHelp(allocator: &Allocator, args: []const []const u8) !void {
try stderr.write(usage);
}
// cmd:zen /////////////////////////////////////////////////////////////////////////////////////////
const info_zen =
\\
\\ * 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 cmdZen(allocator: &Allocator, args: []const []const u8) !void {
try stdout.write(info_zen);
}
// cmd:internal ////////////////////////////////////////////////////////////////////////////////////
const usage_internal =
\\usage: zig internal [subcommand]
\\
\\Sub-Commands:
\\ build-info Print static compiler build-info
\\
\\
;
fn cmdInternal(allocator: &Allocator, args: []const []const u8) !void {
if (args.len == 0) {
try stderr.write(usage_internal);
os.exit(1);
}
const sub_commands = []Command {
Command { .name = "build-info", .exec = cmdInternalBuildInfo },
};
for (sub_commands) |sub_command| {
if (mem.eql(u8, sub_command.name, args[0])) {
try sub_command.exec(allocator, args[1..]);
return;
}
}
try stderr.print("unknown sub command: {}\n\n", args[0]);
try stderr.write(usage_internal);
}
fn cmdInternalBuildInfo(allocator: &Allocator, args: []const []const u8) !void {
try stdout.print(
\\ZIG_CMAKE_BINARY_DIR {}
\\ZIG_CXX_COMPILER {}
\\ZIG_LLVM_CONFIG_EXE {}
\\ZIG_LLD_INCLUDE_PATH {}
\\ZIG_LLD_LIBRARIES {}
\\ZIG_STD_FILES {}
\\ZIG_C_HEADER_FILES {}
\\ZIG_DIA_GUIDS_LIB {}
\\
,
std.cstr.toSliceConst(c.ZIG_CMAKE_BINARY_DIR),
std.cstr.toSliceConst(c.ZIG_CXX_COMPILER),
std.cstr.toSliceConst(c.ZIG_LLVM_CONFIG_EXE),
std.cstr.toSliceConst(c.ZIG_LLD_INCLUDE_PATH),
std.cstr.toSliceConst(c.ZIG_LLD_LIBRARIES),
std.cstr.toSliceConst(c.ZIG_STD_FILES),
std.cstr.toSliceConst(c.ZIG_C_HEADER_FILES),
std.cstr.toSliceConst(c.ZIG_DIA_GUIDS_LIB),
);
}