authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-09-26 01:01:49-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-09-26 01:01:49-04:00
logcba4a9ad4a149766c650e3f3d71435cef14867a3
tree2edcb1357482a548ae821109519118b51b1e5a0e
parent79400bfdfd1e90cf3a4d09f32de965b13796a44d

update std.os.ChildProcess API

* add std.os.ChildProcess.setUserName * add std.os.getUserId

10 files changed, 325 insertions(+), 150 deletions(-)

CMakeLists.txt+1
...@@ -548,6 +548,7 @@ install(FILES "${CMAKE_SOURCE_DIR}/std/net.zig" DESTINATION "${ZIG_STD_DEST}")...@@ -548,6 +548,7 @@ install(FILES "${CMAKE_SOURCE_DIR}/std/net.zig" DESTINATION "${ZIG_STD_DEST}")
548install(FILES "${CMAKE_SOURCE_DIR}/std/os/child_process.zig" DESTINATION "${ZIG_STD_DEST}/os")548install(FILES "${CMAKE_SOURCE_DIR}/std/os/child_process.zig" DESTINATION "${ZIG_STD_DEST}/os")
549install(FILES "${CMAKE_SOURCE_DIR}/std/os/darwin.zig" DESTINATION "${ZIG_STD_DEST}/os")549install(FILES "${CMAKE_SOURCE_DIR}/std/os/darwin.zig" DESTINATION "${ZIG_STD_DEST}/os")
550install(FILES "${CMAKE_SOURCE_DIR}/std/os/darwin_errno.zig" DESTINATION "${ZIG_STD_DEST}/os")550install(FILES "${CMAKE_SOURCE_DIR}/std/os/darwin_errno.zig" DESTINATION "${ZIG_STD_DEST}/os")
551install(FILES "${CMAKE_SOURCE_DIR}/std/os/get_user_id.zig" DESTINATION "${ZIG_STD_DEST}/os")
551install(FILES "${CMAKE_SOURCE_DIR}/std/os/index.zig" DESTINATION "${ZIG_STD_DEST}/os")552install(FILES "${CMAKE_SOURCE_DIR}/std/os/index.zig" DESTINATION "${ZIG_STD_DEST}/os")
552install(FILES "${CMAKE_SOURCE_DIR}/std/os/linux.zig" DESTINATION "${ZIG_STD_DEST}/os")553install(FILES "${CMAKE_SOURCE_DIR}/std/os/linux.zig" DESTINATION "${ZIG_STD_DEST}/os")
553install(FILES "${CMAKE_SOURCE_DIR}/std/os/linux_errno.zig" DESTINATION "${ZIG_STD_DEST}/os")554install(FILES "${CMAKE_SOURCE_DIR}/std/os/linux_errno.zig" DESTINATION "${ZIG_STD_DEST}/os")
example/mix_o_files/build.zig+1-1
...@@ -12,7 +12,7 @@ pub fn build(b: &Builder) {...@@ -12,7 +12,7 @@ pub fn build(b: &Builder) {
1212
13 b.default_step.dependOn(&exe.step);13 b.default_step.dependOn(&exe.step);
1414
15 const run_cmd = b.addCommand(".", b.env_map, exe.getOutputPath(), [][]const u8{});15 const run_cmd = b.addCommand(".", b.env_map, [][]const u8{exe.getOutputPath()});
16 run_cmd.step.dependOn(&exe.step);16 run_cmd.step.dependOn(&exe.step);
1717
18 const test_step = b.step("test", "Test the program");18 const test_step = b.step("test", "Test the program");
example/shared_library/build.zig+1-1
...@@ -12,7 +12,7 @@ pub fn build(b: &Builder) {...@@ -12,7 +12,7 @@ pub fn build(b: &Builder) {
1212
13 b.default_step.dependOn(&exe.step);13 b.default_step.dependOn(&exe.step);
1414
15 const run_cmd = b.addCommand(".", b.env_map, exe.getOutputPath(), [][]const u8{});15 const run_cmd = b.addCommand(".", b.env_map, [][]const u8{exe.getOutputPath()});
16 run_cmd.step.dependOn(&exe.step);16 run_cmd.step.dependOn(&exe.step);
1717
18 const test_step = b.step("test", "Test the program");18 const test_step = b.step("test", "Test the program");
std/build.zig+48-37
...@@ -180,11 +180,11 @@ pub const Builder = struct {...@@ -180,11 +180,11 @@ pub const Builder = struct {
180 return LibExeObjStep.createCObject(self, name, src);180 return LibExeObjStep.createCObject(self, name, src);
181 }181 }
182182
183 /// ::args are copied.183 /// ::argv is copied.
184 pub fn addCommand(self: &Builder, cwd: ?[]const u8, env_map: &const BufMap,184 pub fn addCommand(self: &Builder, cwd: ?[]const u8, env_map: &const BufMap,
185 path: []const u8, args: []const []const u8) -> &CommandStep185 argv: []const []const u8) -> &CommandStep
186 {186 {
187 return CommandStep.create(self, cwd, env_map, path, args);187 return CommandStep.create(self, cwd, env_map, argv);
188 }188 }
189189
190 pub fn addWriteFile(self: &Builder, file_path: []const u8, data: []const u8) -> &WriteFileStep {190 pub fn addWriteFile(self: &Builder, file_path: []const u8, data: []const u8) -> &WriteFileStep {
...@@ -528,42 +528,41 @@ pub const Builder = struct {...@@ -528,42 +528,41 @@ pub const Builder = struct {
528 return self.invalid_user_input;528 return self.invalid_user_input;
529 }529 }
530530
531 fn spawnChild(self: &Builder, exe_path: []const u8, args: []const []const u8) -> %void {531 fn spawnChild(self: &Builder, argv: []const []const u8) -> %void {
532 return self.spawnChildEnvMap(null, &self.env_map, exe_path, args);532 return self.spawnChildEnvMap(null, &self.env_map, argv);
533 }533 }
534534
535 fn spawnChildEnvMap(self: &Builder, cwd: ?[]const u8, env_map: &const BufMap,535 fn spawnChildEnvMap(self: &Builder, cwd: ?[]const u8, env_map: &const BufMap,
536 exe_path: []const u8, args: []const []const u8) -> %void536 argv: []const []const u8) -> %void
537 {537 {
538 if (self.verbose) {538 if (self.verbose) {
539 if (cwd) |yes_cwd| %%io.stderr.print("cd {}; ", yes_cwd);539 if (cwd) |yes_cwd| %%io.stderr.print("cd {}; ", yes_cwd);
540 %%io.stderr.print("{}", exe_path);540 for (argv) |arg| {
541 for (args) |arg| {541 %%io.stderr.print("{} ", arg);
542 %%io.stderr.print(" {}", arg);
543 }542 }
544 %%io.stderr.printf("\n");543 %%io.stderr.printf("\n");
545 }544 }
546545
547 var child = os.ChildProcess.spawn(exe_path, args, cwd, env_map,546 const child = %%os.ChildProcess.init(argv, self.allocator);
548 StdIo.Inherit, StdIo.Inherit, StdIo.Inherit, null, self.allocator) %% |err|547 defer child.deinit();
549 {
550 %%io.stderr.printf("Unable to spawn {}: {}\n", exe_path, @errorName(err));
551 return err;
552 };
553548
554 const term = child.wait() %% |err| {549 child.cwd = cwd;
555 %%io.stderr.printf("Unable to spawn {}: {}\n", exe_path, @errorName(err));550 child.env_map = env_map;
551
552 const term = child.spawnAndWait() %% |err| {
553 %%io.stderr.printf("Unable to spawn {}: {}\n", argv[0], @errorName(err));
556 return err;554 return err;
557 };555 };
556
558 switch (term) {557 switch (term) {
559 Term.Exited => |code| {558 Term.Exited => |code| {
560 if (code != 0) {559 if (code != 0) {
561 %%io.stderr.printf("Process {} exited with error code {}\n", exe_path, code);560 %%io.stderr.printf("Process {} exited with error code {}\n", argv[0], code);
562 return error.UncleanExit;561 return error.UncleanExit;
563 }562 }
564 },563 },
565 else => {564 else => {
566 %%io.stderr.printf("Process {} terminated unexpectedly\n", exe_path);565 %%io.stderr.printf("Process {} terminated unexpectedly\n", argv[0]);
567 return error.UncleanExit;566 return error.UncleanExit;
568 },567 },
569 };568 };
...@@ -1063,6 +1062,8 @@ pub const LibExeObjStep = struct {...@@ -1063,6 +1062,8 @@ pub const LibExeObjStep = struct {
1063 var zig_args = ArrayList([]const u8).init(builder.allocator);1062 var zig_args = ArrayList([]const u8).init(builder.allocator);
1064 defer zig_args.deinit();1063 defer zig_args.deinit();
10651064
1065 %%zig_args.append(builder.zig_exe);
1066
1066 const cmd = switch (self.kind) {1067 const cmd = switch (self.kind) {
1067 Kind.Lib => "build-lib",1068 Kind.Lib => "build-lib",
1068 Kind.Exe => "build-exe",1069 Kind.Exe => "build-exe",
...@@ -1193,7 +1194,7 @@ pub const LibExeObjStep = struct {...@@ -1193,7 +1194,7 @@ pub const LibExeObjStep = struct {
1193 }1194 }
1194 }1195 }
11951196
1196 %return builder.spawnChild(builder.zig_exe, zig_args.toSliceConst());1197 %return builder.spawnChild(zig_args.toSliceConst());
11971198
1198 if (self.kind == Kind.Lib and !self.static) {1199 if (self.kind == Kind.Lib and !self.static) {
1199 %return doAtomicSymLinks(builder.allocator, output_path, self.major_only_filename,1200 %return doAtomicSymLinks(builder.allocator, output_path, self.major_only_filename,
...@@ -1255,6 +1256,8 @@ pub const LibExeObjStep = struct {...@@ -1255,6 +1256,8 @@ pub const LibExeObjStep = struct {
1255 var cc_args = ArrayList([]const u8).init(builder.allocator);1256 var cc_args = ArrayList([]const u8).init(builder.allocator);
1256 defer cc_args.deinit();1257 defer cc_args.deinit();
12571258
1259 %%cc_args.append(cc);
1260
1258 const is_darwin = self.target.isDarwin();1261 const is_darwin = self.target.isDarwin();
12591262
1260 switch (self.kind) {1263 switch (self.kind) {
...@@ -1268,11 +1271,12 @@ pub const LibExeObjStep = struct {...@@ -1268,11 +1271,12 @@ pub const LibExeObjStep = struct {
12681271
1269 self.appendCompileFlags(&cc_args);1272 self.appendCompileFlags(&cc_args);
12701273
1271 %return builder.spawnChild(cc, cc_args.toSliceConst());1274 %return builder.spawnChild(cc_args.toSliceConst());
1272 },1275 },
1273 Kind.Lib => {1276 Kind.Lib => {
1274 for (self.source_files.toSliceConst()) |source_file| {1277 for (self.source_files.toSliceConst()) |source_file| {
1275 %%cc_args.resize(0);1278 %%cc_args.resize(0);
1279 %%cc_args.append(cc);
12761280
1277 if (!self.static) {1281 if (!self.static) {
1278 %%cc_args.append("-fPIC");1282 %%cc_args.append("-fPIC");
...@@ -1291,7 +1295,7 @@ pub const LibExeObjStep = struct {...@@ -1291,7 +1295,7 @@ pub const LibExeObjStep = struct {
12911295
1292 self.appendCompileFlags(&cc_args);1296 self.appendCompileFlags(&cc_args);
12931297
1294 %return builder.spawnChild(cc, cc_args.toSliceConst());1298 %return builder.spawnChild(cc_args.toSliceConst());
12951299
1296 %%self.object_files.append(cache_o_file);1300 %%self.object_files.append(cache_o_file);
1297 }1301 }
...@@ -1299,6 +1303,8 @@ pub const LibExeObjStep = struct {...@@ -1299,6 +1303,8 @@ pub const LibExeObjStep = struct {
1299 if (self.static) {1303 if (self.static) {
1300 // ar1304 // ar
1301 %%cc_args.resize(0);1305 %%cc_args.resize(0);
1306 %%cc_args.append("ar");
1307
1302 %%cc_args.append("qc");1308 %%cc_args.append("qc");
13031309
1304 const output_path = builder.pathFromRoot(self.getOutputPath());1310 const output_path = builder.pathFromRoot(self.getOutputPath());
...@@ -1308,15 +1314,17 @@ pub const LibExeObjStep = struct {...@@ -1308,15 +1314,17 @@ pub const LibExeObjStep = struct {
1308 %%cc_args.append(builder.pathFromRoot(object_file));1314 %%cc_args.append(builder.pathFromRoot(object_file));
1309 }1315 }
13101316
1311 %return builder.spawnChild("ar", cc_args.toSliceConst());1317 %return builder.spawnChild(cc_args.toSliceConst());
13121318
1313 // ranlib1319 // ranlib
1314 %%cc_args.resize(0);1320 %%cc_args.resize(0);
1321 %%cc_args.append("ranlib");
1315 %%cc_args.append(output_path);1322 %%cc_args.append(output_path);
13161323
1317 %return builder.spawnChild("ranlib", cc_args.toSliceConst());1324 %return builder.spawnChild(cc_args.toSliceConst());
1318 } else {1325 } else {
1319 %%cc_args.resize(0);1326 %%cc_args.resize(0);
1327 %%cc_args.append(cc);
13201328
1321 if (is_darwin) {1329 if (is_darwin) {
1322 %%cc_args.append("-dynamiclib");1330 %%cc_args.append("-dynamiclib");
...@@ -1370,7 +1378,7 @@ pub const LibExeObjStep = struct {...@@ -1370,7 +1378,7 @@ pub const LibExeObjStep = struct {
1370 }1378 }
1371 }1379 }
13721380
1373 %return builder.spawnChild(cc, cc_args.toSliceConst());1381 %return builder.spawnChild(cc_args.toSliceConst());
13741382
1375 %return doAtomicSymLinks(builder.allocator, output_path, self.major_only_filename,1383 %return doAtomicSymLinks(builder.allocator, output_path, self.major_only_filename,
1376 self.name_only_filename);1384 self.name_only_filename);
...@@ -1379,6 +1387,7 @@ pub const LibExeObjStep = struct {...@@ -1379,6 +1387,7 @@ pub const LibExeObjStep = struct {
1379 Kind.Exe => {1387 Kind.Exe => {
1380 for (self.source_files.toSliceConst()) |source_file| {1388 for (self.source_files.toSliceConst()) |source_file| {
1381 %%cc_args.resize(0);1389 %%cc_args.resize(0);
1390 %%cc_args.append(cc);
13821391
1383 const abs_source_file = builder.pathFromRoot(source_file);1392 const abs_source_file = builder.pathFromRoot(source_file);
1384 %%cc_args.append("-c");1393 %%cc_args.append("-c");
...@@ -1400,12 +1409,13 @@ pub const LibExeObjStep = struct {...@@ -1400,12 +1409,13 @@ pub const LibExeObjStep = struct {
1400 %%cc_args.append(builder.pathFromRoot(dir));1409 %%cc_args.append(builder.pathFromRoot(dir));
1401 }1410 }
14021411
1403 %return builder.spawnChild(cc, cc_args.toSliceConst());1412 %return builder.spawnChild(cc_args.toSliceConst());
14041413
1405 %%self.object_files.append(cache_o_file);1414 %%self.object_files.append(cache_o_file);
1406 }1415 }
14071416
1408 %%cc_args.resize(0);1417 %%cc_args.resize(0);
1418 %%cc_args.append(cc);
14091419
1410 for (self.object_files.toSliceConst()) |object_file| {1420 for (self.object_files.toSliceConst()) |object_file| {
1411 %%cc_args.append(builder.pathFromRoot(object_file));1421 %%cc_args.append(builder.pathFromRoot(object_file));
...@@ -1441,7 +1451,7 @@ pub const LibExeObjStep = struct {...@@ -1441,7 +1451,7 @@ pub const LibExeObjStep = struct {
1441 }1451 }
1442 }1452 }
14431453
1444 %return builder.spawnChild(cc, cc_args.toSliceConst());1454 %return builder.spawnChild(cc_args.toSliceConst());
1445 },1455 },
1446 }1456 }
1447 }1457 }
...@@ -1518,6 +1528,8 @@ pub const TestStep = struct {...@@ -1518,6 +1528,8 @@ pub const TestStep = struct {
1518 var zig_args = ArrayList([]const u8).init(builder.allocator);1528 var zig_args = ArrayList([]const u8).init(builder.allocator);
1519 defer zig_args.deinit();1529 defer zig_args.deinit();
15201530
1531 %%zig_args.append(builder.zig_exe);
1532
1521 %%zig_args.append("test");1533 %%zig_args.append("test");
1522 %%zig_args.append(builder.pathFromRoot(self.root_src));1534 %%zig_args.append(builder.pathFromRoot(self.root_src));
15231535
...@@ -1590,32 +1602,31 @@ pub const TestStep = struct {...@@ -1590,32 +1602,31 @@ pub const TestStep = struct {
1590 %%zig_args.append(lib_path);1602 %%zig_args.append(lib_path);
1591 }1603 }
15921604
1593 %return builder.spawnChild(builder.zig_exe, zig_args.toSliceConst());1605 %return builder.spawnChild(zig_args.toSliceConst());
1594 }1606 }
1595};1607};
15961608
1597pub const CommandStep = struct {1609pub const CommandStep = struct {
1598 step: Step,1610 step: Step,
1599 builder: &Builder,1611 builder: &Builder,
1600 exe_path: []const u8,1612 argv: [][]const u8,
1601 args: [][]const u8,
1602 cwd: ?[]const u8,1613 cwd: ?[]const u8,
1603 env_map: &const BufMap,1614 env_map: &const BufMap,
16041615
1605 /// ::args are copied.1616 /// ::argv is copied.
1606 pub fn create(builder: &Builder, cwd: ?[]const u8, env_map: &const BufMap,1617 pub fn create(builder: &Builder, cwd: ?[]const u8, env_map: &const BufMap,
1607 exe_path: []const u8, args: []const []const u8) -> &CommandStep1618 argv: []const []const u8) -> &CommandStep
1608 {1619 {
1609 const self = %%builder.allocator.create(CommandStep);1620 const self = %%builder.allocator.create(CommandStep);
1610 *self = CommandStep {1621 *self = CommandStep {
1611 .builder = builder,1622 .builder = builder,
1612 .step = Step.init(exe_path, builder.allocator, make),1623 .step = Step.init(argv[0], builder.allocator, make),
1613 .exe_path = exe_path,1624 .argv = %%builder.allocator.alloc([]u8, argv.len),
1614 .args = %%builder.allocator.alloc([]u8, args.len),
1615 .cwd = cwd,1625 .cwd = cwd,
1616 .env_map = env_map,1626 .env_map = env_map,
1617 };1627 };
1618 mem.copy([]const u8, self.args, args);1628 mem.copy([]const u8, self.argv, argv);
1629 self.step.name = self.argv[0];
1619 return self;1630 return self;
1620 }1631 }
16211632
...@@ -1623,7 +1634,7 @@ pub const CommandStep = struct {...@@ -1623,7 +1634,7 @@ pub const CommandStep = struct {
1623 const self = @fieldParentPtr(CommandStep, "step", step);1634 const self = @fieldParentPtr(CommandStep, "step", step);
16241635
1625 const cwd = if (self.cwd) |cwd| self.builder.pathFromRoot(cwd) else null;1636 const cwd = if (self.cwd) |cwd| self.builder.pathFromRoot(cwd) else null;
1626 return self.builder.spawnChildEnvMap(cwd, self.env_map, self.exe_path, self.args);1637 return self.builder.spawnChildEnvMap(cwd, self.env_map, self.argv);
1627 }1638 }
1628};1639};
16291640
std/os/child_process.zig+120-65
...@@ -16,20 +16,35 @@ error ProcessNotFound;...@@ -16,20 +16,35 @@ error ProcessNotFound;
16var children_nodes = LinkedList(&ChildProcess).init();16var children_nodes = LinkedList(&ChildProcess).init();
1717
18pub const ChildProcess = struct {18pub const ChildProcess = struct {
19 pid: i32,19 pub pid: i32,
20 pub allocator: &mem.Allocator,
2021
21 err_pipe: [2]i32,22 pub stdin: ?&io.OutStream,
22 llnode: LinkedList(&ChildProcess).Node,23 pub stdout: ?&io.InStream,
23 allocator: &mem.Allocator,24 pub stderr: ?&io.InStream,
25
26 pub term: ?%Term,
27
28 pub argv: []const []const u8,
29
30 /// Possibly called from a signal handler. Must set this before calling `spawn`.
31 pub onTerm: ?fn(&ChildProcess),
32
33 /// Leave as null to use the current env map using the supplied allocator.
34 pub env_map: ?&const BufMap,
2435
25 stdin: ?&io.OutStream,36 pub stdin_behavior: StdIo,
26 stdout: ?&io.InStream,37 pub stdout_behavior: StdIo,
27 stderr: ?&io.InStream,38 pub stderr_behavior: StdIo,
2839
29 term: ?%Term,40 /// Set to change the user id when spawning the child process.
41 pub uid: ?u32,
3042
31 /// Possibly called from a signal handler.43 /// Set to change the current working directory when spawning the child process.
32 onTerm: ?fn(&ChildProcess),44 pub cwd: ?[]const u8,
45
46 err_pipe: [2]i32,
47 llnode: LinkedList(&ChildProcess).Node,
3348
34 pub const Term = enum {49 pub const Term = enum {
35 Exited: i32,50 Exited: i32,
...@@ -45,18 +60,50 @@ pub const ChildProcess = struct {...@@ -45,18 +60,50 @@ pub const ChildProcess = struct {
45 Close,60 Close,
46 };61 };
4762
63 /// First argument in argv is the executable.
64 /// On success must call deinit.
65 pub fn init(argv: []const []const u8, allocator: &Allocator) -> %&ChildProcess {
66 const child = %return allocator.create(ChildProcess);
67 %defer allocator.destroy(child);
68
69 *child = ChildProcess {
70 .allocator = allocator,
71 .argv = argv,
72 .pid = undefined,
73 .err_pipe = undefined,
74 .llnode = undefined,
75 .term = null,
76 .onTerm = null,
77 .env_map = null,
78 .cwd = null,
79 .uid = null,
80 .stdin = null,
81 .stdout = null,
82 .stderr = null,
83 .stdin_behavior = StdIo.Inherit,
84 .stdout_behavior = StdIo.Inherit,
85 .stderr_behavior = StdIo.Inherit,
86 };
87
88 return child;
89 }
90
91 pub fn setUserName(self: &ChildProcess, name: []const u8) -> %void {
92 self.uid = %return os.getUserId(name);
93 }
94
48 /// onTerm can be called before `spawn` returns.95 /// onTerm can be called before `spawn` returns.
49 pub fn spawn(exe_path: []const u8, args: []const []const u8,96 /// On success must call `kill` or `wait`.
50 cwd: ?[]const u8, env_map: &const BufMap,97 pub fn spawn(self: &ChildProcess) -> %void {
51 stdin: StdIo, stdout: StdIo, stderr: StdIo,98 return switch (builtin.os) {
52 onTerm: ?fn(&ChildProcess), allocator: &Allocator) -> %&ChildProcess99 Os.linux, Os.macosx, Os.ios, Os.darwin => self.spawnPosix(),
53 {
54 switch (builtin.os) {
55 Os.linux, Os.macosx, Os.ios, Os.darwin => {
56 return spawnPosix(exe_path, args, cwd, env_map, stdin, stdout, stderr, onTerm, allocator);
57 },
58 else => @compileError("Unsupported OS"),100 else => @compileError("Unsupported OS"),
59 }101 };
102 }
103
104 pub fn spawnAndWait(self: &ChildProcess) -> %Term {
105 %return self.spawn();
106 return self.wait();
60 }107 }
61108
62 /// Forcibly terminates child process and then cleans up all resources.109 /// Forcibly terminates child process and then cleans up all resources.
...@@ -96,6 +143,10 @@ pub const ChildProcess = struct {...@@ -96,6 +143,10 @@ pub const ChildProcess = struct {
96 return ??self.term;143 return ??self.term;
97 }144 }
98145
146 pub fn deinit(self: &ChildProcess) {
147 self.allocator.destroy(self);
148 }
149
99 fn waitUnwrapped(self: &ChildProcess) {150 fn waitUnwrapped(self: &ChildProcess) {
100 var status: i32 = undefined;151 var status: i32 = undefined;
101 while (true) {152 while (true) {
...@@ -162,50 +213,56 @@ pub const ChildProcess = struct {...@@ -162,50 +213,56 @@ pub const ChildProcess = struct {
162 };213 };
163 }214 }
164215
165 fn spawnPosix(exe_path: []const u8, args: []const []const u8,216 fn spawnPosix(self: &ChildProcess) -> %void {
166 maybe_cwd: ?[]const u8, env_map: &const BufMap,
167 stdin: StdIo, stdout: StdIo, stderr: StdIo,
168 onTerm: ?fn(&ChildProcess), allocator: &Allocator) -> %&ChildProcess
169 {
170 // TODO atomically set a flag saying that we already did this217 // TODO atomically set a flag saying that we already did this
171 install_SIGCHLD_handler();218 install_SIGCHLD_handler();
172219
173 const stdin_pipe = if (stdin == StdIo.Pipe) %return makePipe() else undefined;220 const stdin_pipe = if (self.stdin_behavior == StdIo.Pipe) %return makePipe() else undefined;
174 %defer if (stdin == StdIo.Pipe) { destroyPipe(stdin_pipe); };221 %defer if (self.stdin_behavior == StdIo.Pipe) { destroyPipe(stdin_pipe); };
175222
176 const stdout_pipe = if (stdout == StdIo.Pipe) %return makePipe() else undefined;223 const stdout_pipe = if (self.stdout_behavior == StdIo.Pipe) %return makePipe() else undefined;
177 %defer if (stdout == StdIo.Pipe) { destroyPipe(stdout_pipe); };224 %defer if (self.stdout_behavior == StdIo.Pipe) { destroyPipe(stdout_pipe); };
178225
179 const stderr_pipe = if (stderr == StdIo.Pipe) %return makePipe() else undefined;226 const stderr_pipe = if (self.stderr_behavior == StdIo.Pipe) %return makePipe() else undefined;
180 %defer if (stderr == StdIo.Pipe) { destroyPipe(stderr_pipe); };227 %defer if (self.stderr_behavior == StdIo.Pipe) { destroyPipe(stderr_pipe); };
181228
182 const any_ignore = (stdin == StdIo.Ignore or stdout == StdIo.Ignore or stderr == StdIo.Ignore);229 const any_ignore = (self.stdin_behavior == StdIo.Ignore or self.stdout_behavior == StdIo.Ignore or self.stderr_behavior == StdIo.Ignore);
183 const dev_null_fd = if (any_ignore) {230 const dev_null_fd = if (any_ignore) {
184 %return os.posixOpen("/dev/null", posix.O_RDWR, 0, null)231 %return os.posixOpen("/dev/null", posix.O_RDWR, 0, null)
185 } else {232 } else {
186 undefined233 undefined
187 };234 };
235 defer { if (any_ignore) os.posixClose(dev_null_fd); };
236
237 var env_map_owned: BufMap = undefined;
238 var we_own_env_map: bool = undefined;
239 const env_map = if (self.env_map) |env_map| {
240 we_own_env_map = false;
241 env_map
242 } else {
243 we_own_env_map = true;
244 env_map_owned = %return os.getEnvMap(self.allocator);
245 &env_map_owned
246 };
247 defer { if (we_own_env_map) env_map_owned.deinit(); }
188248
189 // This pipe is used to communicate errors between the time of fork249 // This pipe is used to communicate errors between the time of fork
190 // and execve from the child process to the parent process.250 // and execve from the child process to the parent process.
191 const err_pipe = %return makePipe();251 const err_pipe = %return makePipe();
192 %defer destroyPipe(err_pipe);252 %defer destroyPipe(err_pipe);
193253
194 const child = %return allocator.create(ChildProcess);254 const stdin_ptr = if (self.stdin_behavior == StdIo.Pipe) {
195 %defer allocator.destroy(child);255 %return self.allocator.create(io.OutStream)
196
197 const stdin_ptr = if (stdin == StdIo.Pipe) {
198 %return allocator.create(io.OutStream)
199 } else {256 } else {
200 null257 null
201 };258 };
202 const stdout_ptr = if (stdout == StdIo.Pipe) {259 const stdout_ptr = if (self.stdout_behavior == StdIo.Pipe) {
203 %return allocator.create(io.InStream)260 %return self.allocator.create(io.InStream)
204 } else {261 } else {
205 null262 null
206 };263 };
207 const stderr_ptr = if (stderr == StdIo.Pipe) {264 const stderr_ptr = if (self.stderr_behavior == StdIo.Pipe) {
208 %return allocator.create(io.InStream)265 %return self.allocator.create(io.InStream)
209 } else {266 } else {
210 null267 null
211 };268 };
...@@ -224,19 +281,23 @@ pub const ChildProcess = struct {...@@ -224,19 +281,23 @@ pub const ChildProcess = struct {
224 // we are the child281 // we are the child
225 restore_SIGCHLD();282 restore_SIGCHLD();
226283
227 setUpChildIo(stdin, stdin_pipe[0], posix.STDIN_FILENO, dev_null_fd) %%284 setUpChildIo(self.stdin_behavior, stdin_pipe[0], posix.STDIN_FILENO, dev_null_fd) %%
228 |err| forkChildErrReport(err_pipe[1], err);285 |err| forkChildErrReport(err_pipe[1], err);
229 setUpChildIo(stdout, stdout_pipe[1], posix.STDOUT_FILENO, dev_null_fd) %%286 setUpChildIo(self.stdout_behavior, stdout_pipe[1], posix.STDOUT_FILENO, dev_null_fd) %%
230 |err| forkChildErrReport(err_pipe[1], err);287 |err| forkChildErrReport(err_pipe[1], err);
231 setUpChildIo(stderr, stderr_pipe[1], posix.STDERR_FILENO, dev_null_fd) %%288 setUpChildIo(self.stderr_behavior, stderr_pipe[1], posix.STDERR_FILENO, dev_null_fd) %%
232 |err| forkChildErrReport(err_pipe[1], err);289 |err| forkChildErrReport(err_pipe[1], err);
233290
234 if (maybe_cwd) |cwd| {291 if (self.cwd) |cwd| {
235 os.changeCurDir(allocator, cwd) %%292 os.changeCurDir(self.allocator, cwd) %%
236 |err| forkChildErrReport(err_pipe[1], err);293 |err| forkChildErrReport(err_pipe[1], err);
237 }294 }
238295
239 os.posixExecve(exe_path, args, env_map, allocator) %%296 if (self.uid) |uid| {
297 os.posix_setuid(uid) %% |err| forkChildErrReport(err_pipe[1], err);
298 }
299
300 os.posixExecve(self.argv, env_map, self.allocator) %%
240 |err| forkChildErrReport(err_pipe[1], err);301 |err| forkChildErrReport(err_pipe[1], err);
241 }302 }
242303
...@@ -266,28 +327,22 @@ pub const ChildProcess = struct {...@@ -266,28 +327,22 @@ pub const ChildProcess = struct {
266 };327 };
267 }328 }
268329
269 *child = ChildProcess {330 self.pid = pid;
270 .allocator = allocator,331 self.err_pipe = err_pipe;
271 .pid = pid,332 self.llnode = LinkedList(&ChildProcess).Node.init(self);
272 .err_pipe = err_pipe,333 self.term = null;
273 .llnode = LinkedList(&ChildProcess).Node.init(child),334 self.stdin = stdin_ptr;
274 .term = null,335 self.stdout = stdout_ptr;
275 .onTerm = onTerm,336 self.stderr = stderr_ptr;
276 .stdin = stdin_ptr,
277 .stdout = stdout_ptr,
278 .stderr = stderr_ptr,
279 };
280337
281 children_nodes.prepend(&child.llnode);338 // TODO make this atomic so it works even with threads
339 children_nodes.prepend(&self.llnode);
282340
283 restore_SIGCHLD();341 restore_SIGCHLD();
284342
285 if (stdin == StdIo.Pipe) { os.posixClose(stdin_pipe[0]); }343 if (self.stdin_behavior == StdIo.Pipe) { os.posixClose(stdin_pipe[0]); }
286 if (stdout == StdIo.Pipe) { os.posixClose(stdout_pipe[1]); }344 if (self.stdout_behavior == StdIo.Pipe) { os.posixClose(stdout_pipe[1]); }
287 if (stderr == StdIo.Pipe) { os.posixClose(stderr_pipe[1]); }345 if (self.stderr_behavior == StdIo.Pipe) { os.posixClose(stderr_pipe[1]); }
288 if (any_ignore) { os.posixClose(dev_null_fd); }
289
290 return child;
291 }346 }
292347
293 fn setUpChildIo(stdio: StdIo, pipe_fd: i32, std_fileno: i32, dev_null_fd: i32) -> %void {348 fn setUpChildIo(stdio: StdIo, pipe_fd: i32, std_fileno: i32, dev_null_fd: i32) -> %void {
std/os/get_user_id.zig created+78
...@@ -0,0 +1,78 @@
1const builtin = @import("builtin");
2const Os = builtin.Os;
3const os = @import("index.zig");
4const io = @import("../io.zig");
5
6/// POSIX function which gets a uid from username.
7pub fn getUserId(name: []const u8) -> %u32 {
8 return switch (builtin.os) {
9 Os.linux, Os.darwin, Os.macosx, Os.ios => posixGetUserId(name),
10 else => @compileError("Unsupported OS"),
11 };
12}
13
14const State = enum {
15 Start,
16 WaitForNextLine,
17 SkipPassword,
18 ReadId,
19};
20
21error UserNotFound;
22error CorruptPasswordFile;
23
24pub fn posixGetUserId(name: []const u8) -> %u32 {
25 var in_stream = %return io.InStream.open("/etc/passwd", null);
26 defer in_stream.close();
27
28 var buf: [os.page_size]u8 = undefined;
29 var name_index: usize = 0;
30 var state = State.Start;
31 var uid: u32 = 0;
32
33 while (true) {
34 const amt_read = %return in_stream.read(buf[0..]);
35 for (buf[0..amt_read]) |byte| {
36 switch (state) {
37 State.Start => switch (byte) {
38 ':' => {
39 state = if (name_index == name.len) State.SkipPassword else State.WaitForNextLine;
40 },
41 '\n' => return error.CorruptPasswordFile,
42 else => {
43 if (name_index == name.len or name[name_index] != byte) {
44 state = State.WaitForNextLine;
45 }
46 name_index += 1;
47 },
48 },
49 State.WaitForNextLine => switch (byte) {
50 '\n' => {
51 name_index = 0;
52 state = State.Start;
53 },
54 else => continue,
55 },
56 State.SkipPassword => switch (byte) {
57 '\n' => return error.CorruptPasswordFile,
58 ':' => {
59 state = State.ReadId;
60 },
61 else => continue,
62 },
63 State.ReadId => switch (byte) {
64 '\n', ':' => return uid,
65 else => {
66 const digit = switch (byte) {
67 '0' ... '9' => byte - '0',
68 else => return error.CorruptPasswordFile,
69 };
70 if (@mulWithOverflow(u32, uid, 10, &uid)) return error.CorruptPasswordFile;
71 if (@addWithOverflow(u32, uid, digit, &uid)) return error.CorruptPasswordFile;
72 },
73 },
74 }
75 }
76 if (amt_read < buf.len) return error.UserNotFound;
77 }
78}
std/os/index.zig+28-21
...@@ -20,6 +20,8 @@ pub const line_sep = switch (builtin.os) {...@@ -20,6 +20,8 @@ pub const line_sep = switch (builtin.os) {
2020
21pub const page_size = 4 * 1024;21pub const page_size = 4 * 1024;
2222
23pub const getUserId = @import("get_user_id.zig").getUserId;
24
23const debug = @import("../debug.zig");25const debug = @import("../debug.zig");
24const assert = debug.assert;26const assert = debug.assert;
2527
...@@ -321,12 +323,12 @@ pub fn posixDup2(old_fd: i32, new_fd: i32) -> %void {...@@ -321,12 +323,12 @@ pub fn posixDup2(old_fd: i32, new_fd: i32) -> %void {
321/// This function must allocate memory to add a null terminating bytes on path and each arg.323/// This function must allocate memory to add a null terminating bytes on path and each arg.
322/// It must also convert to KEY=VALUE\0 format for environment variables, and include null324/// It must also convert to KEY=VALUE\0 format for environment variables, and include null
323/// pointers after the args and after the environment variables.325/// pointers after the args and after the environment variables.
324/// Also make the first arg equal to exe_path.326/// `argv[0]` is the executable path.
325/// This function also uses the PATH environment variable to get the full path to the executable.327/// This function also uses the PATH environment variable to get the full path to the executable.
326pub fn posixExecve(exe_path: []const u8, argv: []const []const u8, env_map: &const BufMap,328pub fn posixExecve(argv: []const []const u8, env_map: &const BufMap,
327 allocator: &Allocator) -> %void329 allocator: &Allocator) -> %void
328{330{
329 const argv_buf = %return allocator.alloc(?&u8, argv.len + 2);331 const argv_buf = %return allocator.alloc(?&u8, argv.len + 1);
330 mem.set(?&u8, argv_buf, null);332 mem.set(?&u8, argv_buf, null);
331 defer {333 defer {
332 for (argv_buf) |arg| {334 for (argv_buf) |arg| {
...@@ -335,22 +337,14 @@ pub fn posixExecve(exe_path: []const u8, argv: []const []const u8, env_map: &con...@@ -335,22 +337,14 @@ pub fn posixExecve(exe_path: []const u8, argv: []const []const u8, env_map: &con
335 }337 }
336 allocator.free(argv_buf);338 allocator.free(argv_buf);
337 }339 }
338 {
339 // Add exe_path to the first argument.
340 const arg_buf = %return allocator.alloc(u8, exe_path.len + 1);
341 @memcpy(&arg_buf[0], exe_path.ptr, exe_path.len);
342 arg_buf[exe_path.len] = 0;
343
344 argv_buf[0] = arg_buf.ptr;
345 }
346 for (argv) |arg, i| {340 for (argv) |arg, i| {
347 const arg_buf = %return allocator.alloc(u8, arg.len + 1);341 const arg_buf = %return allocator.alloc(u8, arg.len + 1);
348 @memcpy(&arg_buf[0], arg.ptr, arg.len);342 @memcpy(&arg_buf[0], arg.ptr, arg.len);
349 arg_buf[arg.len] = 0;343 arg_buf[arg.len] = 0;
350344
351 argv_buf[i + 1] = arg_buf.ptr;345 argv_buf[i] = arg_buf.ptr;
352 }346 }
353 argv_buf[argv.len + 1] = null;347 argv_buf[argv.len] = null;
354348
355 const envp_count = env_map.count();349 const envp_count = env_map.count();
356 const envp_buf = %return allocator.alloc(?&u8, envp_count + 1);350 const envp_buf = %return allocator.alloc(?&u8, envp_count + 1);
...@@ -378,14 +372,9 @@ pub fn posixExecve(exe_path: []const u8, argv: []const []const u8, env_map: &con...@@ -378,14 +372,9 @@ pub fn posixExecve(exe_path: []const u8, argv: []const []const u8, env_map: &con
378 }372 }
379 envp_buf[envp_count] = null;373 envp_buf[envp_count] = null;
380374
381375 const exe_path = argv[0];
382 if (mem.indexOfScalar(u8, exe_path, '/') != null) {376 if (mem.indexOfScalar(u8, exe_path, '/') != null) {
383 // +1 for the null terminating byte377 return posixExecveErrnoToErr(posix.getErrno(posix.execve(??argv_buf[0], argv_buf.ptr, envp_buf.ptr)));
384 const path_buf = %return allocator.alloc(u8, exe_path.len + 1);
385 defer allocator.free(path_buf);
386 @memcpy(&path_buf[0], &exe_path[0], exe_path.len);
387 path_buf[exe_path.len] = 0;
388 return posixExecveErrnoToErr(posix.getErrno(posix.execve(path_buf.ptr, argv_buf.ptr, envp_buf.ptr)));
389 }378 }
390379
391 const PATH = getEnv("PATH") ?? "/usr/local/bin:/bin/:/usr/bin";380 const PATH = getEnv("PATH") ?? "/usr/local/bin:/bin/:/usr/bin";
...@@ -434,6 +423,7 @@ fn posixExecveErrnoToErr(err: usize) -> error {...@@ -434,6 +423,7 @@ fn posixExecveErrnoToErr(err: usize) -> error {
434423
435pub var environ_raw: []&u8 = undefined;424pub var environ_raw: []&u8 = undefined;
436425
426/// Caller must free result when done.
437pub fn getEnvMap(allocator: &Allocator) -> %BufMap {427pub fn getEnvMap(allocator: &Allocator) -> %BufMap {
438 var result = BufMap.init(allocator);428 var result = BufMap.init(allocator);
439 %defer result.deinit();429 %defer result.deinit();
...@@ -840,7 +830,7 @@ pub const Dir = struct {...@@ -840,7 +830,7 @@ pub const Dir = struct {
840 start_over:830 start_over:
841 if (self.index >= self.end_index) {831 if (self.index >= self.end_index) {
842 if (self.buf.len == 0) {832 if (self.buf.len == 0) {
843 self.buf = %return self.allocator.alloc(u8, 2); //page_size);833 self.buf = %return self.allocator.alloc(u8, page_size);
844 }834 }
845835
846 while (true) {836 while (true) {
...@@ -992,3 +982,20 @@ pub fn posixSleep(seconds: u63, nanoseconds: u63) {...@@ -992,3 +982,20 @@ pub fn posixSleep(seconds: u63, nanoseconds: u63) {
992test "os.sleep" {982test "os.sleep" {
993 sleep(0, 1);983 sleep(0, 1);
994}984}
985
986
987error ResourceLimitReached;
988error InvalidUserId;
989error PermissionDenied;
990error Unexpected;
991
992pub fn posix_setuid(uid: u32) -> %void {
993 const err = posix.getErrno(posix.setuid(uid));
994 if (err == 0) return;
995 return switch (err) {
996 posix.EAGAIN => error.ResourceLimitReached,
997 posix.EINVAL => error.InvalidUserId,
998 posix.EPERM => error.PermissionDenied,
999 else => error.Unexpected,
1000 };
1001}
std/os/linux.zig+4
...@@ -480,6 +480,10 @@ pub fn nanosleep(req: &const timespec, rem: ?&timespec) -> usize {...@@ -480,6 +480,10 @@ pub fn nanosleep(req: &const timespec, rem: ?&timespec) -> usize {
480 arch.syscall2(arch.SYS_nanosleep, @ptrToInt(req), @ptrToInt(rem))480 arch.syscall2(arch.SYS_nanosleep, @ptrToInt(req), @ptrToInt(rem))
481}481}
482482
483pub fn setuid(uid: u32) -> usize {
484 arch.syscall1(arch.SYS_setuid, uid)
485}
486
483pub fn sigprocmask(flags: u32, noalias set: &const sigset_t, noalias oldset: ?&sigset_t) -> usize {487pub fn sigprocmask(flags: u32, noalias set: &const sigset_t, noalias oldset: ?&sigset_t) -> usize {
484 arch.syscall4(arch.SYS_rt_sigprocmask, flags, @ptrToInt(set), @ptrToInt(oldset), NSIG/8)488 arch.syscall4(arch.SYS_rt_sigprocmask, flags, @ptrToInt(set), @ptrToInt(oldset), NSIG/8)
485}489}
test/standalone/pkg_import/build.zig+1-1
...@@ -9,7 +9,7 @@ pub fn build(b: &Builder) {...@@ -9,7 +9,7 @@ pub fn build(b: &Builder) {
9 exe.setBuildMode(b.standardReleaseOptions());9 exe.setBuildMode(b.standardReleaseOptions());
10 exe.setBuildMode(b.standardReleaseOptions());10 exe.setBuildMode(b.standardReleaseOptions());
1111
12 const run = b.addCommand(".", b.env_map, exe.getOutputPath(), [][]const u8{});12 const run = b.addCommand(".", b.env_map, [][]const u8{exe.getOutputPath()});
13 run.step.dependOn(&exe.step);13 run.step.dependOn(&exe.step);
1414
15 const test_step = b.step("test", "Test it");15 const test_step = b.step("test", "Test it");
test/tests.zig+43-24
...@@ -241,11 +241,15 @@ pub const CompareOutputContext = struct {...@@ -241,11 +241,15 @@ pub const CompareOutputContext = struct {
241241
242 %%io.stderr.printf("Test {}/{} {}...", self.test_index+1, self.context.test_index, self.name);242 %%io.stderr.printf("Test {}/{} {}...", self.test_index+1, self.context.test_index, self.name);
243243
244 var child = os.ChildProcess.spawn(full_exe_path, [][]u8{}, null, &b.env_map,244 const child = %%os.ChildProcess.init([][]u8{full_exe_path}, b.allocator);
245 StdIo.Ignore, StdIo.Pipe, StdIo.Pipe, null, b.allocator) %% |err|245 defer child.deinit();
246 {246
247 debug.panic("Unable to spawn {}: {}\n", full_exe_path, @errorName(err));247 child.stdin_behavior = StdIo.Ignore;
248 };248 child.stdout_behavior = StdIo.Pipe;
249 child.stderr_behavior = StdIo.Pipe;
250 child.env_map = &b.env_map;
251
252 child.spawn() %% |err| debug.panic("Unable to spawn {}: {}\n", full_exe_path, @errorName(err));
249253
250 var stdout = Buffer.initNull(b.allocator);254 var stdout = Buffer.initNull(b.allocator);
251 var stderr = Buffer.initNull(b.allocator);255 var stderr = Buffer.initNull(b.allocator);
...@@ -316,13 +320,15 @@ pub const CompareOutputContext = struct {...@@ -316,13 +320,15 @@ pub const CompareOutputContext = struct {
316320
317 %%io.stderr.printf("Test {}/{} {}...", self.test_index+1, self.context.test_index, self.name);321 %%io.stderr.printf("Test {}/{} {}...", self.test_index+1, self.context.test_index, self.name);
318322
319 var child = os.ChildProcess.spawn(full_exe_path, [][]u8{}, null, &b.env_map,323 const child = %%os.ChildProcess.init([][]u8{full_exe_path}, b.allocator);
320 StdIo.Ignore, StdIo.Pipe, StdIo.Pipe, null, b.allocator) %% |err|324 defer child.deinit();
321 {
322 debug.panic("Unable to spawn {}: {}\n", full_exe_path, @errorName(err));
323 };
324325
325 const term = child.wait() %% |err| {326 child.env_map = &b.env_map;
327 child.stdin_behavior = StdIo.Ignore;
328 child.stdout_behavior = StdIo.Ignore;
329 child.stderr_behavior = StdIo.Ignore;
330
331 const term = child.spawnAndWait() %% |err| {
326 debug.panic("Unable to spawn {}: {}\n", full_exe_path, @errorName(err));332 debug.panic("Unable to spawn {}: {}\n", full_exe_path, @errorName(err));
327 };333 };
328334
...@@ -539,6 +545,8 @@ pub const CompileErrorContext = struct {...@@ -539,6 +545,8 @@ pub const CompileErrorContext = struct {
539 const obj_path = %%os.path.join(b.allocator, b.cache_root, "test.o");545 const obj_path = %%os.path.join(b.allocator, b.cache_root, "test.o");
540546
541 var zig_args = ArrayList([]const u8).init(b.allocator);547 var zig_args = ArrayList([]const u8).init(b.allocator);
548 %%zig_args.append(b.zig_exe);
549
542 %%zig_args.append(if (self.case.is_exe) "build-exe" else "build-obj");550 %%zig_args.append(if (self.case.is_exe) "build-exe" else "build-obj");
543 %%zig_args.append(b.pathFromRoot(root_src));551 %%zig_args.append(b.pathFromRoot(root_src));
544552
...@@ -560,11 +568,15 @@ pub const CompileErrorContext = struct {...@@ -560,11 +568,15 @@ pub const CompileErrorContext = struct {
560 printInvocation(b.zig_exe, zig_args.toSliceConst());568 printInvocation(b.zig_exe, zig_args.toSliceConst());
561 }569 }
562570
563 var child = os.ChildProcess.spawn(b.zig_exe, zig_args.toSliceConst(), null, &b.env_map,571 const child = %%os.ChildProcess.init(zig_args.toSliceConst(), b.allocator);
564 StdIo.Ignore, StdIo.Pipe, StdIo.Pipe, null, b.allocator) %% |err|572 defer child.deinit();
565 {573
566 debug.panic("Unable to spawn {}: {}\n", b.zig_exe, @errorName(err));574 child.env_map = &b.env_map;
567 };575 child.stdin_behavior = StdIo.Ignore;
576 child.stdout_behavior = StdIo.Pipe;
577 child.stderr_behavior = StdIo.Pipe;
578
579 child.spawn() %% |err| debug.panic("Unable to spawn {}: {}\n", zig_args.items[0], @errorName(err));
568580
569 var stdout_buf = Buffer.initNull(b.allocator);581 var stdout_buf = Buffer.initNull(b.allocator);
570 var stderr_buf = Buffer.initNull(b.allocator);582 var stderr_buf = Buffer.initNull(b.allocator);
...@@ -573,7 +585,7 @@ pub const CompileErrorContext = struct {...@@ -573,7 +585,7 @@ pub const CompileErrorContext = struct {
573 %%(??child.stderr).readAll(&stderr_buf);585 %%(??child.stderr).readAll(&stderr_buf);
574586
575 const term = child.wait() %% |err| {587 const term = child.wait() %% |err| {
576 debug.panic("Unable to spawn {}: {}\n", b.zig_exe, @errorName(err));588 debug.panic("Unable to spawn {}: {}\n", zig_args.items[0], @errorName(err));
577 };589 };
578 switch (term) {590 switch (term) {
579 Term.Exited => |code| {591 Term.Exited => |code| {
...@@ -712,6 +724,7 @@ pub const BuildExamplesContext = struct {...@@ -712,6 +724,7 @@ pub const BuildExamplesContext = struct {
712 }724 }
713725
714 var zig_args = ArrayList([]const u8).init(b.allocator);726 var zig_args = ArrayList([]const u8).init(b.allocator);
727 %%zig_args.append(b.zig_exe);
715 %%zig_args.append("build");728 %%zig_args.append("build");
716729
717 %%zig_args.append("--build-file");730 %%zig_args.append("--build-file");
...@@ -723,7 +736,7 @@ pub const BuildExamplesContext = struct {...@@ -723,7 +736,7 @@ pub const BuildExamplesContext = struct {
723 %%zig_args.append("--verbose");736 %%zig_args.append("--verbose");
724 }737 }
725738
726 const run_cmd = b.addCommand(null, b.env_map, b.zig_exe, zig_args.toSliceConst());739 const run_cmd = b.addCommand(null, b.env_map, zig_args.toSliceConst());
727740
728 const log_step = b.addLog("PASS {}\n", annotated_case_name);741 const log_step = b.addLog("PASS {}\n", annotated_case_name);
729 log_step.step.dependOn(&run_cmd.step);742 log_step.step.dependOn(&run_cmd.step);
...@@ -813,6 +826,8 @@ pub const ParseCContext = struct {...@@ -813,6 +826,8 @@ pub const ParseCContext = struct {
813 const root_src = %%os.path.join(b.allocator, b.cache_root, self.case.sources.items[0].filename);826 const root_src = %%os.path.join(b.allocator, b.cache_root, self.case.sources.items[0].filename);
814827
815 var zig_args = ArrayList([]const u8).init(b.allocator);828 var zig_args = ArrayList([]const u8).init(b.allocator);
829 %%zig_args.append(b.zig_exe);
830
816 %%zig_args.append("parsec");831 %%zig_args.append("parsec");
817 %%zig_args.append(b.pathFromRoot(root_src));832 %%zig_args.append(b.pathFromRoot(root_src));
818833
...@@ -822,11 +837,15 @@ pub const ParseCContext = struct {...@@ -822,11 +837,15 @@ pub const ParseCContext = struct {
822 printInvocation(b.zig_exe, zig_args.toSliceConst());837 printInvocation(b.zig_exe, zig_args.toSliceConst());
823 }838 }
824839
825 var child = os.ChildProcess.spawn(b.zig_exe, zig_args.toSliceConst(), null, &b.env_map,840 const child = %%os.ChildProcess.init(zig_args.toSliceConst(), b.allocator);
826 StdIo.Ignore, StdIo.Pipe, StdIo.Pipe, null, b.allocator) %% |err|841 defer child.deinit();
827 {842
828 debug.panic("Unable to spawn {}: {}\n", b.zig_exe, @errorName(err));843 child.env_map = &b.env_map;
829 };844 child.stdin_behavior = StdIo.Ignore;
845 child.stdout_behavior = StdIo.Pipe;
846 child.stderr_behavior = StdIo.Pipe;
847
848 child.spawn() %% |err| debug.panic("Unable to spawn {}: {}\n", zig_args.toSliceConst()[0], @errorName(err));
830849
831 var stdout_buf = Buffer.initNull(b.allocator);850 var stdout_buf = Buffer.initNull(b.allocator);
832 var stderr_buf = Buffer.initNull(b.allocator);851 var stderr_buf = Buffer.initNull(b.allocator);
...@@ -835,7 +854,7 @@ pub const ParseCContext = struct {...@@ -835,7 +854,7 @@ pub const ParseCContext = struct {
835 %%(??child.stderr).readAll(&stderr_buf);854 %%(??child.stderr).readAll(&stderr_buf);
836855
837 const term = child.wait() %% |err| {856 const term = child.wait() %% |err| {
838 debug.panic("Unable to spawn {}: {}\n", b.zig_exe, @errorName(err));857 debug.panic("Unable to spawn {}: {}\n", zig_args.toSliceConst()[0], @errorName(err));
839 };858 };
840 switch (term) {859 switch (term) {
841 Term.Exited => |code| {860 Term.Exited => |code| {