authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-04-03 04:58:19-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-04-03 05:04:46-04:00
logc400cb429abf2980962739731f5b64861a54ab61
treeafb244401d3aa18547ea7e6a5bf5fbe141472cee
parenta1363b52278b68f59aa72adf985f11e6c1d02cf9

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

10 files changed, 374 insertions(+), 148 deletions(-)

src/all_types.hpp+1
...@@ -944,6 +944,7 @@ struct TypeTableEntryEnum {...@@ -944,6 +944,7 @@ struct TypeTableEntryEnum {
944 AstNode *decl_node;944 AstNode *decl_node;
945 ContainerLayout layout;945 ContainerLayout layout;
946 uint32_t src_field_count;946 uint32_t src_field_count;
947 // number of fields in the union. 0 if enum with no payload
947 uint32_t gen_field_count;948 uint32_t gen_field_count;
948 TypeEnumField *fields;949 TypeEnumField *fields;
949 bool is_invalid; // true if any fields are invalid950 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,6 +3802,35 @@ static const GlobalLinkageValue global_linkage_values[] = {
3802 {GlobalLinkageIdLinkOnce, "LinkOnce"},3802 {GlobalLinkageIdLinkOnce, "LinkOnce"},
3803};3803};
38043804
3805static 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
3805static void define_builtin_types(CodeGen *g) {3834static void define_builtin_types(CodeGen *g) {
3806 {3835 {
3807 // if this type is anywhere in the AST, we should never hit codegen.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,7 +4051,6 @@ static void define_builtin_types(CodeGen *g) {
40224051
4023 {4052 {
4024 TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdEnum);4053 TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdEnum);
4025 entry->zero_bits = true; // only allowed at compile time
4026 buf_init_from_str(&entry->name, "Os");4054 buf_init_from_str(&entry->name, "Os");
4027 uint32_t field_count = target_os_count();4055 uint32_t field_count = target_os_count();
4028 entry->data.enumeration.src_field_count = field_count;4056 entry->data.enumeration.src_field_count = field_count;
...@@ -4038,11 +4066,8 @@ static void define_builtin_types(CodeGen *g) {...@@ -4038,11 +4066,8 @@ static void define_builtin_types(CodeGen *g) {
4038 g->target_os_index = i;4066 g->target_os_index = i;
4039 }4067 }
4040 }4068 }
4041 entry->data.enumeration.complete = true;
4042 entry->data.enumeration.zero_bits_known = true;
40434069
4044 TypeTableEntry *tag_type_entry = get_smallest_unsigned_int_type(g, field_count);4070 init_enum_debug_info(g, entry);
4045 entry->data.enumeration.tag_type = tag_type_entry;
40464071
4047 g->builtin_types.entry_os_enum = entry;4072 g->builtin_types.entry_os_enum = entry;
4048 g->primitive_type_table.put(&entry->name, entry);4073 g->primitive_type_table.put(&entry->name, entry);
...@@ -4050,7 +4075,6 @@ static void define_builtin_types(CodeGen *g) {...@@ -4050,7 +4075,6 @@ static void define_builtin_types(CodeGen *g) {
40504075
4051 {4076 {
4052 TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdEnum);4077 TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdEnum);
4053 entry->zero_bits = true; // only allowed at compile time
4054 buf_init_from_str(&entry->name, "Arch");4078 buf_init_from_str(&entry->name, "Arch");
4055 uint32_t field_count = target_arch_count();4079 uint32_t field_count = target_arch_count();
4056 entry->data.enumeration.src_field_count = field_count;4080 entry->data.enumeration.src_field_count = field_count;
...@@ -4072,11 +4096,8 @@ static void define_builtin_types(CodeGen *g) {...@@ -4072,11 +4096,8 @@ static void define_builtin_types(CodeGen *g) {
4072 g->target_arch_index = i;4096 g->target_arch_index = i;
4073 }4097 }
4074 }4098 }
4075 entry->data.enumeration.complete = true;
4076 entry->data.enumeration.zero_bits_known = true;
40774099
4078 TypeTableEntry *tag_type_entry = get_smallest_unsigned_int_type(g, field_count);4100 init_enum_debug_info(g, entry);
4079 entry->data.enumeration.tag_type = tag_type_entry;
40804101
4081 g->builtin_types.entry_arch_enum = entry;4102 g->builtin_types.entry_arch_enum = entry;
4082 g->primitive_type_table.put(&entry->name, entry);4103 g->primitive_type_table.put(&entry->name, entry);
...@@ -4084,7 +4105,6 @@ static void define_builtin_types(CodeGen *g) {...@@ -4084,7 +4105,6 @@ static void define_builtin_types(CodeGen *g) {
40844105
4085 {4106 {
4086 TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdEnum);4107 TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdEnum);
4087 entry->zero_bits = true; // only allowed at compile time
4088 buf_init_from_str(&entry->name, "Environ");4108 buf_init_from_str(&entry->name, "Environ");
4089 uint32_t field_count = target_environ_count();4109 uint32_t field_count = target_environ_count();
4090 entry->data.enumeration.src_field_count = field_count;4110 entry->data.enumeration.src_field_count = field_count;
...@@ -4100,11 +4120,8 @@ static void define_builtin_types(CodeGen *g) {...@@ -4100,11 +4120,8 @@ static void define_builtin_types(CodeGen *g) {
4100 g->target_environ_index = i;4120 g->target_environ_index = i;
4101 }4121 }
4102 }4122 }
4103 entry->data.enumeration.complete = true;
4104 entry->data.enumeration.zero_bits_known = true;
41054123
4106 TypeTableEntry *tag_type_entry = get_smallest_unsigned_int_type(g, field_count);4124 init_enum_debug_info(g, entry);
4107 entry->data.enumeration.tag_type = tag_type_entry;
41084125
4109 g->builtin_types.entry_environ_enum = entry;4126 g->builtin_types.entry_environ_enum = entry;
4110 g->primitive_type_table.put(&entry->name, entry);4127 g->primitive_type_table.put(&entry->name, entry);
...@@ -4112,7 +4129,6 @@ static void define_builtin_types(CodeGen *g) {...@@ -4112,7 +4129,6 @@ static void define_builtin_types(CodeGen *g) {
41124129
4113 {4130 {
4114 TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdEnum);4131 TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdEnum);
4115 entry->zero_bits = true; // only allowed at compile time
4116 buf_init_from_str(&entry->name, "ObjectFormat");4132 buf_init_from_str(&entry->name, "ObjectFormat");
4117 uint32_t field_count = target_oformat_count();4133 uint32_t field_count = target_oformat_count();
4118 entry->data.enumeration.src_field_count = field_count;4134 entry->data.enumeration.src_field_count = field_count;
...@@ -4128,11 +4144,8 @@ static void define_builtin_types(CodeGen *g) {...@@ -4128,11 +4144,8 @@ static void define_builtin_types(CodeGen *g) {
4128 g->target_oformat_index = i;4144 g->target_oformat_index = i;
4129 }4145 }
4130 }4146 }
4131 entry->data.enumeration.complete = true;
4132 entry->data.enumeration.zero_bits_known = true;
41334147
4134 TypeTableEntry *tag_type_entry = get_smallest_unsigned_int_type(g, field_count);4148 init_enum_debug_info(g, entry);
4135 entry->data.enumeration.tag_type = tag_type_entry;
41364149
4137 g->builtin_types.entry_oformat_enum = entry;4150 g->builtin_types.entry_oformat_enum = entry;
4138 g->primitive_type_table.put(&entry->name, entry);4151 g->primitive_type_table.put(&entry->name, entry);
std/build.zig+202
...@@ -32,6 +32,8 @@ pub const Builder = struct {...@@ -32,6 +32,8 @@ pub const Builder = struct {
32 *exe = Exe {32 *exe = Exe {
33 .root_src = root_src,33 .root_src = root_src,
34 .name = name,34 .name = name,
35 .target = Target.Native,
36 .linker_script = LinkerScript.None,
35 };37 };
36 %return self.exe_list.append(exe);38 %return self.exe_list.append(exe);
37 return exe;39 return exe;
...@@ -53,9 +55,43 @@ pub const Builder = struct {...@@ -53,9 +55,43 @@ pub const Builder = struct {
5355
54 %return zig_args.append("build_exe"[0...]); // TODO issue #29656 %return zig_args.append("build_exe"[0...]); // TODO issue #296
55 %return zig_args.append(exe.root_src);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 %return zig_args.append("--name"[0...]); // TODO issue #29663 %return zig_args.append("--name"[0...]); // TODO issue #296
57 %return zig_args.append(exe.name);64 %return zig_args.append(exe.name);
5865
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 printInvocation(self.zig_exe, zig_args);95 printInvocation(self.zig_exe, zig_args);
60 var child = %return os.ChildProcess.spawn(self.zig_exe, zig_args.toSliceConst(), os.environ,96 var child = %return os.ChildProcess.spawn(self.zig_exe, zig_args.toSliceConst(), os.environ,
61 StdIo.Ignore, StdIo.Inherit, StdIo.Inherit, self.allocator);97 StdIo.Ignore, StdIo.Inherit, StdIo.Inherit, self.allocator);
...@@ -72,9 +108,48 @@ pub const Builder = struct {...@@ -72,9 +108,48 @@ pub const Builder = struct {
72 }108 }
73};109};
74110
111const CrossTarget = struct {
112 arch: Arch,
113 os: Os,
114 environ: Environ,
115};
116
117const Target = enum {
118 Native,
119 Cross: CrossTarget,
120};
121
122const LinkerScript = enum {
123 None,
124 Embed: []const u8,
125 Path: []const u8,
126};
127
75const Exe = struct {128const Exe = struct {
76 root_src: []const u8,129 root_src: []const u8,
77 name: []const u8,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};
79154
80fn handleErr(err: error) -> noreturn {155fn handleErr(err: error) -> noreturn {
...@@ -88,3 +163,130 @@ fn printInvocation(exe_name: []const u8, args: &const List([]const u8)) {...@@ -88,3 +163,130 @@ fn printInvocation(exe_name: []const u8, args: &const List([]const u8)) {
88 }163 }
89 %%io.stderr.printf("\n");164 %%io.stderr.printf("\n");
90}165}
166
167// TODO issue #299
168fn 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
203fn 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
272fn 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,7 +28,7 @@ pub coldcc fn panic(comptime format: []const u8, args: ...) -> noreturn {
28 panicking = true;28 panicking = true;
29 }29 }
3030
31 %%io.stderr.printf(format, args);31 %%io.stderr.printf(format ++ "\n", args);
32 %%printStackTrace();32 %%printStackTrace();
3333
34 os.abort();34 os.abort();
...@@ -52,8 +52,8 @@ pub fn writeStackTrace(out_stream: &io.OutStream) -> %void {...@@ -52,8 +52,8 @@ pub fn writeStackTrace(out_stream: &io.OutStream) -> %void {
52 .compile_unit_list = List(CompileUnit).init(&global_allocator),52 .compile_unit_list = List(CompileUnit).init(&global_allocator),
53 };53 };
54 const st = &stack_trace;54 const st = &stack_trace;
55 %return io.openSelfExe(&st.self_exe_stream);55 st.self_exe_stream = %return io.openSelfExe();
56 defer st.self_exe_stream.close() %% {};56 defer st.self_exe_stream.close();
5757
58 %return st.elf.openStream(&global_allocator, &st.self_exe_stream);58 %return st.elf.openStream(&global_allocator, &st.self_exe_stream);
59 defer st.elf.close();59 defer st.elf.close();
std/io.zig+64-78
...@@ -69,6 +69,27 @@ pub const OutStream = struct {...@@ -69,6 +69,27 @@ pub const OutStream = struct {
69 buffer: [buffer_size]u8,69 buffer: [buffer_size]u8,
70 index: usize,70 index: usize,
7171
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 pub fn writeByte(self: &OutStream, b: u8) -> %void {93 pub fn writeByte(self: &OutStream, b: u8) -> %void {
73 if (self.buffer.len == self.index) %return self.flush();94 if (self.buffer.len == self.index) %return self.flush();
74 self.buffer[self.index] = b;95 self.buffer[self.index] = b;
...@@ -76,6 +97,11 @@ pub const OutStream = struct {...@@ -76,6 +97,11 @@ pub const OutStream = struct {
76 }97 }
7798
78 pub fn write(self: &OutStream, bytes: []const u8) -> %void {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 var src_index: usize = 0;105 var src_index: usize = 0;
80106
81 while (src_index < bytes.len) {107 while (src_index < bytes.len) {
...@@ -119,35 +145,15 @@ pub const OutStream = struct {...@@ -119,35 +145,15 @@ pub const OutStream = struct {
119 }145 }
120146
121 pub fn flush(self: &OutStream) -> %void {147 pub fn flush(self: &OutStream) -> %void {
122 while (true) {148 if (self.index != 0) {
123 const write_ret = system.write(self.fd, &self.buffer[0], self.index);149 %return os.posixWrite(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 }
138 self.index = 0;150 self.index = 0;
139 return;
140 }151 }
141 }152 }
142153
143 pub fn close(self: &OutStream) {154 pub fn close(self: &OutStream) {
144 while (true) {155 assert(self.index == 0);
145 const close_ret = system.close(self.fd);156 os.posixClose(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 }
151 }157 }
152};158};
153159
...@@ -156,65 +162,32 @@ pub const OutStream = struct {...@@ -156,65 +162,32 @@ pub const OutStream = struct {
156pub const InStream = struct {162pub const InStream = struct {
157 fd: i32,163 fd: i32,
158164
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 /// Call close to clean up.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 switch (@compileVar("os")) {171 switch (@compileVar("os")) {
162 Os.linux, Os.darwin => {172 Os.linux, Os.darwin, Os.macosx, Os.ios => {
163 while (true) {173 const flags = system.O_LARGEFILE|system.O_RDONLY;
164 const result = system.open(path, system.O_LARGEFILE|system.O_RDONLY, 0);174 const fd = %return os.posixOpen(path, flags, 0, allocator);
165 const err = system.getErrno(result);175 return InStream {
166 if (err > 0) {176 .fd = fd,
167 return switch (err) {177 };
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 }
191 },178 },
192 else => @compileError("unsupported OS"),179 else => @compileError("Unsupported OS"),
193 }180 }
194
195 }181 }
196182
197 /// Upon success, the stream is in an uninitialized state. To continue using it,183 /// Upon success, the stream is in an uninitialized state. To continue using it,
198 /// you must use the open() function.184 /// you must use the open() function.
199 pub fn close(is: &InStream) -> %void {185 pub fn close(self: &InStream) {
200 switch (@compileVar("os")) {186 switch (@compileVar("os")) {
201 Os.linux, Os.darwin => {187 Os.linux, Os.darwin, Os.macosx, Os.ios => {
202 while (true) {188 os.posixClose(self.fd);
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 }
216 },189 },
217 else => @compileError("unsupported OS"),190 else => @compileError("Unsupported OS"),
218 }191 }
219 }192 }
220193
...@@ -373,15 +346,28 @@ pub const InStream = struct {...@@ -373,15 +346,28 @@ pub const InStream = struct {
373 }346 }
374};347};
375348
376pub fn openSelfExe(stream: &InStream) -> %void {349pub fn openSelfExe() -> %InStream {
377 switch (@compileVar("os")) {350 switch (@compileVar("os")) {
378 Os.linux => {351 Os.linux => {
379 %return stream.open("/proc/self/exe");352 return InStream.open("/proc/self/exe", null);
380 },353 },
381 Os.darwin => {354 Os.darwin => {
382 %%stderr.printf("TODO: openSelfExe on Darwin\n");355 debug.panic("TODO: openSelfExe on Darwin");
383 os.abort();
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.
365pub 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,17 +71,10 @@ pub fn close(fd: i32) -> usize {
71 arch.syscall1(arch.SYS_close, usize(fd))71 arch.syscall1(arch.SYS_close, usize(fd))
72}72}
7373
74pub fn open_c(path: &const u8, flags: usize, perm: usize) -> usize {74pub fn open(path: &const u8, flags: usize, perm: usize) -> usize {
75 arch.syscall3(arch.SYS_open, usize(path), flags, perm)75 arch.syscall3(arch.SYS_open, usize(path), flags, perm)
76}76}
7777
78pub 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
85pub fn read(fd: i32, buf: &u8, count: usize) -> usize {78pub fn read(fd: i32, buf: &u8, count: usize) -> usize {
86 arch.syscall3(arch.SYS_read, usize(fd), usize(buf), count)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,6 +8,8 @@ pub const posix = switch(@compileVar("os")) {
8 else => @compileError("Unsupported OS"),8 else => @compileError("Unsupported OS"),
9};9};
1010
11pub const max_noalloc_path_len = 1024;
12
11const debug = @import("../debug.zig");13const debug = @import("../debug.zig");
12const assert = debug.assert;14const assert = debug.assert;
1315
...@@ -105,11 +107,12 @@ fn makePipe() -> %[2]i32 {...@@ -105,11 +107,12 @@ fn makePipe() -> %[2]i32 {
105}107}
106108
107fn destroyPipe(pipe: &const [2]i32) {109fn destroyPipe(pipe: &const [2]i32) {
108 closeNoIntr((*pipe)[0]);110 posixClose((*pipe)[0]);
109 closeNoIntr((*pipe)[1]);111 posixClose((*pipe)[1]);
110}112}
111113
112fn closeNoIntr(fd: i32) {114/// Calls POSIX close, and keeps trying if it gets interrupted.
115pub fn posixClose(fd: i32) {
113 while (true) {116 while (true) {
114 const err = posix.getErrno(posix.close(fd));117 const err = posix.getErrno(posix.close(fd));
115 if (err == errno.EINTR) {118 if (err == errno.EINTR) {
...@@ -120,9 +123,56 @@ fn closeNoIntr(fd: i32) {...@@ -120,9 +123,56 @@ fn closeNoIntr(fd: i32) {
120 }123 }
121}124}
122125
123fn openNoIntr(path: []const u8, flags: usize, perm: usize) -> %i32 {126/// Calls POSIX write, and keeps trying if it gets interrupted.
127pub 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.
155pub 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 while (true) {174 while (true) {
125 const result = posix.open(path, flags, perm);175 const result = posix.open(path0.ptr, flags, perm);
126 const err = posix.getErrno(result);176 const err = posix.getErrno(result);
127 if (err > 0) {177 if (err > 0) {
128 return switch (err) {178 return switch (err) {
...@@ -247,8 +297,8 @@ pub const ChildProcess = struct {...@@ -247,8 +297,8 @@ pub const ChildProcess = struct {
247297
248 pub fn wait(self: &ChildProcess) -> %Term {298 pub fn wait(self: &ChildProcess) -> %Term {
249 defer {299 defer {
250 closeNoIntr(self.err_pipe[0]);300 posixClose(self.err_pipe[0]);
251 closeNoIntr(self.err_pipe[1]);301 posixClose(self.err_pipe[1]);
252 };302 };
253303
254 var status: i32 = undefined;304 var status: i32 = undefined;
...@@ -328,13 +378,13 @@ pub const ChildProcess = struct {...@@ -328,13 +378,13 @@ pub const ChildProcess = struct {
328 const any_ignore = (stdin == StdIo.Ignore or stdout == StdIo.Ignore or stderr == StdIo.Ignore);378 const any_ignore = (stdin == StdIo.Ignore or stdout == StdIo.Ignore or stderr == StdIo.Ignore);
329 // TODO issue #295379 // TODO issue #295
330 //const dev_null_fd = if (any_ignore) {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 //} else {382 //} else {
333 // undefined383 // undefined
334 //};384 //};
335 var dev_null_fd: i32 = undefined;385 var dev_null_fd: i32 = undefined;
336 if (any_ignore)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);
338388
339 // This pipe is used to communicate errors between the time of fork389 // This pipe is used to communicate errors between the time of fork
340 // and execve from the child process to the parent process.390 // and execve from the child process to the parent process.
...@@ -374,10 +424,10 @@ pub const ChildProcess = struct {...@@ -374,10 +424,10 @@ pub const ChildProcess = struct {
374 }424 }
375425
376 // we are the parent426 // we are the parent
377 if (stdin == StdIo.Pipe) { closeNoIntr(stdin_pipe[0]); }427 if (stdin == StdIo.Pipe) { posixClose(stdin_pipe[0]); }
378 if (stdout == StdIo.Pipe) { closeNoIntr(stdout_pipe[1]); }428 if (stdout == StdIo.Pipe) { posixClose(stdout_pipe[1]); }
379 if (stderr == StdIo.Pipe) { closeNoIntr(stderr_pipe[1]); }429 if (stderr == StdIo.Pipe) { posixClose(stderr_pipe[1]); }
380 if (any_ignore) { closeNoIntr(dev_null_fd); }430 if (any_ignore) { posixClose(dev_null_fd); }
381431
382 return ChildProcess {432 return ChildProcess {
383 .pid = i32(pid),433 .pid = i32(pid),
...@@ -412,7 +462,7 @@ pub const ChildProcess = struct {...@@ -412,7 +462,7 @@ pub const ChildProcess = struct {
412 fn setUpChildIo(stdio: StdIo, pipe_fd: i32, std_fileno: i32, dev_null_fd: i32) -> %void {462 fn setUpChildIo(stdio: StdIo, pipe_fd: i32, std_fileno: i32, dev_null_fd: i32) -> %void {
413 switch (stdio) {463 switch (stdio) {
414 StdIo.Pipe => %return dup2NoIntr(pipe_fd, std_fileno),464 StdIo.Pipe => %return dup2NoIntr(pipe_fd, std_fileno),
415 StdIo.Close => closeNoIntr(std_fileno),465 StdIo.Close => posixClose(std_fileno),
416 StdIo.Inherit => {},466 StdIo.Inherit => {},
417 StdIo.Ignore => %return dup2NoIntr(dev_null_fd, std_fileno),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,39 +303,18 @@ pub fn pwrite(fd: i32, buf: &const u8, count: usize, offset: usize) -> usize {
303 arch.syscall4(arch.SYS_pwrite, usize(fd), usize(buf), count, offset)303 arch.syscall4(arch.SYS_pwrite, usize(fd), usize(buf), count, offset)
304}304}
305305
306pub fn open_c(path: &const u8, flags: usize, perm: usize) -> usize {306pub fn open(path: &const u8, flags: usize, perm: usize) -> usize {
307 arch.syscall3(arch.SYS_open, usize(path), flags, perm)307 arch.syscall3(arch.SYS_open, usize(path), flags, perm)
308}308}
309309
310pub fn open(path: []const u8, flags: usize, perm: usize) -> usize {310pub fn create(path: &const u8, 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
317pub fn create_c(path: &const u8, perm: usize) -> usize {
318 arch.syscall2(arch.SYS_creat, usize(path), perm)311 arch.syscall2(arch.SYS_creat, usize(path), perm)
319}312}
320313
321pub fn create(path: []const u8, perm: usize) -> usize {314pub fn openat(dirfd: i32, path: &const u8, flags: usize, mode: 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
328pub fn openat_c(dirfd: i32, path: &const u8, flags: usize, mode: usize) -> usize {
329 arch.syscall4(arch.SYS_openat, usize(dirfd), usize(path), flags, mode)315 arch.syscall4(arch.SYS_openat, usize(dirfd), usize(path), flags, mode)
330}316}
331317
332pub 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
339pub fn close(fd: i32) -> usize {318pub fn close(fd: i32) -> usize {
340 arch.syscall1(arch.SYS_close, usize(fd))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,6 +33,7 @@ export nakedcc fn _start() -> noreturn {
33}33}
3434
35fn callMain(envp: &?&u8) -> %void {35fn callMain(envp: &?&u8) -> %void {
36 // TODO issue #225
36 const args = @alloca([]u8, argc);37 const args = @alloca([]u8, argc);
37 for (args) |_, i| {38 for (args) |_, i| {
38 const ptr = argv[i];39 const ptr = argv[i];
...@@ -41,6 +42,7 @@ fn callMain(envp: &?&u8) -> %void {...@@ -41,6 +42,7 @@ fn callMain(envp: &?&u8) -> %void {
4142
42 var env_count: usize = 0;43 var env_count: usize = 0;
43 while (envp[env_count] != null; env_count += 1) {}44 while (envp[env_count] != null; env_count += 1) {}
45 // TODO issue #225
44 const environ = @alloca(std.os.EnvPair, env_count);46 const environ = @alloca(std.os.EnvPair, env_count);
45 for (environ) |_, env_i| {47 for (environ) |_, env_i| {
46 const ptr = ??envp[env_i];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,6 +11,6 @@ export coldcc fn __zig_panic(message_ptr: &const u8, message_len: usize) -> nore
11 } else if (@compileVar("os") == Os.freestanding) {11 } else if (@compileVar("os") == Os.freestanding) {
12 while (true) {}12 while (true) {}
13 } else {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}