From 400bc949e6c4edd02742aec06dd51c9e4a6d6a7c Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Wed, 6 Sep 2023 06:37:59 -0800 Subject: [PATCH] more things work --- .vscode/settings.json | 4 +- src/bun.js/node/node_fs.zig | 36 +++++++++-- src/bun.zig | 18 ++++++ src/bundler/bundle_v2.zig | 4 +- src/cli.zig | 12 ++-- src/cli/run_command.zig | 19 ++++-- src/cli/upgrade_command.zig | 120 ++++++++++++++++++++++++++---------- src/deps/uws.zig | 8 +++ src/fs.zig | 9 +-- src/install/install.zig | 12 ++-- src/io/io_darwin.zig | 11 ++-- src/io/io_linux.zig | 4 +- src/io/io_windows.zig | 4 +- src/network_thread.zig | 2 +- src/string_immutable.zig | 8 +++ 15 files changed, 200 insertions(+), 71 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 0e97b5d53b..425a6d1005 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -239,7 +239,9 @@ "xstring": "cpp", "xtree": "cpp", "xutility": "cpp", - "string.h": "c" + "string.h": "c", + "zutil.h": "c", + "gzguts.h": "c" }, "cmake.configureOnOpen": false, "C_Cpp.errorSquiggles": "enabled", diff --git a/src/bun.js/node/node_fs.zig b/src/bun.js/node/node_fs.zig index c8e0ba1f14..968738bd89 100644 --- a/src/bun.js/node/node_fs.zig +++ b/src/bun.js/node/node_fs.zig @@ -4,6 +4,7 @@ const std = @import("std"); const bun = @import("root").bun; const strings = bun.strings; +const windows = bun.windows; const string = bun.string; const AsyncIO = @import("root").bun.AsyncIO; const JSC = @import("root").bun.JSC; @@ -3545,13 +3546,16 @@ pub const NodeFS = struct { _ = flavor; const ret = Maybe(Return.CopyFile); - var src_buf: [bun.MAX_PATH_BYTES]u8 = undefined; - var dest_buf: [bun.MAX_PATH_BYTES]u8 = undefined; - var src = args.src.sliceZ(&src_buf); - var dest = args.dest.sliceZ(&dest_buf); // TODO: do we need to fchown? if (comptime Environment.isMac) { + + var src_buf: [bun.MAX_PATH_BYTES]u8 = undefined; + var dest_buf: [bun.MAX_PATH_BYTES]u8 = undefined; + + var src = args.src.sliceZ(&src_buf); + var dest = args.dest.sliceZ(&dest_buf); + if (args.mode.isForceClone()) { // https://www.manpagez.com/man/2/clonefile/ return ret.errnoSysP(C.clonefile(src, dest, 0), .clonefile, src) orelse ret.success; @@ -3618,6 +3622,12 @@ pub const NodeFS = struct { } if (comptime Environment.isLinux) { + + var src_buf: [bun.MAX_PATH_BYTES]u8 = undefined; + var dest_buf: [bun.MAX_PATH_BYTES]u8 = undefined; + var src = args.src.sliceZ(&src_buf); + var dest = args.dest.sliceZ(&dest_buf); + // https://manpages.debian.org/testing/manpages-dev/ioctl_ficlone.2.en.html if (args.mode.isForceClone()) { return Maybe(Return.CopyFile).todo; @@ -3702,6 +3712,24 @@ pub const NodeFS = struct { return ret.success; } + if (comptime Environment.isWindows) { + if (args.mode.isForceClone()) { + return Maybe(Return.CopyFile).todo; + } + + var src_buf: bun.MAX_WPATH = undefined; + var dest_buf: bun.MAX_WPATH = undefined; + var src = strings.toWPathNormalizeAutoExtend(&src_buf, args.src.slice()); + var dest = strings.toWPathNormalizeAutoExtend(&dest_buf, args.dest.slice()); + if (windows.CopyFileW(src.ptr, dest.ptr, if (args.mode.shouldntOverwrite()) 1 else 0) == windows.FALSE) { + if (ret.errnoSysP(0, .copyfile, args.src.slice())) |rest| { + return rest; + } + } + + return ret.success; + } + return Maybe(Return.CopyFile).todo; } diff --git a/src/bun.zig b/src/bun.zig index 755affc005..a0dd645c2b 100644 --- a/src/bun.zig +++ b/src/bun.zig @@ -753,6 +753,24 @@ pub fn openFileZ(pathZ: [:0]const u8, open_flags: std.fs.File.OpenFlags) !std.fs return std.fs.File{ .handle = fdcast(res.result) }; } + +pub fn openFile(path_: []const u8, open_flags: std.fs.File.OpenFlags) !std.fs.File { + if (comptime Environment.isWindows) { + var flags: Mode = 0; + switch (open_flags.mode) { + .read_only => flags |= std.os.O.RDONLY, + .write_only => flags |= std.os.O.WRONLY, + .read_write => flags |= std.os.O.RDWR, + } + + const res = sys.openA(path_, flags, 0); + try res.throw(); + return std.fs.File{ .handle = fdcast(res.result) }; + } + + return try openFileZ(try std.os.toPosixPath(path_), open_flags); +} + pub fn openDir(dir: std.fs.Dir, path_: [:0]const u8) !std.fs.IterableDir { if (comptime Environment.isWindows) { const res = sys.openDirAtWindowsA(toFD(dir.fd), path_, true, false); diff --git a/src/bundler/bundle_v2.zig b/src/bundler/bundle_v2.zig index 0f3257b717..48e6383620 100644 --- a/src/bundler/bundle_v2.zig +++ b/src/bundler/bundle_v2.zig @@ -1164,7 +1164,7 @@ pub const BundleV2 = struct { thread.detach(); } else { BundleThread.instance.queue.push(completion); - BundleThread.instance.waker.wake() catch {}; + BundleThread.instance.waker.wake(); } completion.poll_ref.ref(globalThis.bunVM()); @@ -1567,7 +1567,7 @@ pub const BundleV2 = struct { if (any) { bun.Mimalloc.mi_collect(false); } - _ = instance.waker.wait() catch 0; + _ = instance.waker.wait(); } } diff --git a/src/cli.zig b/src/cli.zig index 493d91c8b7..06abe8516e 100644 --- a/src/cli.zig +++ b/src/cli.zig @@ -112,7 +112,7 @@ pub const Arguments = struct { var paths = [_]string{ cwd, filename }; const outpath = try std.fs.path.resolve(allocator, &paths); defer allocator.free(outpath); - var file = try std.fs.openFileAbsolute(outpath, std.fs.File.OpenFlags{ .mode = .read_only }); + var file =try bun.openFileZ(&try std.os.toPosixPath( outpath), std.fs.File.OpenFlags{ .mode = .read_only }); defer file.close(); const size = try file.getEndPos(); return try file.readToEndAlloc(allocator, size); @@ -1568,9 +1568,9 @@ pub const Command = struct { const script_name_to_search = ctx.args.entry_points[0]; var file_path = script_name_to_search; - const file_: std.fs.File.OpenError!std.fs.File = brk: { - if (script_name_to_search[0] == std.fs.path.sep) { - break :brk std.fs.openFileAbsolute(script_name_to_search, .{ .mode = .read_only }); + const file_: anyerror!std.fs.File = brk: { + if (std.fs.path.isAbsoluteWindows(script_name_to_search)) { + break :brk bun.openFile(script_name_to_search, .{ .mode = .read_only }); } else if (!strings.hasPrefix(script_name_to_search, "..") and script_name_to_search[0] != '~') { const file_pathZ = brk2: { if (!strings.hasPrefix(file_path, "./")) { @@ -1585,7 +1585,7 @@ pub const Command = struct { } }; - break :brk std.fs.cwd().openFileZ(file_pathZ, .{ .mode = .read_only }); + break :brk bun.openFileZ(file_pathZ, .{ .mode = .read_only }); } else { var path_buf: [bun.MAX_PATH_BYTES]u8 = undefined; const cwd = bun.getcwd(&path_buf) catch return false; @@ -1600,7 +1600,7 @@ pub const Command = struct { if (file_path.len == 0) return false; script_name_buf[file_path.len] = 0; var file_pathZ = script_name_buf[0..file_path.len :0]; - break :brk std.fs.openFileAbsoluteZ(file_pathZ, .{ .mode = .read_only }); + break :brk bun.openFileZ(file_pathZ, .{ .mode = .read_only }); } }; diff --git a/src/cli/run_command.zig b/src/cli/run_command.zig index c2afcdd85c..400ed3fd13 100644 --- a/src/cli/run_command.zig +++ b/src/cli/run_command.zig @@ -911,10 +911,11 @@ pub const RunCommand = struct { } var file_path = script_name_to_search; - - const file_: std.fs.File.OpenError!std.fs.File = brk: { - if (script_name_to_search[0] == std.fs.path.sep) { - break :brk std.fs.openFileAbsolute(script_name_to_search, .{ .mode = .read_only }); +var must_normalize = false; + const file_: anyerror!std.fs.File = brk: { + if (std.fs.path.isAbsolute( script_name_to_search)) { + must_normalize = Environment.isWindows; + break :brk bun.openFile(script_name_to_search, .{ .mode = .read_only }); } else { const cwd = bun.getcwd(&path_buf) catch break :possibly_open_with_bun_js; path_buf[cwd.len] = std.fs.path.sep_posix; @@ -928,7 +929,7 @@ pub const RunCommand = struct { if (file_path.len == 0) break :possibly_open_with_bun_js; path_buf2[file_path.len] = 0; var file_pathZ = path_buf2[0..file_path.len :0]; - break :brk std.fs.openFileAbsoluteZ(file_pathZ, .{ .mode = .read_only }); + break :brk bun.openFileZ(file_pathZ, .{ .mode = .read_only }); } }; @@ -970,7 +971,13 @@ pub const RunCommand = struct { } Global.configureAllocator(.{ .long_running = true }); - Run.boot(ctx, ctx.allocator.dupe(u8, file_path) catch unreachable) catch |err| { + var out_path = ctx.allocator.dupe(u8, file_path) catch unreachable; + if (must_normalize) { + if (comptime Environment.isWindows) { + std.mem.replaceScalar(u8, out_path, std.fs.path.sep_windows, std.fs.path.sep_posix); + } + } + Run.boot(ctx, out_path) catch |err| { if (Output.enable_ansi_colors) { ctx.log.printForLogLevelWithEnableAnsiColors(Output.errorWriter(), true) catch {}; } else { diff --git a/src/cli/upgrade_command.zig b/src/cli/upgrade_command.zig index c48e32aca3..17d55bc1c1 100644 --- a/src/cli/upgrade_command.zig +++ b/src/cli/upgrade_command.zig @@ -78,7 +78,13 @@ pub const Version = struct { return this.tag["bun-v".len..]; } - pub const platform_label = if (Environment.isMac) "darwin" else "linux"; + pub const platform_label = switch (Environment.os) { + .mac => "darwin", + .linux => "linux", + .windows => "windows", + else => "TODO", + }; + pub const arch_label = if (Environment.isAarch64) "aarch64" else "x64"; pub const triplet = platform_label ++ "-" ++ arch_label; const suffix = if (Environment.baseline) "-baseline" else ""; @@ -557,39 +563,80 @@ pub const UpgradeCommand = struct { save_dir.deleteFileZ(tmpname) catch {}; } - const unzip_exe = which(&unzip_path_buf, env_loader.map.get("PATH") orelse "", filesystem.top_level_dir, "unzip") orelse { - save_dir.deleteFileZ(tmpname) catch {}; - Output.prettyErrorln("error: Failed to locate \"unzip\" in PATH. bun upgrade needs \"unzip\" to work.", .{}); - Global.exit(1); - }; + if (comptime Environment.isPosix) { + const unzip_exe = which(&unzip_path_buf, env_loader.map.get("PATH") orelse "", filesystem.top_level_dir, "unzip") orelse { + save_dir.deleteFileZ(tmpname) catch {}; + Output.prettyErrorln("error: Failed to locate \"unzip\" in PATH. bun upgrade needs \"unzip\" to work.", .{}); + Global.exit(1); + }; - // We could just embed libz2 - // however, we want to be sure that xattrs are preserved - // xattrs are used for codesigning - // it'd be easy to mess that up - var unzip_argv = [_]string{ - bun.asByteSlice(unzip_exe), - "-q", - "-o", - tmpname, - }; + // We could just embed libz2 + // however, we want to be sure that xattrs are preserved + // xattrs are used for codesigning + // it'd be easy to mess that up + var unzip_argv = [_]string{ + bun.asByteSlice(unzip_exe), + "-q", + "-o", + tmpname, + }; - var unzip_process = std.ChildProcess.init(&unzip_argv, ctx.allocator); - unzip_process.cwd = tmpdir_path; - unzip_process.stdin_behavior = .Inherit; - unzip_process.stdout_behavior = .Inherit; - unzip_process.stderr_behavior = .Inherit; + var unzip_process = std.ChildProcess.init(&unzip_argv, ctx.allocator); + unzip_process.cwd = tmpdir_path; + unzip_process.stdin_behavior = .Inherit; + unzip_process.stdout_behavior = .Inherit; + unzip_process.stderr_behavior = .Inherit; - const unzip_result = unzip_process.spawnAndWait() catch |err| { - save_dir.deleteFileZ(tmpname) catch {}; - Output.prettyErrorln("error: Failed to spawn unzip due to {s}.", .{@errorName(err)}); - Global.exit(1); - }; + const unzip_result = unzip_process.spawnAndWait() catch |err| { + save_dir.deleteFileZ(tmpname) catch {}; + Output.prettyErrorln("error: Failed to spawn unzip due to {s}.", .{@errorName(err)}); + Global.exit(1); + }; + + if (unzip_result.Exited != 0) { + Output.prettyErrorln("Unzip failed (exit code: {d})", .{unzip_result.Exited}); + save_dir.deleteFileZ(tmpname) catch {}; + Global.exit(1); + } + } else if (Environment.isWindows) { + // Run a powershell script to unzip the file + var unzip_script = try std.fmt.allocPrint( + ctx.allocator, + "Expand-Archive -Path {s} -DestinationPath {s} -Force", + .{ + tmpname, + tmpdir_path, + }, + ); + + var unzip_argv = [_]string{ + "powershell.exe", + "-NoProfile", + "-ExecutionPolicy", + "Bypass", + "-Command", + unzip_script, + }; + + var unzip_process = std.ChildProcess.init(&unzip_argv, ctx.allocator); + + unzip_process.cwd = tmpdir_path; + unzip_process.stdin_behavior = .Inherit; + unzip_process.stdout_behavior = .Inherit; + unzip_process.stderr_behavior = .Inherit; + + const unzip_result = unzip_process.spawnAndWait() catch |err| { + save_dir.deleteFileZ(tmpname) catch {}; + Output.prettyErrorln("error: Failed to spawn unzip due to {s}.", .{@errorName(err)}); + Global.exit(1); + }; + + if (unzip_result.Exited != 0) { + Output.prettyErrorln("Unzip failed (exit code: {d})", .{unzip_result.Exited}); + save_dir.deleteFileZ(tmpname) catch {}; + Global.exit(1); + } - if (unzip_result.Exited != 0) { - Output.prettyErrorln("Unzip failed (exit code: {d})", .{unzip_result.Exited}); - save_dir.deleteFileZ(tmpname) catch {}; - Global.exit(1); } } { @@ -696,11 +743,22 @@ pub const UpgradeCommand = struct { } if (env_loader.map.get("BUN_DRY_RUN") == null) { - C.moveFileZ(save_dir.fd, exe, target_dir.fd, target_filename) catch |err| { + if (comptime Environment.isWindows) { + // On Windows, we cannot replace the running executable directly. + // we rename the old executable to a temporary name, and then move the new executable to the old name. + // This is because Windows locks the executable while it's running. + + // var tmpname = try std.fmt.allocPrint(ctx.allocator, "{s}.old.exe", .{target_filename}); + + + + } else { + C.moveFileZ(save_dir.fd, exe, target_dir.fd, target_filename) catch |err| { save_dir_.deleteTree(version_name) catch {}; Output.prettyErrorln("error: Failed to move new version of bun due to {s}. You could try the install script instead:\n curl -fsSL https://bun.sh/install | bash", .{@errorName(err)}); Global.exit(1); }; + } } // Ensure completions are up to date. diff --git a/src/deps/uws.zig b/src/deps/uws.zig index 4478f01676..0143ce9796 100644 --- a/src/deps/uws.zig +++ b/src/deps/uws.zig @@ -845,6 +845,8 @@ pub const PosixLoop = extern struct { return us_wakeup_loop(this); } + pub const wake = wakeup; + pub fn tick(this: *PosixLoop) void { us_loop_run_bun_tick(this, 0); } @@ -2279,6 +2281,8 @@ pub const UVLoop = extern struct { us_wakeup_loop(this); } + pub const wake = wakeup; + pub fn tickWithTimeout(this: *UVLoop, _: i64) void { us_loop_run(this); } @@ -2298,6 +2302,10 @@ pub const UVLoop = extern struct { } pub const tick = run; + pub fn wait(this: *UVLoop) void { + us_loop_run(this); + } + pub fn inc(this: *UVLoop) void { this.uv_loop.inc(); } diff --git a/src/fs.zig b/src/fs.zig index 2ef023494c..862f737300 100644 --- a/src/fs.zig +++ b/src/fs.zig @@ -711,11 +711,12 @@ pub const FileSystem = struct { var new_buf: bun.MAX_WPATH = undefined; this.close(); const existing = bun.strings.toExtendedPathNormalized(&new_buf, this.existing_path); - const new = if (std.fs.path.isAbsoluteWindows(name)) - bun.strings.toExtendedPathNormalized(&existing_buf, name) else - bun.strings.toWPathNormalized(&existing_buf, name); + const new = if (std.fs.path.isAbsoluteWindows(name)) + bun.strings.toExtendedPathNormalized(&existing_buf, name) + else + bun.strings.toWPathNormalized(&existing_buf, name); if (comptime Environment.allow_assert) { - debug("moveFileExW({s}, {s})", .{strings.fmtUTF16(existing), strings.fmtUTF16( new)}); + debug("moveFileExW({s}, {s})", .{ strings.fmtUTF16(existing), strings.fmtUTF16(new) }); } if (bun.windows.kernel32.MoveFileExW(existing.ptr, new.ptr, bun.windows.MOVEFILE_COPY_ALLOWED | bun.windows.MOVEFILE_REPLACE_EXISTING | bun.windows.MOVEFILE_WRITE_THROUGH) == bun.windows.FALSE) { diff --git a/src/install/install.zig b/src/install/install.zig index 741c70b061..cab94be8f8 100644 --- a/src/install/install.zig +++ b/src/install/install.zig @@ -1190,8 +1190,6 @@ const PackageInstall = struct { if (comptime Environment.isPosix) { const stat = infile.stat() catch continue; _ = C.fchmod(outfile.handle, stat.mode); - } else { - bun.todo(@src(), {}); } bun.copyFile(infile.handle, outfile.handle) catch |err| { @@ -1682,7 +1680,7 @@ pub const CacheLevel = struct { use_last_modified: bool, }; const AsyncIO = bun.AsyncIO; -const Waker = bun.AsyncIO.Waker; +const Waker = if (Environment.isPosix) bun.AsyncIO.Waker else *bun.uws.UVLoop; // We can't know all the packages we need until we've downloaded all the packages // The easy way would be: @@ -1795,13 +1793,13 @@ pub const PackageManager = struct { } _ = this.wait_count.fetchAdd(1, .Monotonic); - this.waiter.wake() catch {}; + this.waiter.wake(); } pub fn sleep(this: *PackageManager) void { if (this.wait_count.swap(0, .Monotonic) > 0) return; bun.Mimalloc.mi_collect(false); - _ = this.waiter.wait() catch 0; + this.waiter.wait(); } const DependencyToEnqueue = union(enum) { @@ -5362,7 +5360,7 @@ pub const PackageManager = struct { .resolve_tasks = TaskChannel.init(), .lockfile = undefined, .root_package_json_file = package_json_file, - .waiter = try Waker.init(ctx.allocator), + .waiter = if (Environment.isPosix) try Waker.init(ctx.allocator) else bun.uws.Loop.get(), // .progress }; manager.lockfile = try ctx.allocator.create(Lockfile); @@ -5439,7 +5437,7 @@ pub const PackageManager = struct { .resolve_tasks = TaskChannel.init(), .lockfile = undefined, .root_package_json_file = undefined, - .waiter = try Waker.init(allocator), + .waiter = if (Environment.isPosix) try Waker.init(allocator) else bun.uws.Loop.get(), }; manager.lockfile = try allocator.create(Lockfile); diff --git a/src/io/io_darwin.zig b/src/io/io_darwin.zig index 226a4d284e..adfd8c3a93 100644 --- a/src/io/io_darwin.zig +++ b/src/io/io_darwin.zig @@ -522,7 +522,7 @@ pub const Waker = struct { this.has_pending_wake = true; } - pub fn wait(this: Waker) !usize { + pub fn wait(this: Waker) usize { bun.JSC.markBinding(@src()); var events = zeroed; @@ -536,11 +536,12 @@ pub const Waker = struct { null, ); - if (count < 0) { - return asError(std.c.getErrno(count)); - } + // we are not going to realistically handle these errors + // if (count < 0) { + // return asError(std.c.getErrno(count)); + // } - return @as(usize, @intCast(count)); + return @as(usize, @intCast(@max(count, 0))); } extern fn io_darwin_create_machport( diff --git a/src/io/io_linux.zig b/src/io/io_linux.zig index 8f054490b7..f3c7dd5bfd 100644 --- a/src/io/io_linux.zig +++ b/src/io/io_linux.zig @@ -997,13 +997,13 @@ pub const Waker = struct { }; } - pub fn wait(this: Waker) !u64 { + pub fn wait(this: Waker) u64 { var bytes: usize = 0; _ = std.os.read(this.fd, @as(*[8]u8, @ptrCast(&bytes))) catch 0; return @as(u64, @intCast(bytes)); } - pub fn wake(this: *const Waker) !void { + pub fn wake(this: *const Waker) void { var bytes: usize = 1; _ = std.os.write( this.fd, diff --git a/src/io/io_windows.zig b/src/io/io_windows.zig index 47f813c35b..32d3280730 100644 --- a/src/io/io_windows.zig +++ b/src/io/io_windows.zig @@ -115,14 +115,14 @@ pub const Waker = struct { }; } - pub fn wait(this: Waker) !u64 { + pub fn wait(this: Waker) u64 { var overlapped = [_]os.windows.OVERLAPPED_ENTRY{std.mem.zeroes(os.windows.OVERLAPPED_ENTRY)} ** 1; var removed: u32 = 0; _ = kernel32.GetQueuedCompletionStatusEx(this.iocp, &overlapped, 1, &removed, 0, 1); return 0; } - pub fn wake(this: Waker) !void { + pub fn wake(this: Waker) void { var overlapped: os.windows.OVERLAPPED = std.mem.zeroes(os.windows.OVERLAPPED); _ = kernel32.PostQueuedCompletionStatus(this.iocp, 1, completion_key, &overlapped); } diff --git a/src/network_thread.zig b/src/network_thread.zig index 9c19b7424f..a2353ebd28 100644 --- a/src/network_thread.zig +++ b/src/network_thread.zig @@ -163,7 +163,7 @@ pub fn schedule(this: *@This(), batch: Batch) void { const one = @as([8]u8, @bitCast(@as(usize, batch.len))); _ = std.os.write(this.waker.fd, &one) catch @panic("Failed to write to eventfd"); } else { - this.waker.wake() catch @panic("Failed to wake"); + this.waker.wake(); } } diff --git a/src/string_immutable.zig b/src/string_immutable.zig index db181e695c..3490fc5352 100644 --- a/src/string_immutable.zig +++ b/src/string_immutable.zig @@ -1621,6 +1621,14 @@ pub fn toExtendedPathNormalized(wbuf: []u16, utf8: []const u8) [:0]const u16 { return wbuf[0 .. toWPathNormalized(wbuf[4..], utf8).len + 4 :0]; } +pub fn toWPathNormalizeAutoExtend(wbuf: []u16, utf8: []const u8) [:0]const u16 { + if (std.fs.path.isAbsoluteWindows(utf8)) { + return toExtendedPathNormalized(wbuf, utf8); + } + + return toWPathNormalized(wbuf, utf8); +} + pub fn toWPathNormalized(wbuf: []u16, utf8: []const u8) [:0]const u16 { var renormalized: [bun.MAX_PATH_BYTES]u8 = undefined; var path_to_use = utf8;