Get bunx tests to pass on Windows (#9729)

* Get bunx tests to pass on Windows

* wip

* WIP

* wip

* wip

* ads

* asdf

* makeOpenPath

* almost revert

* fix build

* enoent

* fix bun install git repos

* cleanup

* use custom zig stdlib from submodule

* update dockerfile to copy zig stdlib sources

* fix dockerfile, update gitmodules

* fix dockerfile

* fix build

* fix build

* fix symlinkat

* fix build

* fix build

* Remove usages of unreachable

* Fixup

* Fixup

* wip

* fixup

* Fix one of the bugs

* asd

* Normalize BUN_INSTALL_CACHE_DIR var

* Set iterable to false when we're about to delete

* Update bun.zig

* I still can't repro this outside CI

* i think that fixes it?

* fix posix compile

* factor out directory creation

* update all install methods to use InstallDirState

* move walker creation to init function

* fix error cleanup

* fix posix compile

* all install tests pass locally

* cleanup

* [autofix.ci] apply automated fixes

* Fix posix regressions

---------

Co-authored-by: Dylan Conway <dylan.conway567@gmail.com>
Co-authored-by: Meghan Denny <hello@nektro.net>
Co-authored-by: Georgijs Vilums <georgijs.vilums@gmail.com>
Co-authored-by: Georgijs <48869301+gvilums@users.noreply.github.com>
Co-authored-by: Georgijs Vilums <georgijs@bun.sh>
Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
Jarred Sumner
2024-04-03 20:53:28 -07:00
committed by GitHub
parent 76795af695
commit 00f27fbeec
32 changed files with 1418 additions and 792 deletions

View File

@@ -63,43 +63,19 @@ pub const BunxCommand = struct {
const nanoseconds_cache_valid = seconds_cache_valid * 1000000000;
fn getBinNameFromSubpath(bundler: *bun.Bundler, dir_fd: bun.FileDescriptor, subpath_z: [:0]const u8) ![]const u8 {
const target_package_json_fd = try std.os.openatZ(dir_fd.cast(), subpath_z, std.os.O.RDONLY, 0);
const target_package_json = std.fs.File{ .handle = target_package_json_fd };
const is_stale = is_stale: {
if (Environment.isWindows) {
var io_status_block: std.os.windows.IO_STATUS_BLOCK = undefined;
var info: std.os.windows.FILE_BASIC_INFORMATION = undefined;
const rc = std.os.windows.ntdll.NtQueryInformationFile(target_package_json_fd, &io_status_block, &info, @sizeOf(std.os.windows.FILE_BASIC_INFORMATION), .FileBasicInformation);
switch (rc) {
.SUCCESS => {
const time = std.os.windows.fromSysTime(info.LastWriteTime);
const now = std.time.nanoTimestamp();
break :is_stale (now - time > nanoseconds_cache_valid);
},
// treat failures to stat as stale
else => break :is_stale true,
}
} else {
var stat: std.os.Stat = undefined;
const rc = std.c.fstat(target_package_json_fd, &stat);
if (rc != 0) {
break :is_stale true;
}
break :is_stale std.time.timestamp() - stat.mtime().tv_sec > seconds_cache_valid;
}
};
if (is_stale) {
target_package_json.close();
// If delete fails, oh well. Hope installation takes care of it.
dir_fd.asDir().deleteTree(subpath_z) catch {};
return error.NeedToInstall;
}
const target_package_json_fd = try bun.sys.openat(dir_fd, subpath_z, std.os.O.RDONLY, 0).unwrap();
const target_package_json = bun.sys.File{ .handle = target_package_json_fd };
defer target_package_json.close();
const package_json_contents = try target_package_json.readToEndAlloc(bundler.allocator, std.math.maxInt(u32));
const package_json_read = target_package_json.readToEnd(bundler.allocator);
// TODO: make this better
if (package_json_read.err) |err| {
try (bun.JSC.Maybe(void){ .err = err }).unwrap();
}
const package_json_contents = package_json_read.bytes.items;
const source = bun.logger.Source.initPathString(bun.span(subpath_z), package_json_contents);
bun.JSAst.Expr.Data.Store.create(default_allocator);
@@ -134,9 +110,9 @@ pub const BunxCommand = struct {
if (expr.asProperty("directories")) |dirs| {
if (dirs.expr.asProperty("bin")) |bin_prop| {
if (bin_prop.expr.asString(bundler.allocator)) |dir_name| {
const bin_dir = try std.os.openat(dir_fd.cast(), dir_name, std.os.O.RDONLY, 0);
defer std.os.close(bin_dir);
const dir = std.fs.Dir{ .fd = bin_dir };
const bin_dir = try bun.sys.openatA(dir_fd, dir_name, std.os.O.RDONLY | std.os.O.DIRECTORY, 0).unwrap();
defer _ = bun.sys.close(bin_dir);
const dir = std.fs.Dir{ .fd = bin_dir.cast() };
var iterator = bun.DirIterator.iterate(dir, .u8);
var entry = iterator.next();
while (true) : (entry = iterator.next()) {
@@ -159,17 +135,56 @@ pub const BunxCommand = struct {
fn getBinNameFromProjectDirectory(bundler: *bun.Bundler, dir_fd: bun.FileDescriptor, package_name: []const u8) ![]const u8 {
var subpath: [bun.MAX_PATH_BYTES]u8 = undefined;
const subpath_z = std.fmt.bufPrintZ(&subpath, "node_modules/{s}/package.json", .{package_name}) catch unreachable;
const subpath_z = std.fmt.bufPrintZ(&subpath, bun.pathLiteral("node_modules/{s}/package.json"), .{package_name}) catch unreachable;
return try getBinNameFromSubpath(bundler, dir_fd, subpath_z);
}
fn getBinNameFromTempDirectory(bundler: *bun.Bundler, tempdir_name: []const u8, package_name: []const u8) ![]const u8 {
fn getBinNameFromTempDirectory(bundler: *bun.Bundler, tempdir_name: []const u8, package_name: []const u8, with_stale_check: bool) ![]const u8 {
var subpath: [bun.MAX_PATH_BYTES]u8 = undefined;
if (with_stale_check) {
const subpath_z = std.fmt.bufPrintZ(
&subpath,
bun.pathLiteral("{s}/package.json"),
.{tempdir_name},
) catch unreachable;
const target_package_json_fd = bun.sys.openat(bun.toFD(std.fs.cwd().fd), subpath_z, std.os.O.RDONLY, 0).unwrap() catch return error.NeedToInstall;
const target_package_json = bun.sys.File{ .handle = target_package_json_fd };
const is_stale = is_stale: {
if (Environment.isWindows) {
var io_status_block: std.os.windows.IO_STATUS_BLOCK = undefined;
var info: std.os.windows.FILE_BASIC_INFORMATION = undefined;
const rc = std.os.windows.ntdll.NtQueryInformationFile(target_package_json_fd.cast(), &io_status_block, &info, @sizeOf(std.os.windows.FILE_BASIC_INFORMATION), .FileBasicInformation);
switch (rc) {
.SUCCESS => {
const time = std.os.windows.fromSysTime(info.LastWriteTime);
const now = std.time.nanoTimestamp();
break :is_stale (now - time > nanoseconds_cache_valid);
},
// treat failures to stat as stale
else => break :is_stale true,
}
} else {
const stat = target_package_json.stat().unwrap() catch break :is_stale true;
break :is_stale std.time.timestamp() - stat.mtime().tv_sec > seconds_cache_valid;
}
};
if (is_stale) {
_ = target_package_json.close();
// If delete fails, oh well. Hope installation takes care of it.
std.fs.cwd().deleteTree(tempdir_name) catch {};
return error.NeedToInstall;
}
_ = target_package_json.close();
}
const subpath_z = std.fmt.bufPrintZ(
&subpath,
"{s}/node_modules/{s}/package.json",
bun.pathLiteral("{s}/node_modules/{s}/package.json"),
.{ tempdir_name, package_name },
) catch unreachable;
return try getBinNameFromSubpath(bundler, bun.toFD(std.fs.cwd().fd), subpath_z);
}
@@ -182,7 +197,7 @@ pub const BunxCommand = struct {
return error.NoBinFound;
}
return getBinNameFromTempDirectory(bundler, tempdir_name, package_name) catch |err2| {
return getBinNameFromTempDirectory(bundler, tempdir_name, package_name, true) catch |err2| {
if (err2 == error.NoBinFound) {
return error.NoBinFound;
}
@@ -520,7 +535,7 @@ pub const BunxCommand = struct {
if (getBinName(&this_bundler, root_dir_fd, bunx_cache_dir, initial_bin_name)) |package_name_for_bin| {
// if we check the bin name and its actually the same, we don't need to check $PATH here again
if (!strings.eqlLong(package_name_for_bin, initial_bin_name, true)) {
absolute_in_cache_dir = std.fmt.bufPrint(&absolute_in_cache_dir_buf, "{s}/node_modules/.bin/{s}{s}", .{ bunx_cache_dir, package_name_for_bin, bun.exe_suffix }) catch unreachable;
absolute_in_cache_dir = std.fmt.bufPrint(&absolute_in_cache_dir_buf, bun.pathLiteral("{s}/node_modules/.bin/{s}{s}"), .{ bunx_cache_dir, package_name_for_bin, bun.exe_suffix }) catch unreachable;
// Only use the system-installed version if there is no version specified
if (update_request.version.literal.isEmpty()) {
@@ -559,7 +574,6 @@ pub const BunxCommand = struct {
}
}
}
const bunx_install_dir = try std.fs.cwd().makeOpenPath(bunx_cache_dir, .{});
create_package_json: {
@@ -674,7 +688,7 @@ pub const BunxCommand = struct {
}
// 2. The "bin" is possibly not the same as the package name, so we load the package.json to figure out what "bin" to use
if (getBinNameFromTempDirectory(&this_bundler, bunx_cache_dir, result_package_name)) |package_name_for_bin| {
if (getBinNameFromTempDirectory(&this_bundler, bunx_cache_dir, result_package_name, false)) |package_name_for_bin| {
if (!strings.eqlLong(package_name_for_bin, initial_bin_name, true)) {
absolute_in_cache_dir = std.fmt.bufPrint(&absolute_in_cache_dir_buf, "{s}/node_modules/.bin/{s}{s}", .{ bunx_cache_dir, package_name_for_bin, bun.exe_suffix }) catch unreachable;