| ... | @@ -383,8 +383,6 @@ pub const ChildProcess = struct { | ... | @@ -383,8 +383,6 @@ pub const ChildProcess = struct { |
| 383 | | 383 | |
| 384 | try child.spawn(); | 384 | try child.spawn(); |
| 385 | | 385 | |
| 386 | // TODO collect output in a deadlock-avoiding way on Windows. | | |
| 387 | // https://github.com/ziglang/zig/issues/6343 | | |
| 388 | if (builtin.os.tag == .haiku) { | 386 | if (builtin.os.tag == .haiku) { |
| 389 | const stdout_in = child.stdout.?.reader(); | 387 | const stdout_in = child.stdout.?.reader(); |
| 390 | const stderr_in = child.stderr.?.reader(); | 388 | const stderr_in = child.stderr.?.reader(); |
| ... | @@ -1019,7 +1017,10 @@ var pipe_name_counter = std.atomic.Atomic(u32).init(1); | ... | @@ -1019,7 +1017,10 @@ var pipe_name_counter = std.atomic.Atomic(u32).init(1); |
| 1019 | fn windowsMakeAsyncPipe(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *const windows.SECURITY_ATTRIBUTES) !void { | 1017 | fn windowsMakeAsyncPipe(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *const windows.SECURITY_ATTRIBUTES) !void { |
| 1020 | var tmp_bufw: [128]u16 = undefined; | 1018 | var tmp_bufw: [128]u16 = undefined; |
| 1021 | | 1019 | |
| 1022 | // We must make a named pipe on windows because anonymous pipes do not support async IO | 1020 | // Anonymous pipes are built upon Named pipes. |
| | 1021 | // https://docs.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-createpipe |
| | 1022 | // Asynchronous (overlapped) read and write operations are not supported by anonymous pipes. |
| | 1023 | // https://docs.microsoft.com/en-us/windows/win32/ipc/anonymous-pipe-operations |
| 1023 | const pipe_path = blk: { | 1024 | const pipe_path = blk: { |
| 1024 | var tmp_buf: [128]u8 = undefined; | 1025 | var tmp_buf: [128]u8 = undefined; |
| 1025 | // Forge a random path for the pipe. | 1026 | // Forge a random path for the pipe. |
| ... | @@ -1201,3 +1202,65 @@ test "createNullDelimitedEnvMap" { | ... | @@ -1201,3 +1202,65 @@ test "createNullDelimitedEnvMap" { |
| 1201 | } | 1202 | } |
| 1202 | } | 1203 | } |
| 1203 | } | 1204 | } |
| | 1205 | |
| | 1206 | const childstr = |
| | 1207 | \\ const std = @import("std"); |
| | 1208 | \\ const builtin = @import("builtin"); |
| | 1209 | \\ pub fn main() !void { |
| | 1210 | \\ var it = try std.process.argsWithAllocator(std.testing.allocator); |
| | 1211 | \\ defer it.deinit(); // no-op unless WASI or Windows |
| | 1212 | \\ _ = it.next() orelse unreachable; // skip binary name |
| | 1213 | \\ const input = it.next() orelse unreachable; |
| | 1214 | \\ var expect_helloworld = "hello world".*; |
| | 1215 | \\ try std.testing.expect(std.mem.eql(u8, &expect_helloworld, input)); |
| | 1216 | \\ try std.testing.expect(it.next() == null); |
| | 1217 | \\ try std.testing.expect(!it.skip()); |
| | 1218 | \\ } |
| | 1219 | ; |
| | 1220 | |
| | 1221 | test "build and call child_process" { |
| | 1222 | if (builtin.os.tag == .wasi) return error.SkipZigTest; |
| | 1223 | const testing = std.testing; |
| | 1224 | var it = try std.process.argsWithAllocator(std.testing.allocator); |
| | 1225 | defer it.deinit(); // no-op unless WASI or Windows |
| | 1226 | |
| | 1227 | _ = it.next() orelse unreachable; |
| | 1228 | const zigexec = it.next() orelse unreachable; |
| | 1229 | try testing.expect(it.next() == null); |
| | 1230 | try testing.expect(!it.skip()); |
| | 1231 | const cwd_str = try process.getCwdAlloc(testing.allocator); |
| | 1232 | defer testing.allocator.free(cwd_str); |
| | 1233 | var tmp = testing.tmpDir(.{ .no_follow = true }); // ie zig-cache/tmp/8DLgoSEqz593PAEE |
| | 1234 | defer tmp.cleanup(); |
| | 1235 | const cache = "zig-cache"; |
| | 1236 | const tmpdir = "tmp"; |
| | 1237 | const child_name = "child"; // no need for suffixes (.exe, .wasm) due to '-femit-bin' |
| | 1238 | const suffix_zig = ".zig"; |
| | 1239 | const child_path = try fs.path.join(testing.allocator, &[_][]const u8{ cwd_str, std.fs.path.sep_str, cache, tmpdir, &tmp.sub_path, child_name }); |
| | 1240 | defer testing.allocator.free(child_path); |
| | 1241 | |
| | 1242 | const child_zig = try mem.concat(testing.allocator, u8, &[_][]const u8{ child_path, suffix_zig }); |
| | 1243 | defer testing.allocator.free(child_zig); |
| | 1244 | const emit_flag = "-femit-bin="; |
| | 1245 | const emit_bin = try mem.concat(testing.allocator, u8, &[_][]const u8{ emit_flag, child_path }); |
| | 1246 | defer testing.allocator.free(emit_bin); |
| | 1247 | { |
| | 1248 | // 'zigexec build-exe path/to/child.zig -femit-bin=path/to/child' expect success |
| | 1249 | try tmp.dir.writeFile("child.zig", childstr); |
| | 1250 | const args = [_][]const u8{ zigexec, "build-exe", child_zig, emit_bin }; |
| | 1251 | var procCompileChild = try ChildProcess.init(&args, testing.allocator); |
| | 1252 | defer procCompileChild.deinit(); |
| | 1253 | try procCompileChild.spawn(); |
| | 1254 | const ret_val = try procCompileChild.wait(); |
| | 1255 | try testing.expectEqual(ret_val, .{ .Exited = 0 }); |
| | 1256 | } |
| | 1257 | { |
| | 1258 | // spawn compiled file as child_process with argument 'hello world' + expect success |
| | 1259 | const args = [_][]const u8{ child_path, "hello world" }; |
| | 1260 | var child_proc = try ChildProcess.init(&args, testing.allocator); |
| | 1261 | defer child_proc.deinit(); |
| | 1262 | try child_proc.spawn(); |
| | 1263 | const ret_val = try child_proc.wait(); |
| | 1264 | try testing.expectEqual(ret_val, .{ .Exited = 0 }); |
| | 1265 | } |
| | 1266 | } |