diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index d63ea3a75..8e4764f82 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -30,6 +30,7 @@ const usage = \\ build-obj [source] Create object from source or assembly \\ fmt [source] Parse file and render in canonical zig format \\ targets List available compilation targets + \\ info Print lib path, std path, compiler id and version \\ version Print version number and exit \\ zen Print zen of zig and exit \\ @@ -95,8 +96,9 @@ pub fn main() !void { const stdout = io.getStdOut().outStream(); return @import("print_targets.zig").cmdTargets(arena, cmd_args, stdout, info.target); } else if (mem.eql(u8, cmd, "version")) { - std.io.getStdOut().writeAll(build_options.version ++ "\n") catch process.exit(1); - return; + try std.io.getStdOut().writeAll(build_options.version ++ "\n"); + } else if (mem.eql(u8, cmd, "info")) { + try @import("print_info.zig").cmdInfo(arena, cmd_args, .SelfHosted, io.getStdOut().outStream()); } else if (mem.eql(u8, cmd, "zen")) { try io.getStdOut().writeAll(info_zen); } else if (mem.eql(u8, cmd, "help")) { diff --git a/src-self-hosted/print_info.zig b/src-self-hosted/print_info.zig new file mode 100644 index 000000000..dadf7efbf --- /dev/null +++ b/src-self-hosted/print_info.zig @@ -0,0 +1,192 @@ +const builtin = @import("builtin"); +const std = @import("std"); +const process = std.process; +const mem = std.mem; +const unicode = std.unicode; +const io = std.io; +const fs = std.fs; +const os = std.os; +const json = std.json; +const StringifyOptions = json.StringifyOptions; +const Allocator = std.mem.Allocator; +const introspect = @import("introspect.zig"); + +const usage_info = + \\Usage: zig info [options] + \\ + \\ Outputs path to zig lib dir, std dir and the global cache dir. + \\ + \\Options: + \\ --help Print this help and exit + \\ --format [text|json] Choose output format (defaults to text) + \\ +; + +pub const CompilerInfo = struct { + // TODO: port compiler id hash from cpp + // /// Compiler id hash + // id: []const u8, + + // /// Compiler version + // version: []const u8, + /// Path to lib/ + lib_dir: []const u8, + + /// Path to lib/zig/std + std_dir: []const u8, + + /// Path to the global cache dir + global_cache_dir: []const u8, + + const CompilerType = enum { + Stage1, + SelfHosted, + }; + + pub fn getVersionString() []const u8 { + // TODO: get this from build.zig somehow + return "0.6.0"; + } + + pub fn getCacheDir(allocator: *Allocator, compiler_type: CompilerType) ![]u8 { + const global_cache_dir = try getAppCacheDir(allocator, "zig"); + defer allocator.free(global_cache_dir); + + const postfix = switch (compiler_type) { + .SelfHosted => "self_hosted", + .Stage1 => "stage1", + }; + return try fs.path.join(allocator, &[_][]const u8{ global_cache_dir, postfix }); // stage1 compiler uses $cache_dir/zig/stage1 + } + + // TODO: add CacheType argument here to make it return correct cache dir for stage1 + pub fn init(allocator: *Allocator, compiler_type: CompilerType) !CompilerInfo { + const zig_lib_dir = try introspect.resolveZigLibDir(allocator); + errdefer allocator.free(zig_lib_dir); + + const zig_std_dir = try fs.path.join(allocator, &[_][]const u8{ zig_lib_dir, "std" }); + errdefer allocator.free(zig_std_dir); + + const cache_dir = try CompilerInfo.getCacheDir(allocator, compiler_type); + errdefer allocator.free(cache_dir); + + return CompilerInfo{ + .lib_dir = zig_lib_dir, + .std_dir = zig_std_dir, + .global_cache_dir = cache_dir, + }; + } + + pub fn toString(self: *CompilerInfo, out_stream: var) !void { + inline for (@typeInfo(CompilerInfo).Struct.fields) |field| { + try std.fmt.format(out_stream, "{: <16}\t{: <}\n", .{ field.name, @field(self, field.name) }); + } + } + + pub fn deinit(self: *CompilerInfo, allocator: *Allocator) void { + allocator.free(self.lib_dir); + allocator.free(self.std_dir); + allocator.free(self.global_cache_dir); + } +}; + +pub fn cmdInfo(allocator: *Allocator, cmd_args: []const []const u8, compiler_type: CompilerInfo.CompilerType, stdout: var) !void { + var info = try CompilerInfo.init(allocator, compiler_type); + defer info.deinit(allocator); + + var bos = io.bufferedOutStream(stdout); + const bos_stream = bos.outStream(); + + var json_format = false; + + var i: usize = 0; + while (i < cmd_args.len) : (i += 1) { + const arg = cmd_args[i]; + if (mem.eql(u8, arg, "--format")) { + if (cmd_args.len <= i + 1) { + std.debug.warn("expected [text|json] after --format\n", .{}); + process.exit(1); + } + const format = cmd_args[i + 1]; + i += 1; + if (mem.eql(u8, format, "text")) { + json_format = false; + } else if (mem.eql(u8, format, "json")) { + json_format = true; + } else { + std.debug.warn("expected [text|json] after --format, found '{}'\n", .{format}); + process.exit(1); + } + } else if (mem.eql(u8, arg, "--help")) { + try stdout.writeAll(usage_info); + return; + } else { + std.debug.warn("unrecognized parameter: '{}'\n", .{arg}); + process.exit(1); + } + } + + if (json_format) { + try json.stringify(info, StringifyOptions{ + .whitespace = StringifyOptions.Whitespace{ .indent = .{ .Space = 2 } }, + }, bos_stream); + try bos_stream.writeByte('\n'); + } else { + try info.toString(bos_stream); + } + + try bos.flush(); +} + +pub const GetAppCacheDirError = error{ + OutOfMemory, + AppCacheDirUnavailable, +}; + +// Copied from fs.getAppDataDir, but changed it to return .cache/ dir on linux. +// This is the same behavior as the current zig compiler global cache resolution. +fn getAppCacheDir(allocator: *Allocator, appname: []const u8) GetAppCacheDirError![]u8 { + switch (builtin.os.tag) { + .windows => { + var dir_path_ptr: [*:0]u16 = undefined; + switch (os.windows.shell32.SHGetKnownFolderPath( + &os.windows.FOLDERID_LocalAppData, + os.windows.KF_FLAG_CREATE, + null, + &dir_path_ptr, + )) { + os.windows.S_OK => { + defer os.windows.ole32.CoTaskMemFree(@ptrCast(*c_void, dir_path_ptr)); + const global_dir = unicode.utf16leToUtf8Alloc(allocator, mem.spanZ(dir_path_ptr)) catch |err| switch (err) { + error.UnexpectedSecondSurrogateHalf => return error.AppCacheDirUnavailable, + error.ExpectedSecondSurrogateHalf => return error.AppCacheDirUnavailable, + error.DanglingSurrogateHalf => return error.AppCacheDirUnavailable, + error.OutOfMemory => return error.OutOfMemory, + }; + defer allocator.free(global_dir); + return fs.path.join(allocator, &[_][]const u8{ global_dir, appname }); + }, + os.windows.E_OUTOFMEMORY => return error.OutOfMemory, + else => return error.AppCacheDirUnavailable, + } + }, + .macosx => { + const home_dir = os.getenv("HOME") orelse { + // TODO look in /etc/passwd + return error.AppCacheDirUnavailable; + }; + return fs.path.join(allocator, &[_][]const u8{ home_dir, "Library", "Application Support", appname }); + }, + .linux, .freebsd, .netbsd, .dragonfly => { + if (os.getenv("XDG_CACHE_HOME")) |cache_home| { + return fs.path.join(allocator, &[_][]const u8{ cache_home, appname }); + } + + const home_dir = os.getenv("HOME") orelse { + return error.AppCacheDirUnavailable; + }; + return fs.path.join(allocator, &[_][]const u8{ home_dir, ".cache", appname }); + }, + else => @compileError("Unsupported OS"), + } +} diff --git a/src-self-hosted/stage2.zig b/src-self-hosted/stage2.zig index d96f27a84..c73fc9790 100644 --- a/src-self-hosted/stage2.zig +++ b/src-self-hosted/stage2.zig @@ -179,8 +179,7 @@ export fn stage2_fmt(argc: c_int, argv: [*]const [*:0]const u8) c_int { return 0; } -fn fmtMain(argc: c_int, argv: [*]const [*:0]const u8) !void { - const allocator = std.heap.c_allocator; +fn argvToArrayList(allocator: *Allocator, argc: c_int, argv: [*]const [*:0]const u8) !ArrayList([]const u8) { var args_list = std.ArrayList([]const u8).init(allocator); const argc_usize = @intCast(usize, argc); var arg_i: usize = 0; @@ -188,8 +187,16 @@ fn fmtMain(argc: c_int, argv: [*]const [*:0]const u8) !void { try args_list.append(mem.spanZ(argv[arg_i])); } - const args = args_list.span()[2..]; + return args_list; +} +fn fmtMain(argc: c_int, argv: [*]const [*:0]const u8) !void { + const allocator = std.heap.c_allocator; + + var args_list = try argvToArrayList(allocator, argc, argv); + defer args_list.deinit(); + + const args = args_list.span()[2..]; return self_hosted_main.cmdFmt(allocator, args); } @@ -387,6 +394,25 @@ fn detectNativeCpuWithLLVM( return result; } +export fn stage2_info(argc: c_int, argv: [*]const [*:0]const u8) c_int { + const allocator = std.heap.c_allocator; + + var args_list = argvToArrayList(allocator, argc, argv) catch |err| { + std.debug.warn("unable to parse arguments: {}\n", .{@errorName(err)}); + return -1; + }; + defer args_list.deinit(); + + const args = args_list.span()[2..]; + + @import("print_info.zig").cmdInfo(allocator, args, .Stage1, std.io.getStdOut().outStream()) catch |err| { + std.debug.warn("unable to print info: {}\n", .{@errorName(err)}); + return -1; + }; + + return 0; +} + // ABI warning export fn stage2_cmd_targets( zig_triple: ?[*:0]const u8, diff --git a/src/main.cpp b/src/main.cpp index 6d5f1f3f7..23c5bcd02 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -47,6 +47,7 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) { " translate-c [source] convert c code to zig code\n" " targets list available compilation targets\n" " test [source] create and run a test build\n" + " info print lib path, std path, compiler id and version\n" " version print version number and exit\n" " zen print zen of zig and exit\n" "\n" @@ -582,6 +583,8 @@ static int main0(int argc, char **argv) { return (term.how == TerminationIdClean) ? term.code : -1; } else if (argc >= 2 && strcmp(argv[1], "fmt") == 0) { return stage2_fmt(argc, argv); + } else if (argc >= 2 && strcmp(argv[1], "info") == 0) { + return stage2_info(argc, argv); } else if (argc >= 2 && (strcmp(argv[1], "cc") == 0 || strcmp(argv[1], "c++") == 0)) { emit_h = false; strip = true; diff --git a/src/stage2.cpp b/src/stage2.cpp index 887ad461b..ef2b2348e 100644 --- a/src/stage2.cpp +++ b/src/stage2.cpp @@ -27,6 +27,11 @@ void stage2_zen(const char **ptr, size_t *len) { stage2_panic(msg, strlen(msg)); } +int stage2_info(int argc, char** argv) { + const char *msg = "stage0 called stage2_info"; + stage2_panic(msg, strlen(msg)); +} + void stage2_attach_segfault_handler(void) { } void stage2_panic(const char *ptr, size_t len) { diff --git a/src/stage2.h b/src/stage2.h index 7875ef26c..9ec67ea3f 100644 --- a/src/stage2.h +++ b/src/stage2.h @@ -141,6 +141,9 @@ ZIG_EXTERN_C void stage2_render_ast(struct Stage2Ast *ast, FILE *output_file); // ABI warning ZIG_EXTERN_C void stage2_zen(const char **ptr, size_t *len); +// ABI warning +ZIG_EXTERN_C int stage2_info(int argc, char **argv); + // ABI warning ZIG_EXTERN_C void stage2_attach_segfault_handler(void);