Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

zig info command - print lib path, std path and global cache path #5394

Closed
wants to merge 18 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src-self-hosted/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,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
\\
Expand Down Expand Up @@ -70,6 +71,8 @@ pub fn main() !void {
// Need to set up the build script to give the version as a comptime value.
std.debug.warn("TODO version command not implemented yet\n", .{});
return error.Unimplemented;
} 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")) {
Expand Down
192 changes: 192 additions & 0 deletions src-self-hosted/print_info.zig
Original file line number Diff line number Diff line change
@@ -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"),
}
}
32 changes: 29 additions & 3 deletions src-self-hosted/stage2.zig
Original file line number Diff line number Diff line change
Expand Up @@ -179,17 +179,24 @@ 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;
while (arg_i < argc_usize) : (arg_i += 1) {
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);
}

Expand Down Expand Up @@ -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,
Expand Down
3 changes: 3 additions & 0 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -581,6 +582,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;
Expand Down
5 changes: 5 additions & 0 deletions src/stage2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
3 changes: 3 additions & 0 deletions src/stage2.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down