authorgravatar for topolarity@tapscott.meCody Tapscott <topolarity@tapscott.me> 2022-03-01 10:41:38-07:00
committergravatar for topolarity@tapscott.meCody Tapscott <topolarity@tapscott.me> 2022-04-18 10:19:34-07:00
log922d8378e724063063e709223e90e3a577e4566a
tree5f9dc11661b8a954c41e59ad9a286bda899c6f3c
parentf8d2b87fa122a948e2c8e1056e2ec4cfd4cf01bf

stage2: Add limited WASI support for selfExePath and globalCacheDir

This change adds support for locating the Zig executable and the library and global cache directories, based on looking in the fixed "/zig" and "/cache" directories. Since our argv[0] on WASI is just the basename (any absolute/relative path information is deleted by the runtime), there's very limited introspection we can do on WASI, so we rely on these fixed directories. These can be provided on the command-line using `--mapdir`, as follows: ``` wasmtime --mapdir=/cwd::. --mapdir=/cache::"$HOME/.cache/zig" --mapdir=/zig::./zig-out/ ./zig-out/bin/zig.wasm ```

2 files changed, 56 insertions(+), 1 deletions(-)

lib/std/fs.zig+44
......@@ -2547,6 +2547,15 @@ pub const SelfExePathError = os.ReadLinkError || os.SysCtlError || os.RealPathEr
25472547/// `selfExePath` except allocates the result on the heap.
25482548/// Caller owns returned memory.
25492549pub fn selfExePathAlloc(allocator: Allocator) ![]u8 {
2550 if (builtin.os.tag == .wasi) {
2551 var args = try std.process.argsWithAllocator(allocator);
2552 defer args.deinit();
2553 // On WASI, argv[0] is always just the basename of the current executable
2554 const exe_name = args.next() orelse return error.FileNotFound;
2555
2556 var buf: [MAX_PATH_BYTES]u8 = undefined;
2557 return allocator.dupe(u8, try selfExePathWasi(&buf, exe_name));
2558 }
25502559 // Use of MAX_PATH_BYTES here is justified as, at least on one tested Linux
25512560 // system, readlink will completely fail to return a result larger than
25522561 // PATH_MAX even if given a sufficiently large buffer. This makes it
......@@ -2643,10 +2652,45 @@ pub fn selfExePath(out_buffer: []u8) SelfExePathError![]u8 {
26432652 const end_index = std.unicode.utf16leToUtf8(out_buffer, utf16le_slice) catch unreachable;
26442653 return out_buffer[0..end_index];
26452654 },
2655 .wasi => @compileError("std.fs.selfExePath not supported for WASI. Use std.fs.selfExePathAlloc instead."),
26462656 else => @compileError("std.fs.selfExePath not supported for this target"),
26472657 }
26482658}
26492659
2660/// WASI-specific implementation of selfExePath
2661///
2662/// On WASI argv0 is always just the executable basename, so this function relies
2663/// using a fixed executable directory path: "/zig"
2664///
2665/// This path can be configured in wasmtime using `--mapdir=/zig::/path/to/zig/dir/`
2666fn selfExePathWasi(out_buffer: []u8, exe_name: []const u8) SelfExePathError![]const u8 {
2667 var allocator = std.heap.FixedBufferAllocator.init(out_buffer);
2668 var alloc = allocator.allocator();
2669
2670 // Check these paths:
2671 // 1. "/zig/{exe_name}"
2672 // 2. "/zig/bin/{exe_name}"
2673 const base_paths_to_check = &[_][]const u8{ "/zig", "/zig/bin" };
2674
2675 for (base_paths_to_check) |base_path| {
2676 const test_path = path.join(alloc, &.{ base_path, path.basename(exe_name) }) catch continue;
2677
2678 // Make sure it's a file we're pointing to
2679 const file = os.fstatat(os.wasi.AT.FDCWD, test_path, 0) catch continue;
2680 if (file.filetype != .REGULAR_FILE) continue;
2681
2682 // Path seems to be valid, let's try to turn it into an absolute path
2683 var real_path_buf: [MAX_PATH_BYTES]u8 = undefined;
2684 if (os.realpath(test_path, &real_path_buf)) |real_path| {
2685 if (real_path.len > out_buffer.len)
2686 return error.NameTooLong;
2687 mem.copy(u8, out_buffer, real_path);
2688 return out_buffer[0..real_path.len];
2689 } else |_| continue;
2690 }
2691 return error.FileNotFound;
2692}
2693
26502694/// The result is UTF16LE-encoded.
26512695pub fn selfExePathW() [:0]const u16 {
26522696 const image_path_name = &os.windows.peb().ProcessParameters.ImagePathName;
src/introspect.zig+12-1
......@@ -1,6 +1,7 @@
11const std = @import("std");
22const builtin = @import("builtin");
33const mem = std.mem;
4const os = std.os;
45const fs = std.fs;
56const Compilation = @import("Compilation.zig");
67
......@@ -80,5 +81,15 @@ pub fn resolveGlobalCacheDir(allocator: mem.Allocator) ![]u8 {
8081 }
8182 }
8283
83 return fs.getAppDataDir(allocator, appname);
84 if (builtin.os.tag == .wasi) {
85 // On WASI, we have no way to get an App data dir, so we try to use a fixed
86 // Preopen path "/cache" as a last resort
87 const path = "/cache";
88
89 const file = os.fstatat(os.wasi.AT.FDCWD, path, 0) catch return error.CacheDirUnavailable;
90 if (file.filetype != .DIRECTORY) return error.CacheDirUnavailable;
91 return allocator.dupe(u8, path);
92 } else {
93 return fs.getAppDataDir(allocator, appname);
94 }
8495}