move some of the installation from cmake to zig build

This moves the installation of shipped source files from large
CMakeLists.txt lists to zig build recursive directory installation.

On my computer a cmake `make install` takes 2.4 seconds even when it has
to do nothing, and prints a lot of unnecessary lines to stdout that say
"up-to-date: [some file it is installing]".

After this commit, the default output of `make` is down to 1
second, and it does not print any junk to stdout. Further, a `make
install` is no longer required and `make` is sufficient.

This closes #2874.

It also closes #2585. `make` now always invokes `zig build` for
installing files and libuserland.a, and zig's own caching system makes
that go fast.
master
Andrew Kelley 2019-07-14 03:06:20 -04:00
parent 7d9ee5d6d5
commit 6096dc5f94
No known key found for this signature in database
GPG Key ID: 7C5F548F728501A9
13 changed files with 414 additions and 6582 deletions

File diff suppressed because it is too large Load Diff

View File

@ -9,6 +9,7 @@ const ArrayList = std.ArrayList;
const Buffer = std.Buffer;
const io = std.io;
const fs = std.fs;
const InstallDirectoryOptions = std.build.InstallDirectoryOptions;
pub fn build(b: *Builder) !void {
b.setPreferredReleaseMode(.ReleaseFast);
@ -46,8 +47,6 @@ pub fn build(b: *Builder) !void {
.llvm_config_exe = nextValue(&index, build_info),
.lld_include_dir = nextValue(&index, build_info),
.lld_libraries = nextValue(&index, build_info),
.std_files = nextValue(&index, build_info),
.c_header_files = nextValue(&index, build_info),
.dia_guids_lib = nextValue(&index, build_info),
.llvm = undefined,
};
@ -64,8 +63,6 @@ pub fn build(b: *Builder) !void {
try configureStage2(b, test_stage2, ctx);
try configureStage2(b, exe, ctx);
b.default_step.dependOn(&exe.step);
addLibUserlandStep(b);
const skip_release = b.option(bool, "skip-release", "Main test suite skips release builds") orelse false;
@ -79,9 +76,38 @@ pub fn build(b: *Builder) !void {
//test_step.dependOn(&exe.step);
}
exe.install();
installStdLib(b, ctx.std_files);
installCHeaders(b, ctx.c_header_files);
const only_install_lib_files = b.option(bool, "lib-files-only", "Only install library files") orelse false;
if (!only_install_lib_files) {
b.default_step.dependOn(&exe.step);
exe.install();
}
b.installDirectory(InstallDirectoryOptions{
.source_dir = "c_headers",
.install_dir = .Lib,
.install_subdir = "zig" ++ fs.path.sep_str ++ "include",
});
b.installDirectory(InstallDirectoryOptions{
.source_dir = "libc",
.install_dir = .Lib,
.install_subdir = "zig" ++ fs.path.sep_str ++ "libc",
});
b.installDirectory(InstallDirectoryOptions{
.source_dir = "libcxx",
.install_dir = .Lib,
.install_subdir = "zig" ++ fs.path.sep_str ++ "libcxx",
});
b.installDirectory(InstallDirectoryOptions{
.source_dir = "libunwind",
.install_dir = .Lib,
.install_subdir = "zig" ++ fs.path.sep_str ++ "libunwind",
});
b.installDirectory(InstallDirectoryOptions{
.source_dir = "std",
.install_dir = .Lib,
.install_subdir = "zig" ++ fs.path.sep_str ++ "std",
.exclude_extensions = [_][]const u8{"test.zig"},
});
const test_filter = b.option([]const u8, "test-filter", "Skip tests that do not match filter");
@ -251,30 +277,6 @@ fn findLLVM(b: *Builder, llvm_config_exe: []const u8) !LibraryDep {
return result;
}
pub fn installStdLib(b: *Builder, stdlib_files: []const u8) void {
var it = mem.tokenize(stdlib_files, ";");
while (it.next()) |stdlib_file| {
const src_path = fs.path.join(b.allocator, [_][]const u8{ "std", stdlib_file }) catch unreachable;
const dest_path = fs.path.join(
b.allocator,
[_][]const u8{ "lib", "zig", "std", stdlib_file },
) catch unreachable;
b.installFile(src_path, dest_path);
}
}
pub fn installCHeaders(b: *Builder, c_header_files: []const u8) void {
var it = mem.tokenize(c_header_files, ";");
while (it.next()) |c_header_file| {
const src_path = fs.path.join(b.allocator, [_][]const u8{ "c_headers", c_header_file }) catch unreachable;
const dest_path = fs.path.join(
b.allocator,
[_][]const u8{ "lib", "zig", "include", c_header_file },
) catch unreachable;
b.installFile(src_path, dest_path);
}
}
fn nextValue(index: *usize, build_info: []const u8) []const u8 {
const start = index.*;
while (true) : (index.* += 1) {
@ -375,8 +377,6 @@ const Context = struct {
llvm_config_exe: []const u8,
lld_include_dir: []const u8,
lld_libraries: []const u8,
std_files: []const u8,
c_header_files: []const u8,
dia_guids_lib: []const u8,
llvm: LibraryDep,
};

View File

@ -23,7 +23,5 @@
#define ZIG_LLD_LIBRARIES "@LLD_LIBRARIES@"
#define ZIG_LLVM_CONFIG_EXE "@LLVM_CONFIG_EXE@"
#define ZIG_DIA_GUIDS_LIB "@ZIG_DIA_GUIDS_LIB_ESCAPED@"
#define ZIG_STD_FILES "@ZIG_STD_FILES@"
#define ZIG_C_HEADER_FILES "@ZIG_C_HEADER_FILES@"
#endif

View File

@ -305,14 +305,12 @@ int main(int argc, char **argv) {
Error err;
if (argc == 2 && strcmp(argv[1], "BUILD_INFO") == 0) {
printf("%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
printf("%s\n%s\n%s\n%s\n%s\n%s\n",
ZIG_CMAKE_BINARY_DIR,
ZIG_CXX_COMPILER,
ZIG_LLVM_CONFIG_EXE,
ZIG_LLD_INCLUDE_PATH,
ZIG_LLD_LIBRARIES,
ZIG_STD_FILES,
ZIG_C_HEADER_FILES,
ZIG_DIA_GUIDS_LIB);
return 0;
}

View File

@ -296,7 +296,7 @@ pub const Builder = struct {
if (self.verbose) {
warn("rm {}\n", full_path);
}
fs.deleteFile(full_path) catch {};
fs.deleteTree(self.allocator, full_path) catch {};
}
// TODO remove empty directories
@ -704,6 +704,10 @@ pub const Builder = struct {
self.getInstallStep().dependOn(&self.addInstallFileWithDir(src_path, .Prefix, dest_rel_path).step);
}
pub fn installDirectory(self: *Builder, options: InstallDirectoryOptions) void {
self.getInstallStep().dependOn(&self.addInstallDirectory(options).step);
}
///`dest_rel_path` is relative to bin path
pub fn installBinFile(self: *Builder, src_path: []const u8, dest_rel_path: []const u8) void {
self.getInstallStep().dependOn(&self.addInstallFileWithDir(src_path, .Bin, dest_rel_path).step);
@ -740,6 +744,12 @@ pub const Builder = struct {
return install_step;
}
pub fn addInstallDirectory(self: *Builder, options: InstallDirectoryOptions) *InstallDirStep {
const install_step = self.allocator.create(InstallDirStep) catch unreachable;
install_step.* = InstallDirStep.init(self, options);
return install_step;
}
pub fn pushInstalledFile(self: *Builder, dir: InstallDir, dest_rel_path: []const u8) void {
self.installed_files.append(InstalledFile{
.dir = dir,
@ -747,24 +757,14 @@ pub const Builder = struct {
}) catch unreachable;
}
fn copyFile(self: *Builder, source_path: []const u8, dest_path: []const u8) !void {
return self.copyFileMode(source_path, dest_path, File.default_mode);
}
fn copyFileMode(self: *Builder, source_path: []const u8, dest_path: []const u8, mode: File.Mode) !void {
fn updateFile(self: *Builder, source_path: []const u8, dest_path: []const u8) !void {
if (self.verbose) {
warn("cp {} {}\n", source_path, dest_path);
warn("cp {} {} ", source_path, dest_path);
}
const dirname = fs.path.dirname(dest_path) orelse ".";
const abs_source_path = self.pathFromRoot(source_path);
fs.makePath(self.allocator, dirname) catch |err| {
warn("Unable to create path {}: {}\n", dirname, @errorName(err));
return err;
};
fs.copyFileMode(abs_source_path, dest_path, mode) catch |err| {
warn("Unable to copy {} to {}: {}\n", abs_source_path, dest_path, @errorName(err));
return err;
const prev_status = try fs.updateFile(source_path, dest_path);
if (self.verbose) switch (prev_status) {
.stale => warn("# installed\n"),
.fresh => warn("# up-to-date\n"),
};
}
@ -1892,12 +1892,7 @@ pub const LibExeObjStep = struct {
try zig_args.append(builder.pathFromRoot(dir));
}
if (self.output_dir) |output_dir| {
try zig_args.append("--output-dir");
try zig_args.append(output_dir);
try builder.spawnChild(zig_args.toSliceConst());
} else if (self.kind == Kind.Test) {
if (self.kind == Kind.Test) {
try builder.spawnChild(zig_args.toSliceConst());
} else {
try zig_args.append("--cache");
@ -1905,7 +1900,16 @@ pub const LibExeObjStep = struct {
const output_path_nl = try builder.exec(zig_args.toSliceConst());
const output_path = mem.trimRight(u8, output_path_nl, "\r\n");
self.output_dir = fs.path.dirname(output_path).?;
if (self.output_dir) |output_dir| {
const full_dest = try fs.path.join(builder.allocator, [_][]const u8{
output_dir,
fs.path.basename(output_path),
});
try builder.updateFile(output_path, full_dest);
} else {
self.output_dir = fs.path.dirname(output_path).?;
}
}
if (self.kind == Kind.Lib and self.is_dynamic and self.target.wantSharedLibSymLinks()) {
@ -2077,23 +2081,14 @@ const InstallArtifactStep = struct {
const self = @fieldParentPtr(Self, "step", step);
const builder = self.builder;
const mode = switch (builtin.os) {
.windows => {},
else => switch (self.artifact.kind) {
.Obj => unreachable,
.Test => unreachable,
.Exe => u32(0o755),
.Lib => if (!self.artifact.is_dynamic) u32(0o666) else u32(0o755),
},
};
const full_dest_path = builder.getInstallPath(self.dest_dir, self.artifact.out_filename);
try builder.copyFileMode(self.artifact.getOutputPath(), full_dest_path, mode);
try builder.updateFile(self.artifact.getOutputPath(), full_dest_path);
if (self.artifact.isDynamicLibrary()) {
try doAtomicSymLinks(builder.allocator, full_dest_path, self.artifact.major_only_filename, self.artifact.name_only_filename);
}
if (self.pdb_dir) |pdb_dir| {
const full_pdb_path = builder.getInstallPath(pdb_dir, self.artifact.out_pdb_filename);
try builder.copyFile(self.artifact.getOutputPdbPath(), full_pdb_path);
try builder.updateFile(self.artifact.getOutputPdbPath(), full_pdb_path);
}
self.artifact.installed_path = full_dest_path;
}
@ -2125,7 +2120,55 @@ pub const InstallFileStep = struct {
fn make(step: *Step) !void {
const self = @fieldParentPtr(InstallFileStep, "step", step);
const full_dest_path = self.builder.getInstallPath(self.dir, self.dest_rel_path);
try self.builder.copyFile(self.src_path, full_dest_path);
const full_src_path = self.builder.pathFromRoot(self.src_path);
try self.builder.updateFile(full_src_path, full_dest_path);
}
};
pub const InstallDirectoryOptions = struct {
source_dir: []const u8,
install_dir: InstallDir,
install_subdir: []const u8,
exclude_extensions: ?[]const []const u8 = null,
};
pub const InstallDirStep = struct {
step: Step,
builder: *Builder,
options: InstallDirectoryOptions,
pub fn init(
builder: *Builder,
options: InstallDirectoryOptions,
) InstallDirStep {
builder.pushInstalledFile(options.install_dir, options.install_subdir);
return InstallDirStep{
.builder = builder,
.step = Step.init(builder.fmt("install {}/", options.source_dir), builder.allocator, make),
.options = options,
};
}
fn make(step: *Step) !void {
const self = @fieldParentPtr(InstallDirStep, "step", step);
const dest_prefix = self.builder.getInstallPath(self.options.install_dir, self.options.install_subdir);
const full_src_dir = self.builder.pathFromRoot(self.options.source_dir);
var it = try fs.walkPath(self.builder.allocator, full_src_dir);
next_entry: while (try it.next()) |entry| {
if (self.options.exclude_extensions) |ext_list| for (ext_list) |ext| {
if (mem.endsWith(u8, entry.path, ext)) {
continue :next_entry;
}
};
const rel_path = entry.path[full_src_dir.len + 1 ..];
const dest_path = try fs.path.join(self.builder.allocator, [_][]const u8{ dest_prefix, rel_path });
switch (entry.kind) {
.Directory => try fs.makePath(self.builder.allocator, dest_path),
.File => try self.builder.updateFile(entry.path, dest_path),
else => continue,
}
}
}
};

View File

@ -124,6 +124,9 @@ pub extern "c" fn realloc(?*c_void, usize) ?*c_void;
pub extern "c" fn free(*c_void) void;
pub extern "c" fn posix_memalign(memptr: **c_void, alignment: usize, size: usize) c_int;
pub extern "c" fn utimensat(dirfd: fd_t, pathname: [*]const u8, times: *[2]timespec, flags: u32) c_int;
pub extern "c" fn futimens(fd: fd_t, times: *const [2]timespec) c_int;
pub extern "c" fn pthread_create(noalias newthread: *pthread_t, noalias attr: ?*const pthread_attr_t, start_routine: extern fn (?*c_void) ?*c_void, noalias arg: ?*c_void) c_int;
pub extern "c" fn pthread_attr_init(attr: *pthread_attr_t) c_int;
pub extern "c" fn pthread_attr_setstack(attr: *pthread_attr_t, stackaddr: *c_void, stacksize: usize) c_int;

View File

@ -70,6 +70,61 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path:
}
}
// TODO fix enum literal not casting to error union
const PrevStatus = enum {
stale,
fresh,
};
pub fn updateFile(source_path: []const u8, dest_path: []const u8) !PrevStatus {
return updateFileMode(source_path, dest_path, null);
}
/// Check the file size and mtime of `source_path` and `dest_path`. If they are equal, does nothing.
/// Otherwise, atomically copies `source_path` to `dest_path`. The destination file gains the mtime
/// and atime of the source file so that the next call to `updateFile` will not need a copy.
/// TODO https://github.com/ziglang/zig/issues/2885
/// Returns the previous status of the file before updating.
pub fn updateFileMode(source_path: []const u8, dest_path: []const u8, mode: ?File.Mode) !PrevStatus {
var src_file = try File.openRead(source_path);
defer src_file.close();
const src_stat = try src_file.stat();
check_dest_stat: {
const dest_stat = blk: {
var dest_file = File.openRead(dest_path) catch |err| switch (err) {
error.FileNotFound => break :check_dest_stat,
else => |e| return e,
};
defer dest_file.close();
break :blk try dest_file.stat();
};
if (src_stat.size == dest_stat.size and
src_stat.mtime == dest_stat.mtime and
src_stat.mode == dest_stat.mode)
{
return PrevStatus.fresh;
}
}
const in_stream = &src_file.inStream().stream;
var atomic_file = try AtomicFile.init(dest_path, mode orelse src_stat.mode);
defer atomic_file.deinit();
var buf: [mem.page_size * 6]u8 = undefined;
while (true) {
const amt = try in_stream.readFull(buf[0..]);
try atomic_file.file.write(buf[0..amt]);
if (amt != buf.len) {
try atomic_file.file.updateTimes(src_stat.atime, src_stat.mtime);
try atomic_file.finish();
return PrevStatus.stale;
}
}
}
/// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is
/// merged and readily available,
/// there is a possibility of power loss or application termination leaving temporary files present
@ -105,7 +160,7 @@ pub fn copyFileMode(source_path: []const u8, dest_path: []const u8, mode: File.M
var atomic_file = try AtomicFile.init(dest_path, mode);
defer atomic_file.deinit();
var buf: [mem.page_size]u8 = undefined;
var buf: [mem.page_size * 6]u8 = undefined;
while (true) {
const amt = try in_file.read(buf[0..]);
try atomic_file.file.write(buf[0..amt]);
@ -256,9 +311,6 @@ pub fn deleteDirW(dir_path: [*]const u16) !void {
return os.rmdirW(dir_path);
}
/// Whether ::full_path describes a symlink, file, or directory, this function
/// removes it. If it cannot be removed because it is a non-empty directory,
/// this function recursively removes its entries and then tries again.
const DeleteTreeError = error{
OutOfMemory,
AccessDenied,
@ -290,7 +342,11 @@ const DeleteTreeError = error{
Unexpected,
};
/// Whether `full_path` describes a symlink, file, or directory, this function
/// removes it. If it cannot be removed because it is a non-empty directory,
/// this function recursively removes its entries and then tries again.
/// TODO determine if we can remove the allocator requirement
/// https://github.com/ziglang/zig/issues/2886
pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!void {
start_over: while (true) {
var got_access_denied = false;
@ -427,7 +483,9 @@ pub const Dir = struct {
Unexpected,
};
/// Call close when done.
/// TODO remove the allocator requirement from this API
/// https://github.com/ziglang/zig/issues/2885
pub fn open(allocator: *Allocator, dir_path: []const u8) OpenError!Dir {
return Dir{
.allocator = allocator,
@ -683,13 +741,98 @@ pub const Dir = struct {
}
};
pub const Walker = struct {
stack: std.ArrayList(StackItem),
name_buffer: std.Buffer,
pub const Entry = struct {
path: []const u8,
basename: []const u8,
kind: Dir.Entry.Kind,
};
const StackItem = struct {
dir_it: Dir,
dirname_len: usize,
};
/// After each call to this function, and on deinit(), the memory returned
/// from this function becomes invalid. A copy must be made in order to keep
/// a reference to the path.
pub fn next(self: *Walker) !?Entry {
while (true) {
if (self.stack.len == 0) return null;
// `top` becomes invalid after appending to `self.stack`.
const top = &self.stack.toSlice()[self.stack.len - 1];
const dirname_len = top.dirname_len;
if (try top.dir_it.next()) |base| {
self.name_buffer.shrink(dirname_len);
try self.name_buffer.appendByte(path.sep);
try self.name_buffer.append(base.name);
if (base.kind == .Directory) {
// TODO https://github.com/ziglang/zig/issues/2888
var new_dir = try Dir.open(self.stack.allocator, self.name_buffer.toSliceConst());
{
errdefer new_dir.close();
try self.stack.append(StackItem{
.dir_it = new_dir,
.dirname_len = self.name_buffer.len(),
});
}
}
return Entry{
.basename = self.name_buffer.toSliceConst()[dirname_len + 1 ..],
.path = self.name_buffer.toSliceConst(),
.kind = base.kind,
};
} else {
self.stack.pop().dir_it.close();
}
}
}
pub fn deinit(self: *Walker) void {
while (self.stack.popOrNull()) |*item| item.dir_it.close();
self.stack.deinit();
self.name_buffer.deinit();
}
};
/// Recursively iterates over a directory.
/// Must call `Walker.deinit` when done.
/// `dir_path` must not end in a path separator.
/// TODO: https://github.com/ziglang/zig/issues/2888
pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker {
assert(!mem.endsWith(u8, dir_path, path.sep_str));
var dir_it = try Dir.open(allocator, dir_path);
errdefer dir_it.close();
var name_buffer = try std.Buffer.init(allocator, dir_path);
errdefer name_buffer.deinit();
var walker = Walker{
.stack = std.ArrayList(Walker.StackItem).init(allocator),
.name_buffer = name_buffer,
};
try walker.stack.append(Walker.StackItem{
.dir_it = dir_it,
.dirname_len = dir_path.len,
});
return walker;
}
/// Read value of a symbolic link.
/// The return value is a slice of buffer, from index `0`.
/// TODO https://github.com/ziglang/zig/issues/2888
pub fn readLink(pathname: []const u8, buffer: *[os.PATH_MAX]u8) ![]u8 {
return os.readlink(pathname, buffer);
}
/// Same as `readLink`, except the `pathname` parameter is null-terminated.
/// TODO https://github.com/ziglang/zig/issues/2888
pub fn readLinkC(pathname: [*]const u8, buffer: *[os.PATH_MAX]u8) ![]u8 {
return os.readlinkC(pathname, buffer);
}

View File

@ -207,8 +207,7 @@ pub const File = struct {
if (windows.is_the_target) {
return windows.GetFileSizeEx(self.handle);
}
const stat = try os.fstat(self.handle);
return @bitCast(u64, stat.size);
return (try self.stat()).size;
}
pub const ModeError = os.FStatError;
@ -217,10 +216,63 @@ pub const File = struct {
if (windows.is_the_target) {
return {};
}
const stat = try os.fstat(self.handle);
// TODO: we should be able to cast u16 to ModeError!u32, making this
// explicit cast not necessary
return Mode(stat.mode);
return (try self.stat()).mode;
}
pub const Stat = struct {
size: u64,
mode: Mode,
/// access time in nanoseconds
atime: u64,
/// last modification time in nanoseconds
mtime: u64,
/// creation time in nanoseconds
ctime: u64,
};
pub const StatError = os.FStatError;
pub fn stat(self: File) StatError!Stat {
if (windows.is_the_target) {
const info = try windows.GetFileInformationByHandle(self.handle);
return Stat{
.size = (u64(info.nFileSizeHigh) << 32) | u64(info.nFileSizeLow),
.mode = {},
.atime = windows.fileTimeToNanoSeconds(info.ftLastAccessTime),
.mtime = windows.fileTimeToNanoSeconds(info.ftLastWriteTime),
.ctime = windows.fileTimeToNanoSeconds(info.ftCreationTime),
};
}
const st = try os.fstat(self.handle);
return Stat{
.size = @bitCast(u64, st.size),
.mode = st.mode,
.atime = @bitCast(usize, st.atim.tv_sec) * std.time.ns_per_s + @bitCast(usize, st.atim.tv_nsec),
.mtime = @bitCast(usize, st.mtim.tv_sec) * std.time.ns_per_s + @bitCast(usize, st.mtim.tv_nsec),
.ctime = @bitCast(usize, st.ctim.tv_sec) * std.time.ns_per_s + @bitCast(usize, st.ctim.tv_nsec),
};
}
pub const UpdateTimesError = os.FutimensError;
/// `atime`: access timestamp in nanoseconds
/// `mtime`: last modification timestamp in nanoseconds
pub fn updateTimes(self: File, atime: u64, mtime: u64) UpdateTimesError!void {
const times = [2]os.timespec{
os.timespec{
.tv_sec = @bitCast(isize, atime / std.time.ns_per_s),
.tv_nsec = @bitCast(isize, atime % std.time.ns_per_s),
},
os.timespec{
.tv_sec = @bitCast(isize, mtime / std.time.ns_per_s),
.tv_nsec = @bitCast(isize, mtime % std.time.ns_per_s),
},
};
try os.futimens(self.handle, &times);
}
pub const ReadError = os.ReadError;

View File

@ -2546,6 +2546,45 @@ pub fn sigaction(sig: u6, act: *const Sigaction, oact: ?*Sigaction) void {
}
}
pub const FutimensError = error{
/// times is NULL, or both tv_nsec values are UTIME_NOW, and either:
/// * the effective user ID of the caller does not match the owner
/// of the file, the caller does not have write access to the
/// file, and the caller is not privileged (Linux: does not have
/// either the CAP_FOWNER or the CAP_DAC_OVERRIDE capability);
/// or,
/// * the file is marked immutable (see chattr(1)).
AccessDenied,
/// The caller attempted to change one or both timestamps to a value
/// other than the current time, or to change one of the timestamps
/// to the current time while leaving the other timestamp unchanged,
/// (i.e., times is not NULL, neither tv_nsec field is UTIME_NOW,
/// and neither tv_nsec field is UTIME_OMIT) and either:
/// * the caller's effective user ID does not match the owner of
/// file, and the caller is not privileged (Linux: does not have
/// the CAP_FOWNER capability); or,
/// * the file is marked append-only or immutable (see chattr(1)).
PermissionDenied,
ReadOnlyFileSystem,
Unexpected,
};
pub fn futimens(fd: fd_t, times: *const [2]timespec) FutimensError!void {
switch (errno(system.futimens(fd, times))) {
0 => return,
EACCES => return error.AccessDenied,
EPERM => return error.PermissionDenied,
EBADF => unreachable, // always a race condition
EFAULT => unreachable,
EINVAL => unreachable,
EROFS => return error.ReadOnlyFileSystem,
else => |err| return unexpectedErrno(err),
}
}
test "" {
_ = @import("os/darwin.zig");
_ = @import("os/freebsd.zig");

View File

@ -94,6 +94,15 @@ pub inline fn vfork() usize {
return @inlineCall(syscall0, SYS_vfork);
}
pub fn futimens(fd: i32, times: *const [2]timespec) usize {
return utimensat(fd, null, times, 0);
}
// TODO https://github.com/ziglang/zig/issues/265
pub fn utimensat(dirfd: i32, path: ?[*]const u8, times: *const [2]timespec, flags: u32) usize {
return syscall4(SYS_utimensat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), @ptrToInt(times), flags);
}
pub fn futex_wait(uaddr: *const i32, futex_op: u32, val: i32, timeout: ?*timespec) usize {
return syscall4(SYS_futex, @ptrToInt(uaddr), futex_op, @bitCast(u32, val), @ptrToInt(timeout));
}

View File

@ -752,6 +752,26 @@ pub fn HeapDestroy(hHeap: HANDLE) void {
assert(kernel32.HeapDestroy(hHeap) != 0);
}
pub const GetFileInformationByHandleError = error{Unexpected};
pub fn GetFileInformationByHandle(
hFile: HANDLE,
) GetFileInformationByHandleError!BY_HANDLE_FILE_INFORMATION {
var info: BY_HANDLE_FILE_INFORMATION = undefined;
const rc = kernel32.GetFileInformationByHandle(hFile, &info);
if (rc == 0) {
switch (kernel32.GetLastError()) {
else => |err| return unexpectedError(err),
}
}
return info;
}
pub fn fileTimeToNanoSeconds(ft: FILETIME) u64 {
const sec = (u64(ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
return sec * std.time.ns_per_s;
}
pub fn cStrToPrefixedFileW(s: [*]const u8) ![PATH_MAX_WIDE + 1]u16 {
return sliceToPrefixedFileW(mem.toSliceConst(u8, s));
}

View File

@ -104,6 +104,19 @@ pub const FileIdInfo = 18;
pub const FileIdExtdDirectoryInfo = 19;
pub const FileIdExtdDirectoryRestartInfo = 20;
pub const BY_HANDLE_FILE_INFORMATION = extern struct {
dwFileAttributes: DWORD,
ftCreationTime: FILETIME,
ftLastAccessTime: FILETIME,
ftLastWriteTime: FILETIME,
dwVolumeSerialNumber: DWORD,
nFileSizeHigh: DWORD,
nFileSizeLow: DWORD,
nNumberOfLinks: DWORD,
nFileIndexHigh: DWORD,
nFileIndexLow: DWORD,
};
pub const FILE_NAME_INFO = extern struct {
FileNameLength: DWORD,
FileName: [1]WCHAR,

View File

@ -83,6 +83,11 @@ pub extern "kernel32" stdcallcc fn GetModuleHandleW(lpModuleName: ?[*]const WCHA
pub extern "kernel32" stdcallcc fn GetLastError() DWORD;
pub extern "kernel32" stdcallcc fn GetFileInformationByHandle(
hFile: HANDLE,
lpFileInformation: *BY_HANDLE_FILE_INFORMATION,
) BOOL;
pub extern "kernel32" stdcallcc fn GetFileInformationByHandleEx(
in_hFile: HANDLE,
in_FileInformationClass: FILE_INFO_BY_HANDLE_CLASS,