mirror of
https://codeberg.org/ziglang/zig.git
synced 2026-04-26 13:01:34 +03:00
std: find a better home for the "preopens" concept
This commit is contained in:
@@ -1,10 +1,8 @@
|
||||
const std = @import("std");
|
||||
|
||||
pub fn main(init: std.process.Init) !void {
|
||||
const preopens = try std.fs.wasi.preopensAlloc(init.arena.allocator());
|
||||
|
||||
for (preopens.names, 0..) |preopen, i| {
|
||||
std.debug.print("{d}: {s}\n", .{ i, preopen });
|
||||
pub fn main(init: std.process.Init) void {
|
||||
for (init.preopens.map.keys(), 0..) |preopen, i| {
|
||||
std.log.info("{d}: {s}", .{ i, preopen });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ const std = @import("std.zig");
|
||||
|
||||
/// Deprecated, use `std.Io.Dir.path`.
|
||||
pub const path = @import("fs/path.zig");
|
||||
pub const wasi = @import("fs/wasi.zig");
|
||||
|
||||
pub const base64_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".*;
|
||||
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const mem = std.mem;
|
||||
const math = std.math;
|
||||
const fs = std.fs;
|
||||
const assert = std.debug.assert;
|
||||
const Allocator = mem.Allocator;
|
||||
const wasi = std.os.wasi;
|
||||
const fd_t = wasi.fd_t;
|
||||
const prestat_t = wasi.prestat_t;
|
||||
|
||||
pub const Preopens = struct {
|
||||
// Indexed by file descriptor number.
|
||||
names: []const []const u8,
|
||||
|
||||
pub fn find(p: Preopens, name: []const u8) ?std.posix.fd_t {
|
||||
for (p.names, 0..) |elem_name, i| {
|
||||
if (mem.eql(u8, elem_name, name)) {
|
||||
return @intCast(i);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
pub fn preopensAlloc(gpa: Allocator) Allocator.Error!Preopens {
|
||||
var names: std.ArrayList([]const u8) = .empty;
|
||||
defer names.deinit(gpa);
|
||||
|
||||
try names.ensureUnusedCapacity(gpa, 3);
|
||||
|
||||
names.appendAssumeCapacity("stdin"); // 0
|
||||
names.appendAssumeCapacity("stdout"); // 1
|
||||
names.appendAssumeCapacity("stderr"); // 2
|
||||
while (true) {
|
||||
const fd = @as(wasi.fd_t, @intCast(names.items.len));
|
||||
var prestat: prestat_t = undefined;
|
||||
switch (wasi.fd_prestat_get(fd, &prestat)) {
|
||||
.SUCCESS => {},
|
||||
.OPNOTSUPP, .BADF => return .{ .names = try names.toOwnedSlice(gpa) },
|
||||
else => @panic("fd_prestat_get: unexpected error"),
|
||||
}
|
||||
try names.ensureUnusedCapacity(gpa, 1);
|
||||
// This length does not include a null byte. Let's keep it this way to
|
||||
// gently encourage WASI implementations to behave properly.
|
||||
const name_len = prestat.u.dir.pr_name_len;
|
||||
const name = try gpa.alloc(u8, name_len);
|
||||
errdefer gpa.free(name);
|
||||
switch (wasi.fd_prestat_dir_name(fd, name.ptr, name.len)) {
|
||||
.SUCCESS => {},
|
||||
else => @panic("fd_prestat_dir_name: unexpected error"),
|
||||
}
|
||||
names.appendAssumeCapacity(name);
|
||||
}
|
||||
}
|
||||
+3
-2
@@ -288,8 +288,9 @@ pub const oflags_t = packed struct(u16) {
|
||||
_: u12 = 0,
|
||||
};
|
||||
|
||||
pub const preopentype_t = u8;
|
||||
pub const PREOPENTYPE_DIR: preopentype_t = 0;
|
||||
pub const preopentype_t = enum(u8) {
|
||||
DIR = 0,
|
||||
};
|
||||
|
||||
pub const prestat_t = extern struct {
|
||||
pr_type: preopentype_t,
|
||||
|
||||
@@ -18,6 +18,7 @@ const max_path_bytes = std.fs.max_path_bytes;
|
||||
pub const Child = @import("process/Child.zig");
|
||||
pub const Args = @import("process/Args.zig");
|
||||
pub const Environ = @import("process/Environ.zig");
|
||||
pub const Preopens = @import("process/Preopens.zig");
|
||||
|
||||
/// This is the global, process-wide protection to coordinate stderr writes.
|
||||
///
|
||||
@@ -48,6 +49,10 @@ pub const Init = struct {
|
||||
io: Io,
|
||||
/// Environment variables, initialized with `gpa`. Not threadsafe.
|
||||
environ_map: *Environ.Map,
|
||||
/// Named files that have been provided by the parent process. This is
|
||||
/// mainly useful on WASI, but can be used on other systems to mimic the
|
||||
/// behavior with respect to stdio.
|
||||
preopens: Preopens,
|
||||
|
||||
/// Alternative to `Init` as the first parameter of the main function.
|
||||
pub const Minimal = struct {
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
const Preopens = @This();
|
||||
|
||||
const builtin = @import("builtin");
|
||||
const native_os = builtin.os.tag;
|
||||
|
||||
const std = @import("../std.zig");
|
||||
const Io = std.Io;
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
map: Map,
|
||||
|
||||
pub const empty: Preopens = switch (native_os) {
|
||||
.wasi => .{ .map = .empty },
|
||||
else => .{ .map = {} },
|
||||
};
|
||||
|
||||
pub const Map = switch (native_os) {
|
||||
// Indexed by file descriptor number.
|
||||
.wasi => std.StringArrayHashMapUnmanaged(void),
|
||||
else => void,
|
||||
};
|
||||
|
||||
pub const Resource = union(enum) {
|
||||
file: Io.File,
|
||||
dir: Io.Dir,
|
||||
};
|
||||
|
||||
pub fn get(p: *const Preopens, name: []const u8) ?Resource {
|
||||
switch (native_os) {
|
||||
.wasi => {
|
||||
const index = p.map.getIndex(name) orelse return null;
|
||||
if (index <= 2) return .{ .file = .{ .handle = @intCast(index) } };
|
||||
return .{ .dir = .{ .handle = @intCast(index) } };
|
||||
},
|
||||
else => {
|
||||
if (std.mem.eql(u8, name, "stdin")) return .{ .file = .stdin() };
|
||||
if (std.mem.eql(u8, name, "stdout")) return .{ .file = .stdout() };
|
||||
if (std.mem.eql(u8, name, "stderr")) return .{ .file = .stderr() };
|
||||
return null;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub const InitError = Allocator.Error || error{Unexpected};
|
||||
|
||||
pub fn init(arena: Allocator) InitError!Preopens {
|
||||
if (native_os != .wasi) return .{ .map = {} };
|
||||
const wasi = std.os.wasi;
|
||||
var map: Map = .empty;
|
||||
|
||||
try map.ensureUnusedCapacity(arena, 3);
|
||||
|
||||
map.putAssumeCapacityNoClobber("stdin", {}); // 0
|
||||
map.putAssumeCapacityNoClobber("stdout", {}); // 1
|
||||
map.putAssumeCapacityNoClobber("stderr", {}); // 2
|
||||
while (true) {
|
||||
const fd: wasi.fd_t = @intCast(map.entries.len);
|
||||
var prestat: wasi.prestat_t = undefined;
|
||||
switch (wasi.fd_prestat_get(fd, &prestat)) {
|
||||
.SUCCESS => {},
|
||||
.OPNOTSUPP, .BADF => return .{ .map = map },
|
||||
else => return error.Unexpected,
|
||||
}
|
||||
try map.ensureUnusedCapacity(arena, 1);
|
||||
// This length does not include a null byte. Let's keep it this way to
|
||||
// gently encourage WASI implementations to behave properly.
|
||||
const name_len = prestat.u.dir.pr_name_len;
|
||||
const name = try arena.alloc(u8, name_len);
|
||||
switch (wasi.fd_prestat_dir_name(fd, name.ptr, name.len)) {
|
||||
.SUCCESS => {},
|
||||
else => return error.Unexpected,
|
||||
}
|
||||
map.putAssumeCapacityNoClobber(name, {});
|
||||
}
|
||||
}
|
||||
@@ -708,6 +708,9 @@ inline fn callMain(args: std.process.Args.Vector, environ: std.process.Environ.B
|
||||
std.process.fatal("failed to parse environment variables: {t}", .{err});
|
||||
defer environ_map.deinit();
|
||||
|
||||
const preopens = std.process.Preopens.init(arena_allocator.allocator()) catch |err|
|
||||
std.process.fatal("failed to init preopens: {t}", .{err});
|
||||
|
||||
return wrapMain(root.main(.{
|
||||
.minimal = .{
|
||||
.args = .{ .vector = args },
|
||||
@@ -717,6 +720,7 @@ inline fn callMain(args: std.process.Args.Vector, environ: std.process.Environ.B
|
||||
.gpa = gpa,
|
||||
.io = threaded.io(),
|
||||
.environ_map = &environ_map,
|
||||
.preopens = preopens,
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
+7
-9
@@ -758,10 +758,7 @@ pub const Directories = struct {
|
||||
search,
|
||||
global,
|
||||
},
|
||||
wasi_preopens: switch (builtin.target.os.tag) {
|
||||
.wasi => fs.wasi.Preopens,
|
||||
else => void,
|
||||
},
|
||||
preopens: std.process.Preopens,
|
||||
self_exe_path: switch (builtin.target.os.tag) {
|
||||
.wasi => void,
|
||||
else => []const u8,
|
||||
@@ -776,7 +773,7 @@ pub const Directories = struct {
|
||||
|
||||
const zig_lib: Cache.Directory = d: {
|
||||
if (override_zig_lib) |path| break :d openUnresolved(arena, io, cwd, path, .@"zig lib");
|
||||
if (wasi) break :d openWasiPreopen(wasi_preopens, "/lib");
|
||||
if (wasi) break :d getPreopen(preopens, "/lib");
|
||||
break :d introspect.findZigLibDirFromSelfExe(arena, io, cwd, self_exe_path) catch |err| {
|
||||
fatal("unable to find zig installation directory '{s}': {t}", .{ self_exe_path, err });
|
||||
};
|
||||
@@ -784,7 +781,7 @@ pub const Directories = struct {
|
||||
|
||||
const global_cache: Cache.Directory = d: {
|
||||
if (override_global_cache) |path| break :d openUnresolved(arena, io, cwd, path, .@"global cache");
|
||||
if (wasi) break :d openWasiPreopen(wasi_preopens, "/cache");
|
||||
if (wasi) break :d getPreopen(preopens, "/cache");
|
||||
const path = introspect.resolveGlobalCacheDir(arena, environ_map) catch |err| {
|
||||
fatal("unable to resolve zig cache directory: {t}", .{err});
|
||||
};
|
||||
@@ -817,11 +814,12 @@ pub const Directories = struct {
|
||||
.local_cache = local_cache,
|
||||
};
|
||||
}
|
||||
fn openWasiPreopen(preopens: fs.wasi.Preopens, name: []const u8) Cache.Directory {
|
||||
fn getPreopen(preopens: std.process.Preopens, name: []const u8) Cache.Directory {
|
||||
return .{
|
||||
.path = if (std.mem.eql(u8, name, ".")) null else name,
|
||||
.handle = .{
|
||||
.handle = preopens.find(name) orelse fatal("WASI preopen not found: '{s}'", .{name}),
|
||||
.handle = switch (preopens.get(name) orelse fatal("preopen not found: '{s}'", .{name})) {
|
||||
.file => fatal("preopen {s} is not a directory", .{name}),
|
||||
.dir => |d| d,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
+7
-7
@@ -55,11 +55,11 @@ pub const std_options_cwd = if (native_os == .wasi) wasi_cwd else null;
|
||||
pub const panic = crash_report.panic;
|
||||
pub const debug = crash_report.debug;
|
||||
|
||||
var wasi_preopens: fs.wasi.Preopens = undefined;
|
||||
var preopens: std.process.Preopens = .empty;
|
||||
pub fn wasi_cwd() Io.Dir {
|
||||
// Expect the first preopen to be current working directory.
|
||||
const cwd_fd: std.posix.fd_t = 3;
|
||||
assert(mem.eql(u8, wasi_preopens.names[cwd_fd], "."));
|
||||
assert(mem.eql(u8, preopens.map.keys()[cwd_fd], "."));
|
||||
return .{ .handle = cwd_fd };
|
||||
}
|
||||
|
||||
@@ -210,7 +210,7 @@ pub fn main(init: std.process.Init.Minimal) anyerror!void {
|
||||
}
|
||||
|
||||
if (native_os == .wasi) {
|
||||
wasi_preopens = try fs.wasi.preopensAlloc(arena);
|
||||
preopens = try .init(arena);
|
||||
}
|
||||
|
||||
return mainArgs(gpa, arena, io, args, &environ_map);
|
||||
@@ -360,7 +360,7 @@ fn mainArgs(
|
||||
io,
|
||||
&stdout_writer.interface,
|
||||
args,
|
||||
if (native_os == .wasi) wasi_preopens,
|
||||
preopens,
|
||||
&host,
|
||||
environ_map,
|
||||
);
|
||||
@@ -3107,7 +3107,7 @@ fn buildOutputType(
|
||||
else => .search,
|
||||
};
|
||||
},
|
||||
if (native_os == .wasi) wasi_preopens,
|
||||
preopens,
|
||||
self_exe_path,
|
||||
environ_map,
|
||||
);
|
||||
@@ -5141,7 +5141,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,
|
||||
if (override_local_cache_dir) |d| break :path d;
|
||||
break :path try build_root.directory.join(arena, &.{introspect.default_local_zig_cache_basename});
|
||||
} },
|
||||
{},
|
||||
.empty,
|
||||
self_exe_path,
|
||||
environ_map,
|
||||
);
|
||||
@@ -5556,7 +5556,7 @@ fn jitCmd(
|
||||
override_lib_dir,
|
||||
override_global_cache_dir,
|
||||
.global,
|
||||
if (native_os == .wasi) wasi_preopens,
|
||||
preopens,
|
||||
self_exe_path,
|
||||
environ_map,
|
||||
);
|
||||
|
||||
+2
-5
@@ -14,10 +14,7 @@ pub fn cmdEnv(
|
||||
io: Io,
|
||||
out: *std.Io.Writer,
|
||||
args: []const []const u8,
|
||||
wasi_preopens: switch (builtin.target.os.tag) {
|
||||
.wasi => std.fs.wasi.Preopens,
|
||||
else => void,
|
||||
},
|
||||
preopens: std.process.Preopens,
|
||||
host: *const std.Target,
|
||||
environ_map: *std.process.Environ.Map,
|
||||
) !void {
|
||||
@@ -37,7 +34,7 @@ pub fn cmdEnv(
|
||||
override_lib_dir,
|
||||
override_global_cache_dir,
|
||||
.global,
|
||||
if (builtin.target.os.tag == .wasi) wasi_preopens,
|
||||
preopens,
|
||||
if (builtin.target.os.tag != .wasi) self_exe_path,
|
||||
environ_map,
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user