mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 05:44:20 +00:00
std: all Dir functions moved to std.Io
This commit is contained in:
parent
d113257d42
commit
48d70cfc38
24 changed files with 3064 additions and 3651 deletions
|
|
@ -437,8 +437,6 @@ set(ZIG_STAGE2_SOURCES
|
|||
lib/std/fmt.zig
|
||||
lib/std/fmt/parse_float.zig
|
||||
lib/std/fs.zig
|
||||
lib/std/fs/AtomicFile.zig
|
||||
lib/std/fs/Dir.zig
|
||||
lib/std/fs/File.zig
|
||||
lib/std/fs/get_app_data_dir.zig
|
||||
lib/std/fs/path.zig
|
||||
|
|
|
|||
35
build.zig
35
build.zig
|
|
@ -1,18 +1,20 @@
|
|||
const std = @import("std");
|
||||
const builtin = std.builtin;
|
||||
const tests = @import("test/tests.zig");
|
||||
const BufMap = std.BufMap;
|
||||
const mem = std.mem;
|
||||
const io = std.io;
|
||||
const fs = std.fs;
|
||||
const InstallDirectoryOptions = std.Build.InstallDirectoryOptions;
|
||||
const assert = std.debug.assert;
|
||||
const Io = std.Io;
|
||||
|
||||
const tests = @import("test/tests.zig");
|
||||
const DevEnv = @import("src/dev.zig").Env;
|
||||
const ValueInterpretMode = enum { direct, by_name };
|
||||
|
||||
const zig_version: std.SemanticVersion = .{ .major = 0, .minor = 16, .patch = 0 };
|
||||
const stack_size = 46 * 1024 * 1024;
|
||||
|
||||
const ValueInterpretMode = enum { direct, by_name };
|
||||
|
||||
pub fn build(b: *std.Build) !void {
|
||||
const only_c = b.option(bool, "only-c", "Translate the Zig compiler to C code, with only the C backend enabled") orelse false;
|
||||
const target = b.standardTargetOptions(.{
|
||||
|
|
@ -306,8 +308,10 @@ pub fn build(b: *std.Build) !void {
|
|||
|
||||
if (enable_llvm) {
|
||||
const cmake_cfg = if (static_llvm) null else blk: {
|
||||
const io = b.graph.io;
|
||||
const cwd: Io.Dir = .cwd();
|
||||
if (findConfigH(b, config_h_path_option)) |config_h_path| {
|
||||
const file_contents = fs.cwd().readFileAlloc(config_h_path, b.allocator, .limited(max_config_h_bytes)) catch unreachable;
|
||||
const file_contents = cwd.readFileAlloc(io, config_h_path, b.allocator, .limited(max_config_h_bytes)) catch unreachable;
|
||||
break :blk parseConfigH(b, file_contents);
|
||||
} else {
|
||||
std.log.warn("config.h could not be located automatically. Consider providing it explicitly via \"-Dconfig_h\"", .{});
|
||||
|
|
@ -1146,10 +1150,13 @@ const CMakeConfig = struct {
|
|||
const max_config_h_bytes = 1 * 1024 * 1024;
|
||||
|
||||
fn findConfigH(b: *std.Build, config_h_path_option: ?[]const u8) ?[]const u8 {
|
||||
const io = b.graph.io;
|
||||
const cwd: Io.Dir = .cwd();
|
||||
|
||||
if (config_h_path_option) |path| {
|
||||
var config_h_or_err = fs.cwd().openFile(path, .{});
|
||||
var config_h_or_err = cwd.openFile(io, path, .{});
|
||||
if (config_h_or_err) |*file| {
|
||||
file.close();
|
||||
file.close(io);
|
||||
return path;
|
||||
} else |_| {
|
||||
std.log.err("Could not open provided config.h: \"{s}\"", .{path});
|
||||
|
|
@ -1159,13 +1166,13 @@ fn findConfigH(b: *std.Build, config_h_path_option: ?[]const u8) ?[]const u8 {
|
|||
|
||||
var check_dir = fs.path.dirname(b.graph.zig_exe).?;
|
||||
while (true) {
|
||||
var dir = fs.cwd().openDir(check_dir, .{}) catch unreachable;
|
||||
defer dir.close();
|
||||
var dir = cwd.openDir(io, check_dir, .{}) catch unreachable;
|
||||
defer dir.close(io);
|
||||
|
||||
// Check if config.h is present in dir
|
||||
var config_h_or_err = dir.openFile("config.h", .{});
|
||||
var config_h_or_err = dir.openFile(io, "config.h", .{});
|
||||
if (config_h_or_err) |*file| {
|
||||
file.close();
|
||||
file.close(io);
|
||||
return fs.path.join(
|
||||
b.allocator,
|
||||
&[_][]const u8{ check_dir, "config.h" },
|
||||
|
|
@ -1176,9 +1183,9 @@ fn findConfigH(b: *std.Build, config_h_path_option: ?[]const u8) ?[]const u8 {
|
|||
}
|
||||
|
||||
// Check if we reached the source root by looking for .git, and bail if so
|
||||
var git_dir_or_err = dir.openDir(".git", .{});
|
||||
var git_dir_or_err = dir.openDir(io, ".git", .{});
|
||||
if (git_dir_or_err) |*git_dir| {
|
||||
git_dir.close();
|
||||
git_dir.close(io);
|
||||
return null;
|
||||
} else |_| {}
|
||||
|
||||
|
|
@ -1574,6 +1581,8 @@ const llvm_libs_xtensa = [_][]const u8{
|
|||
};
|
||||
|
||||
fn generateLangRef(b: *std.Build) std.Build.LazyPath {
|
||||
const io = b.graph.io;
|
||||
|
||||
const doctest_exe = b.addExecutable(.{
|
||||
.name = "doctest",
|
||||
.root_module = b.createModule(.{
|
||||
|
|
@ -1583,7 +1592,7 @@ fn generateLangRef(b: *std.Build) std.Build.LazyPath {
|
|||
}),
|
||||
});
|
||||
|
||||
var dir = b.build_root.handle.openDir("doc/langref", .{ .iterate = true }) catch |err| {
|
||||
var dir = b.build_root.handle.openDir(io, "doc/langref", .{ .iterate = true }) catch |err| {
|
||||
std.debug.panic("unable to open '{f}doc/langref' directory: {s}", .{
|
||||
b.build_root, @errorName(err),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -53,24 +53,26 @@ pub fn main() !void {
|
|||
const cache_root = nextArg(args, &arg_idx) orelse fatal("missing cache root directory path", .{});
|
||||
const global_cache_root = nextArg(args, &arg_idx) orelse fatal("missing global cache root directory path", .{});
|
||||
|
||||
const cwd: Io.Dir = .cwd();
|
||||
|
||||
const zig_lib_directory: std.Build.Cache.Directory = .{
|
||||
.path = zig_lib_dir,
|
||||
.handle = try std.fs.cwd().openDir(zig_lib_dir, .{}),
|
||||
.handle = try cwd.openDir(io, zig_lib_dir, .{}),
|
||||
};
|
||||
|
||||
const build_root_directory: std.Build.Cache.Directory = .{
|
||||
.path = build_root,
|
||||
.handle = try std.fs.cwd().openDir(build_root, .{}),
|
||||
.handle = try cwd.openDir(io, build_root, .{}),
|
||||
};
|
||||
|
||||
const local_cache_directory: std.Build.Cache.Directory = .{
|
||||
.path = cache_root,
|
||||
.handle = try std.fs.cwd().makeOpenPath(cache_root, .{}),
|
||||
.handle = try cwd.makeOpenPath(io, cache_root, .{}),
|
||||
};
|
||||
|
||||
const global_cache_directory: std.Build.Cache.Directory = .{
|
||||
.path = global_cache_root,
|
||||
.handle = try std.fs.cwd().makeOpenPath(global_cache_root, .{}),
|
||||
.handle = try cwd.makeOpenPath(io, global_cache_root, .{}),
|
||||
};
|
||||
|
||||
var graph: std.Build.Graph = .{
|
||||
|
|
@ -79,7 +81,7 @@ pub fn main() !void {
|
|||
.cache = .{
|
||||
.io = io,
|
||||
.gpa = arena,
|
||||
.manifest_dir = try local_cache_directory.handle.makeOpenPath("h", .{}),
|
||||
.manifest_dir = try local_cache_directory.handle.makeOpenPath(io, "h", .{}),
|
||||
},
|
||||
.zig_exe = zig_exe,
|
||||
.env_map = try process.getEnvMap(arena),
|
||||
|
|
@ -92,7 +94,7 @@ pub fn main() !void {
|
|||
.time_report = false,
|
||||
};
|
||||
|
||||
graph.cache.addPrefix(.{ .path = null, .handle = std.fs.cwd() });
|
||||
graph.cache.addPrefix(.{ .path = null, .handle = cwd });
|
||||
graph.cache.addPrefix(build_root_directory);
|
||||
graph.cache.addPrefix(local_cache_directory);
|
||||
graph.cache.addPrefix(global_cache_directory);
|
||||
|
|
|
|||
|
|
@ -1700,9 +1700,8 @@ pub fn addCheckFile(
|
|||
}
|
||||
|
||||
pub fn truncateFile(b: *Build, dest_path: []const u8) (fs.Dir.MakeError || fs.Dir.StatFileError)!void {
|
||||
if (b.verbose) {
|
||||
log.info("truncate {s}", .{dest_path});
|
||||
}
|
||||
const io = b.graph.io;
|
||||
if (b.verbose) log.info("truncate {s}", .{dest_path});
|
||||
const cwd = fs.cwd();
|
||||
var src_file = cwd.createFile(dest_path, .{}) catch |err| switch (err) {
|
||||
error.FileNotFound => blk: {
|
||||
|
|
@ -1713,7 +1712,7 @@ pub fn truncateFile(b: *Build, dest_path: []const u8) (fs.Dir.MakeError || fs.Di
|
|||
},
|
||||
else => |e| return e,
|
||||
};
|
||||
src_file.close();
|
||||
src_file.close(io);
|
||||
}
|
||||
|
||||
/// References a file or directory relative to the source root.
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ const builtin = @import("builtin");
|
|||
const std = @import("std");
|
||||
const Io = std.Io;
|
||||
const crypto = std.crypto;
|
||||
const fs = std.fs;
|
||||
const assert = std.debug.assert;
|
||||
const testing = std.testing;
|
||||
const mem = std.mem;
|
||||
|
|
@ -18,7 +17,7 @@ const log = std.log.scoped(.cache);
|
|||
|
||||
gpa: Allocator,
|
||||
io: Io,
|
||||
manifest_dir: fs.Dir,
|
||||
manifest_dir: Io.Dir,
|
||||
hash: HashHelper = .{},
|
||||
/// This value is accessed from multiple threads, protected by mutex.
|
||||
recent_problematic_timestamp: Io.Timestamp = .zero,
|
||||
|
|
@ -71,7 +70,7 @@ const PrefixedPath = struct {
|
|||
|
||||
fn findPrefix(cache: *const Cache, file_path: []const u8) !PrefixedPath {
|
||||
const gpa = cache.gpa;
|
||||
const resolved_path = try fs.path.resolve(gpa, &.{file_path});
|
||||
const resolved_path = try std.fs.path.resolve(gpa, &.{file_path});
|
||||
errdefer gpa.free(resolved_path);
|
||||
return findPrefixResolved(cache, resolved_path);
|
||||
}
|
||||
|
|
@ -102,9 +101,9 @@ fn findPrefixResolved(cache: *const Cache, resolved_path: []u8) !PrefixedPath {
|
|||
}
|
||||
|
||||
fn getPrefixSubpath(allocator: Allocator, prefix: []const u8, path: []u8) ![]u8 {
|
||||
const relative = try fs.path.relative(allocator, prefix, path);
|
||||
const relative = try std.fs.path.relative(allocator, prefix, path);
|
||||
errdefer allocator.free(relative);
|
||||
var component_iterator = fs.path.NativeComponentIterator.init(relative);
|
||||
var component_iterator = std.fs.path.NativeComponentIterator.init(relative);
|
||||
if (component_iterator.root() != null) {
|
||||
return error.NotASubPath;
|
||||
}
|
||||
|
|
@ -145,17 +144,17 @@ pub const File = struct {
|
|||
max_file_size: ?usize,
|
||||
/// Populated if the user calls `addOpenedFile`.
|
||||
/// The handle is not owned here.
|
||||
handle: ?fs.File,
|
||||
handle: ?Io.File,
|
||||
stat: Stat,
|
||||
bin_digest: BinDigest,
|
||||
contents: ?[]const u8,
|
||||
|
||||
pub const Stat = struct {
|
||||
inode: fs.File.INode,
|
||||
inode: Io.File.INode,
|
||||
size: u64,
|
||||
mtime: Io.Timestamp,
|
||||
|
||||
pub fn fromFs(fs_stat: fs.File.Stat) Stat {
|
||||
pub fn fromFs(fs_stat: Io.File.Stat) Stat {
|
||||
return .{
|
||||
.inode = fs_stat.inode,
|
||||
.size = fs_stat.size,
|
||||
|
|
@ -178,7 +177,7 @@ pub const File = struct {
|
|||
file.max_file_size = if (file.max_file_size) |old| @max(old, new) else new;
|
||||
}
|
||||
|
||||
pub fn updateHandle(file: *File, new_handle: ?fs.File) void {
|
||||
pub fn updateHandle(file: *File, new_handle: ?Io.File) void {
|
||||
const handle = new_handle orelse return;
|
||||
file.handle = handle;
|
||||
}
|
||||
|
|
@ -293,16 +292,16 @@ pub fn binToHex(bin_digest: BinDigest) HexDigest {
|
|||
}
|
||||
|
||||
pub const Lock = struct {
|
||||
manifest_file: fs.File,
|
||||
manifest_file: Io.File,
|
||||
|
||||
pub fn release(lock: *Lock) void {
|
||||
pub fn release(lock: *Lock, io: Io) void {
|
||||
if (builtin.os.tag == .windows) {
|
||||
// Windows does not guarantee that locks are immediately unlocked when
|
||||
// the file handle is closed. See LockFileEx documentation.
|
||||
lock.manifest_file.unlock();
|
||||
}
|
||||
|
||||
lock.manifest_file.close();
|
||||
lock.manifest_file.close(io);
|
||||
lock.* = undefined;
|
||||
}
|
||||
};
|
||||
|
|
@ -311,7 +310,7 @@ pub const Manifest = struct {
|
|||
cache: *Cache,
|
||||
/// Current state for incremental hashing.
|
||||
hash: HashHelper,
|
||||
manifest_file: ?fs.File,
|
||||
manifest_file: ?Io.File,
|
||||
manifest_dirty: bool,
|
||||
/// Set this flag to true before calling hit() in order to indicate that
|
||||
/// upon a cache hit, the code using the cache will not modify the files
|
||||
|
|
@ -332,9 +331,9 @@ pub const Manifest = struct {
|
|||
|
||||
pub const Diagnostic = union(enum) {
|
||||
none,
|
||||
manifest_create: fs.File.OpenError,
|
||||
manifest_read: fs.File.ReadError,
|
||||
manifest_lock: fs.File.LockError,
|
||||
manifest_create: Io.File.OpenError,
|
||||
manifest_read: Io.File.Reader.Error,
|
||||
manifest_lock: Io.File.LockError,
|
||||
file_open: FileOp,
|
||||
file_stat: FileOp,
|
||||
file_read: FileOp,
|
||||
|
|
@ -393,10 +392,10 @@ pub const Manifest = struct {
|
|||
}
|
||||
|
||||
/// Same as `addFilePath` except the file has already been opened.
|
||||
pub fn addOpenedFile(m: *Manifest, path: Path, handle: ?fs.File, max_file_size: ?usize) !usize {
|
||||
pub fn addOpenedFile(m: *Manifest, path: Path, handle: ?Io.File, max_file_size: ?usize) !usize {
|
||||
const gpa = m.cache.gpa;
|
||||
try m.files.ensureUnusedCapacity(gpa, 1);
|
||||
const resolved_path = try fs.path.resolve(gpa, &.{
|
||||
const resolved_path = try std.fs.path.resolve(gpa, &.{
|
||||
path.root_dir.path orelse ".",
|
||||
path.subPathOrDot(),
|
||||
});
|
||||
|
|
@ -417,7 +416,7 @@ pub const Manifest = struct {
|
|||
return addFileInner(self, prefixed_path, null, max_file_size);
|
||||
}
|
||||
|
||||
fn addFileInner(self: *Manifest, prefixed_path: PrefixedPath, handle: ?fs.File, max_file_size: ?usize) usize {
|
||||
fn addFileInner(self: *Manifest, prefixed_path: PrefixedPath, handle: ?Io.File, max_file_size: ?usize) usize {
|
||||
const gop = self.files.getOrPutAssumeCapacityAdapted(prefixed_path, FilesAdapter{});
|
||||
if (gop.found_existing) {
|
||||
self.cache.gpa.free(prefixed_path.sub_path);
|
||||
|
|
@ -460,7 +459,7 @@ pub const Manifest = struct {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn addDepFile(self: *Manifest, dir: fs.Dir, dep_file_sub_path: []const u8) !void {
|
||||
pub fn addDepFile(self: *Manifest, dir: Io.Dir, dep_file_sub_path: []const u8) !void {
|
||||
assert(self.manifest_file == null);
|
||||
return self.addDepFileMaybePost(dir, dep_file_sub_path);
|
||||
}
|
||||
|
|
@ -702,7 +701,7 @@ pub const Manifest = struct {
|
|||
const file_path = iter.rest();
|
||||
|
||||
const stat_size = fmt.parseInt(u64, size, 10) catch return error.InvalidFormat;
|
||||
const stat_inode = fmt.parseInt(fs.File.INode, inode, 10) catch return error.InvalidFormat;
|
||||
const stat_inode = fmt.parseInt(Io.File.INode, inode, 10) catch return error.InvalidFormat;
|
||||
const stat_mtime = fmt.parseInt(i64, mtime_nsec_str, 10) catch return error.InvalidFormat;
|
||||
const file_bin_digest = b: {
|
||||
if (digest_str.len != hex_digest_len) return error.InvalidFormat;
|
||||
|
|
@ -772,7 +771,7 @@ pub const Manifest = struct {
|
|||
return error.CacheCheckFailed;
|
||||
},
|
||||
};
|
||||
defer this_file.close();
|
||||
defer this_file.close(io);
|
||||
|
||||
const actual_stat = this_file.stat() catch |err| {
|
||||
self.diagnostic = .{ .file_stat = .{
|
||||
|
|
@ -879,7 +878,7 @@ pub const Manifest = struct {
|
|||
error.Canceled => return error.Canceled,
|
||||
else => return true,
|
||||
};
|
||||
defer file.close();
|
||||
defer file.close(io);
|
||||
|
||||
// Save locally and also save globally (we still hold the global lock).
|
||||
const stat = file.stat() catch |err| switch (err) {
|
||||
|
|
@ -894,18 +893,20 @@ pub const Manifest = struct {
|
|||
}
|
||||
|
||||
fn populateFileHash(self: *Manifest, ch_file: *File) !void {
|
||||
const io = self.cache.io;
|
||||
|
||||
if (ch_file.handle) |handle| {
|
||||
return populateFileHashHandle(self, ch_file, handle);
|
||||
} else {
|
||||
const pp = ch_file.prefixed_path;
|
||||
const dir = self.cache.prefixes()[pp.prefix].handle;
|
||||
const handle = try dir.openFile(pp.sub_path, .{});
|
||||
defer handle.close();
|
||||
defer handle.close(io);
|
||||
return populateFileHashHandle(self, ch_file, handle);
|
||||
}
|
||||
}
|
||||
|
||||
fn populateFileHashHandle(self: *Manifest, ch_file: *File, handle: fs.File) !void {
|
||||
fn populateFileHashHandle(self: *Manifest, ch_file: *File, handle: Io.File) !void {
|
||||
const actual_stat = try handle.stat();
|
||||
ch_file.stat = .{
|
||||
.size = actual_stat.size,
|
||||
|
|
@ -1064,12 +1065,12 @@ pub const Manifest = struct {
|
|||
self.hash.hasher.update(&new_file.bin_digest);
|
||||
}
|
||||
|
||||
pub fn addDepFilePost(self: *Manifest, dir: fs.Dir, dep_file_sub_path: []const u8) !void {
|
||||
pub fn addDepFilePost(self: *Manifest, dir: Io.Dir, dep_file_sub_path: []const u8) !void {
|
||||
assert(self.manifest_file != null);
|
||||
return self.addDepFileMaybePost(dir, dep_file_sub_path);
|
||||
}
|
||||
|
||||
fn addDepFileMaybePost(self: *Manifest, dir: fs.Dir, dep_file_sub_path: []const u8) !void {
|
||||
fn addDepFileMaybePost(self: *Manifest, dir: Io.Dir, dep_file_sub_path: []const u8) !void {
|
||||
const gpa = self.cache.gpa;
|
||||
const dep_file_contents = try dir.readFileAlloc(dep_file_sub_path, gpa, .limited(manifest_file_size_max));
|
||||
defer gpa.free(dep_file_contents);
|
||||
|
|
@ -1148,7 +1149,7 @@ pub const Manifest = struct {
|
|||
}
|
||||
}
|
||||
|
||||
fn writeDirtyManifestToStream(self: *Manifest, fw: *fs.File.Writer) !void {
|
||||
fn writeDirtyManifestToStream(self: *Manifest, fw: *Io.File.Writer) !void {
|
||||
try fw.interface.writeAll(manifest_header ++ "\n");
|
||||
for (self.files.keys()) |file| {
|
||||
try fw.interface.print("{d} {d} {d} {x} {d} {s}\n", .{
|
||||
|
|
@ -1214,13 +1215,15 @@ pub const Manifest = struct {
|
|||
/// `Manifest.hit` must be called first.
|
||||
/// Don't forget to call `writeManifest` before this!
|
||||
pub fn deinit(self: *Manifest) void {
|
||||
const io = self.cache.io;
|
||||
|
||||
if (self.manifest_file) |file| {
|
||||
if (builtin.os.tag == .windows) {
|
||||
// See Lock.release for why this is required on Windows
|
||||
file.unlock();
|
||||
}
|
||||
|
||||
file.close();
|
||||
file.close(io);
|
||||
}
|
||||
for (self.files.keys()) |*file| {
|
||||
file.deinit(self.cache.gpa);
|
||||
|
|
@ -1281,7 +1284,7 @@ pub const Manifest = struct {
|
|||
/// On operating systems that support symlinks, does a readlink. On other operating systems,
|
||||
/// uses the file contents. Windows supports symlinks but only with elevated privileges, so
|
||||
/// it is treated as not supporting symlinks.
|
||||
pub fn readSmallFile(dir: fs.Dir, sub_path: []const u8, buffer: []u8) ![]u8 {
|
||||
pub fn readSmallFile(dir: Io.Dir, sub_path: []const u8, buffer: []u8) ![]u8 {
|
||||
if (builtin.os.tag == .windows) {
|
||||
return dir.readFile(sub_path, buffer);
|
||||
} else {
|
||||
|
|
@ -1293,7 +1296,7 @@ pub fn readSmallFile(dir: fs.Dir, sub_path: []const u8, buffer: []u8) ![]u8 {
|
|||
/// uses the file contents. Windows supports symlinks but only with elevated privileges, so
|
||||
/// it is treated as not supporting symlinks.
|
||||
/// `data` must be a valid UTF-8 encoded file path and 255 bytes or fewer.
|
||||
pub fn writeSmallFile(dir: fs.Dir, sub_path: []const u8, data: []const u8) !void {
|
||||
pub fn writeSmallFile(dir: Io.Dir, sub_path: []const u8, data: []const u8) !void {
|
||||
assert(data.len <= 255);
|
||||
if (builtin.os.tag == .windows) {
|
||||
return dir.writeFile(.{ .sub_path = sub_path, .data = data });
|
||||
|
|
@ -1302,7 +1305,7 @@ pub fn writeSmallFile(dir: fs.Dir, sub_path: []const u8, data: []const u8) !void
|
|||
}
|
||||
}
|
||||
|
||||
fn hashFile(file: fs.File, bin_digest: *[Hasher.mac_length]u8) fs.File.PReadError!void {
|
||||
fn hashFile(file: Io.File, bin_digest: *[Hasher.mac_length]u8) Io.File.PReadError!void {
|
||||
var buf: [1024]u8 = undefined;
|
||||
var hasher = hasher_init;
|
||||
var off: u64 = 0;
|
||||
|
|
@ -1316,7 +1319,7 @@ fn hashFile(file: fs.File, bin_digest: *[Hasher.mac_length]u8) fs.File.PReadErro
|
|||
}
|
||||
|
||||
// Create/Write a file, close it, then grab its stat.mtime timestamp.
|
||||
fn testGetCurrentFileTimestamp(dir: fs.Dir) !Io.Timestamp {
|
||||
fn testGetCurrentFileTimestamp(io: Io, dir: Io.Dir) !Io.Timestamp {
|
||||
const test_out_file = "test-filetimestamp.tmp";
|
||||
|
||||
var file = try dir.createFile(test_out_file, .{
|
||||
|
|
@ -1324,7 +1327,7 @@ fn testGetCurrentFileTimestamp(dir: fs.Dir) !Io.Timestamp {
|
|||
.truncate = true,
|
||||
});
|
||||
defer {
|
||||
file.close();
|
||||
file.close(io);
|
||||
dir.deleteFile(test_out_file) catch {};
|
||||
}
|
||||
|
||||
|
|
@ -1343,8 +1346,8 @@ test "cache file and then recall it" {
|
|||
try tmp.dir.writeFile(.{ .sub_path = temp_file, .data = "Hello, world!\n" });
|
||||
|
||||
// Wait for file timestamps to tick
|
||||
const initial_time = try testGetCurrentFileTimestamp(tmp.dir);
|
||||
while ((try testGetCurrentFileTimestamp(tmp.dir)).nanoseconds == initial_time.nanoseconds) {
|
||||
const initial_time = try testGetCurrentFileTimestamp(io, tmp.dir);
|
||||
while ((try testGetCurrentFileTimestamp(io, tmp.dir)).nanoseconds == initial_time.nanoseconds) {
|
||||
try std.Io.Clock.Duration.sleep(.{ .clock = .boot, .raw = .fromNanoseconds(1) }, io);
|
||||
}
|
||||
|
||||
|
|
@ -1358,7 +1361,7 @@ test "cache file and then recall it" {
|
|||
.manifest_dir = try tmp.dir.makeOpenPath(temp_manifest_dir, .{}),
|
||||
};
|
||||
cache.addPrefix(.{ .path = null, .handle = tmp.dir });
|
||||
defer cache.manifest_dir.close();
|
||||
defer cache.manifest_dir.close(io);
|
||||
|
||||
{
|
||||
var ch = cache.obtain();
|
||||
|
|
@ -1424,7 +1427,7 @@ test "check that changing a file makes cache fail" {
|
|||
.manifest_dir = try tmp.dir.makeOpenPath(temp_manifest_dir, .{}),
|
||||
};
|
||||
cache.addPrefix(.{ .path = null, .handle = tmp.dir });
|
||||
defer cache.manifest_dir.close();
|
||||
defer cache.manifest_dir.close(io);
|
||||
|
||||
{
|
||||
var ch = cache.obtain();
|
||||
|
|
@ -1484,7 +1487,7 @@ test "no file inputs" {
|
|||
.manifest_dir = try tmp.dir.makeOpenPath(temp_manifest_dir, .{}),
|
||||
};
|
||||
cache.addPrefix(.{ .path = null, .handle = tmp.dir });
|
||||
defer cache.manifest_dir.close();
|
||||
defer cache.manifest_dir.close(io);
|
||||
|
||||
{
|
||||
var man = cache.obtain();
|
||||
|
|
@ -1543,7 +1546,7 @@ test "Manifest with files added after initial hash work" {
|
|||
.manifest_dir = try tmp.dir.makeOpenPath(temp_manifest_dir, .{}),
|
||||
};
|
||||
cache.addPrefix(.{ .path = null, .handle = tmp.dir });
|
||||
defer cache.manifest_dir.close();
|
||||
defer cache.manifest_dir.close(io);
|
||||
|
||||
{
|
||||
var ch = cache.obtain();
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
const Directory = @This();
|
||||
|
||||
const std = @import("../../std.zig");
|
||||
const assert = std.debug.assert;
|
||||
const Io = std.Io;
|
||||
const fs = std.fs;
|
||||
const assert = std.debug.assert;
|
||||
const fmt = std.fmt;
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
|
|
@ -9,7 +11,7 @@ const Allocator = std.mem.Allocator;
|
|||
/// directly, but it is needed when passing the directory to a child process.
|
||||
/// `null` means cwd.
|
||||
path: ?[]const u8,
|
||||
handle: fs.Dir,
|
||||
handle: Io.Dir,
|
||||
|
||||
pub fn clone(d: Directory, arena: Allocator) Allocator.Error!Directory {
|
||||
return .{
|
||||
|
|
@ -21,7 +23,7 @@ pub fn clone(d: Directory, arena: Allocator) Allocator.Error!Directory {
|
|||
pub fn cwd() Directory {
|
||||
return .{
|
||||
.path = null,
|
||||
.handle = fs.cwd(),
|
||||
.handle = .cwd(),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -64,5 +66,5 @@ pub fn format(self: Directory, writer: *std.Io.Writer) std.Io.Writer.Error!void
|
|||
}
|
||||
|
||||
pub fn eql(self: Directory, other: Directory) bool {
|
||||
return self.handle.fd == other.handle.fd;
|
||||
return self.handle.handle == other.handle.handle;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ const Path = @This();
|
|||
|
||||
const std = @import("../../std.zig");
|
||||
const Io = std.Io;
|
||||
const assert = std.debug.assert;
|
||||
const fs = std.fs;
|
||||
const assert = std.debug.assert;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const Cache = std.Build.Cache;
|
||||
|
||||
|
|
@ -62,8 +62,8 @@ pub fn joinStringZ(p: Path, gpa: Allocator, sub_path: []const u8) Allocator.Erro
|
|||
pub fn openFile(
|
||||
p: Path,
|
||||
sub_path: []const u8,
|
||||
flags: fs.File.OpenFlags,
|
||||
) !fs.File {
|
||||
flags: Io.File.OpenFlags,
|
||||
) !Io.File {
|
||||
var buf: [fs.max_path_bytes]u8 = undefined;
|
||||
const joined_path = if (p.sub_path.len == 0) sub_path else p: {
|
||||
break :p std.fmt.bufPrint(&buf, "{s}" ++ fs.path.sep_str ++ "{s}", .{
|
||||
|
|
@ -76,8 +76,8 @@ pub fn openFile(
|
|||
pub fn openDir(
|
||||
p: Path,
|
||||
sub_path: []const u8,
|
||||
args: fs.Dir.OpenOptions,
|
||||
) fs.Dir.OpenError!fs.Dir {
|
||||
args: Io.Dir.OpenOptions,
|
||||
) Io.Dir.OpenError!Io.Dir {
|
||||
var buf: [fs.max_path_bytes]u8 = undefined;
|
||||
const joined_path = if (p.sub_path.len == 0) sub_path else p: {
|
||||
break :p std.fmt.bufPrint(&buf, "{s}" ++ fs.path.sep_str ++ "{s}", .{
|
||||
|
|
@ -87,7 +87,7 @@ pub fn openDir(
|
|||
return p.root_dir.handle.openDir(joined_path, args);
|
||||
}
|
||||
|
||||
pub fn makeOpenPath(p: Path, sub_path: []const u8, opts: fs.Dir.OpenOptions) !fs.Dir {
|
||||
pub fn makeOpenPath(p: Path, sub_path: []const u8, opts: Io.Dir.OpenOptions) !Io.Dir {
|
||||
var buf: [fs.max_path_bytes]u8 = undefined;
|
||||
const joined_path = if (p.sub_path.len == 0) sub_path else p: {
|
||||
break :p std.fmt.bufPrint(&buf, "{s}" ++ fs.path.sep_str ++ "{s}", .{
|
||||
|
|
@ -97,7 +97,7 @@ pub fn makeOpenPath(p: Path, sub_path: []const u8, opts: fs.Dir.OpenOptions) !fs
|
|||
return p.root_dir.handle.makeOpenPath(joined_path, opts);
|
||||
}
|
||||
|
||||
pub fn statFile(p: Path, sub_path: []const u8) !fs.Dir.Stat {
|
||||
pub fn statFile(p: Path, sub_path: []const u8) !Io.Dir.Stat {
|
||||
var buf: [fs.max_path_bytes]u8 = undefined;
|
||||
const joined_path = if (p.sub_path.len == 0) sub_path else p: {
|
||||
break :p std.fmt.bufPrint(&buf, "{s}" ++ fs.path.sep_str ++ "{s}", .{
|
||||
|
|
@ -110,7 +110,7 @@ pub fn statFile(p: Path, sub_path: []const u8) !fs.Dir.Stat {
|
|||
pub fn atomicFile(
|
||||
p: Path,
|
||||
sub_path: []const u8,
|
||||
options: fs.Dir.AtomicFileOptions,
|
||||
options: Io.Dir.AtomicFileOptions,
|
||||
buf: *[fs.max_path_bytes]u8,
|
||||
) !fs.AtomicFile {
|
||||
const joined_path = if (p.sub_path.len == 0) sub_path else p: {
|
||||
|
|
@ -180,7 +180,7 @@ pub fn formatEscapeChar(path: Path, writer: *Io.Writer) Io.Writer.Error!void {
|
|||
}
|
||||
|
||||
pub fn format(self: Path, writer: *Io.Writer) Io.Writer.Error!void {
|
||||
if (std.fs.path.isAbsolute(self.sub_path)) {
|
||||
if (fs.path.isAbsolute(self.sub_path)) {
|
||||
try writer.writeAll(self.sub_path);
|
||||
return;
|
||||
}
|
||||
|
|
@ -225,9 +225,9 @@ pub const TableAdapter = struct {
|
|||
|
||||
pub fn hash(self: TableAdapter, a: Cache.Path) u32 {
|
||||
_ = self;
|
||||
const seed = switch (@typeInfo(@TypeOf(a.root_dir.handle.fd))) {
|
||||
.pointer => @intFromPtr(a.root_dir.handle.fd),
|
||||
.int => @as(u32, @bitCast(a.root_dir.handle.fd)),
|
||||
const seed = switch (@typeInfo(@TypeOf(a.root_dir.handle.handle))) {
|
||||
.pointer => @intFromPtr(a.root_dir.handle.handle),
|
||||
.int => @as(u32, @bitCast(a.root_dir.handle.handle)),
|
||||
else => @compileError("unimplemented hash function"),
|
||||
};
|
||||
return @truncate(Hash.hash(seed, a.sub_path));
|
||||
|
|
|
|||
|
|
@ -510,20 +510,16 @@ pub fn installFile(s: *Step, src_lazy_path: Build.LazyPath, dest_path: []const u
|
|||
const io = b.graph.io;
|
||||
const src_path = src_lazy_path.getPath3(b, s);
|
||||
try handleVerbose(b, null, &.{ "install", "-C", b.fmt("{f}", .{src_path}), dest_path });
|
||||
return Io.Dir.updateFile(src_path.root_dir.handle.adaptToNewApi(), io, src_path.sub_path, .cwd(), dest_path, .{}) catch |err| {
|
||||
return s.fail("unable to update file from '{f}' to '{s}': {t}", .{
|
||||
src_path, dest_path, err,
|
||||
});
|
||||
};
|
||||
return Io.Dir.updateFile(src_path.root_dir.handle, io, src_path.sub_path, .cwd(), dest_path, .{}) catch |err|
|
||||
return s.fail("unable to update file from '{f}' to '{s}': {t}", .{ src_path, dest_path, err });
|
||||
}
|
||||
|
||||
/// Wrapper around `std.fs.Dir.makePathStatus` that handles verbose and error output.
|
||||
pub fn installDir(s: *Step, dest_path: []const u8) !std.fs.Dir.MakePathStatus {
|
||||
const b = s.owner;
|
||||
try handleVerbose(b, null, &.{ "install", "-d", dest_path });
|
||||
return std.fs.cwd().makePathStatus(dest_path) catch |err| {
|
||||
return std.fs.cwd().makePathStatus(dest_path) catch |err|
|
||||
return s.fail("unable to create dir '{s}': {t}", .{ dest_path, err });
|
||||
};
|
||||
}
|
||||
|
||||
fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool, web_server: ?*Build.WebServer, gpa: Allocator) !?Path {
|
||||
|
|
|
|||
|
|
@ -1,12 +1,15 @@
|
|||
const Compile = @This();
|
||||
const builtin = @import("builtin");
|
||||
|
||||
const std = @import("std");
|
||||
const Io = std.Io;
|
||||
const mem = std.mem;
|
||||
const fs = std.fs;
|
||||
const assert = std.debug.assert;
|
||||
const panic = std.debug.panic;
|
||||
const StringHashMap = std.StringHashMap;
|
||||
const Sha256 = std.crypto.hash.sha2.Sha256;
|
||||
const Allocator = mem.Allocator;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const Step = std.Build.Step;
|
||||
const LazyPath = std.Build.LazyPath;
|
||||
const PkgConfigPkg = std.Build.PkgConfigPkg;
|
||||
|
|
@ -15,7 +18,6 @@ const RunError = std.Build.RunError;
|
|||
const Module = std.Build.Module;
|
||||
const InstallDir = std.Build.InstallDir;
|
||||
const GeneratedFile = std.Build.GeneratedFile;
|
||||
const Compile = @This();
|
||||
const Path = std.Build.Cache.Path;
|
||||
|
||||
pub const base_id: Step.Id = .compile;
|
||||
|
|
@ -1694,19 +1696,22 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
|
|||
}
|
||||
|
||||
// -I and -L arguments that appear after the last --mod argument apply to all modules.
|
||||
const cwd: Io.Dir = .cwd();
|
||||
const io = b.graph.io;
|
||||
|
||||
for (b.search_prefixes.items) |search_prefix| {
|
||||
var prefix_dir = fs.cwd().openDir(search_prefix, .{}) catch |err| {
|
||||
var prefix_dir = cwd.openDir(io, search_prefix, .{}) catch |err| {
|
||||
return step.fail("unable to open prefix directory '{s}': {s}", .{
|
||||
search_prefix, @errorName(err),
|
||||
});
|
||||
};
|
||||
defer prefix_dir.close();
|
||||
defer prefix_dir.close(io);
|
||||
|
||||
// Avoid passing -L and -I flags for nonexistent directories.
|
||||
// This prevents a warning, that should probably be upgraded to an error in Zig's
|
||||
// CLI parsing code, when the linker sees an -L directory that does not exist.
|
||||
|
||||
if (prefix_dir.access("lib", .{})) |_| {
|
||||
if (prefix_dir.access(io, "lib", .{})) |_| {
|
||||
try zig_args.appendSlice(&.{
|
||||
"-L", b.pathJoin(&.{ search_prefix, "lib" }),
|
||||
});
|
||||
|
|
@ -1717,7 +1722,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
|
|||
}),
|
||||
}
|
||||
|
||||
if (prefix_dir.access("include", .{})) |_| {
|
||||
if (prefix_dir.access(io, "include", .{})) |_| {
|
||||
try zig_args.appendSlice(&.{
|
||||
"-I", b.pathJoin(&.{ search_prefix, "include" }),
|
||||
});
|
||||
|
|
@ -1793,7 +1798,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
|
|||
args_length += arg.len + 1; // +1 to account for null terminator
|
||||
}
|
||||
if (args_length >= 30 * 1024) {
|
||||
try b.cache_root.handle.makePath("args");
|
||||
try b.cache_root.handle.makePath(io, "args");
|
||||
|
||||
const args_to_escape = zig_args.items[2..];
|
||||
var escaped_args = try std.array_list.Managed([]const u8).initCapacity(arena, args_to_escape.len);
|
||||
|
|
@ -1826,18 +1831,18 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
|
|||
_ = try std.fmt.bufPrint(&args_hex_hash, "{x}", .{&args_hash});
|
||||
|
||||
const args_file = "args" ++ fs.path.sep_str ++ args_hex_hash;
|
||||
if (b.cache_root.handle.access(args_file, .{})) |_| {
|
||||
if (b.cache_root.handle.access(io, args_file, .{})) |_| {
|
||||
// The args file is already present from a previous run.
|
||||
} else |err| switch (err) {
|
||||
error.FileNotFound => {
|
||||
try b.cache_root.handle.makePath("tmp");
|
||||
try b.cache_root.handle.makePath(io, "tmp");
|
||||
const rand_int = std.crypto.random.int(u64);
|
||||
const tmp_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(rand_int);
|
||||
try b.cache_root.handle.writeFile(.{ .sub_path = tmp_path, .data = args });
|
||||
defer b.cache_root.handle.deleteFile(tmp_path) catch {
|
||||
try b.cache_root.handle.writeFile(io, .{ .sub_path = tmp_path, .data = args });
|
||||
defer b.cache_root.handle.deleteFile(io, tmp_path) catch {
|
||||
// It's fine if the temporary file can't be cleaned up.
|
||||
};
|
||||
b.cache_root.handle.rename(tmp_path, args_file) catch |rename_err| switch (rename_err) {
|
||||
b.cache_root.handle.rename(io, tmp_path, args_file) catch |rename_err| switch (rename_err) {
|
||||
error.PathAlreadyExists => {
|
||||
// The args file was created by another concurrent build process.
|
||||
},
|
||||
|
|
@ -1949,18 +1954,20 @@ pub fn doAtomicSymLinks(
|
|||
filename_name_only: []const u8,
|
||||
) !void {
|
||||
const b = step.owner;
|
||||
const io = b.graph.io;
|
||||
const out_dir = fs.path.dirname(output_path) orelse ".";
|
||||
const out_basename = fs.path.basename(output_path);
|
||||
// sym link for libfoo.so.1 to libfoo.so.1.2.3
|
||||
const major_only_path = b.pathJoin(&.{ out_dir, filename_major_only });
|
||||
fs.cwd().atomicSymLink(out_basename, major_only_path, .{}) catch |err| {
|
||||
const cwd: Io.Dir = .cwd();
|
||||
cwd.atomicSymLink(io, out_basename, major_only_path, .{}) catch |err| {
|
||||
return step.fail("unable to symlink {s} -> {s}: {s}", .{
|
||||
major_only_path, out_basename, @errorName(err),
|
||||
});
|
||||
};
|
||||
// sym link for libfoo.so to libfoo.so.1
|
||||
const name_only_path = b.pathJoin(&.{ out_dir, filename_name_only });
|
||||
fs.cwd().atomicSymLink(filename_major_only, name_only_path, .{}) catch |err| {
|
||||
cwd.atomicSymLink(io, filename_major_only, name_only_path, .{}) catch |err| {
|
||||
return step.fail("Unable to symlink {s} -> {s}: {s}", .{
|
||||
name_only_path, filename_major_only, @errorName(err),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -58,16 +58,15 @@ pub fn create(owner: *std.Build, options: Options) *InstallDir {
|
|||
fn make(step: *Step, options: Step.MakeOptions) !void {
|
||||
_ = options;
|
||||
const b = step.owner;
|
||||
const io = b.graph.io;
|
||||
const install_dir: *InstallDir = @fieldParentPtr("step", step);
|
||||
step.clearWatchInputs();
|
||||
const arena = b.allocator;
|
||||
const dest_prefix = b.getInstallPath(install_dir.options.install_dir, install_dir.options.install_subdir);
|
||||
const src_dir_path = install_dir.options.source_dir.getPath3(b, step);
|
||||
const need_derived_inputs = try step.addDirectoryWatchInput(install_dir.options.source_dir);
|
||||
var src_dir = src_dir_path.root_dir.handle.openDir(src_dir_path.subPathOrDot(), .{ .iterate = true }) catch |err| {
|
||||
return step.fail("unable to open source directory '{f}': {s}", .{
|
||||
src_dir_path, @errorName(err),
|
||||
});
|
||||
var src_dir = src_dir_path.root_dir.handle.openDir(io, src_dir_path.subPathOrDot(), .{ .iterate = true }) catch |err| {
|
||||
return step.fail("unable to open source directory '{f}': {t}", .{ src_dir_path, err });
|
||||
};
|
||||
defer src_dir.close();
|
||||
var it = try src_dir.walk(arena);
|
||||
|
|
|
|||
|
|
@ -441,6 +441,7 @@ fn make(step: *Step, make_options: Step.MakeOptions) !void {
|
|||
_ = make_options;
|
||||
|
||||
const b = step.owner;
|
||||
const io = b.graph.io;
|
||||
const options: *Options = @fieldParentPtr("step", step);
|
||||
|
||||
for (options.args.items) |item| {
|
||||
|
|
@ -468,18 +469,15 @@ fn make(step: *Step, make_options: Step.MakeOptions) !void {
|
|||
|
||||
// Optimize for the hot path. Stat the file, and if it already exists,
|
||||
// cache hit.
|
||||
if (b.cache_root.handle.access(sub_path, .{})) |_| {
|
||||
if (b.cache_root.handle.access(io, sub_path, .{})) |_| {
|
||||
// This is the hot path, success.
|
||||
step.result_cached = true;
|
||||
return;
|
||||
} else |outer_err| switch (outer_err) {
|
||||
error.FileNotFound => {
|
||||
const sub_dirname = fs.path.dirname(sub_path).?;
|
||||
b.cache_root.handle.makePath(sub_dirname) catch |e| {
|
||||
return step.fail("unable to make path '{f}{s}': {s}", .{
|
||||
b.cache_root, sub_dirname, @errorName(e),
|
||||
});
|
||||
};
|
||||
b.cache_root.handle.makePath(io, sub_dirname) catch |e|
|
||||
return step.fail("unable to make path '{f}{s}': {t}", .{ b.cache_root, sub_dirname, e });
|
||||
|
||||
const rand_int = std.crypto.random.int(u64);
|
||||
const tmp_sub_path = "tmp" ++ fs.path.sep_str ++
|
||||
|
|
@ -487,40 +485,40 @@ fn make(step: *Step, make_options: Step.MakeOptions) !void {
|
|||
basename;
|
||||
const tmp_sub_path_dirname = fs.path.dirname(tmp_sub_path).?;
|
||||
|
||||
b.cache_root.handle.makePath(tmp_sub_path_dirname) catch |err| {
|
||||
return step.fail("unable to make temporary directory '{f}{s}': {s}", .{
|
||||
b.cache_root, tmp_sub_path_dirname, @errorName(err),
|
||||
b.cache_root.handle.makePath(io, tmp_sub_path_dirname) catch |err| {
|
||||
return step.fail("unable to make temporary directory '{f}{s}': {t}", .{
|
||||
b.cache_root, tmp_sub_path_dirname, err,
|
||||
});
|
||||
};
|
||||
|
||||
b.cache_root.handle.writeFile(.{ .sub_path = tmp_sub_path, .data = options.contents.items }) catch |err| {
|
||||
return step.fail("unable to write options to '{f}{s}': {s}", .{
|
||||
b.cache_root, tmp_sub_path, @errorName(err),
|
||||
b.cache_root.handle.writeFile(io, .{ .sub_path = tmp_sub_path, .data = options.contents.items }) catch |err| {
|
||||
return step.fail("unable to write options to '{f}{s}': {t}", .{
|
||||
b.cache_root, tmp_sub_path, err,
|
||||
});
|
||||
};
|
||||
|
||||
b.cache_root.handle.rename(tmp_sub_path, sub_path) catch |err| switch (err) {
|
||||
b.cache_root.handle.rename(io, tmp_sub_path, sub_path) catch |err| switch (err) {
|
||||
error.PathAlreadyExists => {
|
||||
// Other process beat us to it. Clean up the temp file.
|
||||
b.cache_root.handle.deleteFile(tmp_sub_path) catch |e| {
|
||||
try step.addError("warning: unable to delete temp file '{f}{s}': {s}", .{
|
||||
b.cache_root, tmp_sub_path, @errorName(e),
|
||||
b.cache_root.handle.deleteFile(io, tmp_sub_path) catch |e| {
|
||||
try step.addError("warning: unable to delete temp file '{f}{s}': {t}", .{
|
||||
b.cache_root, tmp_sub_path, e,
|
||||
});
|
||||
};
|
||||
step.result_cached = true;
|
||||
return;
|
||||
},
|
||||
else => {
|
||||
return step.fail("unable to rename options from '{f}{s}' to '{f}{s}': {s}", .{
|
||||
b.cache_root, tmp_sub_path,
|
||||
b.cache_root, sub_path,
|
||||
@errorName(err),
|
||||
return step.fail("unable to rename options from '{f}{s}' to '{f}{s}': {t}", .{
|
||||
b.cache_root, tmp_sub_path,
|
||||
b.cache_root, sub_path,
|
||||
err,
|
||||
});
|
||||
},
|
||||
};
|
||||
},
|
||||
else => |e| return step.fail("unable to access options file '{f}{s}': {s}", .{
|
||||
b.cache_root, sub_path, @errorName(e),
|
||||
else => |e| return step.fail("unable to access options file '{f}{s}': {t}", .{
|
||||
b.cache_root, sub_path, e,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -662,16 +662,27 @@ pub const VTable = struct {
|
|||
conditionWaitUncancelable: *const fn (?*anyopaque, cond: *Condition, mutex: *Mutex) void,
|
||||
conditionWake: *const fn (?*anyopaque, cond: *Condition, wake: Condition.Wake) void,
|
||||
|
||||
dirMake: *const fn (?*anyopaque, Dir, sub_path: []const u8, Dir.Mode) Dir.MakeError!void,
|
||||
dirMakePath: *const fn (?*anyopaque, Dir, sub_path: []const u8, Dir.Mode) Dir.MakePathError!Dir.MakePathStatus,
|
||||
dirMakeOpenPath: *const fn (?*anyopaque, Dir, sub_path: []const u8, Dir.OpenOptions) Dir.MakeOpenPathError!Dir,
|
||||
dirMake: *const fn (?*anyopaque, Dir, []const u8, Dir.Mode) Dir.MakeError!void,
|
||||
dirMakePath: *const fn (?*anyopaque, Dir, []const u8, Dir.Mode) Dir.MakePathError!Dir.MakePathStatus,
|
||||
dirMakeOpenPath: *const fn (?*anyopaque, Dir, []const u8, Dir.OpenOptions) Dir.MakeOpenPathError!Dir,
|
||||
dirStat: *const fn (?*anyopaque, Dir) Dir.StatError!Dir.Stat,
|
||||
dirStatPath: *const fn (?*anyopaque, Dir, sub_path: []const u8, Dir.StatPathOptions) Dir.StatPathError!File.Stat,
|
||||
dirAccess: *const fn (?*anyopaque, Dir, sub_path: []const u8, Dir.AccessOptions) Dir.AccessError!void,
|
||||
dirCreateFile: *const fn (?*anyopaque, Dir, sub_path: []const u8, File.CreateFlags) File.OpenError!File,
|
||||
dirOpenFile: *const fn (?*anyopaque, Dir, sub_path: []const u8, File.OpenFlags) File.OpenError!File,
|
||||
dirOpenDir: *const fn (?*anyopaque, Dir, sub_path: []const u8, Dir.OpenOptions) Dir.OpenError!Dir,
|
||||
dirStatPath: *const fn (?*anyopaque, Dir, []const u8, Dir.StatPathOptions) Dir.StatPathError!File.Stat,
|
||||
dirAccess: *const fn (?*anyopaque, Dir, []const u8, Dir.AccessOptions) Dir.AccessError!void,
|
||||
dirCreateFile: *const fn (?*anyopaque, Dir, []const u8, File.CreateFlags) File.OpenError!File,
|
||||
dirOpenFile: *const fn (?*anyopaque, Dir, []const u8, File.OpenFlags) File.OpenError!File,
|
||||
dirOpenDir: *const fn (?*anyopaque, Dir, []const u8, Dir.OpenOptions) Dir.OpenError!Dir,
|
||||
dirClose: *const fn (?*anyopaque, Dir) void,
|
||||
dirRead: *const fn (?*anyopaque, *Dir.Reader, []Dir.Entry) Dir.Reader.Error!usize,
|
||||
dirRealPath: *const fn (?*anyopaque, Dir, path_name: []const u8, out_buffer: []u8) Dir.RealPathError!usize,
|
||||
dirDeleteFile: *const fn (?*anyopaque, Dir, []const u8) Dir.DeleteFileError!void,
|
||||
dirDeleteDir: *const fn (?*anyopaque, Dir, []const u8) Dir.DeleteDirError!void,
|
||||
dirRename: *const fn (?*anyopaque, old_dir: Dir, old_sub_path: []const u8, new_dir: Dir, new_sub_path: []const u8) Dir.RenameError!void,
|
||||
dirSymLink: *const fn (?*anyopaque, Dir, target_path: []const u8, sym_link_path: []const u8, Dir.SymLinkFlags) Dir.RenameError!void,
|
||||
dirReadLink: *const fn (?*anyopaque, Dir, sub_path: []const u8, buffer: []u8) Dir.ReadLinkError!usize,
|
||||
dirSetMode: *const fn (?*anyopaque, Dir, File.Mode) Dir.SetModeError!void,
|
||||
dirSetOwner: *const fn (?*anyopaque, Dir, ?File.Uid, ?File.Gid) Dir.SetOwnerError!void,
|
||||
dirSetPermissions: *const fn (?*anyopaque, Dir, Dir.Permissions) Dir.SetPermissionsError!void,
|
||||
|
||||
fileStat: *const fn (?*anyopaque, File) File.StatError!File.Stat,
|
||||
fileClose: *const fn (?*anyopaque, File) void,
|
||||
fileWriteStreaming: *const fn (?*anyopaque, File, buffer: [][]const u8) File.WriteStreamingError!usize,
|
||||
|
|
|
|||
1204
lib/std/Io/Dir.zig
1204
lib/std/Io/Dir.zig
File diff suppressed because it is too large
Load diff
|
|
@ -7,12 +7,23 @@ const is_windows = native_os == .windows;
|
|||
const std = @import("../std.zig");
|
||||
const Io = std.Io;
|
||||
const assert = std.debug.assert;
|
||||
const Dir = std.Io.Dir;
|
||||
|
||||
handle: Handle,
|
||||
|
||||
pub const Handle = std.posix.fd_t;
|
||||
pub const Mode = std.posix.mode_t;
|
||||
pub const INode = std.posix.ino_t;
|
||||
pub const Uid = std.posix.uid_t;
|
||||
pub const Gid = std.posix.gid_t;
|
||||
|
||||
/// This is the default mode given to POSIX operating systems for creating
|
||||
/// files. `0o666` is "-rw-rw-rw-" which is counter-intuitive at first,
|
||||
/// since most people would expect "-rw-r--r--", for example, when using
|
||||
/// the `touch` command, which would correspond to `0o644`. However, POSIX
|
||||
/// libc implementations use `0o666` inside `fopen` and then rely on the
|
||||
/// process-scoped "umask" setting to adjust this number for file creation.
|
||||
pub const default_mode: Mode = if (Mode == u0) 0 else 0o666;
|
||||
|
||||
pub const Kind = enum {
|
||||
block_device,
|
||||
|
|
@ -92,6 +103,11 @@ pub const Lock = enum {
|
|||
exclusive,
|
||||
};
|
||||
|
||||
pub const LockError = error{
|
||||
SystemResources,
|
||||
FileLocksNotSupported,
|
||||
} || Io.UnexpectedError;
|
||||
|
||||
pub const OpenFlags = struct {
|
||||
mode: OpenMode = .read_only,
|
||||
|
||||
|
|
@ -141,7 +157,53 @@ pub const OpenFlags = struct {
|
|||
}
|
||||
};
|
||||
|
||||
pub const CreateFlags = std.fs.File.CreateFlags;
|
||||
pub const CreateFlags = struct {
|
||||
/// Whether the file will be created with read access.
|
||||
read: bool = false,
|
||||
|
||||
/// If the file already exists, and is a regular file, and the access
|
||||
/// mode allows writing, it will be truncated to length 0.
|
||||
truncate: bool = true,
|
||||
|
||||
/// Ensures that this open call creates the file, otherwise causes
|
||||
/// `error.PathAlreadyExists` to be returned.
|
||||
exclusive: bool = false,
|
||||
|
||||
/// Open the file with an advisory lock to coordinate with other processes
|
||||
/// accessing it at the same time. An exclusive lock will prevent other
|
||||
/// processes from acquiring a lock. A shared lock will prevent other
|
||||
/// processes from acquiring a exclusive lock, but does not prevent
|
||||
/// other process from getting their own shared locks.
|
||||
///
|
||||
/// The lock is advisory, except on Linux in very specific circumstances[1].
|
||||
/// This means that a process that does not respect the locking API can still get access
|
||||
/// to the file, despite the lock.
|
||||
///
|
||||
/// On these operating systems, the lock is acquired atomically with
|
||||
/// opening the file:
|
||||
/// * Darwin
|
||||
/// * DragonFlyBSD
|
||||
/// * FreeBSD
|
||||
/// * Haiku
|
||||
/// * NetBSD
|
||||
/// * OpenBSD
|
||||
/// On these operating systems, the lock is acquired via a separate syscall
|
||||
/// after opening the file:
|
||||
/// * Linux
|
||||
/// * Windows
|
||||
///
|
||||
/// [1]: https://www.kernel.org/doc/Documentation/filesystems/mandatory-locking.txt
|
||||
lock: Lock = .none,
|
||||
|
||||
/// Sets whether or not to wait until the file is locked to return. If set to true,
|
||||
/// `error.WouldBlock` will be returned. Otherwise, the file will wait until the file
|
||||
/// is available to proceed.
|
||||
lock_nonblocking: bool = false,
|
||||
|
||||
/// For POSIX systems this is the file system mode the file will
|
||||
/// be created with. On other systems this is always 0.
|
||||
mode: Mode = default_mode,
|
||||
};
|
||||
|
||||
pub const OpenError = error{
|
||||
SharingViolation,
|
||||
|
|
@ -231,6 +293,17 @@ pub fn writePositional(file: File, io: Io, buffer: [][]const u8, offset: u64) Wr
|
|||
return io.vtable.fileWritePositional(io.userdata, file, buffer, offset);
|
||||
}
|
||||
|
||||
/// Opens a file for reading or writing, without attempting to create a new
|
||||
/// file, based on an absolute path.
|
||||
///
|
||||
/// Returns an open resource to be released with `close`.
|
||||
///
|
||||
/// Asserts that the path is absolute. See `Dir.openFile` for a function that
|
||||
/// operates on both absolute and relative paths.
|
||||
///
|
||||
/// On Windows, `absolute_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/).
|
||||
/// On WASI, `absolute_path` should be encoded as valid UTF-8.
|
||||
/// On other platforms, `absolute_path` is an opaque sequence of bytes with no particular encoding.
|
||||
pub fn openAbsolute(io: Io, absolute_path: []const u8, flags: OpenFlags) OpenError!File {
|
||||
assert(std.fs.path.isAbsolute(absolute_path));
|
||||
return Io.Dir.cwd().openFile(io, absolute_path, flags);
|
||||
|
|
@ -364,11 +437,6 @@ pub const Reader = struct {
|
|||
};
|
||||
}
|
||||
|
||||
/// Takes a legacy `std.fs.File` to help with upgrading.
|
||||
pub fn initAdapted(file: std.fs.File, io: Io, buffer: []u8) Reader {
|
||||
return .init(.{ .handle = file.handle }, io, buffer);
|
||||
}
|
||||
|
||||
pub fn initSize(file: File, io: Io, buffer: []u8, size: ?u64) Reader {
|
||||
return .{
|
||||
.io = io,
|
||||
|
|
@ -652,3 +720,113 @@ pub const Reader = struct {
|
|||
return size - logicalPos(r) == 0;
|
||||
}
|
||||
};
|
||||
|
||||
pub const Atomic = struct {
|
||||
file_writer: File.Writer,
|
||||
random_integer: u64,
|
||||
dest_basename: []const u8,
|
||||
file_open: bool,
|
||||
file_exists: bool,
|
||||
close_dir_on_deinit: bool,
|
||||
dir: Dir,
|
||||
|
||||
pub const InitError = File.OpenError;
|
||||
|
||||
/// Note that the `Dir.atomicFile` API may be more handy than this lower-level function.
|
||||
pub fn init(
|
||||
dest_basename: []const u8,
|
||||
mode: File.Mode,
|
||||
dir: Dir,
|
||||
close_dir_on_deinit: bool,
|
||||
write_buffer: []u8,
|
||||
) InitError!Atomic {
|
||||
while (true) {
|
||||
const random_integer = std.crypto.random.int(u64);
|
||||
const tmp_sub_path = std.fmt.hex(random_integer);
|
||||
const file = dir.createFile(&tmp_sub_path, .{ .mode = mode, .exclusive = true }) catch |err| switch (err) {
|
||||
error.PathAlreadyExists => continue,
|
||||
else => |e| return e,
|
||||
};
|
||||
return .{
|
||||
.file_writer = file.writer(write_buffer),
|
||||
.random_integer = random_integer,
|
||||
.dest_basename = dest_basename,
|
||||
.file_open = true,
|
||||
.file_exists = true,
|
||||
.close_dir_on_deinit = close_dir_on_deinit,
|
||||
.dir = dir,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Always call deinit, even after a successful finish().
|
||||
pub fn deinit(af: *Atomic) void {
|
||||
if (af.file_open) {
|
||||
af.file_writer.file.close();
|
||||
af.file_open = false;
|
||||
}
|
||||
if (af.file_exists) {
|
||||
const tmp_sub_path = std.fmt.hex(af.random_integer);
|
||||
af.dir.deleteFile(&tmp_sub_path) catch {};
|
||||
af.file_exists = false;
|
||||
}
|
||||
if (af.close_dir_on_deinit) {
|
||||
af.dir.close();
|
||||
}
|
||||
af.* = undefined;
|
||||
}
|
||||
|
||||
pub const FlushError = File.WriteError;
|
||||
|
||||
pub fn flush(af: *Atomic) FlushError!void {
|
||||
af.file_writer.interface.flush() catch |err| switch (err) {
|
||||
error.WriteFailed => return af.file_writer.err.?,
|
||||
};
|
||||
}
|
||||
|
||||
pub const RenameIntoPlaceError = Dir.RenameError;
|
||||
|
||||
/// On Windows, this function introduces a period of time where some file
|
||||
/// system operations on the destination file will result in
|
||||
/// `error.AccessDenied`, including rename operations (such as the one used in
|
||||
/// this function).
|
||||
pub fn renameIntoPlace(af: *Atomic) RenameIntoPlaceError!void {
|
||||
const io = af.file_writer.io;
|
||||
assert(af.file_exists);
|
||||
if (af.file_open) {
|
||||
af.file_writer.file.close();
|
||||
af.file_open = false;
|
||||
}
|
||||
const tmp_sub_path = std.fmt.hex(af.random_integer);
|
||||
try af.dir.rename(&tmp_sub_path, af.dir, af.dest_basename, io);
|
||||
af.file_exists = false;
|
||||
}
|
||||
|
||||
pub const FinishError = FlushError || RenameIntoPlaceError;
|
||||
|
||||
/// Combination of `flush` followed by `renameIntoPlace`.
|
||||
pub fn finish(af: *Atomic) FinishError!void {
|
||||
try af.flush();
|
||||
try af.renameIntoPlace();
|
||||
}
|
||||
};
|
||||
|
||||
pub const SetModeError = error{
|
||||
AccessDenied,
|
||||
PermissionDenied,
|
||||
InputOutput,
|
||||
SymLinkLoop,
|
||||
FileNotFound,
|
||||
SystemResources,
|
||||
ReadOnlyFileSystem,
|
||||
} || Io.Cancelable || Io.UnexpectedError;
|
||||
|
||||
pub const SetOwnerError = error{
|
||||
AccessDenied,
|
||||
PermissionDenied,
|
||||
InputOutput,
|
||||
SymLinkLoop,
|
||||
FileNotFound,
|
||||
SystemResources,
|
||||
ReadOnlyFileSystem,
|
||||
} || Io.Cancelable || Io.UnexpectedError;
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1104,7 +1104,14 @@ pub inline fn stripInstructionPtrAuthCode(ptr: usize) usize {
|
|||
return ptr;
|
||||
}
|
||||
|
||||
fn printSourceAtAddress(gpa: Allocator, io: Io, debug_info: *SelfInfo, writer: *Writer, address: usize, tty_config: tty.Config) Writer.Error!void {
|
||||
fn printSourceAtAddress(
|
||||
gpa: Allocator,
|
||||
io: Io,
|
||||
debug_info: *SelfInfo,
|
||||
writer: *Writer,
|
||||
address: usize,
|
||||
tty_config: tty.Config,
|
||||
) Writer.Error!void {
|
||||
const symbol: Symbol = debug_info.getSymbol(gpa, io, address) catch |err| switch (err) {
|
||||
error.MissingDebugInfo,
|
||||
error.UnsupportedDebugInfo,
|
||||
|
|
@ -1125,6 +1132,7 @@ fn printSourceAtAddress(gpa: Allocator, io: Io, debug_info: *SelfInfo, writer: *
|
|||
};
|
||||
defer if (symbol.source_location) |sl| gpa.free(sl.file_name);
|
||||
return printLineInfo(
|
||||
io,
|
||||
writer,
|
||||
symbol.source_location,
|
||||
address,
|
||||
|
|
@ -1134,6 +1142,7 @@ fn printSourceAtAddress(gpa: Allocator, io: Io, debug_info: *SelfInfo, writer: *
|
|||
);
|
||||
}
|
||||
fn printLineInfo(
|
||||
io: Io,
|
||||
writer: *Writer,
|
||||
source_location: ?SourceLocation,
|
||||
address: usize,
|
||||
|
|
@ -1159,7 +1168,7 @@ fn printLineInfo(
|
|||
|
||||
// Show the matching source code line if possible
|
||||
if (source_location) |sl| {
|
||||
if (printLineFromFile(writer, sl)) {
|
||||
if (printLineFromFile(io, writer, sl)) {
|
||||
if (sl.column > 0) {
|
||||
// The caret already takes one char
|
||||
const space_needed = @as(usize, @intCast(sl.column - 1));
|
||||
|
|
@ -1177,16 +1186,17 @@ fn printLineInfo(
|
|||
}
|
||||
}
|
||||
}
|
||||
fn printLineFromFile(writer: *Writer, source_location: SourceLocation) !void {
|
||||
fn printLineFromFile(io: Io, writer: *Writer, source_location: SourceLocation) !void {
|
||||
// Allow overriding the target-agnostic source line printing logic by exposing `root.debug.printLineFromFile`.
|
||||
if (@hasDecl(root, "debug") and @hasDecl(root.debug, "printLineFromFile")) {
|
||||
return root.debug.printLineFromFile(writer, source_location);
|
||||
return root.debug.printLineFromFile(io, writer, source_location);
|
||||
}
|
||||
|
||||
// Need this to always block even in async I/O mode, because this could potentially
|
||||
// be called from e.g. the event loop code crashing.
|
||||
var f = try fs.cwd().openFile(source_location.file_name, .{});
|
||||
defer f.close();
|
||||
const cwd: Io.Dir = .cwd();
|
||||
var f = try cwd.openFile(io, source_location.file_name, .{});
|
||||
defer f.close(io);
|
||||
// TODO fstat and make sure that the file has the correct size
|
||||
|
||||
var buf: [4096]u8 = undefined;
|
||||
|
|
@ -1237,11 +1247,13 @@ fn printLineFromFile(writer: *Writer, source_location: SourceLocation) !void {
|
|||
}
|
||||
|
||||
test printLineFromFile {
|
||||
var aw: Writer.Allocating = .init(std.testing.allocator);
|
||||
const io = std.testing.io;
|
||||
const gpa = std.testing.allocator;
|
||||
|
||||
var aw: Writer.Allocating = .init(gpa);
|
||||
defer aw.deinit();
|
||||
const output_stream = &aw.writer;
|
||||
|
||||
const allocator = std.testing.allocator;
|
||||
const join = std.fs.path.join;
|
||||
const expectError = std.testing.expectError;
|
||||
const expectEqualStrings = std.testing.expectEqualStrings;
|
||||
|
|
@ -1249,24 +1261,24 @@ test printLineFromFile {
|
|||
var test_dir = std.testing.tmpDir(.{});
|
||||
defer test_dir.cleanup();
|
||||
// Relies on testing.tmpDir internals which is not ideal, but SourceLocation requires paths.
|
||||
const test_dir_path = try join(allocator, &.{ ".zig-cache", "tmp", test_dir.sub_path[0..] });
|
||||
defer allocator.free(test_dir_path);
|
||||
const test_dir_path = try join(gpa, &.{ ".zig-cache", "tmp", test_dir.sub_path[0..] });
|
||||
defer gpa.free(test_dir_path);
|
||||
|
||||
// Cases
|
||||
{
|
||||
const path = try join(allocator, &.{ test_dir_path, "one_line.zig" });
|
||||
defer allocator.free(path);
|
||||
const path = try join(gpa, &.{ test_dir_path, "one_line.zig" });
|
||||
defer gpa.free(path);
|
||||
try test_dir.dir.writeFile(.{ .sub_path = "one_line.zig", .data = "no new lines in this file, but one is printed anyway" });
|
||||
|
||||
try expectError(error.EndOfFile, printLineFromFile(output_stream, .{ .file_name = path, .line = 2, .column = 0 }));
|
||||
try expectError(error.EndOfFile, printLineFromFile(io, output_stream, .{ .file_name = path, .line = 2, .column = 0 }));
|
||||
|
||||
try printLineFromFile(output_stream, .{ .file_name = path, .line = 1, .column = 0 });
|
||||
try printLineFromFile(io, output_stream, .{ .file_name = path, .line = 1, .column = 0 });
|
||||
try expectEqualStrings("no new lines in this file, but one is printed anyway\n", aw.written());
|
||||
aw.clearRetainingCapacity();
|
||||
}
|
||||
{
|
||||
const path = try fs.path.join(allocator, &.{ test_dir_path, "three_lines.zig" });
|
||||
defer allocator.free(path);
|
||||
const path = try fs.path.join(gpa, &.{ test_dir_path, "three_lines.zig" });
|
||||
defer gpa.free(path);
|
||||
try test_dir.dir.writeFile(.{
|
||||
.sub_path = "three_lines.zig",
|
||||
.data =
|
||||
|
|
@ -1276,19 +1288,19 @@ test printLineFromFile {
|
|||
,
|
||||
});
|
||||
|
||||
try printLineFromFile(output_stream, .{ .file_name = path, .line = 1, .column = 0 });
|
||||
try printLineFromFile(io, output_stream, .{ .file_name = path, .line = 1, .column = 0 });
|
||||
try expectEqualStrings("1\n", aw.written());
|
||||
aw.clearRetainingCapacity();
|
||||
|
||||
try printLineFromFile(output_stream, .{ .file_name = path, .line = 3, .column = 0 });
|
||||
try printLineFromFile(io, output_stream, .{ .file_name = path, .line = 3, .column = 0 });
|
||||
try expectEqualStrings("3\n", aw.written());
|
||||
aw.clearRetainingCapacity();
|
||||
}
|
||||
{
|
||||
const file = try test_dir.dir.createFile("line_overlaps_page_boundary.zig", .{});
|
||||
defer file.close();
|
||||
const path = try fs.path.join(allocator, &.{ test_dir_path, "line_overlaps_page_boundary.zig" });
|
||||
defer allocator.free(path);
|
||||
const path = try fs.path.join(gpa, &.{ test_dir_path, "line_overlaps_page_boundary.zig" });
|
||||
defer gpa.free(path);
|
||||
|
||||
const overlap = 10;
|
||||
var buf: [16]u8 = undefined;
|
||||
|
|
@ -1299,55 +1311,55 @@ test printLineFromFile {
|
|||
try writer.splatByteAll('a', overlap);
|
||||
try writer.flush();
|
||||
|
||||
try printLineFromFile(output_stream, .{ .file_name = path, .line = 2, .column = 0 });
|
||||
try printLineFromFile(io, output_stream, .{ .file_name = path, .line = 2, .column = 0 });
|
||||
try expectEqualStrings(("a" ** overlap) ++ "\n", aw.written());
|
||||
aw.clearRetainingCapacity();
|
||||
}
|
||||
{
|
||||
const file = try test_dir.dir.createFile("file_ends_on_page_boundary.zig", .{});
|
||||
defer file.close();
|
||||
const path = try fs.path.join(allocator, &.{ test_dir_path, "file_ends_on_page_boundary.zig" });
|
||||
defer allocator.free(path);
|
||||
const path = try fs.path.join(gpa, &.{ test_dir_path, "file_ends_on_page_boundary.zig" });
|
||||
defer gpa.free(path);
|
||||
|
||||
var file_writer = file.writer(&.{});
|
||||
const writer = &file_writer.interface;
|
||||
try writer.splatByteAll('a', std.heap.page_size_max);
|
||||
|
||||
try printLineFromFile(output_stream, .{ .file_name = path, .line = 1, .column = 0 });
|
||||
try printLineFromFile(io, output_stream, .{ .file_name = path, .line = 1, .column = 0 });
|
||||
try expectEqualStrings(("a" ** std.heap.page_size_max) ++ "\n", aw.written());
|
||||
aw.clearRetainingCapacity();
|
||||
}
|
||||
{
|
||||
const file = try test_dir.dir.createFile("very_long_first_line_spanning_multiple_pages.zig", .{});
|
||||
defer file.close();
|
||||
const path = try fs.path.join(allocator, &.{ test_dir_path, "very_long_first_line_spanning_multiple_pages.zig" });
|
||||
defer allocator.free(path);
|
||||
const path = try fs.path.join(gpa, &.{ test_dir_path, "very_long_first_line_spanning_multiple_pages.zig" });
|
||||
defer gpa.free(path);
|
||||
|
||||
var file_writer = file.writer(&.{});
|
||||
const writer = &file_writer.interface;
|
||||
try writer.splatByteAll('a', 3 * std.heap.page_size_max);
|
||||
|
||||
try expectError(error.EndOfFile, printLineFromFile(output_stream, .{ .file_name = path, .line = 2, .column = 0 }));
|
||||
try expectError(error.EndOfFile, printLineFromFile(io, output_stream, .{ .file_name = path, .line = 2, .column = 0 }));
|
||||
|
||||
try printLineFromFile(output_stream, .{ .file_name = path, .line = 1, .column = 0 });
|
||||
try printLineFromFile(io, output_stream, .{ .file_name = path, .line = 1, .column = 0 });
|
||||
try expectEqualStrings(("a" ** (3 * std.heap.page_size_max)) ++ "\n", aw.written());
|
||||
aw.clearRetainingCapacity();
|
||||
|
||||
try writer.writeAll("a\na");
|
||||
|
||||
try printLineFromFile(output_stream, .{ .file_name = path, .line = 1, .column = 0 });
|
||||
try printLineFromFile(io, output_stream, .{ .file_name = path, .line = 1, .column = 0 });
|
||||
try expectEqualStrings(("a" ** (3 * std.heap.page_size_max)) ++ "a\n", aw.written());
|
||||
aw.clearRetainingCapacity();
|
||||
|
||||
try printLineFromFile(output_stream, .{ .file_name = path, .line = 2, .column = 0 });
|
||||
try printLineFromFile(io, output_stream, .{ .file_name = path, .line = 2, .column = 0 });
|
||||
try expectEqualStrings("a\n", aw.written());
|
||||
aw.clearRetainingCapacity();
|
||||
}
|
||||
{
|
||||
const file = try test_dir.dir.createFile("file_of_newlines.zig", .{});
|
||||
defer file.close();
|
||||
const path = try fs.path.join(allocator, &.{ test_dir_path, "file_of_newlines.zig" });
|
||||
defer allocator.free(path);
|
||||
const path = try fs.path.join(gpa, &.{ test_dir_path, "file_of_newlines.zig" });
|
||||
defer gpa.free(path);
|
||||
|
||||
var file_writer = file.writer(&.{});
|
||||
const writer = &file_writer.interface;
|
||||
|
|
@ -1355,11 +1367,11 @@ test printLineFromFile {
|
|||
try writer.splatByteAll('\n', real_file_start);
|
||||
try writer.writeAll("abc\ndef");
|
||||
|
||||
try printLineFromFile(output_stream, .{ .file_name = path, .line = real_file_start + 1, .column = 0 });
|
||||
try printLineFromFile(io, output_stream, .{ .file_name = path, .line = real_file_start + 1, .column = 0 });
|
||||
try expectEqualStrings("abc\n", aw.written());
|
||||
aw.clearRetainingCapacity();
|
||||
|
||||
try printLineFromFile(output_stream, .{ .file_name = path, .line = real_file_start + 2, .column = 0 });
|
||||
try printLineFromFile(io, output_stream, .{ .file_name = path, .line = real_file_start + 2, .column = 0 });
|
||||
try expectEqualStrings("def\n", aw.written());
|
||||
aw.clearRetainingCapacity();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,9 +15,13 @@ const windows = std.os.windows;
|
|||
|
||||
const is_darwin = native_os.isDarwin();
|
||||
|
||||
pub const AtomicFile = @import("fs/AtomicFile.zig");
|
||||
pub const Dir = @import("fs/Dir.zig");
|
||||
pub const File = @import("fs/File.zig");
|
||||
/// Deprecated.
|
||||
pub const AtomicFile = std.Io.File.Atomic;
|
||||
/// Deprecated.
|
||||
pub const Dir = std.Io.Dir;
|
||||
/// Deprecated.
|
||||
pub const File = std.Io.File;
|
||||
|
||||
pub const path = @import("fs/path.zig");
|
||||
|
||||
pub const has_executable_bit = switch (native_os) {
|
||||
|
|
@ -153,42 +157,9 @@ pub fn deleteDirAbsoluteZ(dir_path: [*:0]const u8) !void {
|
|||
return posix.rmdirZ(dir_path);
|
||||
}
|
||||
|
||||
/// Same as `Dir.rename` except the paths are absolute.
|
||||
/// On Windows, both paths should be encoded as [WTF-8](https://wtf-8.codeberg.page/).
|
||||
/// On WASI, both paths should be encoded as valid UTF-8.
|
||||
/// On other platforms, both paths are an opaque sequence of bytes with no particular encoding.
|
||||
pub fn renameAbsolute(old_path: []const u8, new_path: []const u8) !void {
|
||||
assert(path.isAbsolute(old_path));
|
||||
assert(path.isAbsolute(new_path));
|
||||
return posix.rename(old_path, new_path);
|
||||
}
|
||||
|
||||
/// Same as `renameAbsolute` except the path parameters are null-terminated.
|
||||
pub fn renameAbsoluteZ(old_path: [*:0]const u8, new_path: [*:0]const u8) !void {
|
||||
assert(path.isAbsoluteZ(old_path));
|
||||
assert(path.isAbsoluteZ(new_path));
|
||||
return posix.renameZ(old_path, new_path);
|
||||
}
|
||||
|
||||
/// Same as `Dir.rename`, except `new_sub_path` is relative to `new_dir`
|
||||
pub fn rename(old_dir: Dir, old_sub_path: []const u8, new_dir: Dir, new_sub_path: []const u8) !void {
|
||||
return posix.renameat(old_dir.fd, old_sub_path, new_dir.fd, new_sub_path);
|
||||
}
|
||||
|
||||
/// Same as `rename` except the parameters are null-terminated.
|
||||
pub fn renameZ(old_dir: Dir, old_sub_path_z: [*:0]const u8, new_dir: Dir, new_sub_path_z: [*:0]const u8) !void {
|
||||
return posix.renameatZ(old_dir.fd, old_sub_path_z, new_dir.fd, new_sub_path_z);
|
||||
}
|
||||
|
||||
/// Deprecated in favor of `Io.Dir.cwd`.
|
||||
pub fn cwd() Dir {
|
||||
if (native_os == .windows) {
|
||||
return .{ .fd = windows.peb().ProcessParameters.CurrentDirectory.Handle };
|
||||
} else if (native_os == .wasi) {
|
||||
return .{ .fd = std.options.wasiCwd() };
|
||||
} else {
|
||||
return .{ .fd = posix.AT.FDCWD };
|
||||
}
|
||||
pub fn cwd() Io.Dir {
|
||||
return .cwd();
|
||||
}
|
||||
|
||||
pub fn defaultWasiCwd() std.os.wasi.fd_t {
|
||||
|
|
@ -209,23 +180,11 @@ pub fn openDirAbsolute(absolute_path: []const u8, flags: Dir.OpenOptions) File.O
|
|||
return cwd().openDir(absolute_path, flags);
|
||||
}
|
||||
|
||||
/// Same as `openDirAbsolute` but the path parameter is null-terminated.
|
||||
pub fn openDirAbsoluteZ(absolute_path_c: [*:0]const u8, flags: Dir.OpenOptions) File.OpenError!Dir {
|
||||
assert(path.isAbsoluteZ(absolute_path_c));
|
||||
return cwd().openDirZ(absolute_path_c, flags);
|
||||
}
|
||||
/// Opens a file for reading or writing, without attempting to create a new file, based on an absolute path.
|
||||
/// Call `File.close` to release the resource.
|
||||
/// Asserts that the path is absolute. See `Dir.openFile` for a function that
|
||||
/// operates on both absolute and relative paths.
|
||||
/// Asserts that the path parameter has no null bytes. See `openFileAbsoluteZ` for a function
|
||||
/// that accepts a null-terminated path.
|
||||
/// On Windows, `absolute_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/).
|
||||
/// On WASI, `absolute_path` should be encoded as valid UTF-8.
|
||||
/// On other platforms, `absolute_path` is an opaque sequence of bytes with no particular encoding.
|
||||
pub fn openFileAbsolute(absolute_path: []const u8, flags: File.OpenFlags) File.OpenError!File {
|
||||
assert(path.isAbsolute(absolute_path));
|
||||
return cwd().openFile(absolute_path, flags);
|
||||
/// Deprecated in favor of `Io.File.openAbsolute`.
|
||||
pub fn openFileAbsolute(absolute_path: []const u8, flags: File.OpenFlags) Io.File.OpenError!Io.File {
|
||||
var threaded: Io.Threaded = .init_single_threaded;
|
||||
const io = threaded.ioBasic();
|
||||
return Io.File.openAbsolute(io, absolute_path, flags);
|
||||
}
|
||||
|
||||
/// Test accessing `path`.
|
||||
|
|
|
|||
|
|
@ -1,94 +0,0 @@
|
|||
const AtomicFile = @This();
|
||||
const std = @import("../std.zig");
|
||||
const File = std.fs.File;
|
||||
const Dir = std.fs.Dir;
|
||||
const fs = std.fs;
|
||||
const assert = std.debug.assert;
|
||||
const posix = std.posix;
|
||||
|
||||
file_writer: File.Writer,
|
||||
random_integer: u64,
|
||||
dest_basename: []const u8,
|
||||
file_open: bool,
|
||||
file_exists: bool,
|
||||
close_dir_on_deinit: bool,
|
||||
dir: Dir,
|
||||
|
||||
pub const InitError = File.OpenError;
|
||||
|
||||
/// Note that the `Dir.atomicFile` API may be more handy than this lower-level function.
|
||||
pub fn init(
|
||||
dest_basename: []const u8,
|
||||
mode: File.Mode,
|
||||
dir: Dir,
|
||||
close_dir_on_deinit: bool,
|
||||
write_buffer: []u8,
|
||||
) InitError!AtomicFile {
|
||||
while (true) {
|
||||
const random_integer = std.crypto.random.int(u64);
|
||||
const tmp_sub_path = std.fmt.hex(random_integer);
|
||||
const file = dir.createFile(&tmp_sub_path, .{ .mode = mode, .exclusive = true }) catch |err| switch (err) {
|
||||
error.PathAlreadyExists => continue,
|
||||
else => |e| return e,
|
||||
};
|
||||
return .{
|
||||
.file_writer = file.writer(write_buffer),
|
||||
.random_integer = random_integer,
|
||||
.dest_basename = dest_basename,
|
||||
.file_open = true,
|
||||
.file_exists = true,
|
||||
.close_dir_on_deinit = close_dir_on_deinit,
|
||||
.dir = dir,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Always call deinit, even after a successful finish().
|
||||
pub fn deinit(af: *AtomicFile) void {
|
||||
if (af.file_open) {
|
||||
af.file_writer.file.close();
|
||||
af.file_open = false;
|
||||
}
|
||||
if (af.file_exists) {
|
||||
const tmp_sub_path = std.fmt.hex(af.random_integer);
|
||||
af.dir.deleteFile(&tmp_sub_path) catch {};
|
||||
af.file_exists = false;
|
||||
}
|
||||
if (af.close_dir_on_deinit) {
|
||||
af.dir.close();
|
||||
}
|
||||
af.* = undefined;
|
||||
}
|
||||
|
||||
pub const FlushError = File.WriteError;
|
||||
|
||||
pub fn flush(af: *AtomicFile) FlushError!void {
|
||||
af.file_writer.interface.flush() catch |err| switch (err) {
|
||||
error.WriteFailed => return af.file_writer.err.?,
|
||||
};
|
||||
}
|
||||
|
||||
pub const RenameIntoPlaceError = posix.RenameError;
|
||||
|
||||
/// On Windows, this function introduces a period of time where some file
|
||||
/// system operations on the destination file will result in
|
||||
/// `error.AccessDenied`, including rename operations (such as the one used in
|
||||
/// this function).
|
||||
pub fn renameIntoPlace(af: *AtomicFile) RenameIntoPlaceError!void {
|
||||
assert(af.file_exists);
|
||||
if (af.file_open) {
|
||||
af.file_writer.file.close();
|
||||
af.file_open = false;
|
||||
}
|
||||
const tmp_sub_path = std.fmt.hex(af.random_integer);
|
||||
try posix.renameat(af.dir.fd, &tmp_sub_path, af.dir.fd, af.dest_basename);
|
||||
af.file_exists = false;
|
||||
}
|
||||
|
||||
pub const FinishError = FlushError || RenameIntoPlaceError;
|
||||
|
||||
/// Combination of `flush` followed by `renameIntoPlace`.
|
||||
pub fn finish(af: *AtomicFile) FinishError!void {
|
||||
try af.flush();
|
||||
try af.renameIntoPlace();
|
||||
}
|
||||
2067
lib/std/fs/Dir.zig
2067
lib/std/fs/Dir.zig
File diff suppressed because it is too large
Load diff
|
|
@ -22,18 +22,10 @@ handle: Handle,
|
|||
pub const Handle = Io.File.Handle;
|
||||
pub const Mode = Io.File.Mode;
|
||||
pub const INode = Io.File.INode;
|
||||
pub const Uid = posix.uid_t;
|
||||
pub const Gid = posix.gid_t;
|
||||
pub const Uid = Io.File.Uid;
|
||||
pub const Gid = Io.File.Gid;
|
||||
pub const Kind = Io.File.Kind;
|
||||
|
||||
/// This is the default mode given to POSIX operating systems for creating
|
||||
/// files. `0o666` is "-rw-rw-rw-" which is counter-intuitive at first,
|
||||
/// since most people would expect "-rw-r--r--", for example, when using
|
||||
/// the `touch` command, which would correspond to `0o644`. However, POSIX
|
||||
/// libc implementations use `0o666` inside `fopen` and then rely on the
|
||||
/// process-scoped "umask" setting to adjust this number for file creation.
|
||||
pub const default_mode: Mode = if (Mode == u0) 0 else 0o666;
|
||||
|
||||
/// Deprecated in favor of `Io.File.OpenError`.
|
||||
pub const OpenError = Io.File.OpenError || error{WouldBlock};
|
||||
/// Deprecated in favor of `Io.File.OpenMode`.
|
||||
|
|
@ -43,53 +35,7 @@ pub const Lock = Io.File.Lock;
|
|||
/// Deprecated in favor of `Io.File.OpenFlags`.
|
||||
pub const OpenFlags = Io.File.OpenFlags;
|
||||
|
||||
pub const CreateFlags = struct {
|
||||
/// Whether the file will be created with read access.
|
||||
read: bool = false,
|
||||
|
||||
/// If the file already exists, and is a regular file, and the access
|
||||
/// mode allows writing, it will be truncated to length 0.
|
||||
truncate: bool = true,
|
||||
|
||||
/// Ensures that this open call creates the file, otherwise causes
|
||||
/// `error.PathAlreadyExists` to be returned.
|
||||
exclusive: bool = false,
|
||||
|
||||
/// Open the file with an advisory lock to coordinate with other processes
|
||||
/// accessing it at the same time. An exclusive lock will prevent other
|
||||
/// processes from acquiring a lock. A shared lock will prevent other
|
||||
/// processes from acquiring a exclusive lock, but does not prevent
|
||||
/// other process from getting their own shared locks.
|
||||
///
|
||||
/// The lock is advisory, except on Linux in very specific circumstances[1].
|
||||
/// This means that a process that does not respect the locking API can still get access
|
||||
/// to the file, despite the lock.
|
||||
///
|
||||
/// On these operating systems, the lock is acquired atomically with
|
||||
/// opening the file:
|
||||
/// * Darwin
|
||||
/// * DragonFlyBSD
|
||||
/// * FreeBSD
|
||||
/// * Haiku
|
||||
/// * NetBSD
|
||||
/// * OpenBSD
|
||||
/// On these operating systems, the lock is acquired via a separate syscall
|
||||
/// after opening the file:
|
||||
/// * Linux
|
||||
/// * Windows
|
||||
///
|
||||
/// [1]: https://www.kernel.org/doc/Documentation/filesystems/mandatory-locking.txt
|
||||
lock: Lock = .none,
|
||||
|
||||
/// Sets whether or not to wait until the file is locked to return. If set to true,
|
||||
/// `error.WouldBlock` will be returned. Otherwise, the file will wait until the file
|
||||
/// is available to proceed.
|
||||
lock_nonblocking: bool = false,
|
||||
|
||||
/// For POSIX systems this is the file system mode the file will
|
||||
/// be created with. On other systems this is always 0.
|
||||
mode: Mode = default_mode,
|
||||
};
|
||||
pub const CreateFlags = std.Io.File.CreateFlags;
|
||||
|
||||
pub fn stdout() File {
|
||||
return .{ .handle = if (is_windows) windows.peb().ProcessParameters.hStdOutput else posix.STDOUT_FILENO };
|
||||
|
|
@ -259,33 +205,6 @@ pub fn setEndPos(self: File, length: u64) SetEndPosError!void {
|
|||
try posix.ftruncate(self.handle, length);
|
||||
}
|
||||
|
||||
pub const SeekError = posix.SeekError;
|
||||
|
||||
/// Repositions read/write file offset relative to the current offset.
|
||||
/// TODO: integrate with async I/O
|
||||
pub fn seekBy(self: File, offset: i64) SeekError!void {
|
||||
return posix.lseek_CUR(self.handle, offset);
|
||||
}
|
||||
|
||||
/// Repositions read/write file offset relative to the end.
|
||||
/// TODO: integrate with async I/O
|
||||
pub fn seekFromEnd(self: File, offset: i64) SeekError!void {
|
||||
return posix.lseek_END(self.handle, offset);
|
||||
}
|
||||
|
||||
/// Repositions read/write file offset relative to the beginning.
|
||||
/// TODO: integrate with async I/O
|
||||
pub fn seekTo(self: File, offset: u64) SeekError!void {
|
||||
return posix.lseek_SET(self.handle, offset);
|
||||
}
|
||||
|
||||
pub const GetSeekPosError = posix.SeekError || StatError;
|
||||
|
||||
/// TODO: integrate with async I/O
|
||||
pub fn getPos(self: File) GetSeekPosError!u64 {
|
||||
return posix.lseek_CUR_get(self.handle);
|
||||
}
|
||||
|
||||
pub const GetEndPosError = std.os.windows.GetFileSizeError || StatError;
|
||||
|
||||
/// TODO: integrate with async I/O
|
||||
|
|
@ -306,11 +225,13 @@ pub fn mode(self: File) ModeError!Mode {
|
|||
return (try self.stat()).mode;
|
||||
}
|
||||
|
||||
/// Deprecated in favor of `Io.File.Stat`.
|
||||
pub const Stat = Io.File.Stat;
|
||||
|
||||
/// Deprecated in favor of `Io.File.StatError`.
|
||||
pub const StatError = posix.FStatError;
|
||||
|
||||
/// Returns `Stat` containing basic information about the `File`.
|
||||
/// Deprecated in favor of `Io.File.stat`.
|
||||
pub fn stat(self: File) StatError!Stat {
|
||||
var threaded: Io.Threaded = .init_single_threaded;
|
||||
const io = threaded.ioBasic();
|
||||
|
|
@ -710,7 +631,7 @@ pub const Writer = struct {
|
|||
Unexpected,
|
||||
};
|
||||
|
||||
pub const SeekError = File.SeekError;
|
||||
pub const SeekError = Io.File.SeekError;
|
||||
|
||||
/// Number of slices to store on the stack, when trying to send as many byte
|
||||
/// vectors through the underlying write calls as possible.
|
||||
|
|
@ -1268,10 +1189,8 @@ pub fn writerStreaming(file: File, buffer: []u8) Writer {
|
|||
const range_off: windows.LARGE_INTEGER = 0;
|
||||
const range_len: windows.LARGE_INTEGER = 1;
|
||||
|
||||
pub const LockError = error{
|
||||
SystemResources,
|
||||
FileLocksNotSupported,
|
||||
} || posix.UnexpectedError;
|
||||
/// Deprecated
|
||||
pub const LockError = Io.File.LockError;
|
||||
|
||||
/// Blocks when an incompatible lock is held by another process.
|
||||
/// A process may hold only one type of lock (shared or exclusive) on
|
||||
|
|
|
|||
|
|
@ -2022,21 +2022,6 @@ test "chown" {
|
|||
try dir.chown(null, null);
|
||||
}
|
||||
|
||||
test "delete a setAsCwd directory on Windows" {
|
||||
if (native_os != .windows) return error.SkipZigTest;
|
||||
|
||||
var tmp = tmpDir(.{});
|
||||
// Set tmp dir as current working directory.
|
||||
try tmp.dir.setAsCwd();
|
||||
tmp.dir.close();
|
||||
try testing.expectError(error.FileBusy, tmp.parent_dir.deleteTree(&tmp.sub_path));
|
||||
// Now set the parent dir as the current working dir for clean up.
|
||||
try tmp.parent_dir.setAsCwd();
|
||||
try tmp.parent_dir.deleteTree(&tmp.sub_path);
|
||||
// Close the parent "tmp" so we don't leak the HANDLE.
|
||||
tmp.parent_dir.close();
|
||||
}
|
||||
|
||||
test "invalid UTF-8/WTF-8 paths" {
|
||||
const expected_err = switch (native_os) {
|
||||
.wasi => error.BadPathName,
|
||||
|
|
|
|||
130
lib/std/os.zig
130
lib/std/os.zig
|
|
@ -21,7 +21,6 @@ const mem = std.mem;
|
|||
const elf = std.elf;
|
||||
const fs = std.fs;
|
||||
const dl = @import("dynamic_library.zig");
|
||||
const max_path_bytes = std.fs.max_path_bytes;
|
||||
const posix = std.posix;
|
||||
const native_os = builtin.os.tag;
|
||||
|
||||
|
|
@ -56,135 +55,6 @@ pub var argv: [][*:0]u8 = if (builtin.link_libc) undefined else switch (native_o
|
|||
else => undefined,
|
||||
};
|
||||
|
||||
pub fn isGetFdPathSupportedOnTarget(os: std.Target.Os) bool {
|
||||
return switch (os.tag) {
|
||||
.windows,
|
||||
.driverkit,
|
||||
.ios,
|
||||
.maccatalyst,
|
||||
.macos,
|
||||
.tvos,
|
||||
.visionos,
|
||||
.watchos,
|
||||
.linux,
|
||||
.illumos,
|
||||
.freebsd,
|
||||
.serenity,
|
||||
=> true,
|
||||
|
||||
.dragonfly => os.version_range.semver.max.order(.{ .major = 6, .minor = 0, .patch = 0 }) != .lt,
|
||||
.netbsd => os.version_range.semver.max.order(.{ .major = 10, .minor = 0, .patch = 0 }) != .lt,
|
||||
else => false,
|
||||
};
|
||||
}
|
||||
|
||||
/// Return canonical path of handle `fd`.
|
||||
///
|
||||
/// This function is very host-specific and is not universally supported by all hosts.
|
||||
/// For example, while it generally works on Linux, macOS, FreeBSD or Windows, it is
|
||||
/// unsupported on WASI.
|
||||
///
|
||||
/// * On Windows, the result is encoded as [WTF-8](https://wtf-8.codeberg.page/).
|
||||
/// * On other platforms, the result is an opaque sequence of bytes with no particular encoding.
|
||||
///
|
||||
/// Calling this function is usually a bug.
|
||||
pub fn getFdPath(fd: std.posix.fd_t, out_buffer: *[max_path_bytes]u8) std.posix.RealPathError![]u8 {
|
||||
if (!comptime isGetFdPathSupportedOnTarget(builtin.os)) {
|
||||
@compileError("querying for canonical path of a handle is unsupported on this host");
|
||||
}
|
||||
switch (native_os) {
|
||||
.windows => {
|
||||
var wide_buf: [windows.PATH_MAX_WIDE]u16 = undefined;
|
||||
const wide_slice = try windows.GetFinalPathNameByHandle(fd, .{}, wide_buf[0..]);
|
||||
|
||||
const end_index = std.unicode.wtf16LeToWtf8(out_buffer, wide_slice);
|
||||
return out_buffer[0..end_index];
|
||||
},
|
||||
.driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => {
|
||||
// On macOS, we can use F.GETPATH fcntl command to query the OS for
|
||||
// the path to the file descriptor.
|
||||
@memset(out_buffer[0..max_path_bytes], 0);
|
||||
switch (posix.errno(posix.system.fcntl(fd, posix.F.GETPATH, out_buffer))) {
|
||||
.SUCCESS => {},
|
||||
.BADF => return error.FileNotFound,
|
||||
.NOSPC => return error.NameTooLong,
|
||||
.NOENT => return error.FileNotFound,
|
||||
// TODO man pages for fcntl on macOS don't really tell you what
|
||||
// errno values to expect when command is F.GETPATH...
|
||||
else => |err| return posix.unexpectedErrno(err),
|
||||
}
|
||||
const len = mem.indexOfScalar(u8, out_buffer[0..], 0) orelse max_path_bytes;
|
||||
return out_buffer[0..len];
|
||||
},
|
||||
.linux, .serenity => {
|
||||
var procfs_buf: ["/proc/self/fd/-2147483648\x00".len]u8 = undefined;
|
||||
const proc_path = std.fmt.bufPrintSentinel(procfs_buf[0..], "/proc/self/fd/{d}", .{fd}, 0) catch unreachable;
|
||||
|
||||
const target = posix.readlinkZ(proc_path, out_buffer) catch |err| {
|
||||
switch (err) {
|
||||
error.NotLink => unreachable,
|
||||
error.BadPathName => unreachable,
|
||||
error.UnsupportedReparsePointType => unreachable, // Windows-only
|
||||
error.NetworkNotFound => unreachable, // Windows-only
|
||||
else => |e| return e,
|
||||
}
|
||||
};
|
||||
return target;
|
||||
},
|
||||
.illumos => {
|
||||
var procfs_buf: ["/proc/self/path/-2147483648\x00".len]u8 = undefined;
|
||||
const proc_path = std.fmt.bufPrintSentinel(procfs_buf[0..], "/proc/self/path/{d}", .{fd}, 0) catch unreachable;
|
||||
|
||||
const target = posix.readlinkZ(proc_path, out_buffer) catch |err| switch (err) {
|
||||
error.UnsupportedReparsePointType => unreachable,
|
||||
error.NotLink => unreachable,
|
||||
else => |e| return e,
|
||||
};
|
||||
return target;
|
||||
},
|
||||
.freebsd => {
|
||||
var kfile: std.c.kinfo_file = undefined;
|
||||
kfile.structsize = std.c.KINFO_FILE_SIZE;
|
||||
switch (posix.errno(std.c.fcntl(fd, std.c.F.KINFO, @intFromPtr(&kfile)))) {
|
||||
.SUCCESS => {},
|
||||
.BADF => return error.FileNotFound,
|
||||
else => |err| return posix.unexpectedErrno(err),
|
||||
}
|
||||
const len = mem.indexOfScalar(u8, &kfile.path, 0) orelse max_path_bytes;
|
||||
if (len == 0) return error.NameTooLong;
|
||||
const result = out_buffer[0..len];
|
||||
@memcpy(result, kfile.path[0..len]);
|
||||
return result;
|
||||
},
|
||||
.dragonfly => {
|
||||
@memset(out_buffer[0..max_path_bytes], 0);
|
||||
switch (posix.errno(std.c.fcntl(fd, posix.F.GETPATH, out_buffer))) {
|
||||
.SUCCESS => {},
|
||||
.BADF => return error.FileNotFound,
|
||||
.RANGE => return error.NameTooLong,
|
||||
else => |err| return posix.unexpectedErrno(err),
|
||||
}
|
||||
const len = mem.indexOfScalar(u8, out_buffer[0..], 0) orelse max_path_bytes;
|
||||
return out_buffer[0..len];
|
||||
},
|
||||
.netbsd => {
|
||||
@memset(out_buffer[0..max_path_bytes], 0);
|
||||
switch (posix.errno(std.c.fcntl(fd, posix.F.GETPATH, out_buffer))) {
|
||||
.SUCCESS => {},
|
||||
.ACCES => return error.AccessDenied,
|
||||
.BADF => return error.FileNotFound,
|
||||
.NOENT => return error.FileNotFound,
|
||||
.NOMEM => return error.SystemResources,
|
||||
.RANGE => return error.NameTooLong,
|
||||
else => |err| return posix.unexpectedErrno(err),
|
||||
}
|
||||
const len = mem.indexOfScalar(u8, out_buffer[0..], 0) orelse max_path_bytes;
|
||||
return out_buffer[0..len];
|
||||
},
|
||||
else => unreachable, // made unreachable by isGetFdPathSupportedOnTarget above
|
||||
}
|
||||
}
|
||||
|
||||
pub const FstatError = error{
|
||||
SystemResources,
|
||||
AccessDenied,
|
||||
|
|
|
|||
1014
lib/std/posix.zig
1014
lib/std/posix.zig
File diff suppressed because it is too large
Load diff
|
|
@ -786,7 +786,9 @@ test glibcVerFromLinkName {
|
|||
}
|
||||
|
||||
fn glibcVerFromRPath(io: Io, rpath: []const u8) !std.SemanticVersion {
|
||||
var dir = fs.cwd().openDir(rpath, .{}) catch |err| switch (err) {
|
||||
const cwd: Io.Dir = .cwd();
|
||||
|
||||
var dir = cwd.openDir(io, rpath, .{}) catch |err| switch (err) {
|
||||
error.NameTooLong => return error.Unexpected,
|
||||
error.BadPathName => return error.Unexpected,
|
||||
error.DeviceBusy => return error.Unexpected,
|
||||
|
|
@ -805,7 +807,7 @@ fn glibcVerFromRPath(io: Io, rpath: []const u8) !std.SemanticVersion {
|
|||
error.Unexpected => |e| return e,
|
||||
error.Canceled => |e| return e,
|
||||
};
|
||||
defer dir.close();
|
||||
defer dir.close(io);
|
||||
|
||||
// Now we have a candidate for the path to libc shared object. In
|
||||
// the past, we used readlink() here because the link name would
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue