| author | |
| committer | |
| log | b03345f32a5ba2849ddbeeae0e31e0e77ca01b01 |
| tree | 4100326d0b400a38525d980739214feaf48508b8 |
| parent | 5195b87639a1dc56b90751d0aecc4fcf4f2a1bb0 |
| parent | 3a63fa6b7f56a2f384ebd460e80c00e6bbd2efee |
| signature |
stage2: Add limited WASI support for selfExePath and globalCacheDir4 files changed, 80 insertions(+), 8 deletions(-)
lib/std/fs.zig+47| ... | @@ -2547,6 +2547,15 @@ pub const SelfExePathError = os.ReadLinkError || os.SysCtlError || os.RealPathEr | ... | @@ -2547,6 +2547,15 @@ pub const SelfExePathError = os.ReadLinkError || os.SysCtlError || os.RealPathEr |
| 2547 | /// `selfExePath` except allocates the result on the heap. | 2547 | /// `selfExePath` except allocates the result on the heap. |
| 2548 | /// Caller owns returned memory. | 2548 | /// Caller owns returned memory. |
| 2549 | pub fn selfExePathAlloc(allocator: Allocator) ![]u8 { | 2549 | pub 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 | } | ||
| 2550 | // Use of MAX_PATH_BYTES here is justified as, at least on one tested Linux | 2559 | // Use of MAX_PATH_BYTES here is justified as, at least on one tested Linux |
| 2551 | // system, readlink will completely fail to return a result larger than | 2560 | // system, readlink will completely fail to return a result larger than |
| 2552 | // PATH_MAX even if given a sufficiently large buffer. This makes it | 2561 | // PATH_MAX even if given a sufficiently large buffer. This makes it |
| ... | @@ -2643,10 +2652,48 @@ pub fn selfExePath(out_buffer: []u8) SelfExePathError![]u8 { | ... | @@ -2643,10 +2652,48 @@ pub fn selfExePath(out_buffer: []u8) SelfExePathError![]u8 { |
| 2643 | const end_index = std.unicode.utf16leToUtf8(out_buffer, utf16le_slice) catch unreachable; | 2652 | const end_index = std.unicode.utf16leToUtf8(out_buffer, utf16le_slice) catch unreachable; |
| 2644 | return out_buffer[0..end_index]; | 2653 | return out_buffer[0..end_index]; |
| 2645 | }, | 2654 | }, |
| 2655 | .wasi => @compileError("std.fs.selfExePath not supported for WASI. Use std.fs.selfExePathAlloc instead."), | ||
| 2646 | else => @compileError("std.fs.selfExePath not supported for this target"), | 2656 | else => @compileError("std.fs.selfExePath not supported for this target"), |
| 2647 | } | 2657 | } |
| 2648 | } | 2658 | } |
| 2649 | 2659 | ||
| 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/` | ||
| 2666 | fn selfExePathWasi(out_buffer: []u8, argv0: []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 | const exe_names_to_check = &[_][]const u8{ path.basename(argv0), "zig.wasm" }; | ||
| 2675 | |||
| 2676 | for (base_paths_to_check) |base_path| { | ||
| 2677 | for (exe_names_to_check) |exe_name| { | ||
| 2678 | const test_path = path.join(alloc, &.{ base_path, exe_name }) catch continue; | ||
| 2679 | |||
| 2680 | // Make sure it's a file we're pointing to | ||
| 2681 | const file = os.fstatat(os.wasi.AT.FDCWD, test_path, 0) catch continue; | ||
| 2682 | if (file.filetype != .REGULAR_FILE) continue; | ||
| 2683 | |||
| 2684 | // Path seems to be valid, let's try to turn it into an absolute path | ||
| 2685 | var real_path_buf: [MAX_PATH_BYTES]u8 = undefined; | ||
| 2686 | if (os.realpath(test_path, &real_path_buf)) |real_path| { | ||
| 2687 | if (real_path.len > out_buffer.len) | ||
| 2688 | return error.NameTooLong; | ||
| 2689 | mem.copy(u8, out_buffer, real_path); | ||
| 2690 | return out_buffer[0..real_path.len]; | ||
| 2691 | } else |_| continue; | ||
| 2692 | } | ||
| 2693 | } | ||
| 2694 | return error.FileNotFound; | ||
| 2695 | } | ||
| 2696 | |||
| 2650 | /// The result is UTF16LE-encoded. | 2697 | /// The result is UTF16LE-encoded. |
| 2651 | pub fn selfExePathW() [:0]const u16 { | 2698 | pub fn selfExePathW() [:0]const u16 { |
| 2652 | const image_path_name = &os.windows.peb().ProcessParameters.ImagePathName; | 2699 | const image_path_name = &os.windows.peb().ProcessParameters.ImagePathName; |
src/Cache.zig+11-7| ... | @@ -762,18 +762,22 @@ pub const Manifest = struct { | ... | @@ -762,18 +762,22 @@ pub const Manifest = struct { |
| 762 | 762 | ||
| 763 | fn downgradeToSharedLock(self: *Manifest) !void { | 763 | fn downgradeToSharedLock(self: *Manifest) !void { |
| 764 | if (!self.have_exclusive_lock) return; | 764 | if (!self.have_exclusive_lock) return; |
| 765 | const manifest_file = self.manifest_file.?; | 765 | if (std.process.can_spawn or !builtin.single_threaded) { // Some targets (WASI) do not support flock |
| 766 | try manifest_file.downgradeLock(); | 766 | const manifest_file = self.manifest_file.?; |
| 767 | try manifest_file.downgradeLock(); | ||
| 768 | } | ||
| 767 | self.have_exclusive_lock = false; | 769 | self.have_exclusive_lock = false; |
| 768 | } | 770 | } |
| 769 | 771 | ||
| 770 | fn upgradeToExclusiveLock(self: *Manifest) !void { | 772 | fn upgradeToExclusiveLock(self: *Manifest) !void { |
| 771 | if (self.have_exclusive_lock) return; | 773 | if (self.have_exclusive_lock) return; |
| 772 | const manifest_file = self.manifest_file.?; | 774 | if (std.process.can_spawn or !builtin.single_threaded) { // Some targets (WASI) do not support flock |
| 773 | // Here we intentionally have a period where the lock is released, in case there are | 775 | const manifest_file = self.manifest_file.?; |
| 774 | // other processes holding a shared lock. | 776 | // Here we intentionally have a period where the lock is released, in case there are |
| 775 | manifest_file.unlock(); | 777 | // other processes holding a shared lock. |
| 776 | try manifest_file.lock(.Exclusive); | 778 | manifest_file.unlock(); |
| 779 | try manifest_file.lock(.Exclusive); | ||
| 780 | } | ||
| 777 | self.have_exclusive_lock = true; | 781 | self.have_exclusive_lock = true; |
| 778 | } | 782 | } |
| 779 | 783 |
src/introspect.zig+12-1| ... | @@ -1,6 +1,7 @@ | ... | @@ -1,6 +1,7 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const mem = std.mem; | 3 | const mem = std.mem; |
| 4 | const os = std.os; | ||
| 4 | const fs = std.fs; | 5 | const fs = std.fs; |
| 5 | const Compilation = @import("Compilation.zig"); | 6 | const Compilation = @import("Compilation.zig"); |
| 6 | 7 | ||
| ... | @@ -80,5 +81,15 @@ pub fn resolveGlobalCacheDir(allocator: mem.Allocator) ![]u8 { | ... | @@ -80,5 +81,15 @@ pub fn resolveGlobalCacheDir(allocator: mem.Allocator) ![]u8 { |
| 80 | } | 81 | } |
| 81 | } | 82 | } |
| 82 | 83 | ||
| 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 | } | ||
| 84 | } | 95 | } |
src/main.zig+10| ... | @@ -162,6 +162,16 @@ pub fn main() anyerror!void { | ... | @@ -162,6 +162,16 @@ pub fn main() anyerror!void { |
| 162 | return mainArgs(gpa_tracy.allocator(), arena, args); | 162 | return mainArgs(gpa_tracy.allocator(), arena, args); |
| 163 | } | 163 | } |
| 164 | 164 | ||
| 165 | // WASI: `--dir` instructs the WASM runtime to "preopen" a directory, making | ||
| 166 | // it available to the us, the guest program. This is the only way for us to | ||
| 167 | // access files/dirs on the host filesystem | ||
| 168 | if (builtin.os.tag == .wasi) { | ||
| 169 | // This sets our CWD to "/preopens/cwd" | ||
| 170 | // Dot-prefixed preopens like `--dir=.` are "mounted" at "/preopens/cwd" | ||
| 171 | // Other preopens like `--dir=lib` are "mounted" at "/" | ||
| 172 | try std.os.initPreopensWasi(std.heap.page_allocator, "/preopens/cwd"); | ||
| 173 | } | ||
| 174 | |||
| 165 | return mainArgs(gpa, arena, args); | 175 | return mainArgs(gpa, arena, args); |
| 166 | } | 176 | } |
| 167 | 177 |