authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-04-04 00:17:24-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-04-04 00:17:24-04:00
log72fb2443e0e5dd0c8c3a0265428832796b4ff548
treedd4cfda1e44784a80538ccd3c6d776c3fb664651
parentb46344fd01db28384b51d9898ef25c4825bb19b0

API for command line args

closes #300

11 files changed, 381 insertions(+), 374 deletions(-)

CMakeLists.txt+1
......@@ -223,6 +223,7 @@ install(FILES "${CMAKE_SOURCE_DIR}/std/list.zig" DESTINATION "${ZIG_STD_DEST}")
223223install(FILES "${CMAKE_SOURCE_DIR}/std/math.zig" DESTINATION "${ZIG_STD_DEST}")
224224install(FILES "${CMAKE_SOURCE_DIR}/std/mem.zig" DESTINATION "${ZIG_STD_DEST}")
225225install(FILES "${CMAKE_SOURCE_DIR}/std/net.zig" DESTINATION "${ZIG_STD_DEST}")
226install(FILES "${CMAKE_SOURCE_DIR}/std/os/child_process.zig" DESTINATION "${ZIG_STD_DEST}/os")
226227install(FILES "${CMAKE_SOURCE_DIR}/std/os/darwin.zig" DESTINATION "${ZIG_STD_DEST}/os")
227228install(FILES "${CMAKE_SOURCE_DIR}/std/os/darwin_x86_64.zig" DESTINATION "${ZIG_STD_DEST}/os")
228229install(FILES "${CMAKE_SOURCE_DIR}/std/os/errno.zig" DESTINATION "${ZIG_STD_DEST}/os")
example/cat/main.zig+9-7
......@@ -1,23 +1,25 @@
11const std = @import("std");
22const io = std.io;
33const mem = std.mem;
4const os = std.os;
45
5pub fn main(args: [][]u8) -> %void {
6 const exe = args[0];
6pub fn main() -> %void {
7 const exe = os.args.at(0);
78 var catted_anything = false;
8 for (args[1...]) |arg| {
9 var arg_i: usize = 1;
10 while (arg_i < os.args.count(); arg_i += 1) {
11 const arg = os.args.at(arg_i);
912 if (mem.eql(u8, arg, "-")) {
1013 catted_anything = true;
1114 %return cat_stream(&io.stdin);
1215 } else if (arg[0] == '-') {
1316 return usage(exe);
1417 } else {
15 var is: io.InStream = undefined;
16 is.open(arg) %% |err| {
18 var is = io.InStream.open(arg, null) %% |err| {
1719 %%io.stderr.printf("Unable to open file: {}\n", @errorName(err));
1820 return err;
1921 };
20 defer %%is.close();
22 defer is.close();
2123
2224 catted_anything = true;
2325 %return cat_stream(&is);
......@@ -29,7 +31,7 @@ pub fn main(args: [][]u8) -> %void {
2931 %return io.stdout.flush();
3032}
3133
32fn usage(exe: []u8) -> %void {
34fn usage(exe: []const u8) -> %void {
3335 %%io.stderr.printf("Usage: {} [FILE]...\n", exe);
3436 return error.Invalid;
3537}
example/guess_number/main.zig+1-1
......@@ -4,7 +4,7 @@ const fmt = std.fmt;
44const Rand = std.rand.Rand;
55const os = std.os;
66
7pub fn main(args: [][]u8) -> %void {
7pub fn main() -> %void {
88 %%io.stdout.printf("Welcome to the Guess Number Game in Zig.\n");
99
1010 var seed_bytes: [@sizeOf(usize)]u8 = undefined;
example/hello_world/hello.zig+1-1
......@@ -1,5 +1,5 @@
11const io = @import("std").io;
22
3pub fn main(args: [][]u8) -> %void {
3pub fn main() -> %void {
44 %%io.stdout.printf("Hello, world!\n");
55}
std/build.zig+6-3
......@@ -39,11 +39,13 @@ pub const Builder = struct {
3939 return exe;
4040 }
4141
42 pub fn make(self: &Builder, cli_args: []const []const u8) -> %void {
42 pub fn make(self: &Builder, leftover_arg_index: usize) -> %void {
4343 var env_map = %return os.getEnvMap(self.allocator);
4444
4545 var verbose = false;
46 for (cli_args) |arg| {
46 var arg_i: usize = leftover_arg_index;
47 while (arg_i < os.args.count(); arg_i += 1) {
48 const arg = os.args.at(arg_i);
4749 if (mem.eql(u8, arg, "--verbose")) {
4850 verbose = true;
4951 } else {
......@@ -95,7 +97,8 @@ pub const Builder = struct {
9597 }
9698
9799 printInvocation(self.zig_exe, zig_args);
98 var child = %return os.ChildProcess.spawn(self.zig_exe, zig_args.toSliceConst(), env_map,
100 // TODO issue #301
101 var child = %return os.ChildProcess.spawn(self.zig_exe, zig_args.toSliceConst(), &env_map,
99102 StdIo.Ignore, StdIo.Inherit, StdIo.Inherit, self.allocator);
100103 const term = %return child.wait();
101104 switch (term) {
std/os/child_process.zig created+280
......@@ -0,0 +1,280 @@
1const io = @import("../io.zig");
2const os = @import("index.zig");
3const posix = os.posix;
4const mem = @import("../mem.zig");
5const Allocator = mem.Allocator;
6const errno = @import("errno.zig");
7const debug = @import("../debug.zig");
8const assert = debug.assert;
9
10pub const ChildProcess = struct {
11 pid: i32,
12 err_pipe: [2]i32,
13
14 stdin: ?io.OutStream,
15 stdout: ?io.InStream,
16 stderr: ?io.InStream,
17
18 pub const Term = enum {
19 Clean: i32,
20 Signal: i32,
21 Stopped: i32,
22 Unknown: i32,
23 };
24
25 pub const StdIo = enum {
26 Inherit,
27 Ignore,
28 Pipe,
29 Close,
30 };
31
32 pub fn spawn(exe_path: []const u8, args: []const []const u8, env_map: &const os.EnvMap,
33 stdin: StdIo, stdout: StdIo, stderr: StdIo, allocator: &Allocator) -> %ChildProcess
34 {
35 switch (@compileVar("os")) {
36 Os.linux, Os.macosx, Os.ios, Os.darwin => {
37 return spawnPosix(exe_path, args, env_map, stdin, stdout, stderr, allocator);
38 },
39 else => @compileError("Unsupported OS"),
40 }
41 }
42
43 pub fn wait(self: &ChildProcess) -> %Term {
44 defer {
45 os.posixClose(self.err_pipe[0]);
46 os.posixClose(self.err_pipe[1]);
47 };
48
49 var status: i32 = undefined;
50 while (true) {
51 const err = posix.getErrno(posix.waitpid(self.pid, &status, 0));
52 if (err > 0) {
53 switch (err) {
54 errno.EINVAL, errno.ECHILD => unreachable,
55 errno.EINTR => continue,
56 else => {
57 if (const *stdin ?= self.stdin) { stdin.close(); }
58 if (const *stdout ?= self.stdin) { stdout.close(); }
59 if (const *stderr ?= self.stdin) { stderr.close(); }
60 return error.Unexpected;
61 },
62 }
63 }
64 break;
65 }
66
67 if (const *stdin ?= self.stdin) { stdin.close(); }
68 if (const *stdout ?= self.stdin) { stdout.close(); }
69 if (const *stderr ?= self.stdin) { stderr.close(); }
70
71 // Write @maxValue(ErrInt) to the write end of the err_pipe. This is after
72 // waitpid, so this write is guaranteed to be after the child
73 // pid potentially wrote an error. This way we can do a blocking
74 // read on the error pipe and either get @maxValue(ErrInt) (no error) or
75 // an error code.
76 %return writeIntFd(self.err_pipe[1], @maxValue(ErrInt));
77 const err_int = %return readIntFd(self.err_pipe[0]);
78 // Here we potentially return the fork child's error
79 // from the parent pid.
80 if (err_int != @maxValue(ErrInt)) {
81 return error(err_int);
82 }
83
84 return statusToTerm(status);
85 }
86
87 fn statusToTerm(status: i32) -> Term {
88 return if (posix.WIFEXITED(status)) {
89 Term.Clean { posix.WEXITSTATUS(status) }
90 } else if (posix.WIFSIGNALED(status)) {
91 Term.Signal { posix.WTERMSIG(status) }
92 } else if (posix.WIFSTOPPED(status)) {
93 Term.Stopped { posix.WSTOPSIG(status) }
94 } else {
95 Term.Unknown { status }
96 };
97 }
98
99 fn spawnPosix(exe_path: []const u8, args: []const []const u8, env_map: &const os.EnvMap,
100 stdin: StdIo, stdout: StdIo, stderr: StdIo, allocator: &Allocator) -> %ChildProcess
101 {
102 // TODO issue #295
103 //const stdin_pipe = if (stdin == StdIo.Pipe) %return makePipe() else undefined;
104 var stdin_pipe: [2]i32 = undefined;
105 if (stdin == StdIo.Pipe)
106 stdin_pipe = %return makePipe();
107 %defer if (stdin == StdIo.Pipe) { destroyPipe(stdin_pipe); };
108
109 // TODO issue #295
110 //const stdout_pipe = if (stdout == StdIo.Pipe) %return makePipe() else undefined;
111 var stdout_pipe: [2]i32 = undefined;
112 if (stdout == StdIo.Pipe)
113 stdout_pipe = %return makePipe();
114 %defer if (stdout == StdIo.Pipe) { destroyPipe(stdout_pipe); };
115
116 // TODO issue #295
117 //const stderr_pipe = if (stderr == StdIo.Pipe) %return makePipe() else undefined;
118 var stderr_pipe: [2]i32 = undefined;
119 if (stderr == StdIo.Pipe)
120 stderr_pipe = %return makePipe();
121 %defer if (stderr == StdIo.Pipe) { destroyPipe(stderr_pipe); };
122
123 const any_ignore = (stdin == StdIo.Ignore or stdout == StdIo.Ignore or stderr == StdIo.Ignore);
124 // TODO issue #295
125 //const dev_null_fd = if (any_ignore) {
126 // %return os.posixOpen("/dev/null", posix.O_RDWR, 0, null)
127 //} else {
128 // undefined
129 //};
130 var dev_null_fd: i32 = undefined;
131 if (any_ignore)
132 dev_null_fd = %return os.posixOpen("/dev/null", posix.O_RDWR, 0, null);
133
134 // This pipe is used to communicate errors between the time of fork
135 // and execve from the child process to the parent process.
136 const err_pipe = %return makePipe();
137 %defer destroyPipe(err_pipe);
138
139 const pid = posix.fork();
140 const pid_err = posix.getErrno(pid);
141 if (pid_err > 0) {
142 return switch (pid_err) {
143 errno.EAGAIN, errno.ENOMEM, errno.ENOSYS => error.SysResources,
144 else => error.Unexpected,
145 };
146 }
147 if (pid == 0) {
148 // we are the child
149 setUpChildIo(stdin, stdin_pipe[0], posix.STDIN_FILENO, dev_null_fd) %%
150 |err| forkChildErrReport(err_pipe[1], err);
151 setUpChildIo(stdout, stdout_pipe[1], posix.STDOUT_FILENO, dev_null_fd) %%
152 |err| forkChildErrReport(err_pipe[1], err);
153 setUpChildIo(stderr, stderr_pipe[1], posix.STDERR_FILENO, dev_null_fd) %%
154 |err| forkChildErrReport(err_pipe[1], err);
155
156 const err = posix.getErrno(%return os.posixExecve(exe_path, args, env_map, allocator));
157 assert(err > 0);
158 forkChildErrReport(err_pipe[1], switch (err) {
159 errno.EFAULT => unreachable,
160 errno.E2BIG, errno.EMFILE, errno.ENAMETOOLONG, errno.ENFILE, errno.ENOMEM => error.SysResources,
161 errno.EACCES, errno.EPERM => error.AccessDenied,
162 errno.EINVAL, errno.ENOEXEC => error.InvalidExe,
163 errno.EIO, errno.ELOOP => error.FileSystem,
164 errno.EISDIR => error.IsDir,
165 errno.ENOENT, errno.ENOTDIR => error.FileNotFound,
166 errno.ETXTBSY => error.FileBusy,
167 else => error.Unexpected,
168 });
169 }
170
171 // we are the parent
172 if (stdin == StdIo.Pipe) { os.posixClose(stdin_pipe[0]); }
173 if (stdout == StdIo.Pipe) { os.posixClose(stdout_pipe[1]); }
174 if (stderr == StdIo.Pipe) { os.posixClose(stderr_pipe[1]); }
175 if (any_ignore) { os.posixClose(dev_null_fd); }
176
177 return ChildProcess {
178 .pid = i32(pid),
179 .err_pipe = err_pipe,
180
181 .stdin = if (stdin == StdIo.Pipe) {
182 io.OutStream {
183 .fd = stdin_pipe[1],
184 .buffer = undefined,
185 .index = 0,
186 }
187 } else {
188 null
189 },
190 .stdout = if (stdout == StdIo.Pipe) {
191 io.InStream {
192 .fd = stdout_pipe[0],
193 }
194 } else {
195 null
196 },
197 .stderr = if (stderr == StdIo.Pipe) {
198 io.InStream {
199 .fd = stderr_pipe[0],
200 }
201 } else {
202 null
203 },
204 };
205 }
206
207 fn setUpChildIo(stdio: StdIo, pipe_fd: i32, std_fileno: i32, dev_null_fd: i32) -> %void {
208 switch (stdio) {
209 StdIo.Pipe => %return os.posixDup2(pipe_fd, std_fileno),
210 StdIo.Close => os.posixClose(std_fileno),
211 StdIo.Inherit => {},
212 StdIo.Ignore => %return os.posixDup2(dev_null_fd, std_fileno),
213 }
214 }
215};
216
217fn makePipe() -> %[2]i32 {
218 var fds: [2]i32 = undefined;
219 const err = posix.getErrno(posix.pipe(&fds));
220 if (err > 0) {
221 return switch (err) {
222 errno.EMFILE, errno.ENFILE => error.SysResources,
223 else => error.Unexpected,
224 }
225 }
226 return fds;
227}
228
229fn destroyPipe(pipe: &const [2]i32) {
230 os.posixClose((*pipe)[0]);
231 os.posixClose((*pipe)[1]);
232}
233
234// Child of fork calls this to report an error to the fork parent.
235// Then the child exits.
236fn forkChildErrReport(fd: i32, err: error) -> noreturn {
237 _ = writeIntFd(fd, ErrInt(err));
238 posix.exit(1);
239}
240
241const ErrInt = @intType(false, @sizeOf(error) * 8);
242fn writeIntFd(fd: i32, value: ErrInt) -> %void {
243 var bytes: [@sizeOf(ErrInt)]u8 = undefined;
244 mem.writeInt(bytes[0...], value, true);
245
246 var index: usize = 0;
247 while (index < bytes.len) {
248 const amt_written = posix.write(fd, &bytes[index], bytes.len - index);
249 const err = posix.getErrno(amt_written);
250 if (err > 0) {
251 switch (err) {
252 errno.EINTR => continue,
253 errno.EINVAL => unreachable,
254 else => return error.SysResources,
255 }
256 }
257 index += amt_written;
258 }
259}
260
261fn readIntFd(fd: i32) -> %ErrInt {
262 var bytes: [@sizeOf(ErrInt)]u8 = undefined;
263
264 var index: usize = 0;
265 while (index < bytes.len) {
266 const amt_written = posix.read(fd, &bytes[index], bytes.len - index);
267 const err = posix.getErrno(amt_written);
268 if (err > 0) {
269 switch (err) {
270 errno.EINTR => continue,
271 errno.EINVAL => unreachable,
272 else => return error.SysResources,
273 }
274 }
275 index += amt_written;
276 }
277
278 return mem.readInt(bytes[0...], ErrInt, true);
279}
280
std/os/index.zig+28-285
......@@ -9,6 +9,7 @@ pub const posix = switch(@compileVar("os")) {
99};
1010
1111pub const max_noalloc_path_len = 1024;
12pub const ChildProcess = @import("child_process.zig").ChildProcess;
1213
1314const debug = @import("../debug.zig");
1415const assert = debug.assert;
......@@ -20,7 +21,6 @@ const c = @import("../c/index.zig");
2021const mem = @import("../mem.zig");
2122const Allocator = mem.Allocator;
2223
23const io = @import("../io.zig");
2424const HashMap = @import("../hash_map.zig").HashMap;
2525const cstr = @import("../cstr.zig");
2626
......@@ -96,23 +96,6 @@ pub coldcc fn abort() -> noreturn {
9696 }
9797}
9898
99fn makePipe() -> %[2]i32 {
100 var fds: [2]i32 = undefined;
101 const err = posix.getErrno(posix.pipe(&fds));
102 if (err > 0) {
103 return switch (err) {
104 errno.EMFILE, errno.ENFILE => error.SysResources,
105 else => error.Unexpected,
106 }
107 }
108 return fds;
109}
110
111fn destroyPipe(pipe: &const [2]i32) {
112 posixClose((*pipe)[0]);
113 posixClose((*pipe)[1]);
114}
115
11699/// Calls POSIX close, and keeps trying if it gets interrupted.
117100pub fn posixClose(fd: i32) {
118101 while (true) {
......@@ -202,54 +185,7 @@ pub fn posixOpen(path: []const u8, flags: usize, perm: usize, allocator: ?&Alloc
202185 }
203186}
204187
205const ErrInt = @intType(false, @sizeOf(error) * 8);
206fn writeIntFd(fd: i32, value: ErrInt) -> %void {
207 var bytes: [@sizeOf(ErrInt)]u8 = undefined;
208 mem.writeInt(bytes[0...], value, true);
209
210 var index: usize = 0;
211 while (index < bytes.len) {
212 const amt_written = posix.write(fd, &bytes[index], bytes.len - index);
213 const err = posix.getErrno(amt_written);
214 if (err > 0) {
215 switch (err) {
216 errno.EINTR => continue,
217 errno.EINVAL => unreachable,
218 else => return error.SysResources,
219 }
220 }
221 index += amt_written;
222 }
223}
224
225fn readIntFd(fd: i32) -> %ErrInt {
226 var bytes: [@sizeOf(ErrInt)]u8 = undefined;
227
228 var index: usize = 0;
229 while (index < bytes.len) {
230 const amt_written = posix.read(fd, &bytes[index], bytes.len - index);
231 const err = posix.getErrno(amt_written);
232 if (err > 0) {
233 switch (err) {
234 errno.EINTR => continue,
235 errno.EINVAL => unreachable,
236 else => return error.SysResources,
237 }
238 }
239 index += amt_written;
240 }
241
242 return mem.readInt(bytes[0...], ErrInt, true);
243}
244
245// Child of fork calls this to report an error to the fork parent.
246// Then the child exits.
247fn forkChildErrReport(fd: i32, err: error) -> noreturn {
248 _ = writeIntFd(fd, ErrInt(err));
249 posix.exit(1);
250}
251
252fn dup2NoIntr(old_fd: i32, new_fd: i32) -> %void {
188pub fn posixDup2(old_fd: i32, new_fd: i32) -> %void {
253189 while (true) {
254190 const err = posix.getErrno(posix.dup2(old_fd, new_fd));
255191 if (err > 0) {
......@@ -264,218 +200,13 @@ fn dup2NoIntr(old_fd: i32, new_fd: i32) -> %void {
264200 }
265201}
266202
267pub const ChildProcess = struct {
268 pid: i32,
269 err_pipe: [2]i32,
270
271 stdin: ?io.OutStream,
272 stdout: ?io.InStream,
273 stderr: ?io.InStream,
274
275 pub const Term = enum {
276 Clean: i32,
277 Signal: i32,
278 Stopped: i32,
279 Unknown: i32,
280 };
281
282 pub const StdIo = enum {
283 Inherit,
284 Ignore,
285 Pipe,
286 Close,
287 };
288
289 pub fn spawn(exe_path: []const u8, args: []const []const u8, env_map: &const EnvMap,
290 stdin: StdIo, stdout: StdIo, stderr: StdIo, allocator: &Allocator) -> %ChildProcess
291 {
292 switch (@compileVar("os")) {
293 Os.linux, Os.macosx, Os.ios, Os.darwin => {
294 return spawnPosix(exe_path, args, env_map, stdin, stdout, stderr, allocator);
295 },
296 else => @compileError("Unsupported OS"),
297 }
298 }
299
300 pub fn wait(self: &ChildProcess) -> %Term {
301 defer {
302 posixClose(self.err_pipe[0]);
303 posixClose(self.err_pipe[1]);
304 };
305
306 var status: i32 = undefined;
307 while (true) {
308 const err = posix.getErrno(posix.waitpid(self.pid, &status, 0));
309 if (err > 0) {
310 switch (err) {
311 errno.EINVAL, errno.ECHILD => unreachable,
312 errno.EINTR => continue,
313 else => {
314 if (const *stdin ?= self.stdin) { stdin.close(); }
315 if (const *stdout ?= self.stdin) { stdout.close(); }
316 if (const *stderr ?= self.stdin) { stderr.close(); }
317 return error.Unexpected;
318 },
319 }
320 }
321 break;
322 }
323
324 if (const *stdin ?= self.stdin) { stdin.close(); }
325 if (const *stdout ?= self.stdin) { stdout.close(); }
326 if (const *stderr ?= self.stdin) { stderr.close(); }
327
328 // Write @maxValue(ErrInt) to the write end of the err_pipe. This is after
329 // waitpid, so this write is guaranteed to be after the child
330 // pid potentially wrote an error. This way we can do a blocking
331 // read on the error pipe and either get @maxValue(ErrInt) (no error) or
332 // an error code.
333 %return writeIntFd(self.err_pipe[1], @maxValue(ErrInt));
334 const err_int = %return readIntFd(self.err_pipe[0]);
335 // Here we potentially return the fork child's error
336 // from the parent pid.
337 if (err_int != @maxValue(ErrInt)) {
338 return error(err_int);
339 }
340
341 return statusToTerm(status);
342 }
343
344 fn statusToTerm(status: i32) -> Term {
345 return if (posix.WIFEXITED(status)) {
346 Term.Clean { posix.WEXITSTATUS(status) }
347 } else if (posix.WIFSIGNALED(status)) {
348 Term.Signal { posix.WTERMSIG(status) }
349 } else if (posix.WIFSTOPPED(status)) {
350 Term.Stopped { posix.WSTOPSIG(status) }
351 } else {
352 Term.Unknown { status }
353 };
354 }
355
356 fn spawnPosix(exe_path: []const u8, args: []const []const u8, env_map: &const EnvMap,
357 stdin: StdIo, stdout: StdIo, stderr: StdIo, allocator: &Allocator) -> %ChildProcess
358 {
359 // TODO issue #295
360 //const stdin_pipe = if (stdin == StdIo.Pipe) %return makePipe() else undefined;
361 var stdin_pipe: [2]i32 = undefined;
362 if (stdin == StdIo.Pipe)
363 stdin_pipe = %return makePipe();
364 %defer if (stdin == StdIo.Pipe) { destroyPipe(stdin_pipe); };
365
366 // TODO issue #295
367 //const stdout_pipe = if (stdout == StdIo.Pipe) %return makePipe() else undefined;
368 var stdout_pipe: [2]i32 = undefined;
369 if (stdout == StdIo.Pipe)
370 stdout_pipe = %return makePipe();
371 %defer if (stdout == StdIo.Pipe) { destroyPipe(stdout_pipe); };
372
373 // TODO issue #295
374 //const stderr_pipe = if (stderr == StdIo.Pipe) %return makePipe() else undefined;
375 var stderr_pipe: [2]i32 = undefined;
376 if (stderr == StdIo.Pipe)
377 stderr_pipe = %return makePipe();
378 %defer if (stderr == StdIo.Pipe) { destroyPipe(stderr_pipe); };
379
380 const any_ignore = (stdin == StdIo.Ignore or stdout == StdIo.Ignore or stderr == StdIo.Ignore);
381 // TODO issue #295
382 //const dev_null_fd = if (any_ignore) {
383 // %return posixOpen("/dev/null", posix.O_RDWR, 0, null)
384 //} else {
385 // undefined
386 //};
387 var dev_null_fd: i32 = undefined;
388 if (any_ignore)
389 dev_null_fd = %return posixOpen("/dev/null", posix.O_RDWR, 0, null);
390
391 // This pipe is used to communicate errors between the time of fork
392 // and execve from the child process to the parent process.
393 const err_pipe = %return makePipe();
394 %defer destroyPipe(err_pipe);
395
396 const pid = posix.fork();
397 const pid_err = linux.getErrno(pid);
398 if (pid_err > 0) {
399 return switch (pid_err) {
400 errno.EAGAIN, errno.ENOMEM, errno.ENOSYS => error.SysResources,
401 else => error.Unexpected,
402 };
403 }
404 if (pid == 0) {
405 // we are the child
406 setUpChildIo(stdin, stdin_pipe[0], posix.STDIN_FILENO, dev_null_fd) %%
407 |err| forkChildErrReport(err_pipe[1], err);
408 setUpChildIo(stdout, stdout_pipe[1], posix.STDOUT_FILENO, dev_null_fd) %%
409 |err| forkChildErrReport(err_pipe[1], err);
410 setUpChildIo(stderr, stderr_pipe[1], posix.STDERR_FILENO, dev_null_fd) %%
411 |err| forkChildErrReport(err_pipe[1], err);
412
413 const err = posix.getErrno(%return execve(exe_path, args, env_map, allocator));
414 assert(err > 0);
415 forkChildErrReport(err_pipe[1], switch (err) {
416 errno.EFAULT => unreachable,
417 errno.E2BIG, errno.EMFILE, errno.ENAMETOOLONG, errno.ENFILE, errno.ENOMEM => error.SysResources,
418 errno.EACCES, errno.EPERM => error.AccessDenied,
419 errno.EINVAL, errno.ENOEXEC => error.InvalidExe,
420 errno.EIO, errno.ELOOP => error.FileSystem,
421 errno.EISDIR => error.IsDir,
422 errno.ENOENT, errno.ENOTDIR => error.FileNotFound,
423 errno.ETXTBSY => error.FileBusy,
424 else => error.Unexpected,
425 });
426 }
427
428 // we are the parent
429 if (stdin == StdIo.Pipe) { posixClose(stdin_pipe[0]); }
430 if (stdout == StdIo.Pipe) { posixClose(stdout_pipe[1]); }
431 if (stderr == StdIo.Pipe) { posixClose(stderr_pipe[1]); }
432 if (any_ignore) { posixClose(dev_null_fd); }
433
434 return ChildProcess {
435 .pid = i32(pid),
436 .err_pipe = err_pipe,
437
438 .stdin = if (stdin == StdIo.Pipe) {
439 io.OutStream {
440 .fd = stdin_pipe[1],
441 .buffer = undefined,
442 .index = 0,
443 }
444 } else {
445 null
446 },
447 .stdout = if (stdout == StdIo.Pipe) {
448 io.InStream {
449 .fd = stdout_pipe[0],
450 }
451 } else {
452 null
453 },
454 .stderr = if (stderr == StdIo.Pipe) {
455 io.InStream {
456 .fd = stderr_pipe[0],
457 }
458 } else {
459 null
460 },
461 };
462 }
463
464 fn setUpChildIo(stdio: StdIo, pipe_fd: i32, std_fileno: i32, dev_null_fd: i32) -> %void {
465 switch (stdio) {
466 StdIo.Pipe => %return dup2NoIntr(pipe_fd, std_fileno),
467 StdIo.Close => posixClose(std_fileno),
468 StdIo.Inherit => {},
469 StdIo.Ignore => %return dup2NoIntr(dev_null_fd, std_fileno),
470 }
471 }
472};
473
474203/// This function must allocate memory to add a null terminating bytes on path and each arg.
475204/// It must also convert to KEY=VALUE\0 format for environment variables, and include null
476205/// pointers after the args and after the environment variables.
477206/// Also make the first arg equal to path.
478fn execve(path: []const u8, argv: []const []const u8, env_map: &const EnvMap, allocator: &Allocator) -> %usize {
207pub fn posixExecve(path: []const u8, argv: []const []const u8, env_map: &const EnvMap,
208 allocator: &Allocator) -> %usize
209{
479210 const path_buf = %return allocator.alloc(u8, path.len + 1);
480211 defer allocator.free(path_buf);
481212 @memcpy(&path_buf[0], &path[0], path.len);
......@@ -604,6 +335,19 @@ pub const EnvMap = struct {
604335 mem.copy(u8, result, value);
605336 return result;
606337 }
338
339 fn hash_slice_u8(k: []const u8) -> u32 {
340 // FNV 32-bit hash
341 var h: u32 = 2166136261;
342 for (k) |b| {
343 h = (h ^ b) *% 16777619;
344 }
345 return h;
346 }
347
348 fn eql_slice_u8(a: []const u8, b: []const u8) -> bool {
349 return mem.eql(u8, a, b);
350 }
607351};
608352
609353pub fn getEnvMap(allocator: &Allocator) -> %EnvMap {
......@@ -641,15 +385,14 @@ pub fn getEnv(key: []const u8) -> ?[]const u8 {
641385 return null;
642386}
643387
644fn hash_slice_u8(k: []const u8) -> u32 {
645 // FNV 32-bit hash
646 var h: u32 = 2166136261;
647 for (k) |b| {
648 h = (h ^ b) *% 16777619;
649 }
650 return h;
651}
388pub const args = struct {
389 pub var raw: []&u8 = undefined;
652390
653fn eql_slice_u8(a: []const u8, b: []const u8) -> bool {
654 return mem.eql(u8, a, b);
655}
391 pub fn count() -> usize {
392 return raw.len;
393 }
394 pub fn at(i: usize) -> []const u8 {
395 const s = raw[i];
396 return s[0...cstr.len(s)];
397 }
398};
std/special/bootstrap.zig+2-8
......@@ -37,20 +37,14 @@ fn callMainAndExit() -> noreturn {
3737 exit(0);
3838}
3939
40var args_data: [32][]u8 = undefined;
4140fn callMain(argc: usize, argv: &&u8, envp: &?&u8) -> %void {
42 // TODO create args API to make it work with > 32 args
43 const args = args_data[0...argc];
44 for (args) |_, i| {
45 const ptr = argv[i];
46 args[i] = ptr[0...std.cstr.len(ptr)];
47 }
41 std.os.args.raw = argv[0...argc];
4842
4943 var env_count: usize = 0;
5044 while (envp[env_count] != null; env_count += 1) {}
5145 std.os.environ_raw = @ptrcast(&&u8, envp)[0...env_count];
5246
53 return root.main(args);
47 return root.main();
5448}
5549
5650export fn main(c_argc: i32, c_argv: &&u8, c_envp: &?&u8) -> i32 {
std/special/build_runner.zig+6-5
......@@ -1,18 +1,19 @@
11const root = @import("@build");
22const std = @import("std");
33const io = std.io;
4const os = std.os;
45const Builder = std.build.Builder;
56const mem = std.mem;
67
78error InvalidArgs;
89
9pub fn main(args: [][]u8) -> %void {
10 if (args.len < 2) {
10pub fn main() -> %void {
11 if (os.args.count() < 2) {
1112 %%io.stderr.printf("Expected first argument to be path to zig compiler\n");
1213 return error.InvalidArgs;
1314 }
14 const zig_exe = args[1];
15 const leftover_args = args[2...];
15 const zig_exe = os.args.at(1);
16 const leftover_arg_index = 2;
1617
1718 // TODO use a more general purpose allocator here
1819 var inc_allocator = %%mem.IncrementingAllocator.init(10 * 1024 * 1024);
......@@ -20,5 +21,5 @@ pub fn main(args: [][]u8) -> %void {
2021
2122 var builder = Builder.init(zig_exe, &inc_allocator.allocator);
2223 root.build(&builder);
23 %return builder.make(leftover_args);
24 %return builder.make(leftover_arg_index);
2425}
std/special/test_runner.zig+1-1
......@@ -7,7 +7,7 @@ const TestFn = struct {
77
88extern var zig_test_fn_list: []TestFn;
99
10pub fn main(args: [][]u8) -> %void {
10pub fn main() -> %void {
1111 for (zig_test_fn_list) |testFn, i| {
1212 %%io.stderr.printf("Test {}/{} {}...", i + 1, zig_test_fn_list.len, testFn.name);
1313
test/run_tests.cpp+46-63
......@@ -215,7 +215,7 @@ export fn main(argc: c_int, argv: &&u8) -> c_int {
215215use @import("std").io;
216216use @import("foo.zig");
217217
218pub fn main(args: [][]u8) -> %void {
218pub fn main() -> %void {
219219 privateFunction();
220220 %%stdout.printf("OK 2\n");
221221}
......@@ -245,7 +245,7 @@ pub fn printText() {
245245use @import("foo.zig");
246246use @import("bar.zig");
247247
248pub fn main(args: [][]u8) -> %void {
248pub fn main() -> %void {
249249 foo_function();
250250 bar_function();
251251}
......@@ -281,7 +281,7 @@ pub fn foo_function() -> bool {
281281 TestCase *tc = add_simple_case("two files use import each other", R"SOURCE(
282282use @import("a.zig");
283283
284pub fn main(args: [][]u8) -> %void {
284pub fn main() -> %void {
285285 ok();
286286}
287287 )SOURCE", "OK\n");
......@@ -309,7 +309,7 @@ pub const b_text = a_text;
309309 add_simple_case("hello world without libc", R"SOURCE(
310310const io = @import("std").io;
311311
312pub fn main(args: [][]u8) -> %void {
312pub fn main() -> %void {
313313 %%io.stdout.printf("Hello, world!\n{d4} {x3} {c}\n", u32(12), u16(0x12), u8('a'));
314314}
315315 )SOURCE", "Hello, world!\n0012 012 a\n");
......@@ -446,7 +446,7 @@ const io = @import("std").io;
446446const z = io.stdin_fileno;
447447const x : @typeOf(y) = 1234;
448448const y : u16 = 5678;
449pub fn main(args: [][]u8) -> %void {
449pub fn main() -> %void {
450450 var x_local : i32 = print_ok(x);
451451}
452452fn print_ok(val: @typeOf(x)) -> @typeOf(foo) {
......@@ -471,7 +471,7 @@ export fn compare_fn(a: ?&const c_void, b: ?&const c_void) -> c_int {
471471 }
472472}
473473
474export fn main(args: c_int, argv: &&u8) -> c_int {
474export fn main() -> c_int {
475475 var array = []u32 { 1, 7, 3, 2, 0, 9, 4, 8, 6, 5 };
476476
477477 c.qsort(@ptrcast(&c_void, &array[0]), c_ulong(array.len), @sizeOf(i32), compare_fn);
......@@ -516,7 +516,7 @@ const Bar = struct {
516516 fn method(b: &const Bar) -> bool { true }
517517};
518518
519pub fn main(args: [][]u8) -> %void {
519pub fn main() -> %void {
520520 const bar = Bar {.field2 = 13,};
521521 const foo = Foo {.field1 = bar,};
522522 if (!foo.method()) {
......@@ -532,7 +532,7 @@ pub fn main(args: [][]u8) -> %void {
532532
533533 add_simple_case("defer with only fallthrough", R"SOURCE(
534534const io = @import("std").io;
535pub fn main(args: [][]u8) -> %void {
535pub fn main() -> %void {
536536 %%io.stdout.printf("before\n");
537537 defer %%io.stdout.printf("defer1\n");
538538 defer %%io.stdout.printf("defer2\n");
......@@ -544,11 +544,12 @@ pub fn main(args: [][]u8) -> %void {
544544
545545 add_simple_case("defer with return", R"SOURCE(
546546const io = @import("std").io;
547pub fn main(args: [][]u8) -> %void {
547const os = @import("std").os;
548pub fn main() -> %void {
548549 %%io.stdout.printf("before\n");
549550 defer %%io.stdout.printf("defer1\n");
550551 defer %%io.stdout.printf("defer2\n");
551 if (args.len == 1) return;
552 if (os.args.count() == 1) return;
552553 defer %%io.stdout.printf("defer3\n");
553554 %%io.stdout.printf("after\n");
554555}
......@@ -557,7 +558,7 @@ pub fn main(args: [][]u8) -> %void {
557558
558559 add_simple_case("%defer and it fails", R"SOURCE(
559560const io = @import("std").io;
560pub fn main(args: [][]u8) -> %void {
561pub fn main() -> %void {
561562 do_test() %% return;
562563}
563564fn do_test() -> %void {
......@@ -577,7 +578,7 @@ fn its_gonna_fail() -> %void {
577578
578579 add_simple_case("%defer and it passes", R"SOURCE(
579580const io = @import("std").io;
580pub fn main(args: [][]u8) -> %void {
581pub fn main() -> %void {
581582 do_test() %% return;
582583}
583584fn do_test() -> %void {
......@@ -597,7 +598,7 @@ fn its_gonna_pass() -> %void { }
597598const foo_txt = @embedFile("foo.txt");
598599const io = @import("std").io;
599600
600pub fn main(args: [][]u8) -> %void {
601pub fn main() -> %void {
601602 %%io.stdout.printf(foo_txt);
602603}
603604 )SOURCE", "1234\nabcd\n");
......@@ -1388,9 +1389,13 @@ fn something() -> %void { }
13881389 ".tmp_source.zig:3:5: error: expected type 'void', found 'error'");
13891390
13901391 add_compile_fail_case("wrong return type for main", R"SOURCE(
1391pub fn main(args: [][]u8) { }
1392 )SOURCE", 1, ".tmp_source.zig:2:27: error: expected return type of main to be '%void', instead is 'void'");
1392pub fn main() { }
1393 )SOURCE", 1, ".tmp_source.zig:2:15: error: expected return type of main to be '%void', instead is 'void'");
13931394
1395 add_compile_fail_case("double ?? on main return value", R"SOURCE(
1396pub fn main() -> ??void {
1397}
1398 )SOURCE", 1, ".tmp_source.zig:2:18: error: expected return type of main to be '%void', instead is '??void'");
13941399
13951400 add_compile_fail_case("invalid pointer for var type", R"SOURCE(
13961401extern fn ext() -> usize;
......@@ -1689,11 +1694,6 @@ fn bar(a: i32, b: []const u8) {
16891694 ".tmp_source.zig:8:5: error: found compile log statement",
16901695 ".tmp_source.zig:3:17: note: called from here");
16911696
1692 add_compile_fail_case("double ?? on main return value", R"SOURCE(
1693pub fn main(args: [][]u8) -> ??void {
1694}
1695 )SOURCE", 1, ".tmp_source.zig:2:30: error: expected return type of main to be '%void', instead is '??void'");
1696
16971697 add_compile_fail_case("casting bit offset pointer to regular pointer", R"SOURCE(
16981698const u2 = @intType(false, 2);
16991699const u3 = @intType(false, 3);
......@@ -1844,7 +1844,7 @@ pub fn panic(message: []const u8) -> noreturn {
18441844 @breakpoint();
18451845 while (true) {}
18461846}
1847pub fn main(args: [][]u8) -> %void {
1847pub fn main() -> %void {
18481848 if (!@compileVar("is_release")) {
18491849 @panic("oh no");
18501850 }
......@@ -1856,7 +1856,7 @@ pub fn panic(message: []const u8) -> noreturn {
18561856 @breakpoint();
18571857 while (true) {}
18581858}
1859pub fn main(args: [][]u8) -> %void {
1859pub fn main() -> %void {
18601860 const a = []i32{1, 2, 3, 4};
18611861 baz(bar(a));
18621862}
......@@ -1872,7 +1872,7 @@ pub fn panic(message: []const u8) -> noreturn {
18721872 while (true) {}
18731873}
18741874error Whatever;
1875pub fn main(args: [][]u8) -> %void {
1875pub fn main() -> %void {
18761876 const x = add(65530, 10);
18771877 if (x == 0) return error.Whatever;
18781878}
......@@ -1887,7 +1887,7 @@ pub fn panic(message: []const u8) -> noreturn {
18871887 while (true) {}
18881888}
18891889error Whatever;
1890pub fn main(args: [][]u8) -> %void {
1890pub fn main() -> %void {
18911891 const x = sub(10, 20);
18921892 if (x == 0) return error.Whatever;
18931893}
......@@ -1902,7 +1902,7 @@ pub fn panic(message: []const u8) -> noreturn {
19021902 while (true) {}
19031903}
19041904error Whatever;
1905pub fn main(args: [][]u8) -> %void {
1905pub fn main() -> %void {
19061906 const x = mul(300, 6000);
19071907 if (x == 0) return error.Whatever;
19081908}
......@@ -1917,7 +1917,7 @@ pub fn panic(message: []const u8) -> noreturn {
19171917 while (true) {}
19181918}
19191919error Whatever;
1920pub fn main(args: [][]u8) -> %void {
1920pub fn main() -> %void {
19211921 const x = neg(-32768);
19221922 if (x == 32767) return error.Whatever;
19231923}
......@@ -1932,7 +1932,7 @@ pub fn panic(message: []const u8) -> noreturn {
19321932 while (true) {}
19331933}
19341934error Whatever;
1935pub fn main(args: [][]u8) -> %void {
1935pub fn main() -> %void {
19361936 const x = div(-32768, -1);
19371937 if (x == 32767) return error.Whatever;
19381938}
......@@ -1947,7 +1947,7 @@ pub fn panic(message: []const u8) -> noreturn {
19471947 while (true) {}
19481948}
19491949error Whatever;
1950pub fn main(args: [][]u8) -> %void {
1950pub fn main() -> %void {
19511951 const x = shl(-16385, 1);
19521952 if (x == 0) return error.Whatever;
19531953}
......@@ -1962,7 +1962,7 @@ pub fn panic(message: []const u8) -> noreturn {
19621962 while (true) {}
19631963}
19641964error Whatever;
1965pub fn main(args: [][]u8) -> %void {
1965pub fn main() -> %void {
19661966 const x = shl(0b0010111111111111, 3);
19671967 if (x == 0) return error.Whatever;
19681968}
......@@ -1977,7 +1977,7 @@ pub fn panic(message: []const u8) -> noreturn {
19771977 while (true) {}
19781978}
19791979error Whatever;
1980pub fn main(args: [][]u8) -> %void {
1980pub fn main() -> %void {
19811981 const x = div0(999, 0);
19821982}
19831983fn div0(a: i32, b: i32) -> i32 {
......@@ -1991,7 +1991,7 @@ pub fn panic(message: []const u8) -> noreturn {
19911991 while (true) {}
19921992}
19931993error Whatever;
1994pub fn main(args: [][]u8) -> %void {
1994pub fn main() -> %void {
19951995 const x = divExact(10, 3);
19961996 if (x == 0) return error.Whatever;
19971997}
......@@ -2006,7 +2006,7 @@ pub fn panic(message: []const u8) -> noreturn {
20062006 while (true) {}
20072007}
20082008error Whatever;
2009pub fn main(args: [][]u8) -> %void {
2009pub fn main() -> %void {
20102010 const x = widenSlice([]u8{1, 2, 3, 4, 5});
20112011 if (x.len == 0) return error.Whatever;
20122012}
......@@ -2021,7 +2021,7 @@ pub fn panic(message: []const u8) -> noreturn {
20212021 while (true) {}
20222022}
20232023error Whatever;
2024pub fn main(args: [][]u8) -> %void {
2024pub fn main() -> %void {
20252025 const x = shorten_cast(200);
20262026 if (x == 0) return error.Whatever;
20272027}
......@@ -2036,7 +2036,7 @@ pub fn panic(message: []const u8) -> noreturn {
20362036 while (true) {}
20372037}
20382038error Whatever;
2039pub fn main(args: [][]u8) -> %void {
2039pub fn main() -> %void {
20402040 const x = unsigned_cast(-10);
20412041 if (x == 0) return error.Whatever;
20422042}
......@@ -2051,7 +2051,7 @@ pub fn panic(message: []const u8) -> noreturn {
20512051 while (true) {}
20522052}
20532053error Whatever;
2054pub fn main(args: [][]u8) -> %void {
2054pub fn main() -> %void {
20552055 %%bar();
20562056}
20572057fn bar() -> %void {
......@@ -2064,7 +2064,7 @@ pub fn panic(message: []const u8) -> noreturn {
20642064 @breakpoint();
20652065 while (true) {}
20662066}
2067pub fn main(args: [][]u8) -> %void {
2067pub fn main() -> %void {
20682068 _ = bar(9999);
20692069}
20702070fn bar(x: u32) -> error {
......@@ -2500,25 +2500,13 @@ static void run_test(TestCase *test_case) {
25002500 }
25012501}
25022502
2503static void run_all_tests(bool reverse) {
2504 if (reverse) {
2505 for (size_t i = test_cases.length;;) {
2506 TestCase *test_case = test_cases.at(i);
2507 printf("Test %zu/%zu %s...", i + 1, test_cases.length, test_case->case_name);
2508 fflush(stdout);
2509 run_test(test_case);
2510 printf("OK\n");
2511 if (i == 0) break;
2512 i -= 1;
2513 }
2514 } else {
2515 for (size_t i = 0; i < test_cases.length; i += 1) {
2516 TestCase *test_case = test_cases.at(i);
2517 printf("Test %zu/%zu %s...", i + 1, test_cases.length, test_case->case_name);
2518 fflush(stdout);
2519 run_test(test_case);
2520 printf("OK\n");
2521 }
2503static void run_all_tests(void) {
2504 for (size_t i = 0; i < test_cases.length; i += 1) {
2505 TestCase *test_case = test_cases.at(i);
2506 printf("Test %zu/%zu %s...", i + 1, test_cases.length, test_case->case_name);
2507 fflush(stdout);
2508 run_test(test_case);
2509 printf("OK\n");
25222510 }
25232511 printf("%zu tests passed.\n", test_cases.length);
25242512}
......@@ -2530,18 +2518,13 @@ static void cleanup(void) {
25302518}
25312519
25322520static int usage(const char *arg0) {
2533 fprintf(stderr, "Usage: %s [--reverse]\n", arg0);
2521 fprintf(stderr, "Usage: %s\n", arg0);
25342522 return 1;
25352523}
25362524
25372525int main(int argc, char **argv) {
2538 bool reverse = false;
25392526 for (int i = 1; i < argc; i += 1) {
2540 if (strcmp(argv[i], "--reverse") == 0) {
2541 reverse = true;
2542 } else {
2543 return usage(argv[0]);
2544 }
2527 return usage(argv[0]);
25452528 }
25462529 add_compiling_test_cases();
25472530 add_debug_safety_test_cases();
......@@ -2549,6 +2532,6 @@ int main(int argc, char **argv) {
25492532 add_parseh_test_cases();
25502533 add_self_hosted_tests();
25512534 add_std_lib_tests();
2552 run_all_tests(reverse);
2535 run_all_tests();
25532536 cleanup();
25542537}