diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2022-02-07 00:31:17 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-02-07 00:31:17 -0500 |
| commit | 21135387fb7c2dbaf70a72f2c97341e9c1307045 (patch) | |
| tree | 5926ea2d182a76f80429776a5473202738c9b656 /src | |
| parent | 069dd01ce4ced3cb9664e4f1e09be753cb3ed476 (diff) | |
| parent | 33fa29601921d88097a1ee3c0d92b93047a5186d (diff) | |
| download | zig-21135387fb7c2dbaf70a72f2c97341e9c1307045.tar.gz zig-21135387fb7c2dbaf70a72f2c97341e9c1307045.zip | |
Merge pull request #10782 from topolarity/gate-child-processes
Avoid depending on child process execution when not supported by host OS
Diffstat (limited to 'src')
| -rw-r--r-- | src/Compilation.zig | 101 | ||||
| -rw-r--r-- | src/ThreadPool.zig | 3 | ||||
| -rw-r--r-- | src/libc_installation.zig | 4 | ||||
| -rw-r--r-- | src/link/Coff.zig | 116 | ||||
| -rw-r--r-- | src/link/Elf.zig | 106 | ||||
| -rw-r--r-- | src/link/Wasm.zig | 108 | ||||
| -rw-r--r-- | src/main.zig | 166 | ||||
| -rw-r--r-- | src/mingw.zig | 57 | ||||
| -rw-r--r-- | src/test.zig | 11 |
9 files changed, 383 insertions, 289 deletions
diff --git a/src/Compilation.zig b/src/Compilation.zig index 21a0c6fe58..f07a7c9dd7 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -25,6 +25,7 @@ const libunwind = @import("libunwind.zig"); const libcxx = @import("libcxx.zig"); const wasi_libc = @import("wasi_libc.zig"); const fatal = @import("main.zig").fatal; +const clangMain = @import("main.zig").clangMain; const Module = @import("Module.zig"); const Cache = @import("Cache.zig"); const stage1 = @import("stage1.zig"); @@ -3667,55 +3668,71 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P dump_argv(argv.items); } - const child = try std.ChildProcess.init(argv.items, arena); - defer child.deinit(); + if (std.process.can_spawn) { + const child = try std.ChildProcess.init(argv.items, arena); + defer child.deinit(); - if (comp.clang_passthrough_mode) { - child.stdin_behavior = .Inherit; - child.stdout_behavior = .Inherit; - child.stderr_behavior = .Inherit; + if (comp.clang_passthrough_mode) { + child.stdin_behavior = .Inherit; + child.stdout_behavior = .Inherit; + child.stderr_behavior = .Inherit; - const term = child.spawnAndWait() catch |err| { - return comp.failCObj(c_object, "unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); - }; - switch (term) { - .Exited => |code| { - if (code != 0) { - std.process.exit(code); - } - if (comp.clang_preprocessor_mode == .stdout) - std.process.exit(0); - }, - else => std.process.abort(), - } - } else { - child.stdin_behavior = .Ignore; - child.stdout_behavior = .Ignore; - child.stderr_behavior = .Pipe; + const term = child.spawnAndWait() catch |err| { + return comp.failCObj(c_object, "unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); + }; + switch (term) { + .Exited => |code| { + if (code != 0) { + std.process.exit(code); + } + if (comp.clang_preprocessor_mode == .stdout) + std.process.exit(0); + }, + else => std.process.abort(), + } + } else { + child.stdin_behavior = .Ignore; + child.stdout_behavior = .Ignore; + child.stderr_behavior = .Pipe; - try child.spawn(); + try child.spawn(); - const stderr_reader = child.stderr.?.reader(); + const stderr_reader = child.stderr.?.reader(); - const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024); + const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024); - const term = child.wait() catch |err| { - return comp.failCObj(c_object, "unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); - }; + const term = child.wait() catch |err| { + return comp.failCObj(c_object, "unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); + }; - switch (term) { - .Exited => |code| { - if (code != 0) { - // TODO parse clang stderr and turn it into an error message - // and then call failCObjWithOwnedErrorMsg - log.err("clang failed with stderr: {s}", .{stderr}); - return comp.failCObj(c_object, "clang exited with code {d}", .{code}); - } - }, - else => { - log.err("clang terminated with stderr: {s}", .{stderr}); - return comp.failCObj(c_object, "clang terminated unexpectedly", .{}); - }, + switch (term) { + .Exited => |code| { + if (code != 0) { + // TODO parse clang stderr and turn it into an error message + // and then call failCObjWithOwnedErrorMsg + log.err("clang failed with stderr: {s}", .{stderr}); + return comp.failCObj(c_object, "clang exited with code {d}", .{code}); + } + }, + else => { + log.err("clang terminated with stderr: {s}", .{stderr}); + return comp.failCObj(c_object, "clang terminated unexpectedly", .{}); + }, + } + } + } else { + const exit_code = try clangMain(arena, argv.items); + if (exit_code != 0) { + if (comp.clang_passthrough_mode) { + std.process.exit(exit_code); + } else { + return comp.failCObj(c_object, "clang exited with code {d}", .{exit_code}); + } + } + if (comp.clang_passthrough_mode and + comp.clang_preprocessor_mode == .stdout) + { + std.process.exit(0); } } diff --git a/src/ThreadPool.zig b/src/ThreadPool.zig index 4f9d8dc015..813d67db66 100644 --- a/src/ThreadPool.zig +++ b/src/ThreadPool.zig @@ -82,6 +82,9 @@ pub fn init(self: *ThreadPool, allocator: std.mem.Allocator) !void { } fn destroyWorkers(self: *ThreadPool, spawned: usize) void { + if (builtin.single_threaded) + return; + for (self.workers[0..spawned]) |*worker| { worker.thread.join(); worker.idle_node.data.deinit(); diff --git a/src/libc_installation.zig b/src/libc_installation.zig index 4cd43c7567..fe1a2b2ca5 100644 --- a/src/libc_installation.zig +++ b/src/libc_installation.zig @@ -216,7 +216,7 @@ pub const LibCInstallation = struct { self.crt_dir = try args.allocator.dupeZ(u8, "/system/develop/lib"); break :blk batch.wait(); }; - } else { + } else if (std.process.can_spawn) { try blk: { var batch = Batch(FindError!void, 2, .auto_async).init(); errdefer batch.wait() catch {}; @@ -229,6 +229,8 @@ pub const LibCInstallation = struct { } break :blk batch.wait(); }; + } else { + return error.LibCRuntimeNotFound; } return self; } diff --git a/src/link/Coff.zig b/src/link/Coff.zig index 894d5dd8f7..bc5837fe47 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -9,6 +9,7 @@ const fs = std.fs; const allocPrint = std.fmt.allocPrint; const mem = std.mem; +const lldMain = @import("../main.zig").lldMain; const trace = @import("../tracy.zig").trace; const Module = @import("../Module.zig"); const Compilation = @import("../Compilation.zig"); @@ -1358,60 +1359,71 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void { Compilation.dump_argv(argv.items[1..]); } - // Sadly, we must run LLD as a child process because it does not behave - // properly as a library. - const child = try std.ChildProcess.init(argv.items, arena); - defer child.deinit(); - - if (comp.clang_passthrough_mode) { - child.stdin_behavior = .Inherit; - child.stdout_behavior = .Inherit; - child.stderr_behavior = .Inherit; - - const term = child.spawnAndWait() catch |err| { - log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); - return error.UnableToSpawnSelf; - }; - switch (term) { - .Exited => |code| { - if (code != 0) { - // TODO https://github.com/ziglang/zig/issues/6342 - std.process.exit(1); - } - }, - else => std.process.abort(), + if (std.process.can_spawn) { + // If possible, we run LLD as a child process because it does not always + // behave properly as a library, unfortunately. + // https://github.com/ziglang/zig/issues/3825 + const child = try std.ChildProcess.init(argv.items, arena); + defer child.deinit(); + + if (comp.clang_passthrough_mode) { + child.stdin_behavior = .Inherit; + child.stdout_behavior = .Inherit; + child.stderr_behavior = .Inherit; + + const term = child.spawnAndWait() catch |err| { + log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); + return error.UnableToSpawnSelf; + }; + switch (term) { + .Exited => |code| { + if (code != 0) { + std.process.exit(code); + } + }, + else => std.process.abort(), + } + } else { + child.stdin_behavior = .Ignore; + child.stdout_behavior = .Ignore; + child.stderr_behavior = .Pipe; + + try child.spawn(); + + const stderr = try child.stderr.?.reader().readAllAlloc(arena, 10 * 1024 * 1024); + + const term = child.wait() catch |err| { + log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); + return error.UnableToSpawnSelf; + }; + + switch (term) { + .Exited => |code| { + if (code != 0) { + // TODO parse this output and surface with the Compilation API rather than + // directly outputting to stderr here. + std.debug.print("{s}", .{stderr}); + return error.LLDReportedFailure; + } + }, + else => { + log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr }); + return error.LLDCrashed; + }, + } + + if (stderr.len != 0) { + log.warn("unexpected LLD stderr:\n{s}", .{stderr}); + } } } else { - child.stdin_behavior = .Ignore; - child.stdout_behavior = .Ignore; - child.stderr_behavior = .Pipe; - - try child.spawn(); - - const stderr = try child.stderr.?.reader().readAllAlloc(arena, 10 * 1024 * 1024); - - const term = child.wait() catch |err| { - log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); - return error.UnableToSpawnSelf; - }; - - switch (term) { - .Exited => |code| { - if (code != 0) { - // TODO parse this output and surface with the Compilation API rather than - // directly outputting to stderr here. - std.debug.print("{s}", .{stderr}); - return error.LLDReportedFailure; - } - }, - else => { - log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr }); - return error.LLDCrashed; - }, - } - - if (stderr.len != 0) { - log.warn("unexpected LLD stderr:\n{s}", .{stderr}); + const exit_code = try lldMain(arena, argv.items, false); + if (exit_code != 0) { + if (comp.clang_passthrough_mode) { + std.process.exit(exit_code); + } else { + return error.LLDReportedFailure; + } } } } diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 37afd68c82..afaf41a2f9 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -14,6 +14,7 @@ const leb128 = std.leb; const Module = @import("../Module.zig"); const Compilation = @import("../Compilation.zig"); const codegen = @import("../codegen.zig"); +const lldMain = @import("../main.zig").lldMain; const trace = @import("../tracy.zig").trace; const Package = @import("../Package.zig"); const Value = @import("../value.zig").Value; @@ -1950,60 +1951,71 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { Compilation.dump_argv(argv.items[1..]); } - // Sadly, we must run LLD as a child process because it does not behave - // properly as a library. - const child = try std.ChildProcess.init(argv.items, arena); - defer child.deinit(); + if (std.process.can_spawn) { + // If possible, we run LLD as a child process because it does not always + // behave properly as a library, unfortunately. + // https://github.com/ziglang/zig/issues/3825 + const child = try std.ChildProcess.init(argv.items, arena); + defer child.deinit(); - if (comp.clang_passthrough_mode) { - child.stdin_behavior = .Inherit; - child.stdout_behavior = .Inherit; - child.stderr_behavior = .Inherit; + if (comp.clang_passthrough_mode) { + child.stdin_behavior = .Inherit; + child.stdout_behavior = .Inherit; + child.stderr_behavior = .Inherit; - const term = child.spawnAndWait() catch |err| { - log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); - return error.UnableToSpawnSelf; - }; - switch (term) { - .Exited => |code| { - if (code != 0) { - // TODO https://github.com/ziglang/zig/issues/6342 - std.process.exit(1); - } - }, - else => std.process.abort(), - } - } else { - child.stdin_behavior = .Ignore; - child.stdout_behavior = .Ignore; - child.stderr_behavior = .Pipe; + const term = child.spawnAndWait() catch |err| { + log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); + return error.UnableToSpawnSelf; + }; + switch (term) { + .Exited => |code| { + if (code != 0) { + std.process.exit(code); + } + }, + else => std.process.abort(), + } + } else { + child.stdin_behavior = .Ignore; + child.stdout_behavior = .Ignore; + child.stderr_behavior = .Pipe; - try child.spawn(); + try child.spawn(); - const stderr = try child.stderr.?.reader().readAllAlloc(arena, 10 * 1024 * 1024); + const stderr = try child.stderr.?.reader().readAllAlloc(arena, 10 * 1024 * 1024); - const term = child.wait() catch |err| { - log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); - return error.UnableToSpawnSelf; - }; + const term = child.wait() catch |err| { + log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); + return error.UnableToSpawnSelf; + }; - switch (term) { - .Exited => |code| { - if (code != 0) { - // TODO parse this output and surface with the Compilation API rather than - // directly outputting to stderr here. - std.debug.print("{s}", .{stderr}); - return error.LLDReportedFailure; - } - }, - else => { - log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr }); - return error.LLDCrashed; - }, - } + switch (term) { + .Exited => |code| { + if (code != 0) { + // TODO parse this output and surface with the Compilation API rather than + // directly outputting to stderr here. + std.debug.print("{s}", .{stderr}); + return error.LLDReportedFailure; + } + }, + else => { + log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr }); + return error.LLDCrashed; + }, + } - if (stderr.len != 0) { - log.warn("unexpected LLD stderr:\n{s}", .{stderr}); + if (stderr.len != 0) { + log.warn("unexpected LLD stderr:\n{s}", .{stderr}); + } + } + } else { + const exit_code = try lldMain(arena, argv.items, false); + if (exit_code != 0) { + if (comp.clang_passthrough_mode) { + std.process.exit(exit_code); + } else { + return error.LLDReportedFailure; + } } } } diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index b047e4b68a..e6988e9232 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -15,6 +15,7 @@ const Module = @import("../Module.zig"); const Compilation = @import("../Compilation.zig"); const CodeGen = @import("../arch/wasm/CodeGen.zig"); const link = @import("../link.zig"); +const lldMain = @import("../main.zig").lldMain; const trace = @import("../tracy.zig").trace; const build_options = @import("build_options"); const wasi_libc = @import("../wasi_libc.zig"); @@ -1486,60 +1487,71 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void { Compilation.dump_argv(argv.items[1..]); } - // Sadly, we must run LLD as a child process because it does not behave - // properly as a library. - const child = try std.ChildProcess.init(argv.items, arena); - defer child.deinit(); - - if (comp.clang_passthrough_mode) { - child.stdin_behavior = .Inherit; - child.stdout_behavior = .Inherit; - child.stderr_behavior = .Inherit; + if (std.process.can_spawn) { + // If possible, we run LLD as a child process because it does not always + // behave properly as a library, unfortunately. + // https://github.com/ziglang/zig/issues/3825 + const child = try std.ChildProcess.init(argv.items, arena); + defer child.deinit(); + + if (comp.clang_passthrough_mode) { + child.stdin_behavior = .Inherit; + child.stdout_behavior = .Inherit; + child.stderr_behavior = .Inherit; + + const term = child.spawnAndWait() catch |err| { + log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); + return error.UnableToSpawnSelf; + }; + switch (term) { + .Exited => |code| { + if (code != 0) { + std.process.exit(code); + } + }, + else => std.process.abort(), + } + } else { + child.stdin_behavior = .Ignore; + child.stdout_behavior = .Ignore; + child.stderr_behavior = .Pipe; - const term = child.spawnAndWait() catch |err| { - log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); - return error.UnableToSpawnSelf; - }; - switch (term) { - .Exited => |code| { - if (code != 0) { - // TODO https://github.com/ziglang/zig/issues/6342 - std.process.exit(1); - } - }, - else => std.process.abort(), - } - } else { - child.stdin_behavior = .Ignore; - child.stdout_behavior = .Ignore; - child.stderr_behavior = .Pipe; + try child.spawn(); - try child.spawn(); + const stderr = try child.stderr.?.reader().readAllAlloc(arena, 10 * 1024 * 1024); - const stderr = try child.stderr.?.reader().readAllAlloc(arena, 10 * 1024 * 1024); + const term = child.wait() catch |err| { + log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); + return error.UnableToSpawnSelf; + }; - const term = child.wait() catch |err| { - log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); - return error.UnableToSpawnSelf; - }; + switch (term) { + .Exited => |code| { + if (code != 0) { + // TODO parse this output and surface with the Compilation API rather than + // directly outputting to stderr here. + std.debug.print("{s}", .{stderr}); + return error.LLDReportedFailure; + } + }, + else => { + log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr }); + return error.LLDCrashed; + }, + } - switch (term) { - .Exited => |code| { - if (code != 0) { - // TODO parse this output and surface with the Compilation API rather than - // directly outputting to stderr here. - std.debug.print("{s}", .{stderr}); - return error.LLDReportedFailure; - } - }, - else => { - log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr }); - return error.LLDCrashed; - }, + if (stderr.len != 0) { + log.warn("unexpected LLD stderr:\n{s}", .{stderr}); + } } - - if (stderr.len != 0) { - log.warn("unexpected LLD stderr:\n{s}", .{stderr}); + } else { + const exit_code = try lldMain(arena, argv.items, false); + if (exit_code != 0) { + if (comp.clang_passthrough_mode) { + std.process.exit(exit_code); + } else { + return error.LLDReportedFailure; + } } } } diff --git a/src/main.zig b/src/main.zig index bbdb948c90..3f38fd1f78 100644 --- a/src/main.zig +++ b/src/main.zig @@ -221,7 +221,7 @@ pub fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi mem.eql(u8, cmd, "lib") or mem.eql(u8, cmd, "ar")) { - return punt_to_llvm_ar(arena, args); + return process.exit(try llvmArMain(arena, args)); } else if (mem.eql(u8, cmd, "cc")) { return buildOutputType(gpa, arena, args, .cc); } else if (mem.eql(u8, cmd, "c++")) { @@ -231,12 +231,12 @@ pub fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi } else if (mem.eql(u8, cmd, "clang") or mem.eql(u8, cmd, "-cc1") or mem.eql(u8, cmd, "-cc1as")) { - return punt_to_clang(arena, args); + return process.exit(try clangMain(arena, args)); } else if (mem.eql(u8, cmd, "ld.lld") or mem.eql(u8, cmd, "lld-link") or mem.eql(u8, cmd, "wasm-ld")) { - return punt_to_lld(arena, args); + return process.exit(try lldMain(arena, args, true)); } else if (mem.eql(u8, cmd, "build")) { return cmdBuild(gpa, arena, cmd_args); } else if (mem.eql(u8, cmd, "fmt")) { @@ -1347,7 +1347,7 @@ fn buildOutputType( .ignore => {}, .driver_punt => { // Never mind what we're doing, just pass the args directly. For example --help. - return punt_to_clang(arena, all_args); + return process.exit(try clangMain(arena, all_args)); }, .pic => want_pic = true, .no_pic => want_pic = false, @@ -1866,7 +1866,7 @@ fn buildOutputType( // An error message is generated when there is more than 1 C source file. if (c_source_files.items.len != 1) { // For example `zig cc` and no args should print the "no input files" message. - return punt_to_clang(arena, all_args); + return process.exit(try clangMain(arena, all_args)); } if (out_path) |p| { emit_bin = .{ .yes = p }; @@ -1882,7 +1882,7 @@ fn buildOutputType( { // For example `zig cc` and no args should print the "no input files" message. // There could be other reasons to punt to clang, for example, --help. - return punt_to_clang(arena, all_args); + return process.exit(try clangMain(arena, all_args)); } }, } @@ -2881,9 +2881,9 @@ fn runOrTest( // execv releases the locks; no need to destroy the Compilation here. const err = std.process.execv(gpa, argv.items); try warnAboutForeignBinaries(gpa, arena, arg_mode, target_info, link_libc); - const cmd = try argvCmd(arena, argv.items); + const cmd = try std.mem.join(arena, " ", argv.items); fatal("the following command failed to execve with '{s}':\n{s}", .{ @errorName(err), cmd }); - } else { + } else if (std.process.can_spawn) { const child = try std.ChildProcess.init(argv.items, gpa); defer child.deinit(); @@ -2900,7 +2900,7 @@ fn runOrTest( const term = child.spawnAndWait() catch |err| { try warnAboutForeignBinaries(gpa, arena, arg_mode, target_info, link_libc); - const cmd = try argvCmd(arena, argv.items); + const cmd = try std.mem.join(arena, " ", argv.items); fatal("the following command failed with '{s}':\n{s}", .{ @errorName(err), cmd }); }; switch (arg_mode) { @@ -2931,18 +2931,21 @@ fn runOrTest( if (code == 0) { if (!watch) return cleanExit(); } else { - const cmd = try argvCmd(arena, argv.items); + const cmd = try std.mem.join(arena, " ", argv.items); fatal("the following test command failed with exit code {d}:\n{s}", .{ code, cmd }); } }, else => { - const cmd = try argvCmd(arena, argv.items); + const cmd = try std.mem.join(arena, " ", argv.items); fatal("the following test command crashed:\n{s}", .{cmd}); }, } }, else => unreachable, } + } else { + const cmd = try std.mem.join(arena, " ", argv.items); + fatal("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{ @tagName(builtin.os.tag), cmd }); } } @@ -3553,41 +3556,36 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi break :argv child_argv.items; }; - const child = try std.ChildProcess.init(child_argv, gpa); - defer child.deinit(); - child.stdin_behavior = .Inherit; - child.stdout_behavior = .Inherit; - child.stderr_behavior = .Inherit; + if (std.process.can_spawn) { + const child = try std.ChildProcess.init(child_argv, gpa); + defer child.deinit(); - const term = try child.spawnAndWait(); - switch (term) { - .Exited => |code| { - if (code == 0) return cleanExit(); + child.stdin_behavior = .Inherit; + child.stdout_behavior = .Inherit; + child.stderr_behavior = .Inherit; - if (prominent_compile_errors) { - fatal("the build command failed with exit code {d}", .{code}); - } else { - const cmd = try argvCmd(arena, child_argv); - fatal("the following build command failed with exit code {d}:\n{s}", .{ code, cmd }); - } - }, - else => { - const cmd = try argvCmd(arena, child_argv); - fatal("the following build command crashed:\n{s}", .{cmd}); - }, - } -} + const term = try child.spawnAndWait(); + switch (term) { + .Exited => |code| { + if (code == 0) return cleanExit(); -fn argvCmd(allocator: Allocator, argv: []const []const u8) ![]u8 { - var cmd = std.ArrayList(u8).init(allocator); - defer cmd.deinit(); - for (argv[0 .. argv.len - 1]) |arg| { - try cmd.appendSlice(arg); - try cmd.append(' '); + if (prominent_compile_errors) { + fatal("the build command failed with exit code {d}", .{code}); + } else { + const cmd = try std.mem.join(arena, " ", child_argv); + fatal("the following build command failed with exit code {d}:\n{s}", .{ code, cmd }); + } + }, + else => { + const cmd = try std.mem.join(arena, " ", child_argv); + fatal("the following build command crashed:\n{s}", .{cmd}); + }, + } + } else { + const cmd = try std.mem.join(arena, " ", child_argv); + fatal("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{ @tagName(builtin.os.tag), cmd }); } - try cmd.appendSlice(argv[argv.len - 1]); - return cmd.toOwnedSlice(); } fn readSourceFileToEndAlloc( @@ -4080,65 +4078,87 @@ pub const info_zen = extern "c" fn ZigClang_main(argc: c_int, argv: [*:null]?[*:0]u8) c_int; extern "c" fn ZigLlvmAr_main(argc: c_int, argv: [*:null]?[*:0]u8) c_int; -/// TODO https://github.com/ziglang/zig/issues/3257 -fn punt_to_clang(arena: Allocator, args: []const []const u8) error{OutOfMemory} { - if (!build_options.have_llvm) - fatal("`zig cc` and `zig c++` unavailable: compiler built without LLVM extensions", .{}); - // Convert the args to the format Clang expects. - const argv = try arena.alloc(?[*:0]u8, args.len + 1); +fn argsCopyZ(alloc: Allocator, args: []const []const u8) ![:null]?[*:0]u8 { + var argv = try alloc.allocSentinel(?[*:0]u8, args.len, null); for (args) |arg, i| { - argv[i] = try arena.dupeZ(u8, arg); // TODO If there was an argsAllocZ we could avoid this allocation. + argv[i] = try alloc.dupeZ(u8, arg); // TODO If there was an argsAllocZ we could avoid this allocation. } - argv[args.len] = null; - const exit_code = ZigClang_main(@intCast(c_int, args.len), argv[0..args.len :null].ptr); - process.exit(@bitCast(u8, @truncate(i8, exit_code))); + return argv; } -/// TODO https://github.com/ziglang/zig/issues/3257 -fn punt_to_llvm_ar(arena: Allocator, args: []const []const u8) error{OutOfMemory} { +pub fn clangMain(alloc: Allocator, args: []const []const u8) error{OutOfMemory}!u8 { + if (!build_options.have_llvm) + fatal("`zig cc` and `zig c++` unavailable: compiler built without LLVM extensions", .{}); + + var arena_instance = std.heap.ArenaAllocator.init(alloc); + defer arena_instance.deinit(); + const arena = arena_instance.allocator(); + + // Convert the args to the null-terminated format Clang expects. + const argv = try argsCopyZ(arena, args); + const exit_code = ZigClang_main(@intCast(c_int, argv.len), argv.ptr); + return @bitCast(u8, @truncate(i8, exit_code)); +} + +pub fn llvmArMain(alloc: Allocator, args: []const []const u8) error{OutOfMemory}!u8 { if (!build_options.have_llvm) fatal("`zig ar`, `zig dlltool`, `zig ranlib', and `zig lib` unavailable: compiler built without LLVM extensions", .{}); + var arena_instance = std.heap.ArenaAllocator.init(alloc); + defer arena_instance.deinit(); + const arena = arena_instance.allocator(); + // Convert the args to the format llvm-ar expects. - // We subtract 1 to shave off the zig binary from args[0]. - const argv = try arena.allocSentinel(?[*:0]u8, args.len - 1, null); - for (args[1..]) |arg, i| { - // TODO If there was an argsAllocZ we could avoid this allocation. - argv[i] = try arena.dupeZ(u8, arg); - } - const argc = @intCast(c_int, argv.len); - const exit_code = ZigLlvmAr_main(argc, argv.ptr); - process.exit(@bitCast(u8, @truncate(i8, exit_code))); + // We intentionally shave off the zig binary at args[0]. + const argv = try argsCopyZ(arena, args[1..]); + const exit_code = ZigLlvmAr_main(@intCast(c_int, argv.len), argv.ptr); + return @bitCast(u8, @truncate(i8, exit_code)); } /// The first argument determines which backend is invoked. The options are: /// * `ld.lld` - ELF /// * `lld-link` - COFF /// * `wasm-ld` - WebAssembly -/// TODO https://github.com/ziglang/zig/issues/3257 -pub fn punt_to_lld(arena: Allocator, args: []const []const u8) error{OutOfMemory} { +pub fn lldMain( + alloc: Allocator, + args: []const []const u8, + can_exit_early: bool, +) error{OutOfMemory}!u8 { if (!build_options.have_llvm) fatal("`zig {s}` unavailable: compiler built without LLVM extensions", .{args[0]}); - // Convert the args to the format LLD expects. - // We subtract 1 to shave off the zig binary from args[0]. - const argv = try arena.allocSentinel(?[*:0]const u8, args.len - 1, null); - for (args[1..]) |arg, i| { - argv[i] = try arena.dupeZ(u8, arg); // TODO If there was an argsAllocZ we could avoid this allocation. + + // Print a warning if lld is called multiple times in the same process, + // since it may misbehave + // https://github.com/ziglang/zig/issues/3825 + const CallCounter = struct { + var count: usize = 0; + }; + if (CallCounter.count == 1) { // Issue the warning on the first repeat call + warn("invoking LLD for the second time within the same process because the host OS ({s}) does not support spawning child processes. This sometimes activates LLD bugs", .{@tagName(builtin.os.tag)}); } + CallCounter.count += 1; + + var arena_instance = std.heap.ArenaAllocator.init(alloc); + defer arena_instance.deinit(); + const arena = arena_instance.allocator(); + + // Convert the args to the format llvm-ar expects. + // We intentionally shave off the zig binary at args[0]. + const argv = try argsCopyZ(arena, args[1..]); const exit_code = rc: { const llvm = @import("codegen/llvm/bindings.zig"); const argc = @intCast(c_int, argv.len); if (mem.eql(u8, args[1], "ld.lld")) { - break :rc llvm.LinkELF(argc, argv.ptr, true); + break :rc llvm.LinkELF(argc, argv.ptr, can_exit_early); } else if (mem.eql(u8, args[1], "lld-link")) { - break :rc llvm.LinkCOFF(argc, argv.ptr, true); + break :rc llvm.LinkCOFF(argc, argv.ptr, can_exit_early); } else if (mem.eql(u8, args[1], "wasm-ld")) { - break :rc llvm.LinkWasm(argc, argv.ptr, true); + break :rc llvm.LinkWasm(argc, argv.ptr, can_exit_early); } else { unreachable; } }; - process.exit(@bitCast(u8, @truncate(i8, exit_code))); + return @bitCast(u8, @truncate(i8, exit_code)); } const clang_args = @import("clang_options.zig").list; diff --git a/src/mingw.zig b/src/mingw.zig index f555634459..84ec0795f1 100644 --- a/src/mingw.zig +++ b/src/mingw.zig @@ -5,6 +5,7 @@ const path = std.fs.path; const assert = std.debug.assert; const log = std.log.scoped(.mingw); +const builtin = @import("builtin"); const target_util = @import("target.zig"); const Compilation = @import("Compilation.zig"); const build_options = @import("build_options"); @@ -367,39 +368,43 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void { Compilation.dump_argv(&args); } - const child = try std.ChildProcess.init(&args, arena); - defer child.deinit(); + if (std.process.can_spawn) { + const child = try std.ChildProcess.init(&args, arena); + defer child.deinit(); - child.stdin_behavior = .Ignore; - child.stdout_behavior = .Pipe; - child.stderr_behavior = .Pipe; + child.stdin_behavior = .Ignore; + child.stdout_behavior = .Pipe; + child.stderr_behavior = .Pipe; - try child.spawn(); + try child.spawn(); - const stderr_reader = child.stderr.?.reader(); + const stderr_reader = child.stderr.?.reader(); - // TODO https://github.com/ziglang/zig/issues/6343 - const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024); + // TODO https://github.com/ziglang/zig/issues/6343 + const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024); - const term = child.wait() catch |err| { - // TODO surface a proper error here - log.err("unable to spawn {s}: {s}", .{ args[0], @errorName(err) }); - return error.ClangPreprocessorFailed; - }; - - switch (term) { - .Exited => |code| { - if (code != 0) { - // TODO surface a proper error here - log.err("clang exited with code {d} and stderr: {s}", .{ code, stderr }); - return error.ClangPreprocessorFailed; - } - }, - else => { + const term = child.wait() catch |err| { // TODO surface a proper error here - log.err("clang terminated unexpectedly with stderr: {s}", .{stderr}); + log.err("unable to spawn {s}: {s}", .{ args[0], @errorName(err) }); return error.ClangPreprocessorFailed; - }, + }; + switch (term) { + .Exited => |code| { + if (code != 0) { + // TODO surface a proper error here + log.err("clang exited with code {d} and stderr: {s}", .{ code, stderr }); + return error.ClangPreprocessorFailed; + } + }, + else => { + // TODO surface a proper error here + log.err("clang terminated unexpectedly with stderr: {s}", .{stderr}); + return error.ClangPreprocessorFailed; + }, + } + } else { + log.err("unable to spawn {s}: spawning child process not supported on {s}", .{ args[0], @tagName(builtin.os.tag) }); + return error.ClangPreprocessorFailed; } const lib_final_path = try comp.global_cache_directory.join(comp.gpa, &[_][]const u8{ diff --git a/src/test.zig b/src/test.zig index e02ea04f1c..b73e11d7f5 100644 --- a/src/test.zig +++ b/src/test.zig @@ -730,6 +730,12 @@ pub const TestContext = struct { // * cannot handle updates // because of this we must spawn a child process rather than // using Compilation directly. + + if (!std.process.can_spawn) { + print("Unable to spawn child processes on {s}, skipping test.\n", .{@tagName(builtin.os.tag)}); + return; // Pass test. + } + assert(case.updates.items.len == 1); const update = case.updates.items[0]; try tmp.dir.writeFile(tmp_src_path, update.src); @@ -1104,6 +1110,11 @@ pub const TestContext = struct { } }, .Execution => |expected_stdout| { + if (!std.process.can_spawn) { + print("Unable to spawn child processes on {s}, skipping test.\n", .{@tagName(builtin.os.tag)}); + return; // Pass test. + } + update_node.setEstimatedTotalItems(4); var argv = std.ArrayList([]const u8).init(allocator); |
