| author | |
| committer | |
| log | c400cb429abf2980962739731f5b64861a54ab61 |
| tree | afb244401d3aa18547ea7e6a5bf5fbe141472cee |
| parent | a1363b52278b68f59aa72adf985f11e6c1d02cf9 |
* 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 format10 files changed, 374 insertions(+), 148 deletions(-)
src/all_types.hpp+1| ... | ... | @@ -944,6 +944,7 @@ struct TypeTableEntryEnum { |
| 944 | 944 | AstNode *decl_node; |
| 945 | 945 | ContainerLayout layout; |
| 946 | 946 | uint32_t src_field_count; |
| 947 | // number of fields in the union. 0 if enum with no payload | |
| 947 | 948 | uint32_t gen_field_count; |
| 948 | 949 | TypeEnumField *fields; |
| 949 | 950 | bool is_invalid; // true if any fields are invalid |
src/codegen.cpp+33-20| ... | ... | @@ -3802,6 +3802,35 @@ static const GlobalLinkageValue global_linkage_values[] = { |
| 3802 | 3802 | {GlobalLinkageIdLinkOnce, "LinkOnce"}, |
| 3803 | 3803 | }; |
| 3804 | 3804 | |
| 3805 | static void init_enum_debug_info(CodeGen *g, TypeTableEntry *enum_type) { | |
| 3806 | uint32_t field_count = enum_type->data.enumeration.src_field_count; | |
| 3807 | ||
| 3808 | TypeTableEntry *tag_type_entry = get_smallest_unsigned_int_type(g, field_count); | |
| 3809 | enum_type->data.enumeration.tag_type = tag_type_entry; | |
| 3810 | ||
| 3811 | ZigLLVMDIEnumerator **di_enumerators = allocate<ZigLLVMDIEnumerator*>(field_count); | |
| 3812 | for (uint32_t i = 0; i < field_count; i += 1) { | |
| 3813 | TypeEnumField *field = &enum_type->data.enumeration.fields[i]; | |
| 3814 | di_enumerators[i] = ZigLLVMCreateDebugEnumerator(g->dbuilder, buf_ptr(field->name), i); | |
| 3815 | } | |
| 3816 | ||
| 3817 | // create debug type for tag | |
| 3818 | uint64_t tag_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, tag_type_entry->type_ref); | |
| 3819 | uint64_t tag_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, tag_type_entry->type_ref); | |
| 3820 | enum_type->di_type = ZigLLVMCreateDebugEnumerationType(g->dbuilder, | |
| 3821 | nullptr, buf_ptr(&enum_type->name), | |
| 3822 | nullptr, 0, | |
| 3823 | tag_debug_size_in_bits, | |
| 3824 | tag_debug_align_in_bits, | |
| 3825 | di_enumerators, field_count, | |
| 3826 | tag_type_entry->di_type, ""); | |
| 3827 | ||
| 3828 | enum_type->type_ref = tag_type_entry->type_ref; | |
| 3829 | ||
| 3830 | enum_type->data.enumeration.complete = true; | |
| 3831 | enum_type->data.enumeration.zero_bits_known = true; | |
| 3832 | } | |
| 3833 | ||
| 3805 | 3834 | static void define_builtin_types(CodeGen *g) { |
| 3806 | 3835 | { |
| 3807 | 3836 | // if this type is anywhere in the AST, we should never hit codegen. |
| ... | ... | @@ -4022,7 +4051,6 @@ static void define_builtin_types(CodeGen *g) { |
| 4022 | 4051 | |
| 4023 | 4052 | { |
| 4024 | 4053 | TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdEnum); |
| 4025 | entry->zero_bits = true; // only allowed at compile time | |
| 4026 | 4054 | buf_init_from_str(&entry->name, "Os"); |
| 4027 | 4055 | uint32_t field_count = target_os_count(); |
| 4028 | 4056 | entry->data.enumeration.src_field_count = field_count; |
| ... | ... | @@ -4038,11 +4066,8 @@ static void define_builtin_types(CodeGen *g) { |
| 4038 | 4066 | g->target_os_index = i; |
| 4039 | 4067 | } |
| 4040 | 4068 | } |
| 4041 | entry->data.enumeration.complete = true; | |
| 4042 | entry->data.enumeration.zero_bits_known = true; | |
| 4043 | 4069 | |
| 4044 | TypeTableEntry *tag_type_entry = get_smallest_unsigned_int_type(g, field_count); | |
| 4045 | entry->data.enumeration.tag_type = tag_type_entry; | |
| 4070 | init_enum_debug_info(g, entry); | |
| 4046 | 4071 | |
| 4047 | 4072 | g->builtin_types.entry_os_enum = entry; |
| 4048 | 4073 | g->primitive_type_table.put(&entry->name, entry); |
| ... | ... | @@ -4050,7 +4075,6 @@ static void define_builtin_types(CodeGen *g) { |
| 4050 | 4075 | |
| 4051 | 4076 | { |
| 4052 | 4077 | TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdEnum); |
| 4053 | entry->zero_bits = true; // only allowed at compile time | |
| 4054 | 4078 | buf_init_from_str(&entry->name, "Arch"); |
| 4055 | 4079 | uint32_t field_count = target_arch_count(); |
| 4056 | 4080 | entry->data.enumeration.src_field_count = field_count; |
| ... | ... | @@ -4072,11 +4096,8 @@ static void define_builtin_types(CodeGen *g) { |
| 4072 | 4096 | g->target_arch_index = i; |
| 4073 | 4097 | } |
| 4074 | 4098 | } |
| 4075 | entry->data.enumeration.complete = true; | |
| 4076 | entry->data.enumeration.zero_bits_known = true; | |
| 4077 | 4099 | |
| 4078 | TypeTableEntry *tag_type_entry = get_smallest_unsigned_int_type(g, field_count); | |
| 4079 | entry->data.enumeration.tag_type = tag_type_entry; | |
| 4100 | init_enum_debug_info(g, entry); | |
| 4080 | 4101 | |
| 4081 | 4102 | g->builtin_types.entry_arch_enum = entry; |
| 4082 | 4103 | g->primitive_type_table.put(&entry->name, entry); |
| ... | ... | @@ -4084,7 +4105,6 @@ static void define_builtin_types(CodeGen *g) { |
| 4084 | 4105 | |
| 4085 | 4106 | { |
| 4086 | 4107 | TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdEnum); |
| 4087 | entry->zero_bits = true; // only allowed at compile time | |
| 4088 | 4108 | buf_init_from_str(&entry->name, "Environ"); |
| 4089 | 4109 | uint32_t field_count = target_environ_count(); |
| 4090 | 4110 | entry->data.enumeration.src_field_count = field_count; |
| ... | ... | @@ -4100,11 +4120,8 @@ static void define_builtin_types(CodeGen *g) { |
| 4100 | 4120 | g->target_environ_index = i; |
| 4101 | 4121 | } |
| 4102 | 4122 | } |
| 4103 | entry->data.enumeration.complete = true; | |
| 4104 | entry->data.enumeration.zero_bits_known = true; | |
| 4105 | 4123 | |
| 4106 | TypeTableEntry *tag_type_entry = get_smallest_unsigned_int_type(g, field_count); | |
| 4107 | entry->data.enumeration.tag_type = tag_type_entry; | |
| 4124 | init_enum_debug_info(g, entry); | |
| 4108 | 4125 | |
| 4109 | 4126 | g->builtin_types.entry_environ_enum = entry; |
| 4110 | 4127 | g->primitive_type_table.put(&entry->name, entry); |
| ... | ... | @@ -4112,7 +4129,6 @@ static void define_builtin_types(CodeGen *g) { |
| 4112 | 4129 | |
| 4113 | 4130 | { |
| 4114 | 4131 | TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdEnum); |
| 4115 | entry->zero_bits = true; // only allowed at compile time | |
| 4116 | 4132 | buf_init_from_str(&entry->name, "ObjectFormat"); |
| 4117 | 4133 | uint32_t field_count = target_oformat_count(); |
| 4118 | 4134 | entry->data.enumeration.src_field_count = field_count; |
| ... | ... | @@ -4128,11 +4144,8 @@ static void define_builtin_types(CodeGen *g) { |
| 4128 | 4144 | g->target_oformat_index = i; |
| 4129 | 4145 | } |
| 4130 | 4146 | } |
| 4131 | entry->data.enumeration.complete = true; | |
| 4132 | entry->data.enumeration.zero_bits_known = true; | |
| 4133 | 4147 | |
| 4134 | TypeTableEntry *tag_type_entry = get_smallest_unsigned_int_type(g, field_count); | |
| 4135 | entry->data.enumeration.tag_type = tag_type_entry; | |
| 4148 | init_enum_debug_info(g, entry); | |
| 4136 | 4149 | |
| 4137 | 4150 | g->builtin_types.entry_oformat_enum = entry; |
| 4138 | 4151 | g->primitive_type_table.put(&entry->name, entry); |
std/build.zig+202| ... | ... | @@ -32,6 +32,8 @@ pub const Builder = struct { |
| 32 | 32 | *exe = Exe { |
| 33 | 33 | .root_src = root_src, |
| 34 | 34 | .name = name, |
| 35 | .target = Target.Native, | |
| 36 | .linker_script = LinkerScript.None, | |
| 35 | 37 | }; |
| 36 | 38 | %return self.exe_list.append(exe); |
| 37 | 39 | return exe; |
| ... | ... | @@ -53,9 +55,43 @@ pub const Builder = struct { |
| 53 | 55 | |
| 54 | 56 | %return zig_args.append("build_exe"[0...]); // TODO issue #296 |
| 55 | 57 | %return zig_args.append(exe.root_src); |
| 58 | ||
| 59 | if (verbose) { | |
| 60 | %return zig_args.append("--verbose"[0...]); // TODO issue #296 | |
| 61 | } | |
| 62 | ||
| 56 | 63 | %return zig_args.append("--name"[0...]); // TODO issue #296 |
| 57 | 64 | %return zig_args.append(exe.name); |
| 58 | 65 | |
| 66 | switch (exe.target) { | |
| 67 | Target.Native => {}, | |
| 68 | Target.Cross => |cross_target| { | |
| 69 | %return zig_args.append("--target-arch"[0...]); // TODO issue #296 | |
| 70 | %return zig_args.append(targetArchName(cross_target.arch)); | |
| 71 | ||
| 72 | %return zig_args.append("--target-os"[0...]); // TODO issue #296 | |
| 73 | %return zig_args.append(targetOsName(cross_target.os)); | |
| 74 | ||
| 75 | %return zig_args.append("--target-environ"[0...]); // TODO issue #296 | |
| 76 | %return zig_args.append(targetEnvironName(cross_target.environ)); | |
| 77 | }, | |
| 78 | } | |
| 79 | ||
| 80 | switch (exe.linker_script) { | |
| 81 | LinkerScript.None => {}, | |
| 82 | LinkerScript.Embed => |script| { | |
| 83 | const tmp_file_name = "linker.ld.tmp"; // TODO issue #298 | |
| 84 | io.writeFile(tmp_file_name, script, self.allocator) | |
| 85 | %% |err| debug.panic("unable to write linker script: {}\n", @errorName(err)); | |
| 86 | %return zig_args.append("--linker-script"[0...]); // TODO issue #296 | |
| 87 | %return zig_args.append(tmp_file_name[0...]); // TODO issue #296 | |
| 88 | }, | |
| 89 | LinkerScript.Path => |path| { | |
| 90 | %return zig_args.append("--linker-script"[0...]); // TODO issue #296 | |
| 91 | %return zig_args.append(path); | |
| 92 | }, | |
| 93 | } | |
| 94 | ||
| 59 | 95 | printInvocation(self.zig_exe, zig_args); |
| 60 | 96 | var child = %return os.ChildProcess.spawn(self.zig_exe, zig_args.toSliceConst(), os.environ, |
| 61 | 97 | StdIo.Ignore, StdIo.Inherit, StdIo.Inherit, self.allocator); |
| ... | ... | @@ -72,9 +108,48 @@ pub const Builder = struct { |
| 72 | 108 | } |
| 73 | 109 | }; |
| 74 | 110 | |
| 111 | const CrossTarget = struct { | |
| 112 | arch: Arch, | |
| 113 | os: Os, | |
| 114 | environ: Environ, | |
| 115 | }; | |
| 116 | ||
| 117 | const Target = enum { | |
| 118 | Native, | |
| 119 | Cross: CrossTarget, | |
| 120 | }; | |
| 121 | ||
| 122 | const LinkerScript = enum { | |
| 123 | None, | |
| 124 | Embed: []const u8, | |
| 125 | Path: []const u8, | |
| 126 | }; | |
| 127 | ||
| 75 | 128 | const Exe = struct { |
| 76 | 129 | root_src: []const u8, |
| 77 | 130 | name: []const u8, |
| 131 | target: Target, | |
| 132 | linker_script: LinkerScript, | |
| 133 | ||
| 134 | fn setTarget(self: &Exe, target_arch: Arch, target_os: Os, target_environ: Environ) { | |
| 135 | self.target = Target.Cross { | |
| 136 | CrossTarget { | |
| 137 | .arch = target_arch, | |
| 138 | .os = target_os, | |
| 139 | .environ = target_environ, | |
| 140 | } | |
| 141 | }; | |
| 142 | } | |
| 143 | ||
| 144 | /// Exe keeps a reference to script for its lifetime or until this function | |
| 145 | /// is called again. | |
| 146 | fn setLinkerScriptContents(self: &Exe, script: []const u8) { | |
| 147 | self.linker_script = LinkerScript.Embed { script }; | |
| 148 | } | |
| 149 | ||
| 150 | fn setLinkerScriptPath(self: &Exe, path: []const u8) { | |
| 151 | self.linker_script = LinkerScript.Path { path }; | |
| 152 | } | |
| 78 | 153 | }; |
| 79 | 154 | |
| 80 | 155 | fn handleErr(err: error) -> noreturn { |
| ... | ... | @@ -88,3 +163,130 @@ fn printInvocation(exe_name: []const u8, args: &const List([]const u8)) { |
| 88 | 163 | } |
| 89 | 164 | %%io.stderr.printf("\n"); |
| 90 | 165 | } |
| 166 | ||
| 167 | // TODO issue #299 | |
| 168 | fn targetOsName(target_os: Os) -> []const u8 { | |
| 169 | return switch (target_os) { | |
| 170 | Os.freestanding => ([]const u8)("freestanding"), | |
| 171 | Os.cloudabi => ([]const u8)("cloudabi"), | |
| 172 | Os.darwin => ([]const u8)("darwin"), | |
| 173 | Os.dragonfly => ([]const u8)("dragonfly"), | |
| 174 | Os.freebsd => ([]const u8)("freebsd"), | |
| 175 | Os.ios => ([]const u8)("ios"), | |
| 176 | Os.kfreebsd => ([]const u8)("kfreebsd"), | |
| 177 | Os.linux => ([]const u8)("linux"), | |
| 178 | Os.lv2 => ([]const u8)("lv2"), | |
| 179 | Os.macosx => ([]const u8)("macosx"), | |
| 180 | Os.netbsd => ([]const u8)("netbsd"), | |
| 181 | Os.openbsd => ([]const u8)("openbsd"), | |
| 182 | Os.solaris => ([]const u8)("solaris"), | |
| 183 | Os.windows => ([]const u8)("windows"), | |
| 184 | Os.haiku => ([]const u8)("haiku"), | |
| 185 | Os.minix => ([]const u8)("minix"), | |
| 186 | Os.rtems => ([]const u8)("rtems"), | |
| 187 | Os.nacl => ([]const u8)("nacl"), | |
| 188 | Os.cnk => ([]const u8)("cnk"), | |
| 189 | Os.bitrig => ([]const u8)("bitrig"), | |
| 190 | Os.aix => ([]const u8)("aix"), | |
| 191 | Os.cuda => ([]const u8)("cuda"), | |
| 192 | Os.nvcl => ([]const u8)("nvcl"), | |
| 193 | Os.amdhsa => ([]const u8)("amdhsa"), | |
| 194 | Os.ps4 => ([]const u8)("ps4"), | |
| 195 | Os.elfiamcu => ([]const u8)("elfiamcu"), | |
| 196 | Os.tvos => ([]const u8)("tvos"), | |
| 197 | Os.watchos => ([]const u8)("watchos"), | |
| 198 | Os.mesa3d => ([]const u8)("mesa3d"), | |
| 199 | }; | |
| 200 | } | |
| 201 | ||
| 202 | // TODO issue #299 | |
| 203 | fn targetArchName(target_arch: Arch) -> []const u8 { | |
| 204 | return switch (target_arch) { | |
| 205 | Arch.armv8_2a => ([]const u8)("armv8_2a"), | |
| 206 | Arch.armv8_1a => ([]const u8)("armv8_1a"), | |
| 207 | Arch.armv8 => ([]const u8)("armv8"), | |
| 208 | Arch.armv8m_baseline => ([]const u8)("armv8m_baseline"), | |
| 209 | Arch.armv8m_mainline => ([]const u8)("armv8m_mainline"), | |
| 210 | Arch.armv7 => ([]const u8)("armv7"), | |
| 211 | Arch.armv7em => ([]const u8)("armv7em"), | |
| 212 | Arch.armv7m => ([]const u8)("armv7m"), | |
| 213 | Arch.armv7s => ([]const u8)("armv7s"), | |
| 214 | Arch.armv7k => ([]const u8)("armv7k"), | |
| 215 | Arch.armv6 => ([]const u8)("armv6"), | |
| 216 | Arch.armv6m => ([]const u8)("armv6m"), | |
| 217 | Arch.armv6k => ([]const u8)("armv6k"), | |
| 218 | Arch.armv6t2 => ([]const u8)("armv6t2"), | |
| 219 | Arch.armv5 => ([]const u8)("armv5"), | |
| 220 | Arch.armv5te => ([]const u8)("armv5te"), | |
| 221 | Arch.armv4t => ([]const u8)("armv4t"), | |
| 222 | Arch.armeb => ([]const u8)("armeb"), | |
| 223 | Arch.aarch64 => ([]const u8)("aarch64"), | |
| 224 | Arch.aarch64_be => ([]const u8)("aarch64_be"), | |
| 225 | Arch.avr => ([]const u8)("avr"), | |
| 226 | Arch.bpfel => ([]const u8)("bpfel"), | |
| 227 | Arch.bpfeb => ([]const u8)("bpfeb"), | |
| 228 | Arch.hexagon => ([]const u8)("hexagon"), | |
| 229 | Arch.mips => ([]const u8)("mips"), | |
| 230 | Arch.mipsel => ([]const u8)("mipsel"), | |
| 231 | Arch.mips64 => ([]const u8)("mips64"), | |
| 232 | Arch.mips64el => ([]const u8)("mips64el"), | |
| 233 | Arch.msp430 => ([]const u8)("msp430"), | |
| 234 | Arch.powerpc => ([]const u8)("powerpc"), | |
| 235 | Arch.powerpc64 => ([]const u8)("powerpc64"), | |
| 236 | Arch.powerpc64le => ([]const u8)("powerpc64le"), | |
| 237 | Arch.r600 => ([]const u8)("r600"), | |
| 238 | Arch.amdgcn => ([]const u8)("amdgcn"), | |
| 239 | Arch.sparc => ([]const u8)("sparc"), | |
| 240 | Arch.sparcv9 => ([]const u8)("sparcv9"), | |
| 241 | Arch.sparcel => ([]const u8)("sparcel"), | |
| 242 | Arch.s390x => ([]const u8)("s390x"), | |
| 243 | Arch.tce => ([]const u8)("tce"), | |
| 244 | Arch.thumb => ([]const u8)("thumb"), | |
| 245 | Arch.thumbeb => ([]const u8)("thumbeb"), | |
| 246 | Arch.i386 => ([]const u8)("i386"), | |
| 247 | Arch.x86_64 => ([]const u8)("x86_64"), | |
| 248 | Arch.xcore => ([]const u8)("xcore"), | |
| 249 | Arch.nvptx => ([]const u8)("nvptx"), | |
| 250 | Arch.nvptx64 => ([]const u8)("nvptx64"), | |
| 251 | Arch.le32 => ([]const u8)("le32"), | |
| 252 | Arch.le64 => ([]const u8)("le64"), | |
| 253 | Arch.amdil => ([]const u8)("amdil"), | |
| 254 | Arch.amdil64 => ([]const u8)("amdil64"), | |
| 255 | Arch.hsail => ([]const u8)("hsail"), | |
| 256 | Arch.hsail64 => ([]const u8)("hsail64"), | |
| 257 | Arch.spir => ([]const u8)("spir"), | |
| 258 | Arch.spir64 => ([]const u8)("spir64"), | |
| 259 | Arch.kalimbav3 => ([]const u8)("kalimbav3"), | |
| 260 | Arch.kalimbav4 => ([]const u8)("kalimbav4"), | |
| 261 | Arch.kalimbav5 => ([]const u8)("kalimbav5"), | |
| 262 | Arch.shave => ([]const u8)("shave"), | |
| 263 | Arch.lanai => ([]const u8)("lanai"), | |
| 264 | Arch.wasm32 => ([]const u8)("wasm32"), | |
| 265 | Arch.wasm64 => ([]const u8)("wasm64"), | |
| 266 | Arch.renderscript32 => ([]const u8)("renderscript32"), | |
| 267 | Arch.renderscript64 => ([]const u8)("renderscript64"), | |
| 268 | }; | |
| 269 | } | |
| 270 | ||
| 271 | // TODO issue #299 | |
| 272 | fn targetEnvironName(target_environ: Environ) -> []const u8 { | |
| 273 | return switch (target_environ) { | |
| 274 | Environ.gnu => ([]const u8)("gnu"), | |
| 275 | Environ.gnuabi64 => ([]const u8)("gnuabi64"), | |
| 276 | Environ.gnueabi => ([]const u8)("gnueabi"), | |
| 277 | Environ.gnueabihf => ([]const u8)("gnueabihf"), | |
| 278 | Environ.gnux32 => ([]const u8)("gnux32"), | |
| 279 | Environ.code16 => ([]const u8)("code16"), | |
| 280 | Environ.eabi => ([]const u8)("eabi"), | |
| 281 | Environ.eabihf => ([]const u8)("eabihf"), | |
| 282 | Environ.android => ([]const u8)("android"), | |
| 283 | Environ.musl => ([]const u8)("musl"), | |
| 284 | Environ.musleabi => ([]const u8)("musleabi"), | |
| 285 | Environ.musleabihf => ([]const u8)("musleabihf"), | |
| 286 | Environ.msvc => ([]const u8)("msvc"), | |
| 287 | Environ.itanium => ([]const u8)("itanium"), | |
| 288 | Environ.cygnus => ([]const u8)("cygnus"), | |
| 289 | Environ.amdopencl => ([]const u8)("amdopencl"), | |
| 290 | Environ.coreclr => ([]const u8)("coreclr"), | |
| 291 | }; | |
| 292 | } |
std/debug.zig+3-3| ... | ... | @@ -28,7 +28,7 @@ pub coldcc fn panic(comptime format: []const u8, args: ...) -> noreturn { |
| 28 | 28 | panicking = true; |
| 29 | 29 | } |
| 30 | 30 | |
| 31 | %%io.stderr.printf(format, args); | |
| 31 | %%io.stderr.printf(format ++ "\n", args); | |
| 32 | 32 | %%printStackTrace(); |
| 33 | 33 | |
| 34 | 34 | os.abort(); |
| ... | ... | @@ -52,8 +52,8 @@ pub fn writeStackTrace(out_stream: &io.OutStream) -> %void { |
| 52 | 52 | .compile_unit_list = List(CompileUnit).init(&global_allocator), |
| 53 | 53 | }; |
| 54 | 54 | const st = &stack_trace; |
| 55 | %return io.openSelfExe(&st.self_exe_stream); | |
| 56 | defer st.self_exe_stream.close() %% {}; | |
| 55 | st.self_exe_stream = %return io.openSelfExe(); | |
| 56 | defer st.self_exe_stream.close(); | |
| 57 | 57 | |
| 58 | 58 | %return st.elf.openStream(&global_allocator, &st.self_exe_stream); |
| 59 | 59 | defer st.elf.close(); |
std/io.zig+64-78| ... | ... | @@ -69,6 +69,27 @@ pub const OutStream = struct { |
| 69 | 69 | buffer: [buffer_size]u8, |
| 70 | 70 | index: usize, |
| 71 | 71 | |
| 72 | /// `path` may need to be copied in memory to add a null terminating byte. In this case | |
| 73 | /// a fixed size buffer of size std.os.max_noalloc_path_len is an attempted solution. If the fixed | |
| 74 | /// size buffer is too small, and the provided allocator is null, error.NameTooLong is returned. | |
| 75 | /// otherwise if the fixed size buffer is too small, allocator is used to obtain the needed memory. | |
| 76 | /// Call close to clean up. | |
| 77 | pub fn open(path: []const u8, allocator: ?&mem.Allocator) -> %OutStream { | |
| 78 | switch (@compileVar("os")) { | |
| 79 | Os.linux, Os.darwin, Os.macosx, Os.ios => { | |
| 80 | const flags = system.O_LARGEFILE|system.O_WRONLY|system.O_CREAT|system.O_CLOEXEC|system.O_TRUNC; | |
| 81 | const fd = %return os.posixOpen(path, flags, 0o666, allocator); | |
| 82 | return OutStream { | |
| 83 | .fd = fd, | |
| 84 | .index = 0, | |
| 85 | .buffer = undefined, | |
| 86 | }; | |
| 87 | }, | |
| 88 | else => @compileError("Unsupported OS"), | |
| 89 | } | |
| 90 | ||
| 91 | } | |
| 92 | ||
| 72 | 93 | pub fn writeByte(self: &OutStream, b: u8) -> %void { |
| 73 | 94 | if (self.buffer.len == self.index) %return self.flush(); |
| 74 | 95 | self.buffer[self.index] = b; |
| ... | ... | @@ -76,6 +97,11 @@ pub const OutStream = struct { |
| 76 | 97 | } |
| 77 | 98 | |
| 78 | 99 | pub fn write(self: &OutStream, bytes: []const u8) -> %void { |
| 100 | if (bytes.len >= buffer_size) { | |
| 101 | %return self.flush(); | |
| 102 | return os.posixWrite(self.fd, bytes); | |
| 103 | } | |
| 104 | ||
| 79 | 105 | var src_index: usize = 0; |
| 80 | 106 | |
| 81 | 107 | while (src_index < bytes.len) { |
| ... | ... | @@ -119,35 +145,15 @@ pub const OutStream = struct { |
| 119 | 145 | } |
| 120 | 146 | |
| 121 | 147 | pub fn flush(self: &OutStream) -> %void { |
| 122 | while (true) { | |
| 123 | const write_ret = system.write(self.fd, &self.buffer[0], self.index); | |
| 124 | const write_err = system.getErrno(write_ret); | |
| 125 | if (write_err > 0) { | |
| 126 | return switch (write_err) { | |
| 127 | errno.EINTR => continue, | |
| 128 | errno.EINVAL => unreachable, | |
| 129 | errno.EDQUOT => error.DiskQuota, | |
| 130 | errno.EFBIG => error.FileTooBig, | |
| 131 | errno.EIO => error.Io, | |
| 132 | errno.ENOSPC => error.NoSpaceLeft, | |
| 133 | errno.EPERM => error.BadPerm, | |
| 134 | errno.EPIPE => error.PipeFail, | |
| 135 | else => error.Unexpected, | |
| 136 | } | |
| 137 | } | |
| 148 | if (self.index != 0) { | |
| 149 | %return os.posixWrite(self.fd, self.buffer[0...self.index]); | |
| 138 | 150 | self.index = 0; |
| 139 | return; | |
| 140 | 151 | } |
| 141 | 152 | } |
| 142 | 153 | |
| 143 | 154 | pub fn close(self: &OutStream) { |
| 144 | while (true) { | |
| 145 | const close_ret = system.close(self.fd); | |
| 146 | const close_err = system.getErrno(close_ret); | |
| 147 | if (close_err > 0 and close_err == errno.EINTR) | |
| 148 | continue; | |
| 149 | return; | |
| 150 | } | |
| 155 | assert(self.index == 0); | |
| 156 | os.posixClose(self.fd); | |
| 151 | 157 | } |
| 152 | 158 | }; |
| 153 | 159 | |
| ... | ... | @@ -156,65 +162,32 @@ pub const OutStream = struct { |
| 156 | 162 | pub const InStream = struct { |
| 157 | 163 | fd: i32, |
| 158 | 164 | |
| 165 | /// `path` may need to be copied in memory to add a null terminating byte. In this case | |
| 166 | /// a fixed size buffer of size std.os.max_noalloc_path_len is an attempted solution. If the fixed | |
| 167 | /// size buffer is too small, and the provided allocator is null, error.NameTooLong is returned. | |
| 168 | /// otherwise if the fixed size buffer is too small, allocator is used to obtain the needed memory. | |
| 159 | 169 | /// Call close to clean up. |
| 160 | pub fn open(is: &InStream, path: []const u8) -> %void { | |
| 170 | pub fn open(path: []const u8, allocator: ?&mem.Allocator) -> %InStream { | |
| 161 | 171 | switch (@compileVar("os")) { |
| 162 | Os.linux, Os.darwin => { | |
| 163 | while (true) { | |
| 164 | const result = system.open(path, system.O_LARGEFILE|system.O_RDONLY, 0); | |
| 165 | const err = system.getErrno(result); | |
| 166 | if (err > 0) { | |
| 167 | return switch (err) { | |
| 168 | errno.EINTR => continue, | |
| 169 | ||
| 170 | errno.EFAULT => unreachable, | |
| 171 | errno.EINVAL => unreachable, | |
| 172 | errno.EACCES => error.BadPerm, | |
| 173 | errno.EFBIG, errno.EOVERFLOW => error.FileTooBig, | |
| 174 | errno.EISDIR => error.IsDir, | |
| 175 | errno.ELOOP => error.SymLinkLoop, | |
| 176 | errno.EMFILE => error.ProcessFdQuotaExceeded, | |
| 177 | errno.ENAMETOOLONG => error.NameTooLong, | |
| 178 | errno.ENFILE => error.SystemFdQuotaExceeded, | |
| 179 | errno.ENODEV => error.NoDevice, | |
| 180 | errno.ENOENT => error.PathNotFound, | |
| 181 | errno.ENOMEM => error.NoMem, | |
| 182 | errno.ENOSPC => error.NoSpaceLeft, | |
| 183 | errno.ENOTDIR => error.NotDir, | |
| 184 | errno.EPERM => error.BadPerm, | |
| 185 | else => error.Unexpected, | |
| 186 | } | |
| 187 | } | |
| 188 | is.fd = i32(result); | |
| 189 | return; | |
| 190 | } | |
| 172 | Os.linux, Os.darwin, Os.macosx, Os.ios => { | |
| 173 | const flags = system.O_LARGEFILE|system.O_RDONLY; | |
| 174 | const fd = %return os.posixOpen(path, flags, 0, allocator); | |
| 175 | return InStream { | |
| 176 | .fd = fd, | |
| 177 | }; | |
| 191 | 178 | }, |
| 192 | else => @compileError("unsupported OS"), | |
| 179 | else => @compileError("Unsupported OS"), | |
| 193 | 180 | } |
| 194 | ||
| 195 | 181 | } |
| 196 | 182 | |
| 197 | 183 | /// Upon success, the stream is in an uninitialized state. To continue using it, |
| 198 | 184 | /// you must use the open() function. |
| 199 | pub fn close(is: &InStream) -> %void { | |
| 185 | pub fn close(self: &InStream) { | |
| 200 | 186 | switch (@compileVar("os")) { |
| 201 | Os.linux, Os.darwin => { | |
| 202 | while (true) { | |
| 203 | const close_ret = system.close(is.fd); | |
| 204 | const close_err = system.getErrno(close_ret); | |
| 205 | if (close_err > 0) { | |
| 206 | return switch (close_err) { | |
| 207 | errno.EINTR => continue, | |
| 208 | ||
| 209 | errno.EIO => error.Io, | |
| 210 | errno.EBADF => error.BadFd, | |
| 211 | else => error.Unexpected, | |
| 212 | } | |
| 213 | } | |
| 214 | return; | |
| 215 | } | |
| 187 | Os.linux, Os.darwin, Os.macosx, Os.ios => { | |
| 188 | os.posixClose(self.fd); | |
| 216 | 189 | }, |
| 217 | else => @compileError("unsupported OS"), | |
| 190 | else => @compileError("Unsupported OS"), | |
| 218 | 191 | } |
| 219 | 192 | } |
| 220 | 193 | |
| ... | ... | @@ -373,15 +346,28 @@ pub const InStream = struct { |
| 373 | 346 | } |
| 374 | 347 | }; |
| 375 | 348 | |
| 376 | pub fn openSelfExe(stream: &InStream) -> %void { | |
| 349 | pub fn openSelfExe() -> %InStream { | |
| 377 | 350 | switch (@compileVar("os")) { |
| 378 | 351 | Os.linux => { |
| 379 | %return stream.open("/proc/self/exe"); | |
| 352 | return InStream.open("/proc/self/exe", null); | |
| 380 | 353 | }, |
| 381 | 354 | Os.darwin => { |
| 382 | %%stderr.printf("TODO: openSelfExe on Darwin\n"); | |
| 383 | os.abort(); | |
| 355 | debug.panic("TODO: openSelfExe on Darwin"); | |
| 384 | 356 | }, |
| 385 | else => @compileError("unsupported os"), | |
| 357 | else => @compileError("Unsupported OS"), | |
| 386 | 358 | } |
| 387 | 359 | } |
| 360 | ||
| 361 | /// `path` may need to be copied in memory to add a null terminating byte. In this case | |
| 362 | /// a fixed size buffer of size std.os.max_noalloc_path_len is an attempted solution. If the fixed | |
| 363 | /// size buffer is too small, and the provided allocator is null, error.NameTooLong is returned. | |
| 364 | /// otherwise if the fixed size buffer is too small, allocator is used to obtain the needed memory. | |
| 365 | pub fn writeFile(path: []const u8, data: []const u8, allocator: ?&mem.Allocator) -> %void { | |
| 366 | // TODO have an unbuffered File abstraction and use that here. | |
| 367 | // Then a buffered out stream abstraction can go on top of that for | |
| 368 | // use cases like stdout and stderr. | |
| 369 | var out_stream = %return OutStream.open(path, allocator); | |
| 370 | defer out_stream.close(); | |
| 371 | %return out_stream.write(data); | |
| 372 | %return out_stream.flush(); | |
| 373 | } |
std/os/darwin.zig+1-8| ... | ... | @@ -71,17 +71,10 @@ pub fn close(fd: i32) -> usize { |
| 71 | 71 | arch.syscall1(arch.SYS_close, usize(fd)) |
| 72 | 72 | } |
| 73 | 73 | |
| 74 | pub fn open_c(path: &const u8, flags: usize, perm: usize) -> usize { | |
| 74 | pub fn open(path: &const u8, flags: usize, perm: usize) -> usize { | |
| 75 | 75 | arch.syscall3(arch.SYS_open, usize(path), flags, perm) |
| 76 | 76 | } |
| 77 | 77 | |
| 78 | pub fn open(path: []const u8, flags: usize, perm: usize) -> usize { | |
| 79 | const buf = @alloca(u8, path.len + 1); | |
| 80 | @memcpy(&buf[0], &path[0], path.len); | |
| 81 | buf[path.len] = 0; | |
| 82 | return open_c(buf.ptr, flags, perm); | |
| 83 | } | |
| 84 | ||
| 85 | 78 | pub fn read(fd: i32, buf: &u8, count: usize) -> usize { |
| 86 | 79 | arch.syscall3(arch.SYS_read, usize(fd), usize(buf), count) |
| 87 | 80 | } |
std/os/index.zig+64-14| ... | ... | @@ -8,6 +8,8 @@ pub const posix = switch(@compileVar("os")) { |
| 8 | 8 | else => @compileError("Unsupported OS"), |
| 9 | 9 | }; |
| 10 | 10 | |
| 11 | pub const max_noalloc_path_len = 1024; | |
| 12 | ||
| 11 | 13 | const debug = @import("../debug.zig"); |
| 12 | 14 | const assert = debug.assert; |
| 13 | 15 | |
| ... | ... | @@ -105,11 +107,12 @@ fn makePipe() -> %[2]i32 { |
| 105 | 107 | } |
| 106 | 108 | |
| 107 | 109 | fn destroyPipe(pipe: &const [2]i32) { |
| 108 | closeNoIntr((*pipe)[0]); | |
| 109 | closeNoIntr((*pipe)[1]); | |
| 110 | posixClose((*pipe)[0]); | |
| 111 | posixClose((*pipe)[1]); | |
| 110 | 112 | } |
| 111 | 113 | |
| 112 | fn closeNoIntr(fd: i32) { | |
| 114 | /// Calls POSIX close, and keeps trying if it gets interrupted. | |
| 115 | pub fn posixClose(fd: i32) { | |
| 113 | 116 | while (true) { |
| 114 | 117 | const err = posix.getErrno(posix.close(fd)); |
| 115 | 118 | if (err == errno.EINTR) { |
| ... | ... | @@ -120,9 +123,56 @@ fn closeNoIntr(fd: i32) { |
| 120 | 123 | } |
| 121 | 124 | } |
| 122 | 125 | |
| 123 | fn openNoIntr(path: []const u8, flags: usize, perm: usize) -> %i32 { | |
| 126 | /// Calls POSIX write, and keeps trying if it gets interrupted. | |
| 127 | pub fn posixWrite(fd: i32, bytes: []const u8) -> %void { | |
| 128 | while (true) { | |
| 129 | const write_ret = posix.write(fd, bytes.ptr, bytes.len); | |
| 130 | const write_err = posix.getErrno(write_ret); | |
| 131 | if (write_err > 0) { | |
| 132 | return switch (write_err) { | |
| 133 | errno.EINTR => continue, | |
| 134 | errno.EINVAL => unreachable, | |
| 135 | errno.EDQUOT => error.DiskQuota, | |
| 136 | errno.EFBIG => error.FileTooBig, | |
| 137 | errno.EIO => error.Io, | |
| 138 | errno.ENOSPC => error.NoSpaceLeft, | |
| 139 | errno.EPERM => error.BadPerm, | |
| 140 | errno.EPIPE => error.PipeFail, | |
| 141 | else => error.Unexpected, | |
| 142 | } | |
| 143 | } | |
| 144 | return; | |
| 145 | } | |
| 146 | } | |
| 147 | ||
| 148 | ||
| 149 | /// ::path may need to be copied in memory to add a null terminating byte. In this case | |
| 150 | /// a fixed size buffer of size ::max_noalloc_path_len is an attempted solution. If the fixed | |
| 151 | /// size buffer is too small, and the provided allocator is null, ::error.NameTooLong is returned. | |
| 152 | /// otherwise if the fixed size buffer is too small, allocator is used to obtain the needed memory. | |
| 153 | /// Calls POSIX open, keeps trying if it gets interrupted, and translates | |
| 154 | /// the return value into zig errors. | |
| 155 | pub fn posixOpen(path: []const u8, flags: usize, perm: usize, allocator: ?&Allocator) -> %i32 { | |
| 156 | var stack_buf: [max_noalloc_path_len]u8 = undefined; | |
| 157 | var path0: []u8 = undefined; | |
| 158 | var need_free = false; | |
| 159 | ||
| 160 | if (path.len < stack_buf.len) { | |
| 161 | path0 = stack_buf[0...path.len + 1]; | |
| 162 | } else if (const a ?= allocator) { | |
| 163 | path0 = %return a.alloc(u8, path.len + 1); | |
| 164 | need_free = true; | |
| 165 | } else { | |
| 166 | return error.NameTooLong; | |
| 167 | } | |
| 168 | defer if (need_free) { | |
| 169 | (??allocator).free(path0); | |
| 170 | }; | |
| 171 | mem.copy(u8, path0, path); | |
| 172 | path0[path.len] = 0; | |
| 173 | ||
| 124 | 174 | while (true) { |
| 125 | const result = posix.open(path, flags, perm); | |
| 175 | const result = posix.open(path0.ptr, flags, perm); | |
| 126 | 176 | const err = posix.getErrno(result); |
| 127 | 177 | if (err > 0) { |
| 128 | 178 | return switch (err) { |
| ... | ... | @@ -247,8 +297,8 @@ pub const ChildProcess = struct { |
| 247 | 297 | |
| 248 | 298 | pub fn wait(self: &ChildProcess) -> %Term { |
| 249 | 299 | defer { |
| 250 | closeNoIntr(self.err_pipe[0]); | |
| 251 | closeNoIntr(self.err_pipe[1]); | |
| 300 | posixClose(self.err_pipe[0]); | |
| 301 | posixClose(self.err_pipe[1]); | |
| 252 | 302 | }; |
| 253 | 303 | |
| 254 | 304 | var status: i32 = undefined; |
| ... | ... | @@ -328,13 +378,13 @@ pub const ChildProcess = struct { |
| 328 | 378 | const any_ignore = (stdin == StdIo.Ignore or stdout == StdIo.Ignore or stderr == StdIo.Ignore); |
| 329 | 379 | // TODO issue #295 |
| 330 | 380 | //const dev_null_fd = if (any_ignore) { |
| 331 | // %return openNoIntr("/dev/null", posix.O_RDWR, 0) | |
| 381 | // %return posixOpen("/dev/null", posix.O_RDWR, 0, null) | |
| 332 | 382 | //} else { |
| 333 | 383 | // undefined |
| 334 | 384 | //}; |
| 335 | 385 | var dev_null_fd: i32 = undefined; |
| 336 | 386 | if (any_ignore) |
| 337 | dev_null_fd = %return openNoIntr("/dev/null", posix.O_RDWR, 0); | |
| 387 | dev_null_fd = %return posixOpen("/dev/null", posix.O_RDWR, 0, null); | |
| 338 | 388 | |
| 339 | 389 | // This pipe is used to communicate errors between the time of fork |
| 340 | 390 | // and execve from the child process to the parent process. |
| ... | ... | @@ -374,10 +424,10 @@ pub const ChildProcess = struct { |
| 374 | 424 | } |
| 375 | 425 | |
| 376 | 426 | // we are the parent |
| 377 | if (stdin == StdIo.Pipe) { closeNoIntr(stdin_pipe[0]); } | |
| 378 | if (stdout == StdIo.Pipe) { closeNoIntr(stdout_pipe[1]); } | |
| 379 | if (stderr == StdIo.Pipe) { closeNoIntr(stderr_pipe[1]); } | |
| 380 | if (any_ignore) { closeNoIntr(dev_null_fd); } | |
| 427 | if (stdin == StdIo.Pipe) { posixClose(stdin_pipe[0]); } | |
| 428 | if (stdout == StdIo.Pipe) { posixClose(stdout_pipe[1]); } | |
| 429 | if (stderr == StdIo.Pipe) { posixClose(stderr_pipe[1]); } | |
| 430 | if (any_ignore) { posixClose(dev_null_fd); } | |
| 381 | 431 | |
| 382 | 432 | return ChildProcess { |
| 383 | 433 | .pid = i32(pid), |
| ... | ... | @@ -412,7 +462,7 @@ pub const ChildProcess = struct { |
| 412 | 462 | fn setUpChildIo(stdio: StdIo, pipe_fd: i32, std_fileno: i32, dev_null_fd: i32) -> %void { |
| 413 | 463 | switch (stdio) { |
| 414 | 464 | StdIo.Pipe => %return dup2NoIntr(pipe_fd, std_fileno), |
| 415 | StdIo.Close => closeNoIntr(std_fileno), | |
| 465 | StdIo.Close => posixClose(std_fileno), | |
| 416 | 466 | StdIo.Inherit => {}, |
| 417 | 467 | StdIo.Ignore => %return dup2NoIntr(dev_null_fd, std_fileno), |
| 418 | 468 | } |
std/os/linux.zig+3-24| ... | ... | @@ -303,39 +303,18 @@ pub fn pwrite(fd: i32, buf: &const u8, count: usize, offset: usize) -> usize { |
| 303 | 303 | arch.syscall4(arch.SYS_pwrite, usize(fd), usize(buf), count, offset) |
| 304 | 304 | } |
| 305 | 305 | |
| 306 | pub fn open_c(path: &const u8, flags: usize, perm: usize) -> usize { | |
| 306 | pub fn open(path: &const u8, flags: usize, perm: usize) -> usize { | |
| 307 | 307 | arch.syscall3(arch.SYS_open, usize(path), flags, perm) |
| 308 | 308 | } |
| 309 | 309 | |
| 310 | pub fn open(path: []const u8, flags: usize, perm: usize) -> usize { | |
| 311 | const buf = @alloca(u8, path.len + 1); | |
| 312 | @memcpy(&buf[0], &path[0], path.len); | |
| 313 | buf[path.len] = 0; | |
| 314 | return open_c(buf.ptr, flags, perm); | |
| 315 | } | |
| 316 | ||
| 317 | pub fn create_c(path: &const u8, perm: usize) -> usize { | |
| 310 | pub fn create(path: &const u8, perm: usize) -> usize { | |
| 318 | 311 | arch.syscall2(arch.SYS_creat, usize(path), perm) |
| 319 | 312 | } |
| 320 | 313 | |
| 321 | pub fn create(path: []const u8, perm: usize) -> usize { | |
| 322 | const buf = @alloca(u8, path.len + 1); | |
| 323 | @memcpy(&buf[0], &path[0], path.len); | |
| 324 | buf[path.len] = 0; | |
| 325 | return create_c(buf.ptr, perm); | |
| 326 | } | |
| 327 | ||
| 328 | pub fn openat_c(dirfd: i32, path: &const u8, flags: usize, mode: usize) -> usize { | |
| 314 | pub fn openat(dirfd: i32, path: &const u8, flags: usize, mode: usize) -> usize { | |
| 329 | 315 | arch.syscall4(arch.SYS_openat, usize(dirfd), usize(path), flags, mode) |
| 330 | 316 | } |
| 331 | 317 | |
| 332 | pub fn openat(dirfd: i32, path: []const u8, flags: usize, mode: usize) -> usize { | |
| 333 | const buf = @alloca(u8, path.len + 1); | |
| 334 | @memcpy(&buf[0], &path[0], path.len); | |
| 335 | buf[path.len] = 0; | |
| 336 | return openat_c(dirfd, buf.ptr, flags, mode); | |
| 337 | } | |
| 338 | ||
| 339 | 318 | pub fn close(fd: i32) -> usize { |
| 340 | 319 | arch.syscall1(arch.SYS_close, usize(fd)) |
| 341 | 320 | } |
std/special/bootstrap.zig+2| ... | ... | @@ -33,6 +33,7 @@ export nakedcc fn _start() -> noreturn { |
| 33 | 33 | } |
| 34 | 34 | |
| 35 | 35 | fn callMain(envp: &?&u8) -> %void { |
| 36 | // TODO issue #225 | |
| 36 | 37 | const args = @alloca([]u8, argc); |
| 37 | 38 | for (args) |_, i| { |
| 38 | 39 | const ptr = argv[i]; |
| ... | ... | @@ -41,6 +42,7 @@ fn callMain(envp: &?&u8) -> %void { |
| 41 | 42 | |
| 42 | 43 | var env_count: usize = 0; |
| 43 | 44 | while (envp[env_count] != null; env_count += 1) {} |
| 45 | // TODO issue #225 | |
| 44 | 46 | const environ = @alloca(std.os.EnvPair, env_count); |
| 45 | 47 | for (environ) |_, env_i| { |
| 46 | 48 | const ptr = ??envp[env_i]; |
std/special/zigrt.zig+1-1| ... | ... | @@ -11,6 +11,6 @@ export coldcc fn __zig_panic(message_ptr: &const u8, message_len: usize) -> nore |
| 11 | 11 | } else if (@compileVar("os") == Os.freestanding) { |
| 12 | 12 | while (true) {} |
| 13 | 13 | } else { |
| 14 | @import("std").debug.panic("{}\n", message_ptr[0...message_len]); | |
| 14 | @import("std").debug.panic("{}", message_ptr[0...message_len]); | |
| 15 | 15 | } |
| 16 | 16 | } |