API for command line args

closes #300
master
Andrew Kelley 2017-04-04 00:17:24 -04:00
parent b46344fd01
commit 72fb2443e0
11 changed files with 381 additions and 374 deletions

View File

@ -223,6 +223,7 @@ install(FILES "${CMAKE_SOURCE_DIR}/std/list.zig" DESTINATION "${ZIG_STD_DEST}")
install(FILES "${CMAKE_SOURCE_DIR}/std/math.zig" DESTINATION "${ZIG_STD_DEST}")
install(FILES "${CMAKE_SOURCE_DIR}/std/mem.zig" DESTINATION "${ZIG_STD_DEST}")
install(FILES "${CMAKE_SOURCE_DIR}/std/net.zig" DESTINATION "${ZIG_STD_DEST}")
install(FILES "${CMAKE_SOURCE_DIR}/std/os/child_process.zig" DESTINATION "${ZIG_STD_DEST}/os")
install(FILES "${CMAKE_SOURCE_DIR}/std/os/darwin.zig" DESTINATION "${ZIG_STD_DEST}/os")
install(FILES "${CMAKE_SOURCE_DIR}/std/os/darwin_x86_64.zig" DESTINATION "${ZIG_STD_DEST}/os")
install(FILES "${CMAKE_SOURCE_DIR}/std/os/errno.zig" DESTINATION "${ZIG_STD_DEST}/os")

View File

@ -1,23 +1,25 @@
const std = @import("std");
const io = std.io;
const mem = std.mem;
const os = std.os;
pub fn main(args: [][]u8) -> %void {
const exe = args[0];
pub fn main() -> %void {
const exe = os.args.at(0);
var catted_anything = false;
for (args[1...]) |arg| {
var arg_i: usize = 1;
while (arg_i < os.args.count(); arg_i += 1) {
const arg = os.args.at(arg_i);
if (mem.eql(u8, arg, "-")) {
catted_anything = true;
%return cat_stream(&io.stdin);
} else if (arg[0] == '-') {
return usage(exe);
} else {
var is: io.InStream = undefined;
is.open(arg) %% |err| {
var is = io.InStream.open(arg, null) %% |err| {
%%io.stderr.printf("Unable to open file: {}\n", @errorName(err));
return err;
};
defer %%is.close();
defer is.close();
catted_anything = true;
%return cat_stream(&is);
@ -29,7 +31,7 @@ pub fn main(args: [][]u8) -> %void {
%return io.stdout.flush();
}
fn usage(exe: []u8) -> %void {
fn usage(exe: []const u8) -> %void {
%%io.stderr.printf("Usage: {} [FILE]...\n", exe);
return error.Invalid;
}

View File

@ -4,7 +4,7 @@ const fmt = std.fmt;
const Rand = std.rand.Rand;
const os = std.os;
pub fn main(args: [][]u8) -> %void {
pub fn main() -> %void {
%%io.stdout.printf("Welcome to the Guess Number Game in Zig.\n");
var seed_bytes: [@sizeOf(usize)]u8 = undefined;

View File

@ -1,5 +1,5 @@
const io = @import("std").io;
pub fn main(args: [][]u8) -> %void {
pub fn main() -> %void {
%%io.stdout.printf("Hello, world!\n");
}

View File

@ -39,11 +39,13 @@ pub const Builder = struct {
return exe;
}
pub fn make(self: &Builder, cli_args: []const []const u8) -> %void {
pub fn make(self: &Builder, leftover_arg_index: usize) -> %void {
var env_map = %return os.getEnvMap(self.allocator);
var verbose = false;
for (cli_args) |arg| {
var arg_i: usize = leftover_arg_index;
while (arg_i < os.args.count(); arg_i += 1) {
const arg = os.args.at(arg_i);
if (mem.eql(u8, arg, "--verbose")) {
verbose = true;
} else {
@ -95,7 +97,8 @@ pub const Builder = struct {
}
printInvocation(self.zig_exe, zig_args);
var child = %return os.ChildProcess.spawn(self.zig_exe, zig_args.toSliceConst(), env_map,
// TODO issue #301
var child = %return os.ChildProcess.spawn(self.zig_exe, zig_args.toSliceConst(), &env_map,
StdIo.Ignore, StdIo.Inherit, StdIo.Inherit, self.allocator);
const term = %return child.wait();
switch (term) {

280
std/os/child_process.zig Normal file
View File

@ -0,0 +1,280 @@
const io = @import("../io.zig");
const os = @import("index.zig");
const posix = os.posix;
const mem = @import("../mem.zig");
const Allocator = mem.Allocator;
const errno = @import("errno.zig");
const debug = @import("../debug.zig");
const assert = debug.assert;
pub const ChildProcess = struct {
pid: i32,
err_pipe: [2]i32,
stdin: ?io.OutStream,
stdout: ?io.InStream,
stderr: ?io.InStream,
pub const Term = enum {
Clean: i32,
Signal: i32,
Stopped: i32,
Unknown: i32,
};
pub const StdIo = enum {
Inherit,
Ignore,
Pipe,
Close,
};
pub fn spawn(exe_path: []const u8, args: []const []const u8, env_map: &const os.EnvMap,
stdin: StdIo, stdout: StdIo, stderr: StdIo, allocator: &Allocator) -> %ChildProcess
{
switch (@compileVar("os")) {
Os.linux, Os.macosx, Os.ios, Os.darwin => {
return spawnPosix(exe_path, args, env_map, stdin, stdout, stderr, allocator);
},
else => @compileError("Unsupported OS"),
}
}
pub fn wait(self: &ChildProcess) -> %Term {
defer {
os.posixClose(self.err_pipe[0]);
os.posixClose(self.err_pipe[1]);
};
var status: i32 = undefined;
while (true) {
const err = posix.getErrno(posix.waitpid(self.pid, &status, 0));
if (err > 0) {
switch (err) {
errno.EINVAL, errno.ECHILD => unreachable,
errno.EINTR => continue,
else => {
if (const *stdin ?= self.stdin) { stdin.close(); }
if (const *stdout ?= self.stdin) { stdout.close(); }
if (const *stderr ?= self.stdin) { stderr.close(); }
return error.Unexpected;
},
}
}
break;
}
if (const *stdin ?= self.stdin) { stdin.close(); }
if (const *stdout ?= self.stdin) { stdout.close(); }
if (const *stderr ?= self.stdin) { stderr.close(); }
// Write @maxValue(ErrInt) to the write end of the err_pipe. This is after
// waitpid, so this write is guaranteed to be after the child
// pid potentially wrote an error. This way we can do a blocking
// read on the error pipe and either get @maxValue(ErrInt) (no error) or
// an error code.
%return writeIntFd(self.err_pipe[1], @maxValue(ErrInt));
const err_int = %return readIntFd(self.err_pipe[0]);
// Here we potentially return the fork child's error
// from the parent pid.
if (err_int != @maxValue(ErrInt)) {
return error(err_int);
}
return statusToTerm(status);
}
fn statusToTerm(status: i32) -> Term {
return if (posix.WIFEXITED(status)) {
Term.Clean { posix.WEXITSTATUS(status) }
} else if (posix.WIFSIGNALED(status)) {
Term.Signal { posix.WTERMSIG(status) }
} else if (posix.WIFSTOPPED(status)) {
Term.Stopped { posix.WSTOPSIG(status) }
} else {
Term.Unknown { status }
};
}
fn spawnPosix(exe_path: []const u8, args: []const []const u8, env_map: &const os.EnvMap,
stdin: StdIo, stdout: StdIo, stderr: StdIo, allocator: &Allocator) -> %ChildProcess
{
// TODO issue #295
//const stdin_pipe = if (stdin == StdIo.Pipe) %return makePipe() else undefined;
var stdin_pipe: [2]i32 = undefined;
if (stdin == StdIo.Pipe)
stdin_pipe = %return makePipe();
%defer if (stdin == StdIo.Pipe) { destroyPipe(stdin_pipe); };
// TODO issue #295
//const stdout_pipe = if (stdout == StdIo.Pipe) %return makePipe() else undefined;
var stdout_pipe: [2]i32 = undefined;
if (stdout == StdIo.Pipe)
stdout_pipe = %return makePipe();
%defer if (stdout == StdIo.Pipe) { destroyPipe(stdout_pipe); };
// TODO issue #295
//const stderr_pipe = if (stderr == StdIo.Pipe) %return makePipe() else undefined;
var stderr_pipe: [2]i32 = undefined;
if (stderr == StdIo.Pipe)
stderr_pipe = %return makePipe();
%defer if (stderr == StdIo.Pipe) { destroyPipe(stderr_pipe); };
const any_ignore = (stdin == StdIo.Ignore or stdout == StdIo.Ignore or stderr == StdIo.Ignore);
// TODO issue #295
//const dev_null_fd = if (any_ignore) {
// %return os.posixOpen("/dev/null", posix.O_RDWR, 0, null)
//} else {
// undefined
//};
var dev_null_fd: i32 = undefined;
if (any_ignore)
dev_null_fd = %return os.posixOpen("/dev/null", posix.O_RDWR, 0, null);
// This pipe is used to communicate errors between the time of fork
// and execve from the child process to the parent process.
const err_pipe = %return makePipe();
%defer destroyPipe(err_pipe);
const pid = posix.fork();
const pid_err = posix.getErrno(pid);
if (pid_err > 0) {
return switch (pid_err) {
errno.EAGAIN, errno.ENOMEM, errno.ENOSYS => error.SysResources,
else => error.Unexpected,
};
}
if (pid == 0) {
// we are the child
setUpChildIo(stdin, stdin_pipe[0], posix.STDIN_FILENO, dev_null_fd) %%
|err| forkChildErrReport(err_pipe[1], err);
setUpChildIo(stdout, stdout_pipe[1], posix.STDOUT_FILENO, dev_null_fd) %%
|err| forkChildErrReport(err_pipe[1], err);
setUpChildIo(stderr, stderr_pipe[1], posix.STDERR_FILENO, dev_null_fd) %%
|err| forkChildErrReport(err_pipe[1], err);
const err = posix.getErrno(%return os.posixExecve(exe_path, args, env_map, allocator));
assert(err > 0);
forkChildErrReport(err_pipe[1], switch (err) {
errno.EFAULT => unreachable,
errno.E2BIG, errno.EMFILE, errno.ENAMETOOLONG, errno.ENFILE, errno.ENOMEM => error.SysResources,
errno.EACCES, errno.EPERM => error.AccessDenied,
errno.EINVAL, errno.ENOEXEC => error.InvalidExe,
errno.EIO, errno.ELOOP => error.FileSystem,
errno.EISDIR => error.IsDir,
errno.ENOENT, errno.ENOTDIR => error.FileNotFound,
errno.ETXTBSY => error.FileBusy,
else => error.Unexpected,
});
}
// we are the parent
if (stdin == StdIo.Pipe) { os.posixClose(stdin_pipe[0]); }
if (stdout == StdIo.Pipe) { os.posixClose(stdout_pipe[1]); }
if (stderr == StdIo.Pipe) { os.posixClose(stderr_pipe[1]); }
if (any_ignore) { os.posixClose(dev_null_fd); }
return ChildProcess {
.pid = i32(pid),
.err_pipe = err_pipe,
.stdin = if (stdin == StdIo.Pipe) {
io.OutStream {
.fd = stdin_pipe[1],
.buffer = undefined,
.index = 0,
}
} else {
null
},
.stdout = if (stdout == StdIo.Pipe) {
io.InStream {
.fd = stdout_pipe[0],
}
} else {
null
},
.stderr = if (stderr == StdIo.Pipe) {
io.InStream {
.fd = stderr_pipe[0],
}
} else {
null
},
};
}
fn setUpChildIo(stdio: StdIo, pipe_fd: i32, std_fileno: i32, dev_null_fd: i32) -> %void {
switch (stdio) {
StdIo.Pipe => %return os.posixDup2(pipe_fd, std_fileno),
StdIo.Close => os.posixClose(std_fileno),
StdIo.Inherit => {},
StdIo.Ignore => %return os.posixDup2(dev_null_fd, std_fileno),
}
}
};
fn makePipe() -> %[2]i32 {
var fds: [2]i32 = undefined;
const err = posix.getErrno(posix.pipe(&fds));
if (err > 0) {
return switch (err) {
errno.EMFILE, errno.ENFILE => error.SysResources,
else => error.Unexpected,
}
}
return fds;
}
fn destroyPipe(pipe: &const [2]i32) {
os.posixClose((*pipe)[0]);
os.posixClose((*pipe)[1]);
}
// Child of fork calls this to report an error to the fork parent.
// Then the child exits.
fn forkChildErrReport(fd: i32, err: error) -> noreturn {
_ = writeIntFd(fd, ErrInt(err));
posix.exit(1);
}
const ErrInt = @intType(false, @sizeOf(error) * 8);
fn writeIntFd(fd: i32, value: ErrInt) -> %void {
var bytes: [@sizeOf(ErrInt)]u8 = undefined;
mem.writeInt(bytes[0...], value, true);
var index: usize = 0;
while (index < bytes.len) {
const amt_written = posix.write(fd, &bytes[index], bytes.len - index);
const err = posix.getErrno(amt_written);
if (err > 0) {
switch (err) {
errno.EINTR => continue,
errno.EINVAL => unreachable,
else => return error.SysResources,
}
}
index += amt_written;
}
}
fn readIntFd(fd: i32) -> %ErrInt {
var bytes: [@sizeOf(ErrInt)]u8 = undefined;
var index: usize = 0;
while (index < bytes.len) {
const amt_written = posix.read(fd, &bytes[index], bytes.len - index);
const err = posix.getErrno(amt_written);
if (err > 0) {
switch (err) {
errno.EINTR => continue,
errno.EINVAL => unreachable,
else => return error.SysResources,
}
}
index += amt_written;
}
return mem.readInt(bytes[0...], ErrInt, true);
}

View File

@ -9,6 +9,7 @@ pub const posix = switch(@compileVar("os")) {
};
pub const max_noalloc_path_len = 1024;
pub const ChildProcess = @import("child_process.zig").ChildProcess;
const debug = @import("../debug.zig");
const assert = debug.assert;
@ -20,7 +21,6 @@ const c = @import("../c/index.zig");
const mem = @import("../mem.zig");
const Allocator = mem.Allocator;
const io = @import("../io.zig");
const HashMap = @import("../hash_map.zig").HashMap;
const cstr = @import("../cstr.zig");
@ -96,23 +96,6 @@ pub coldcc fn abort() -> noreturn {
}
}
fn makePipe() -> %[2]i32 {
var fds: [2]i32 = undefined;
const err = posix.getErrno(posix.pipe(&fds));
if (err > 0) {
return switch (err) {
errno.EMFILE, errno.ENFILE => error.SysResources,
else => error.Unexpected,
}
}
return fds;
}
fn destroyPipe(pipe: &const [2]i32) {
posixClose((*pipe)[0]);
posixClose((*pipe)[1]);
}
/// Calls POSIX close, and keeps trying if it gets interrupted.
pub fn posixClose(fd: i32) {
while (true) {
@ -202,54 +185,7 @@ pub fn posixOpen(path: []const u8, flags: usize, perm: usize, allocator: ?&Alloc
}
}
const ErrInt = @intType(false, @sizeOf(error) * 8);
fn writeIntFd(fd: i32, value: ErrInt) -> %void {
var bytes: [@sizeOf(ErrInt)]u8 = undefined;
mem.writeInt(bytes[0...], value, true);
var index: usize = 0;
while (index < bytes.len) {
const amt_written = posix.write(fd, &bytes[index], bytes.len - index);
const err = posix.getErrno(amt_written);
if (err > 0) {
switch (err) {
errno.EINTR => continue,
errno.EINVAL => unreachable,
else => return error.SysResources,
}
}
index += amt_written;
}
}
fn readIntFd(fd: i32) -> %ErrInt {
var bytes: [@sizeOf(ErrInt)]u8 = undefined;
var index: usize = 0;
while (index < bytes.len) {
const amt_written = posix.read(fd, &bytes[index], bytes.len - index);
const err = posix.getErrno(amt_written);
if (err > 0) {
switch (err) {
errno.EINTR => continue,
errno.EINVAL => unreachable,
else => return error.SysResources,
}
}
index += amt_written;
}
return mem.readInt(bytes[0...], ErrInt, true);
}
// Child of fork calls this to report an error to the fork parent.
// Then the child exits.
fn forkChildErrReport(fd: i32, err: error) -> noreturn {
_ = writeIntFd(fd, ErrInt(err));
posix.exit(1);
}
fn dup2NoIntr(old_fd: i32, new_fd: i32) -> %void {
pub fn posixDup2(old_fd: i32, new_fd: i32) -> %void {
while (true) {
const err = posix.getErrno(posix.dup2(old_fd, new_fd));
if (err > 0) {
@ -264,218 +200,13 @@ fn dup2NoIntr(old_fd: i32, new_fd: i32) -> %void {
}
}
pub const ChildProcess = struct {
pid: i32,
err_pipe: [2]i32,
stdin: ?io.OutStream,
stdout: ?io.InStream,
stderr: ?io.InStream,
pub const Term = enum {
Clean: i32,
Signal: i32,
Stopped: i32,
Unknown: i32,
};
pub const StdIo = enum {
Inherit,
Ignore,
Pipe,
Close,
};
pub fn spawn(exe_path: []const u8, args: []const []const u8, env_map: &const EnvMap,
stdin: StdIo, stdout: StdIo, stderr: StdIo, allocator: &Allocator) -> %ChildProcess
{
switch (@compileVar("os")) {
Os.linux, Os.macosx, Os.ios, Os.darwin => {
return spawnPosix(exe_path, args, env_map, stdin, stdout, stderr, allocator);
},
else => @compileError("Unsupported OS"),
}
}
pub fn wait(self: &ChildProcess) -> %Term {
defer {
posixClose(self.err_pipe[0]);
posixClose(self.err_pipe[1]);
};
var status: i32 = undefined;
while (true) {
const err = posix.getErrno(posix.waitpid(self.pid, &status, 0));
if (err > 0) {
switch (err) {
errno.EINVAL, errno.ECHILD => unreachable,
errno.EINTR => continue,
else => {
if (const *stdin ?= self.stdin) { stdin.close(); }
if (const *stdout ?= self.stdin) { stdout.close(); }
if (const *stderr ?= self.stdin) { stderr.close(); }
return error.Unexpected;
},
}
}
break;
}
if (const *stdin ?= self.stdin) { stdin.close(); }
if (const *stdout ?= self.stdin) { stdout.close(); }
if (const *stderr ?= self.stdin) { stderr.close(); }
// Write @maxValue(ErrInt) to the write end of the err_pipe. This is after
// waitpid, so this write is guaranteed to be after the child
// pid potentially wrote an error. This way we can do a blocking
// read on the error pipe and either get @maxValue(ErrInt) (no error) or
// an error code.
%return writeIntFd(self.err_pipe[1], @maxValue(ErrInt));
const err_int = %return readIntFd(self.err_pipe[0]);
// Here we potentially return the fork child's error
// from the parent pid.
if (err_int != @maxValue(ErrInt)) {
return error(err_int);
}
return statusToTerm(status);
}
fn statusToTerm(status: i32) -> Term {
return if (posix.WIFEXITED(status)) {
Term.Clean { posix.WEXITSTATUS(status) }
} else if (posix.WIFSIGNALED(status)) {
Term.Signal { posix.WTERMSIG(status) }
} else if (posix.WIFSTOPPED(status)) {
Term.Stopped { posix.WSTOPSIG(status) }
} else {
Term.Unknown { status }
};
}
fn spawnPosix(exe_path: []const u8, args: []const []const u8, env_map: &const EnvMap,
stdin: StdIo, stdout: StdIo, stderr: StdIo, allocator: &Allocator) -> %ChildProcess
{
// TODO issue #295
//const stdin_pipe = if (stdin == StdIo.Pipe) %return makePipe() else undefined;
var stdin_pipe: [2]i32 = undefined;
if (stdin == StdIo.Pipe)
stdin_pipe = %return makePipe();
%defer if (stdin == StdIo.Pipe) { destroyPipe(stdin_pipe); };
// TODO issue #295
//const stdout_pipe = if (stdout == StdIo.Pipe) %return makePipe() else undefined;
var stdout_pipe: [2]i32 = undefined;
if (stdout == StdIo.Pipe)
stdout_pipe = %return makePipe();
%defer if (stdout == StdIo.Pipe) { destroyPipe(stdout_pipe); };
// TODO issue #295
//const stderr_pipe = if (stderr == StdIo.Pipe) %return makePipe() else undefined;
var stderr_pipe: [2]i32 = undefined;
if (stderr == StdIo.Pipe)
stderr_pipe = %return makePipe();
%defer if (stderr == StdIo.Pipe) { destroyPipe(stderr_pipe); };
const any_ignore = (stdin == StdIo.Ignore or stdout == StdIo.Ignore or stderr == StdIo.Ignore);
// TODO issue #295
//const dev_null_fd = if (any_ignore) {
// %return posixOpen("/dev/null", posix.O_RDWR, 0, null)
//} else {
// undefined
//};
var dev_null_fd: i32 = undefined;
if (any_ignore)
dev_null_fd = %return posixOpen("/dev/null", posix.O_RDWR, 0, null);
// This pipe is used to communicate errors between the time of fork
// and execve from the child process to the parent process.
const err_pipe = %return makePipe();
%defer destroyPipe(err_pipe);
const pid = posix.fork();
const pid_err = linux.getErrno(pid);
if (pid_err > 0) {
return switch (pid_err) {
errno.EAGAIN, errno.ENOMEM, errno.ENOSYS => error.SysResources,
else => error.Unexpected,
};
}
if (pid == 0) {
// we are the child
setUpChildIo(stdin, stdin_pipe[0], posix.STDIN_FILENO, dev_null_fd) %%
|err| forkChildErrReport(err_pipe[1], err);
setUpChildIo(stdout, stdout_pipe[1], posix.STDOUT_FILENO, dev_null_fd) %%
|err| forkChildErrReport(err_pipe[1], err);
setUpChildIo(stderr, stderr_pipe[1], posix.STDERR_FILENO, dev_null_fd) %%
|err| forkChildErrReport(err_pipe[1], err);
const err = posix.getErrno(%return execve(exe_path, args, env_map, allocator));
assert(err > 0);
forkChildErrReport(err_pipe[1], switch (err) {
errno.EFAULT => unreachable,
errno.E2BIG, errno.EMFILE, errno.ENAMETOOLONG, errno.ENFILE, errno.ENOMEM => error.SysResources,
errno.EACCES, errno.EPERM => error.AccessDenied,
errno.EINVAL, errno.ENOEXEC => error.InvalidExe,
errno.EIO, errno.ELOOP => error.FileSystem,
errno.EISDIR => error.IsDir,
errno.ENOENT, errno.ENOTDIR => error.FileNotFound,
errno.ETXTBSY => error.FileBusy,
else => error.Unexpected,
});
}
// we are the parent
if (stdin == StdIo.Pipe) { posixClose(stdin_pipe[0]); }
if (stdout == StdIo.Pipe) { posixClose(stdout_pipe[1]); }
if (stderr == StdIo.Pipe) { posixClose(stderr_pipe[1]); }
if (any_ignore) { posixClose(dev_null_fd); }
return ChildProcess {
.pid = i32(pid),
.err_pipe = err_pipe,
.stdin = if (stdin == StdIo.Pipe) {
io.OutStream {
.fd = stdin_pipe[1],
.buffer = undefined,
.index = 0,
}
} else {
null
},
.stdout = if (stdout == StdIo.Pipe) {
io.InStream {
.fd = stdout_pipe[0],
}
} else {
null
},
.stderr = if (stderr == StdIo.Pipe) {
io.InStream {
.fd = stderr_pipe[0],
}
} else {
null
},
};
}
fn setUpChildIo(stdio: StdIo, pipe_fd: i32, std_fileno: i32, dev_null_fd: i32) -> %void {
switch (stdio) {
StdIo.Pipe => %return dup2NoIntr(pipe_fd, std_fileno),
StdIo.Close => posixClose(std_fileno),
StdIo.Inherit => {},
StdIo.Ignore => %return dup2NoIntr(dev_null_fd, std_fileno),
}
}
};
/// This function must allocate memory to add a null terminating bytes on path and each arg.
/// It must also convert to KEY=VALUE\0 format for environment variables, and include null
/// pointers after the args and after the environment variables.
/// Also make the first arg equal to path.
fn execve(path: []const u8, argv: []const []const u8, env_map: &const EnvMap, allocator: &Allocator) -> %usize {
pub fn posixExecve(path: []const u8, argv: []const []const u8, env_map: &const EnvMap,
allocator: &Allocator) -> %usize
{
const path_buf = %return allocator.alloc(u8, path.len + 1);
defer allocator.free(path_buf);
@memcpy(&path_buf[0], &path[0], path.len);
@ -604,6 +335,19 @@ pub const EnvMap = struct {
mem.copy(u8, result, value);
return result;
}
fn hash_slice_u8(k: []const u8) -> u32 {
// FNV 32-bit hash
var h: u32 = 2166136261;
for (k) |b| {
h = (h ^ b) *% 16777619;
}
return h;
}
fn eql_slice_u8(a: []const u8, b: []const u8) -> bool {
return mem.eql(u8, a, b);
}
};
pub fn getEnvMap(allocator: &Allocator) -> %EnvMap {
@ -641,15 +385,14 @@ pub fn getEnv(key: []const u8) -> ?[]const u8 {
return null;
}
fn hash_slice_u8(k: []const u8) -> u32 {
// FNV 32-bit hash
var h: u32 = 2166136261;
for (k) |b| {
h = (h ^ b) *% 16777619;
}
return h;
}
pub const args = struct {
pub var raw: []&u8 = undefined;
fn eql_slice_u8(a: []const u8, b: []const u8) -> bool {
return mem.eql(u8, a, b);
}
pub fn count() -> usize {
return raw.len;
}
pub fn at(i: usize) -> []const u8 {
const s = raw[i];
return s[0...cstr.len(s)];
}
};

View File

@ -37,20 +37,14 @@ fn callMainAndExit() -> noreturn {
exit(0);
}
var args_data: [32][]u8 = undefined;
fn callMain(argc: usize, argv: &&u8, envp: &?&u8) -> %void {
// TODO create args API to make it work with > 32 args
const args = args_data[0...argc];
for (args) |_, i| {
const ptr = argv[i];
args[i] = ptr[0...std.cstr.len(ptr)];
}
std.os.args.raw = argv[0...argc];
var env_count: usize = 0;
while (envp[env_count] != null; env_count += 1) {}
std.os.environ_raw = @ptrcast(&&u8, envp)[0...env_count];
return root.main(args);
return root.main();
}
export fn main(c_argc: i32, c_argv: &&u8, c_envp: &?&u8) -> i32 {

View File

@ -1,18 +1,19 @@
const root = @import("@build");
const std = @import("std");
const io = std.io;
const os = std.os;
const Builder = std.build.Builder;
const mem = std.mem;
error InvalidArgs;
pub fn main(args: [][]u8) -> %void {
if (args.len < 2) {
pub fn main() -> %void {
if (os.args.count() < 2) {
%%io.stderr.printf("Expected first argument to be path to zig compiler\n");
return error.InvalidArgs;
}
const zig_exe = args[1];
const leftover_args = args[2...];
const zig_exe = os.args.at(1);
const leftover_arg_index = 2;
// TODO use a more general purpose allocator here
var inc_allocator = %%mem.IncrementingAllocator.init(10 * 1024 * 1024);
@ -20,5 +21,5 @@ pub fn main(args: [][]u8) -> %void {
var builder = Builder.init(zig_exe, &inc_allocator.allocator);
root.build(&builder);
%return builder.make(leftover_args);
%return builder.make(leftover_arg_index);
}

View File

@ -7,7 +7,7 @@ const TestFn = struct {
extern var zig_test_fn_list: []TestFn;
pub fn main(args: [][]u8) -> %void {
pub fn main() -> %void {
for (zig_test_fn_list) |testFn, i| {
%%io.stderr.printf("Test {}/{} {}...", i + 1, zig_test_fn_list.len, testFn.name);

View File

@ -215,7 +215,7 @@ export fn main(argc: c_int, argv: &&u8) -> c_int {
use @import("std").io;
use @import("foo.zig");
pub fn main(args: [][]u8) -> %void {
pub fn main() -> %void {
privateFunction();
%%stdout.printf("OK 2\n");
}
@ -245,7 +245,7 @@ pub fn printText() {
use @import("foo.zig");
use @import("bar.zig");
pub fn main(args: [][]u8) -> %void {
pub fn main() -> %void {
foo_function();
bar_function();
}
@ -281,7 +281,7 @@ pub fn foo_function() -> bool {
TestCase *tc = add_simple_case("two files use import each other", R"SOURCE(
use @import("a.zig");
pub fn main(args: [][]u8) -> %void {
pub fn main() -> %void {
ok();
}
)SOURCE", "OK\n");
@ -309,7 +309,7 @@ pub const b_text = a_text;
add_simple_case("hello world without libc", R"SOURCE(
const io = @import("std").io;
pub fn main(args: [][]u8) -> %void {
pub fn main() -> %void {
%%io.stdout.printf("Hello, world!\n{d4} {x3} {c}\n", u32(12), u16(0x12), u8('a'));
}
)SOURCE", "Hello, world!\n0012 012 a\n");
@ -446,7 +446,7 @@ const io = @import("std").io;
const z = io.stdin_fileno;
const x : @typeOf(y) = 1234;
const y : u16 = 5678;
pub fn main(args: [][]u8) -> %void {
pub fn main() -> %void {
var x_local : i32 = print_ok(x);
}
fn 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 {
}
}
export fn main(args: c_int, argv: &&u8) -> c_int {
export fn main() -> c_int {
var array = []u32 { 1, 7, 3, 2, 0, 9, 4, 8, 6, 5 };
c.qsort(@ptrcast(&c_void, &array[0]), c_ulong(array.len), @sizeOf(i32), compare_fn);
@ -516,7 +516,7 @@ const Bar = struct {
fn method(b: &const Bar) -> bool { true }
};
pub fn main(args: [][]u8) -> %void {
pub fn main() -> %void {
const bar = Bar {.field2 = 13,};
const foo = Foo {.field1 = bar,};
if (!foo.method()) {
@ -532,7 +532,7 @@ pub fn main(args: [][]u8) -> %void {
add_simple_case("defer with only fallthrough", R"SOURCE(
const io = @import("std").io;
pub fn main(args: [][]u8) -> %void {
pub fn main() -> %void {
%%io.stdout.printf("before\n");
defer %%io.stdout.printf("defer1\n");
defer %%io.stdout.printf("defer2\n");
@ -544,11 +544,12 @@ pub fn main(args: [][]u8) -> %void {
add_simple_case("defer with return", R"SOURCE(
const io = @import("std").io;
pub fn main(args: [][]u8) -> %void {
const os = @import("std").os;
pub fn main() -> %void {
%%io.stdout.printf("before\n");
defer %%io.stdout.printf("defer1\n");
defer %%io.stdout.printf("defer2\n");
if (args.len == 1) return;
if (os.args.count() == 1) return;
defer %%io.stdout.printf("defer3\n");
%%io.stdout.printf("after\n");
}
@ -557,7 +558,7 @@ pub fn main(args: [][]u8) -> %void {
add_simple_case("%defer and it fails", R"SOURCE(
const io = @import("std").io;
pub fn main(args: [][]u8) -> %void {
pub fn main() -> %void {
do_test() %% return;
}
fn do_test() -> %void {
@ -577,7 +578,7 @@ fn its_gonna_fail() -> %void {
add_simple_case("%defer and it passes", R"SOURCE(
const io = @import("std").io;
pub fn main(args: [][]u8) -> %void {
pub fn main() -> %void {
do_test() %% return;
}
fn do_test() -> %void {
@ -597,7 +598,7 @@ fn its_gonna_pass() -> %void { }
const foo_txt = @embedFile("foo.txt");
const io = @import("std").io;
pub fn main(args: [][]u8) -> %void {
pub fn main() -> %void {
%%io.stdout.printf(foo_txt);
}
)SOURCE", "1234\nabcd\n");
@ -1388,9 +1389,13 @@ fn something() -> %void { }
".tmp_source.zig:3:5: error: expected type 'void', found 'error'");
add_compile_fail_case("wrong return type for main", R"SOURCE(
pub fn main(args: [][]u8) { }
)SOURCE", 1, ".tmp_source.zig:2:27: error: expected return type of main to be '%void', instead is 'void'");
pub fn main() { }
)SOURCE", 1, ".tmp_source.zig:2:15: error: expected return type of main to be '%void', instead is 'void'");
add_compile_fail_case("double ?? on main return value", R"SOURCE(
pub fn main() -> ??void {
}
)SOURCE", 1, ".tmp_source.zig:2:18: error: expected return type of main to be '%void', instead is '??void'");
add_compile_fail_case("invalid pointer for var type", R"SOURCE(
extern fn ext() -> usize;
@ -1689,11 +1694,6 @@ fn bar(a: i32, b: []const u8) {
".tmp_source.zig:8:5: error: found compile log statement",
".tmp_source.zig:3:17: note: called from here");
add_compile_fail_case("double ?? on main return value", R"SOURCE(
pub fn main(args: [][]u8) -> ??void {
}
)SOURCE", 1, ".tmp_source.zig:2:30: error: expected return type of main to be '%void', instead is '??void'");
add_compile_fail_case("casting bit offset pointer to regular pointer", R"SOURCE(
const u2 = @intType(false, 2);
const u3 = @intType(false, 3);
@ -1844,7 +1844,7 @@ pub fn panic(message: []const u8) -> noreturn {
@breakpoint();
while (true) {}
}
pub fn main(args: [][]u8) -> %void {
pub fn main() -> %void {
if (!@compileVar("is_release")) {
@panic("oh no");
}
@ -1856,7 +1856,7 @@ pub fn panic(message: []const u8) -> noreturn {
@breakpoint();
while (true) {}
}
pub fn main(args: [][]u8) -> %void {
pub fn main() -> %void {
const a = []i32{1, 2, 3, 4};
baz(bar(a));
}
@ -1872,7 +1872,7 @@ pub fn panic(message: []const u8) -> noreturn {
while (true) {}
}
error Whatever;
pub fn main(args: [][]u8) -> %void {
pub fn main() -> %void {
const x = add(65530, 10);
if (x == 0) return error.Whatever;
}
@ -1887,7 +1887,7 @@ pub fn panic(message: []const u8) -> noreturn {
while (true) {}
}
error Whatever;
pub fn main(args: [][]u8) -> %void {
pub fn main() -> %void {
const x = sub(10, 20);
if (x == 0) return error.Whatever;
}
@ -1902,7 +1902,7 @@ pub fn panic(message: []const u8) -> noreturn {
while (true) {}
}
error Whatever;
pub fn main(args: [][]u8) -> %void {
pub fn main() -> %void {
const x = mul(300, 6000);
if (x == 0) return error.Whatever;
}
@ -1917,7 +1917,7 @@ pub fn panic(message: []const u8) -> noreturn {
while (true) {}
}
error Whatever;
pub fn main(args: [][]u8) -> %void {
pub fn main() -> %void {
const x = neg(-32768);
if (x == 32767) return error.Whatever;
}
@ -1932,7 +1932,7 @@ pub fn panic(message: []const u8) -> noreturn {
while (true) {}
}
error Whatever;
pub fn main(args: [][]u8) -> %void {
pub fn main() -> %void {
const x = div(-32768, -1);
if (x == 32767) return error.Whatever;
}
@ -1947,7 +1947,7 @@ pub fn panic(message: []const u8) -> noreturn {
while (true) {}
}
error Whatever;
pub fn main(args: [][]u8) -> %void {
pub fn main() -> %void {
const x = shl(-16385, 1);
if (x == 0) return error.Whatever;
}
@ -1962,7 +1962,7 @@ pub fn panic(message: []const u8) -> noreturn {
while (true) {}
}
error Whatever;
pub fn main(args: [][]u8) -> %void {
pub fn main() -> %void {
const x = shl(0b0010111111111111, 3);
if (x == 0) return error.Whatever;
}
@ -1977,7 +1977,7 @@ pub fn panic(message: []const u8) -> noreturn {
while (true) {}
}
error Whatever;
pub fn main(args: [][]u8) -> %void {
pub fn main() -> %void {
const x = div0(999, 0);
}
fn div0(a: i32, b: i32) -> i32 {
@ -1991,7 +1991,7 @@ pub fn panic(message: []const u8) -> noreturn {
while (true) {}
}
error Whatever;
pub fn main(args: [][]u8) -> %void {
pub fn main() -> %void {
const x = divExact(10, 3);
if (x == 0) return error.Whatever;
}
@ -2006,7 +2006,7 @@ pub fn panic(message: []const u8) -> noreturn {
while (true) {}
}
error Whatever;
pub fn main(args: [][]u8) -> %void {
pub fn main() -> %void {
const x = widenSlice([]u8{1, 2, 3, 4, 5});
if (x.len == 0) return error.Whatever;
}
@ -2021,7 +2021,7 @@ pub fn panic(message: []const u8) -> noreturn {
while (true) {}
}
error Whatever;
pub fn main(args: [][]u8) -> %void {
pub fn main() -> %void {
const x = shorten_cast(200);
if (x == 0) return error.Whatever;
}
@ -2036,7 +2036,7 @@ pub fn panic(message: []const u8) -> noreturn {
while (true) {}
}
error Whatever;
pub fn main(args: [][]u8) -> %void {
pub fn main() -> %void {
const x = unsigned_cast(-10);
if (x == 0) return error.Whatever;
}
@ -2051,7 +2051,7 @@ pub fn panic(message: []const u8) -> noreturn {
while (true) {}
}
error Whatever;
pub fn main(args: [][]u8) -> %void {
pub fn main() -> %void {
%%bar();
}
fn bar() -> %void {
@ -2064,7 +2064,7 @@ pub fn panic(message: []const u8) -> noreturn {
@breakpoint();
while (true) {}
}
pub fn main(args: [][]u8) -> %void {
pub fn main() -> %void {
_ = bar(9999);
}
fn bar(x: u32) -> error {
@ -2500,25 +2500,13 @@ static void run_test(TestCase *test_case) {
}
}
static void run_all_tests(bool reverse) {
if (reverse) {
for (size_t i = test_cases.length;;) {
TestCase *test_case = test_cases.at(i);
printf("Test %zu/%zu %s...", i + 1, test_cases.length, test_case->case_name);
fflush(stdout);
run_test(test_case);
printf("OK\n");
if (i == 0) break;
i -= 1;
}
} else {
for (size_t i = 0; i < test_cases.length; i += 1) {
TestCase *test_case = test_cases.at(i);
printf("Test %zu/%zu %s...", i + 1, test_cases.length, test_case->case_name);
fflush(stdout);
run_test(test_case);
printf("OK\n");
}
static void run_all_tests(void) {
for (size_t i = 0; i < test_cases.length; i += 1) {
TestCase *test_case = test_cases.at(i);
printf("Test %zu/%zu %s...", i + 1, test_cases.length, test_case->case_name);
fflush(stdout);
run_test(test_case);
printf("OK\n");
}
printf("%zu tests passed.\n", test_cases.length);
}
@ -2530,18 +2518,13 @@ static void cleanup(void) {
}
static int usage(const char *arg0) {
fprintf(stderr, "Usage: %s [--reverse]\n", arg0);
fprintf(stderr, "Usage: %s\n", arg0);
return 1;
}
int main(int argc, char **argv) {
bool reverse = false;
for (int i = 1; i < argc; i += 1) {
if (strcmp(argv[i], "--reverse") == 0) {
reverse = true;
} else {
return usage(argv[0]);
}
return usage(argv[0]);
}
add_compiling_test_cases();
add_debug_safety_test_cases();
@ -2549,6 +2532,6 @@ int main(int argc, char **argv) {
add_parseh_test_cases();
add_self_hosted_tests();
add_std_lib_tests();
run_all_tests(reverse);
run_all_tests();
cleanup();
}