From c400cb429abf2980962739731f5b64861a54ab61 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 3 Apr 2017 04:58:19 -0400 Subject: [PATCH] zig build system: add setLinkerScript and setTarget * See #204 - zig build system * allow builtin types Os, Environ, Arch to be used at runtime. they have zero_bits=false and debug info now. * Eradicate use of `@alloca` in std for syscalls. See #225 * add `std.io.writeFile` * `std.debug.panic` adds a newline to format --- src/all_types.hpp | 1 + src/codegen.cpp | 53 ++++++---- std/build.zig | 202 ++++++++++++++++++++++++++++++++++++++ std/debug.zig | 6 +- std/io.zig | 142 ++++++++++++--------------- std/os/darwin.zig | 9 +- std/os/index.zig | 78 ++++++++++++--- std/os/linux.zig | 27 +---- std/special/bootstrap.zig | 2 + std/special/zigrt.zig | 2 +- 10 files changed, 374 insertions(+), 148 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 5d8a823e752b..da05a32d1151 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -944,6 +944,7 @@ struct TypeTableEntryEnum { AstNode *decl_node; ContainerLayout layout; uint32_t src_field_count; + // number of fields in the union. 0 if enum with no payload uint32_t gen_field_count; TypeEnumField *fields; bool is_invalid; // true if any fields are invalid diff --git a/src/codegen.cpp b/src/codegen.cpp index ed4b0e0df298..4a26b538c965 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3802,6 +3802,35 @@ static const GlobalLinkageValue global_linkage_values[] = { {GlobalLinkageIdLinkOnce, "LinkOnce"}, }; +static void init_enum_debug_info(CodeGen *g, TypeTableEntry *enum_type) { + uint32_t field_count = enum_type->data.enumeration.src_field_count; + + TypeTableEntry *tag_type_entry = get_smallest_unsigned_int_type(g, field_count); + enum_type->data.enumeration.tag_type = tag_type_entry; + + ZigLLVMDIEnumerator **di_enumerators = allocate(field_count); + for (uint32_t i = 0; i < field_count; i += 1) { + TypeEnumField *field = &enum_type->data.enumeration.fields[i]; + di_enumerators[i] = ZigLLVMCreateDebugEnumerator(g->dbuilder, buf_ptr(field->name), i); + } + + // create debug type for tag + uint64_t tag_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, tag_type_entry->type_ref); + uint64_t tag_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, tag_type_entry->type_ref); + enum_type->di_type = ZigLLVMCreateDebugEnumerationType(g->dbuilder, + nullptr, buf_ptr(&enum_type->name), + nullptr, 0, + tag_debug_size_in_bits, + tag_debug_align_in_bits, + di_enumerators, field_count, + tag_type_entry->di_type, ""); + + enum_type->type_ref = tag_type_entry->type_ref; + + enum_type->data.enumeration.complete = true; + enum_type->data.enumeration.zero_bits_known = true; +} + static void define_builtin_types(CodeGen *g) { { // if this type is anywhere in the AST, we should never hit codegen. @@ -4022,7 +4051,6 @@ static void define_builtin_types(CodeGen *g) { { TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdEnum); - entry->zero_bits = true; // only allowed at compile time buf_init_from_str(&entry->name, "Os"); uint32_t field_count = target_os_count(); entry->data.enumeration.src_field_count = field_count; @@ -4038,11 +4066,8 @@ static void define_builtin_types(CodeGen *g) { g->target_os_index = i; } } - entry->data.enumeration.complete = true; - entry->data.enumeration.zero_bits_known = true; - TypeTableEntry *tag_type_entry = get_smallest_unsigned_int_type(g, field_count); - entry->data.enumeration.tag_type = tag_type_entry; + init_enum_debug_info(g, entry); g->builtin_types.entry_os_enum = entry; g->primitive_type_table.put(&entry->name, entry); @@ -4050,7 +4075,6 @@ static void define_builtin_types(CodeGen *g) { { TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdEnum); - entry->zero_bits = true; // only allowed at compile time buf_init_from_str(&entry->name, "Arch"); uint32_t field_count = target_arch_count(); entry->data.enumeration.src_field_count = field_count; @@ -4072,11 +4096,8 @@ static void define_builtin_types(CodeGen *g) { g->target_arch_index = i; } } - entry->data.enumeration.complete = true; - entry->data.enumeration.zero_bits_known = true; - TypeTableEntry *tag_type_entry = get_smallest_unsigned_int_type(g, field_count); - entry->data.enumeration.tag_type = tag_type_entry; + init_enum_debug_info(g, entry); g->builtin_types.entry_arch_enum = entry; g->primitive_type_table.put(&entry->name, entry); @@ -4084,7 +4105,6 @@ static void define_builtin_types(CodeGen *g) { { TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdEnum); - entry->zero_bits = true; // only allowed at compile time buf_init_from_str(&entry->name, "Environ"); uint32_t field_count = target_environ_count(); entry->data.enumeration.src_field_count = field_count; @@ -4100,11 +4120,8 @@ static void define_builtin_types(CodeGen *g) { g->target_environ_index = i; } } - entry->data.enumeration.complete = true; - entry->data.enumeration.zero_bits_known = true; - TypeTableEntry *tag_type_entry = get_smallest_unsigned_int_type(g, field_count); - entry->data.enumeration.tag_type = tag_type_entry; + init_enum_debug_info(g, entry); g->builtin_types.entry_environ_enum = entry; g->primitive_type_table.put(&entry->name, entry); @@ -4112,7 +4129,6 @@ static void define_builtin_types(CodeGen *g) { { TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdEnum); - entry->zero_bits = true; // only allowed at compile time buf_init_from_str(&entry->name, "ObjectFormat"); uint32_t field_count = target_oformat_count(); entry->data.enumeration.src_field_count = field_count; @@ -4128,11 +4144,8 @@ static void define_builtin_types(CodeGen *g) { g->target_oformat_index = i; } } - entry->data.enumeration.complete = true; - entry->data.enumeration.zero_bits_known = true; - TypeTableEntry *tag_type_entry = get_smallest_unsigned_int_type(g, field_count); - entry->data.enumeration.tag_type = tag_type_entry; + init_enum_debug_info(g, entry); g->builtin_types.entry_oformat_enum = entry; g->primitive_type_table.put(&entry->name, entry); diff --git a/std/build.zig b/std/build.zig index d36879644d5b..be07e9ebec1e 100644 --- a/std/build.zig +++ b/std/build.zig @@ -32,6 +32,8 @@ pub const Builder = struct { *exe = Exe { .root_src = root_src, .name = name, + .target = Target.Native, + .linker_script = LinkerScript.None, }; %return self.exe_list.append(exe); return exe; @@ -53,9 +55,43 @@ pub const Builder = struct { %return zig_args.append("build_exe"[0...]); // TODO issue #296 %return zig_args.append(exe.root_src); + + if (verbose) { + %return zig_args.append("--verbose"[0...]); // TODO issue #296 + } + %return zig_args.append("--name"[0...]); // TODO issue #296 %return zig_args.append(exe.name); + switch (exe.target) { + Target.Native => {}, + Target.Cross => |cross_target| { + %return zig_args.append("--target-arch"[0...]); // TODO issue #296 + %return zig_args.append(targetArchName(cross_target.arch)); + + %return zig_args.append("--target-os"[0...]); // TODO issue #296 + %return zig_args.append(targetOsName(cross_target.os)); + + %return zig_args.append("--target-environ"[0...]); // TODO issue #296 + %return zig_args.append(targetEnvironName(cross_target.environ)); + }, + } + + switch (exe.linker_script) { + LinkerScript.None => {}, + LinkerScript.Embed => |script| { + const tmp_file_name = "linker.ld.tmp"; // TODO issue #298 + io.writeFile(tmp_file_name, script, self.allocator) + %% |err| debug.panic("unable to write linker script: {}\n", @errorName(err)); + %return zig_args.append("--linker-script"[0...]); // TODO issue #296 + %return zig_args.append(tmp_file_name[0...]); // TODO issue #296 + }, + LinkerScript.Path => |path| { + %return zig_args.append("--linker-script"[0...]); // TODO issue #296 + %return zig_args.append(path); + }, + } + printInvocation(self.zig_exe, zig_args); var child = %return os.ChildProcess.spawn(self.zig_exe, zig_args.toSliceConst(), os.environ, StdIo.Ignore, StdIo.Inherit, StdIo.Inherit, self.allocator); @@ -72,9 +108,48 @@ pub const Builder = struct { } }; +const CrossTarget = struct { + arch: Arch, + os: Os, + environ: Environ, +}; + +const Target = enum { + Native, + Cross: CrossTarget, +}; + +const LinkerScript = enum { + None, + Embed: []const u8, + Path: []const u8, +}; + const Exe = struct { root_src: []const u8, name: []const u8, + target: Target, + linker_script: LinkerScript, + + fn setTarget(self: &Exe, target_arch: Arch, target_os: Os, target_environ: Environ) { + self.target = Target.Cross { + CrossTarget { + .arch = target_arch, + .os = target_os, + .environ = target_environ, + } + }; + } + + /// Exe keeps a reference to script for its lifetime or until this function + /// is called again. + fn setLinkerScriptContents(self: &Exe, script: []const u8) { + self.linker_script = LinkerScript.Embed { script }; + } + + fn setLinkerScriptPath(self: &Exe, path: []const u8) { + self.linker_script = LinkerScript.Path { path }; + } }; fn handleErr(err: error) -> noreturn { @@ -88,3 +163,130 @@ fn printInvocation(exe_name: []const u8, args: &const List([]const u8)) { } %%io.stderr.printf("\n"); } + +// TODO issue #299 +fn targetOsName(target_os: Os) -> []const u8 { + return switch (target_os) { + Os.freestanding => ([]const u8)("freestanding"), + Os.cloudabi => ([]const u8)("cloudabi"), + Os.darwin => ([]const u8)("darwin"), + Os.dragonfly => ([]const u8)("dragonfly"), + Os.freebsd => ([]const u8)("freebsd"), + Os.ios => ([]const u8)("ios"), + Os.kfreebsd => ([]const u8)("kfreebsd"), + Os.linux => ([]const u8)("linux"), + Os.lv2 => ([]const u8)("lv2"), + Os.macosx => ([]const u8)("macosx"), + Os.netbsd => ([]const u8)("netbsd"), + Os.openbsd => ([]const u8)("openbsd"), + Os.solaris => ([]const u8)("solaris"), + Os.windows => ([]const u8)("windows"), + Os.haiku => ([]const u8)("haiku"), + Os.minix => ([]const u8)("minix"), + Os.rtems => ([]const u8)("rtems"), + Os.nacl => ([]const u8)("nacl"), + Os.cnk => ([]const u8)("cnk"), + Os.bitrig => ([]const u8)("bitrig"), + Os.aix => ([]const u8)("aix"), + Os.cuda => ([]const u8)("cuda"), + Os.nvcl => ([]const u8)("nvcl"), + Os.amdhsa => ([]const u8)("amdhsa"), + Os.ps4 => ([]const u8)("ps4"), + Os.elfiamcu => ([]const u8)("elfiamcu"), + Os.tvos => ([]const u8)("tvos"), + Os.watchos => ([]const u8)("watchos"), + Os.mesa3d => ([]const u8)("mesa3d"), + }; +} + +// TODO issue #299 +fn targetArchName(target_arch: Arch) -> []const u8 { + return switch (target_arch) { + Arch.armv8_2a => ([]const u8)("armv8_2a"), + Arch.armv8_1a => ([]const u8)("armv8_1a"), + Arch.armv8 => ([]const u8)("armv8"), + Arch.armv8m_baseline => ([]const u8)("armv8m_baseline"), + Arch.armv8m_mainline => ([]const u8)("armv8m_mainline"), + Arch.armv7 => ([]const u8)("armv7"), + Arch.armv7em => ([]const u8)("armv7em"), + Arch.armv7m => ([]const u8)("armv7m"), + Arch.armv7s => ([]const u8)("armv7s"), + Arch.armv7k => ([]const u8)("armv7k"), + Arch.armv6 => ([]const u8)("armv6"), + Arch.armv6m => ([]const u8)("armv6m"), + Arch.armv6k => ([]const u8)("armv6k"), + Arch.armv6t2 => ([]const u8)("armv6t2"), + Arch.armv5 => ([]const u8)("armv5"), + Arch.armv5te => ([]const u8)("armv5te"), + Arch.armv4t => ([]const u8)("armv4t"), + Arch.armeb => ([]const u8)("armeb"), + Arch.aarch64 => ([]const u8)("aarch64"), + Arch.aarch64_be => ([]const u8)("aarch64_be"), + Arch.avr => ([]const u8)("avr"), + Arch.bpfel => ([]const u8)("bpfel"), + Arch.bpfeb => ([]const u8)("bpfeb"), + Arch.hexagon => ([]const u8)("hexagon"), + Arch.mips => ([]const u8)("mips"), + Arch.mipsel => ([]const u8)("mipsel"), + Arch.mips64 => ([]const u8)("mips64"), + Arch.mips64el => ([]const u8)("mips64el"), + Arch.msp430 => ([]const u8)("msp430"), + Arch.powerpc => ([]const u8)("powerpc"), + Arch.powerpc64 => ([]const u8)("powerpc64"), + Arch.powerpc64le => ([]const u8)("powerpc64le"), + Arch.r600 => ([]const u8)("r600"), + Arch.amdgcn => ([]const u8)("amdgcn"), + Arch.sparc => ([]const u8)("sparc"), + Arch.sparcv9 => ([]const u8)("sparcv9"), + Arch.sparcel => ([]const u8)("sparcel"), + Arch.s390x => ([]const u8)("s390x"), + Arch.tce => ([]const u8)("tce"), + Arch.thumb => ([]const u8)("thumb"), + Arch.thumbeb => ([]const u8)("thumbeb"), + Arch.i386 => ([]const u8)("i386"), + Arch.x86_64 => ([]const u8)("x86_64"), + Arch.xcore => ([]const u8)("xcore"), + Arch.nvptx => ([]const u8)("nvptx"), + Arch.nvptx64 => ([]const u8)("nvptx64"), + Arch.le32 => ([]const u8)("le32"), + Arch.le64 => ([]const u8)("le64"), + Arch.amdil => ([]const u8)("amdil"), + Arch.amdil64 => ([]const u8)("amdil64"), + Arch.hsail => ([]const u8)("hsail"), + Arch.hsail64 => ([]const u8)("hsail64"), + Arch.spir => ([]const u8)("spir"), + Arch.spir64 => ([]const u8)("spir64"), + Arch.kalimbav3 => ([]const u8)("kalimbav3"), + Arch.kalimbav4 => ([]const u8)("kalimbav4"), + Arch.kalimbav5 => ([]const u8)("kalimbav5"), + Arch.shave => ([]const u8)("shave"), + Arch.lanai => ([]const u8)("lanai"), + Arch.wasm32 => ([]const u8)("wasm32"), + Arch.wasm64 => ([]const u8)("wasm64"), + Arch.renderscript32 => ([]const u8)("renderscript32"), + Arch.renderscript64 => ([]const u8)("renderscript64"), + }; +} + +// TODO issue #299 +fn targetEnvironName(target_environ: Environ) -> []const u8 { + return switch (target_environ) { + Environ.gnu => ([]const u8)("gnu"), + Environ.gnuabi64 => ([]const u8)("gnuabi64"), + Environ.gnueabi => ([]const u8)("gnueabi"), + Environ.gnueabihf => ([]const u8)("gnueabihf"), + Environ.gnux32 => ([]const u8)("gnux32"), + Environ.code16 => ([]const u8)("code16"), + Environ.eabi => ([]const u8)("eabi"), + Environ.eabihf => ([]const u8)("eabihf"), + Environ.android => ([]const u8)("android"), + Environ.musl => ([]const u8)("musl"), + Environ.musleabi => ([]const u8)("musleabi"), + Environ.musleabihf => ([]const u8)("musleabihf"), + Environ.msvc => ([]const u8)("msvc"), + Environ.itanium => ([]const u8)("itanium"), + Environ.cygnus => ([]const u8)("cygnus"), + Environ.amdopencl => ([]const u8)("amdopencl"), + Environ.coreclr => ([]const u8)("coreclr"), + }; +} diff --git a/std/debug.zig b/std/debug.zig index 7112ad0da2a5..40775eba8dd2 100644 --- a/std/debug.zig +++ b/std/debug.zig @@ -28,7 +28,7 @@ pub coldcc fn panic(comptime format: []const u8, args: ...) -> noreturn { panicking = true; } - %%io.stderr.printf(format, args); + %%io.stderr.printf(format ++ "\n", args); %%printStackTrace(); os.abort(); @@ -52,8 +52,8 @@ pub fn writeStackTrace(out_stream: &io.OutStream) -> %void { .compile_unit_list = List(CompileUnit).init(&global_allocator), }; const st = &stack_trace; - %return io.openSelfExe(&st.self_exe_stream); - defer st.self_exe_stream.close() %% {}; + st.self_exe_stream = %return io.openSelfExe(); + defer st.self_exe_stream.close(); %return st.elf.openStream(&global_allocator, &st.self_exe_stream); defer st.elf.close(); diff --git a/std/io.zig b/std/io.zig index 89b3a8c21f4e..03960867b507 100644 --- a/std/io.zig +++ b/std/io.zig @@ -69,6 +69,27 @@ pub const OutStream = struct { buffer: [buffer_size]u8, index: usize, + /// `path` may need to be copied in memory to add a null terminating byte. In this case + /// a fixed size buffer of size std.os.max_noalloc_path_len is an attempted solution. If the fixed + /// size buffer is too small, and the provided allocator is null, error.NameTooLong is returned. + /// otherwise if the fixed size buffer is too small, allocator is used to obtain the needed memory. + /// Call close to clean up. + pub fn open(path: []const u8, allocator: ?&mem.Allocator) -> %OutStream { + switch (@compileVar("os")) { + Os.linux, Os.darwin, Os.macosx, Os.ios => { + const flags = system.O_LARGEFILE|system.O_WRONLY|system.O_CREAT|system.O_CLOEXEC|system.O_TRUNC; + const fd = %return os.posixOpen(path, flags, 0o666, allocator); + return OutStream { + .fd = fd, + .index = 0, + .buffer = undefined, + }; + }, + else => @compileError("Unsupported OS"), + } + + } + pub fn writeByte(self: &OutStream, b: u8) -> %void { if (self.buffer.len == self.index) %return self.flush(); self.buffer[self.index] = b; @@ -76,6 +97,11 @@ pub const OutStream = struct { } pub fn write(self: &OutStream, bytes: []const u8) -> %void { + if (bytes.len >= buffer_size) { + %return self.flush(); + return os.posixWrite(self.fd, bytes); + } + var src_index: usize = 0; while (src_index < bytes.len) { @@ -119,35 +145,15 @@ pub const OutStream = struct { } pub fn flush(self: &OutStream) -> %void { - while (true) { - const write_ret = system.write(self.fd, &self.buffer[0], self.index); - const write_err = system.getErrno(write_ret); - if (write_err > 0) { - return switch (write_err) { - errno.EINTR => continue, - errno.EINVAL => unreachable, - errno.EDQUOT => error.DiskQuota, - errno.EFBIG => error.FileTooBig, - errno.EIO => error.Io, - errno.ENOSPC => error.NoSpaceLeft, - errno.EPERM => error.BadPerm, - errno.EPIPE => error.PipeFail, - else => error.Unexpected, - } - } + if (self.index != 0) { + %return os.posixWrite(self.fd, self.buffer[0...self.index]); self.index = 0; - return; } } pub fn close(self: &OutStream) { - while (true) { - const close_ret = system.close(self.fd); - const close_err = system.getErrno(close_ret); - if (close_err > 0 and close_err == errno.EINTR) - continue; - return; - } + assert(self.index == 0); + os.posixClose(self.fd); } }; @@ -156,65 +162,32 @@ pub const OutStream = struct { pub const InStream = struct { fd: i32, + /// `path` may need to be copied in memory to add a null terminating byte. In this case + /// a fixed size buffer of size std.os.max_noalloc_path_len is an attempted solution. If the fixed + /// size buffer is too small, and the provided allocator is null, error.NameTooLong is returned. + /// otherwise if the fixed size buffer is too small, allocator is used to obtain the needed memory. /// Call close to clean up. - pub fn open(is: &InStream, path: []const u8) -> %void { + pub fn open(path: []const u8, allocator: ?&mem.Allocator) -> %InStream { switch (@compileVar("os")) { - Os.linux, Os.darwin => { - while (true) { - const result = system.open(path, system.O_LARGEFILE|system.O_RDONLY, 0); - const err = system.getErrno(result); - if (err > 0) { - return switch (err) { - errno.EINTR => continue, - - errno.EFAULT => unreachable, - errno.EINVAL => unreachable, - errno.EACCES => error.BadPerm, - errno.EFBIG, errno.EOVERFLOW => error.FileTooBig, - errno.EISDIR => error.IsDir, - errno.ELOOP => error.SymLinkLoop, - errno.EMFILE => error.ProcessFdQuotaExceeded, - errno.ENAMETOOLONG => error.NameTooLong, - errno.ENFILE => error.SystemFdQuotaExceeded, - errno.ENODEV => error.NoDevice, - errno.ENOENT => error.PathNotFound, - errno.ENOMEM => error.NoMem, - errno.ENOSPC => error.NoSpaceLeft, - errno.ENOTDIR => error.NotDir, - errno.EPERM => error.BadPerm, - else => error.Unexpected, - } - } - is.fd = i32(result); - return; - } + Os.linux, Os.darwin, Os.macosx, Os.ios => { + const flags = system.O_LARGEFILE|system.O_RDONLY; + const fd = %return os.posixOpen(path, flags, 0, allocator); + return InStream { + .fd = fd, + }; }, - else => @compileError("unsupported OS"), + else => @compileError("Unsupported OS"), } - } /// Upon success, the stream is in an uninitialized state. To continue using it, /// you must use the open() function. - pub fn close(is: &InStream) -> %void { + pub fn close(self: &InStream) { switch (@compileVar("os")) { - Os.linux, Os.darwin => { - while (true) { - const close_ret = system.close(is.fd); - const close_err = system.getErrno(close_ret); - if (close_err > 0) { - return switch (close_err) { - errno.EINTR => continue, - - errno.EIO => error.Io, - errno.EBADF => error.BadFd, - else => error.Unexpected, - } - } - return; - } + Os.linux, Os.darwin, Os.macosx, Os.ios => { + os.posixClose(self.fd); }, - else => @compileError("unsupported OS"), + else => @compileError("Unsupported OS"), } } @@ -373,15 +346,28 @@ pub const InStream = struct { } }; -pub fn openSelfExe(stream: &InStream) -> %void { +pub fn openSelfExe() -> %InStream { switch (@compileVar("os")) { Os.linux => { - %return stream.open("/proc/self/exe"); + return InStream.open("/proc/self/exe", null); }, Os.darwin => { - %%stderr.printf("TODO: openSelfExe on Darwin\n"); - os.abort(); + debug.panic("TODO: openSelfExe on Darwin"); }, - else => @compileError("unsupported os"), + else => @compileError("Unsupported OS"), } } + +/// `path` may need to be copied in memory to add a null terminating byte. In this case +/// a fixed size buffer of size std.os.max_noalloc_path_len is an attempted solution. If the fixed +/// size buffer is too small, and the provided allocator is null, error.NameTooLong is returned. +/// otherwise if the fixed size buffer is too small, allocator is used to obtain the needed memory. +pub fn writeFile(path: []const u8, data: []const u8, allocator: ?&mem.Allocator) -> %void { + // TODO have an unbuffered File abstraction and use that here. + // Then a buffered out stream abstraction can go on top of that for + // use cases like stdout and stderr. + var out_stream = %return OutStream.open(path, allocator); + defer out_stream.close(); + %return out_stream.write(data); + %return out_stream.flush(); +} diff --git a/std/os/darwin.zig b/std/os/darwin.zig index fca34960bf65..564fee93a690 100644 --- a/std/os/darwin.zig +++ b/std/os/darwin.zig @@ -71,17 +71,10 @@ pub fn close(fd: i32) -> usize { arch.syscall1(arch.SYS_close, usize(fd)) } -pub fn open_c(path: &const u8, flags: usize, perm: usize) -> usize { +pub fn open(path: &const u8, flags: usize, perm: usize) -> usize { arch.syscall3(arch.SYS_open, usize(path), flags, perm) } -pub fn open(path: []const u8, flags: usize, perm: usize) -> usize { - const buf = @alloca(u8, path.len + 1); - @memcpy(&buf[0], &path[0], path.len); - buf[path.len] = 0; - return open_c(buf.ptr, flags, perm); -} - pub fn read(fd: i32, buf: &u8, count: usize) -> usize { arch.syscall3(arch.SYS_read, usize(fd), usize(buf), count) } diff --git a/std/os/index.zig b/std/os/index.zig index 6b598f99a4d7..ba991e958b18 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -8,6 +8,8 @@ pub const posix = switch(@compileVar("os")) { else => @compileError("Unsupported OS"), }; +pub const max_noalloc_path_len = 1024; + const debug = @import("../debug.zig"); const assert = debug.assert; @@ -105,11 +107,12 @@ fn makePipe() -> %[2]i32 { } fn destroyPipe(pipe: &const [2]i32) { - closeNoIntr((*pipe)[0]); - closeNoIntr((*pipe)[1]); + posixClose((*pipe)[0]); + posixClose((*pipe)[1]); } -fn closeNoIntr(fd: i32) { +/// Calls POSIX close, and keeps trying if it gets interrupted. +pub fn posixClose(fd: i32) { while (true) { const err = posix.getErrno(posix.close(fd)); if (err == errno.EINTR) { @@ -120,9 +123,56 @@ fn closeNoIntr(fd: i32) { } } -fn openNoIntr(path: []const u8, flags: usize, perm: usize) -> %i32 { +/// Calls POSIX write, and keeps trying if it gets interrupted. +pub fn posixWrite(fd: i32, bytes: []const u8) -> %void { + while (true) { + const write_ret = posix.write(fd, bytes.ptr, bytes.len); + const write_err = posix.getErrno(write_ret); + if (write_err > 0) { + return switch (write_err) { + errno.EINTR => continue, + errno.EINVAL => unreachable, + errno.EDQUOT => error.DiskQuota, + errno.EFBIG => error.FileTooBig, + errno.EIO => error.Io, + errno.ENOSPC => error.NoSpaceLeft, + errno.EPERM => error.BadPerm, + errno.EPIPE => error.PipeFail, + else => error.Unexpected, + } + } + return; + } +} + + +/// ::path may need to be copied in memory to add a null terminating byte. In this case +/// a fixed size buffer of size ::max_noalloc_path_len is an attempted solution. If the fixed +/// size buffer is too small, and the provided allocator is null, ::error.NameTooLong is returned. +/// otherwise if the fixed size buffer is too small, allocator is used to obtain the needed memory. +/// Calls POSIX open, keeps trying if it gets interrupted, and translates +/// the return value into zig errors. +pub fn posixOpen(path: []const u8, flags: usize, perm: usize, allocator: ?&Allocator) -> %i32 { + var stack_buf: [max_noalloc_path_len]u8 = undefined; + var path0: []u8 = undefined; + var need_free = false; + + if (path.len < stack_buf.len) { + path0 = stack_buf[0...path.len + 1]; + } else if (const a ?= allocator) { + path0 = %return a.alloc(u8, path.len + 1); + need_free = true; + } else { + return error.NameTooLong; + } + defer if (need_free) { + (??allocator).free(path0); + }; + mem.copy(u8, path0, path); + path0[path.len] = 0; + while (true) { - const result = posix.open(path, flags, perm); + const result = posix.open(path0.ptr, flags, perm); const err = posix.getErrno(result); if (err > 0) { return switch (err) { @@ -247,8 +297,8 @@ pub const ChildProcess = struct { pub fn wait(self: &ChildProcess) -> %Term { defer { - closeNoIntr(self.err_pipe[0]); - closeNoIntr(self.err_pipe[1]); + posixClose(self.err_pipe[0]); + posixClose(self.err_pipe[1]); }; var status: i32 = undefined; @@ -328,13 +378,13 @@ pub const ChildProcess = struct { 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 openNoIntr("/dev/null", posix.O_RDWR, 0) + // %return posixOpen("/dev/null", posix.O_RDWR, 0, null) //} else { // undefined //}; var dev_null_fd: i32 = undefined; if (any_ignore) - dev_null_fd = %return openNoIntr("/dev/null", posix.O_RDWR, 0); + 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. @@ -374,10 +424,10 @@ pub const ChildProcess = struct { } // we are the parent - if (stdin == StdIo.Pipe) { closeNoIntr(stdin_pipe[0]); } - if (stdout == StdIo.Pipe) { closeNoIntr(stdout_pipe[1]); } - if (stderr == StdIo.Pipe) { closeNoIntr(stderr_pipe[1]); } - if (any_ignore) { closeNoIntr(dev_null_fd); } + 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), @@ -412,7 +462,7 @@ pub const ChildProcess = struct { 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 => closeNoIntr(std_fileno), + StdIo.Close => posixClose(std_fileno), StdIo.Inherit => {}, StdIo.Ignore => %return dup2NoIntr(dev_null_fd, std_fileno), } diff --git a/std/os/linux.zig b/std/os/linux.zig index 69a66505c58c..3a55272862e5 100644 --- a/std/os/linux.zig +++ b/std/os/linux.zig @@ -303,39 +303,18 @@ pub fn pwrite(fd: i32, buf: &const u8, count: usize, offset: usize) -> usize { arch.syscall4(arch.SYS_pwrite, usize(fd), usize(buf), count, offset) } -pub fn open_c(path: &const u8, flags: usize, perm: usize) -> usize { +pub fn open(path: &const u8, flags: usize, perm: usize) -> usize { arch.syscall3(arch.SYS_open, usize(path), flags, perm) } -pub fn open(path: []const u8, flags: usize, perm: usize) -> usize { - const buf = @alloca(u8, path.len + 1); - @memcpy(&buf[0], &path[0], path.len); - buf[path.len] = 0; - return open_c(buf.ptr, flags, perm); -} - -pub fn create_c(path: &const u8, perm: usize) -> usize { +pub fn create(path: &const u8, perm: usize) -> usize { arch.syscall2(arch.SYS_creat, usize(path), perm) } -pub fn create(path: []const u8, perm: usize) -> usize { - const buf = @alloca(u8, path.len + 1); - @memcpy(&buf[0], &path[0], path.len); - buf[path.len] = 0; - return create_c(buf.ptr, perm); -} - -pub fn openat_c(dirfd: i32, path: &const u8, flags: usize, mode: usize) -> usize { +pub fn openat(dirfd: i32, path: &const u8, flags: usize, mode: usize) -> usize { arch.syscall4(arch.SYS_openat, usize(dirfd), usize(path), flags, mode) } -pub fn openat(dirfd: i32, path: []const u8, flags: usize, mode: usize) -> usize { - const buf = @alloca(u8, path.len + 1); - @memcpy(&buf[0], &path[0], path.len); - buf[path.len] = 0; - return openat_c(dirfd, buf.ptr, flags, mode); -} - pub fn close(fd: i32) -> usize { arch.syscall1(arch.SYS_close, usize(fd)) } diff --git a/std/special/bootstrap.zig b/std/special/bootstrap.zig index 0705fbb29685..eea04ba393de 100644 --- a/std/special/bootstrap.zig +++ b/std/special/bootstrap.zig @@ -33,6 +33,7 @@ export nakedcc fn _start() -> noreturn { } fn callMain(envp: &?&u8) -> %void { + // TODO issue #225 const args = @alloca([]u8, argc); for (args) |_, i| { const ptr = argv[i]; @@ -41,6 +42,7 @@ fn callMain(envp: &?&u8) -> %void { var env_count: usize = 0; while (envp[env_count] != null; env_count += 1) {} + // TODO issue #225 const environ = @alloca(std.os.EnvPair, env_count); for (environ) |_, env_i| { const ptr = ??envp[env_i]; diff --git a/std/special/zigrt.zig b/std/special/zigrt.zig index 6f7911917d6c..0578d5423e8a 100644 --- a/std/special/zigrt.zig +++ b/std/special/zigrt.zig @@ -11,6 +11,6 @@ export coldcc fn __zig_panic(message_ptr: &const u8, message_len: usize) -> nore } else if (@compileVar("os") == Os.freestanding) { while (true) {} } else { - @import("std").debug.panic("{}\n", message_ptr[0...message_len]); + @import("std").debug.panic("{}", message_ptr[0...message_len]); } }