Commit 22e1b03360
src/link/Coff.zig
@@ -827,6 +827,11 @@ fn resolveRelocs(self: *Coff, atom_index: Atom.Index, code: []u8) void {
}
}
+pub fn ptraceAttach(self: *Coff, handle: std.os.pid_t) !void {
+ _ = self;
+ log.warn("attaching to process with handle {*}", .{handle});
+}
+
fn freeAtom(self: *Coff, atom_index: Atom.Index) void {
log.debug("freeAtom {d}", .{atom_index});
src/link.zig
@@ -379,24 +379,30 @@ pub const File = struct {
if (base.file != null) return;
const emit = base.options.emit orelse return;
if (base.child_pid) |pid| {
- // If we try to open the output file in write mode while it is running,
- // it will return ETXTBSY. So instead, we copy the file, atomically rename it
- // over top of the exe path, and then proceed normally. This changes the inode,
- // avoiding the error.
- const tmp_sub_path = try std.fmt.allocPrint(base.allocator, "{s}-{x}", .{
- emit.sub_path, std.crypto.random.int(u32),
- });
- try emit.directory.handle.copyFile(emit.sub_path, emit.directory.handle, tmp_sub_path, .{});
- try emit.directory.handle.rename(tmp_sub_path, emit.sub_path);
- switch (builtin.os.tag) {
- .linux => std.os.ptrace(std.os.linux.PTRACE.ATTACH, pid, 0, 0) catch |err| {
- log.warn("ptrace failure: {s}", .{@errorName(err)});
- },
- .macos => base.cast(MachO).?.ptraceAttach(pid) catch |err| {
+ if (builtin.os.tag == .windows) {
+ base.cast(Coff).?.ptraceAttach(pid) catch |err| {
log.warn("attaching failed with error: {s}", .{@errorName(err)});
- },
- .windows => {},
- else => return error.HotSwapUnavailableOnHostOperatingSystem,
+ };
+ } else {
+ // If we try to open the output file in write mode while it is running,
+ // it will return ETXTBSY. So instead, we copy the file, atomically rename it
+ // over top of the exe path, and then proceed normally. This changes the inode,
+ // avoiding the error.
+ const tmp_sub_path = try std.fmt.allocPrint(base.allocator, "{s}-{x}", .{
+ emit.sub_path, std.crypto.random.int(u32),
+ });
+ try emit.directory.handle.copyFile(emit.sub_path, emit.directory.handle, tmp_sub_path, .{});
+ try emit.directory.handle.rename(tmp_sub_path, emit.sub_path);
+ switch (builtin.os.tag) {
+ .linux => std.os.ptrace(std.os.linux.PTRACE.ATTACH, pid, 0, 0) catch |err| {
+ log.warn("ptrace failure: {s}", .{@errorName(err)});
+ },
+ .macos => base.cast(MachO).?.ptraceAttach(pid) catch |err| {
+ log.warn("attaching failed with error: {s}", .{@errorName(err)});
+ },
+ .windows => unreachable,
+ else => return error.HotSwapUnavailableOnHostOperatingSystem,
+ }
}
}
base.file = try emit.directory.handle.createFile(emit.sub_path, .{
src/main.zig
@@ -3817,11 +3817,25 @@ fn runOrTestHotSwap(
runtime_args_start: ?usize,
) !std.ChildProcess.Id {
const exe_emit = comp.bin_file.options.emit.?;
- // A naive `directory.join` here will indeed get the correct path to the binary,
- // however, in the case of cwd, we actually want `./foo` so that the path can be executed.
- const exe_path = try fs.path.join(gpa, &[_][]const u8{
- exe_emit.directory.path orelse ".", exe_emit.sub_path,
- });
+
+ const exe_path = switch (builtin.target.os.tag) {
+ // On Windows it seems impossible to perform an atomic rename of a file that is currently
+ // running in a process. Therefore, we do the opposite. We create a copy of the file in
+ // tmp zig-cache and use it to spawn the child process. This way we are free to update
+ // the binary with each requested hot update.
+ .windows => blk: {
+ try exe_emit.directory.handle.copyFile(exe_emit.sub_path, comp.local_cache_directory.handle, exe_emit.sub_path, .{});
+ break :blk try fs.path.join(gpa, &[_][]const u8{
+ comp.local_cache_directory.path orelse ".", exe_emit.sub_path,
+ });
+ },
+
+ // A naive `directory.join` here will indeed get the correct path to the binary,
+ // however, in the case of cwd, we actually want `./foo` so that the path can be executed.
+ else => try fs.path.join(gpa, &[_][]const u8{
+ exe_emit.directory.path orelse ".", exe_emit.sub_path,
+ }),
+ };
defer gpa.free(exe_path);
var argv = std.ArrayList([]const u8).init(gpa);