add `zig info` command

master
Sergey Poznyak 2020-05-20 07:04:22 +03:00 committed by Andrew Kelley
parent 044e3ca592
commit 80e70735fb
6 changed files with 236 additions and 5 deletions

View File

@ -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")) {

View File

@ -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"),
}
}

View File

@ -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,

View File

@ -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;

View File

@ -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) {

View File

@ -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);