| author | |
| committer | |
| log | a94304d3e4b9220d2b22501eb2666e5aa89df236 |
| tree | a3b85b53581853b62dabf6f763feca099e4fee84 |
| parent | d974afde1d366a28f1b07bff6ebfb5c5756d3b61 |
| parent | 2b2bf53a49616192e2b2bdf40b88400964ff1500 |
| signature | Commit is signed but in an unrecognized format. |
45 files changed, 779 insertions(+), 345 deletions(-)
CMakeLists.txt+1| ... | @@ -596,6 +596,7 @@ set(ZIG_STD_FILES | ... | @@ -596,6 +596,7 @@ set(ZIG_STD_FILES |
| 596 | "os/windows/ntdll.zig" | 596 | "os/windows/ntdll.zig" |
| 597 | "os/windows/ole32.zig" | 597 | "os/windows/ole32.zig" |
| 598 | "os/windows/shell32.zig" | 598 | "os/windows/shell32.zig" |
| 599 | "os/windows/tls.zig" | ||
| 599 | "os/windows/util.zig" | 600 | "os/windows/util.zig" |
| 600 | "os/zen.zig" | 601 | "os/zen.zig" |
| 601 | "pdb.zig" | 602 | "pdb.zig" |
build.zig+27-8| ... | @@ -16,7 +16,10 @@ pub fn build(b: *Builder) !void { | ... | @@ -16,7 +16,10 @@ pub fn build(b: *Builder) !void { |
| 16 | var docgen_exe = b.addExecutable("docgen", "doc/docgen.zig"); | 16 | var docgen_exe = b.addExecutable("docgen", "doc/docgen.zig"); |
| 17 | 17 | ||
| 18 | const rel_zig_exe = try os.path.relative(b.allocator, b.build_root, b.zig_exe); | 18 | const rel_zig_exe = try os.path.relative(b.allocator, b.build_root, b.zig_exe); |
| 19 | const langref_out_path = os.path.join(b.allocator, b.cache_root, "langref.html") catch unreachable; | 19 | const langref_out_path = os.path.join( |
| 20 | b.allocator, | ||
| 21 | [][]const u8{ b.cache_root, "langref.html" }, | ||
| 22 | ) catch unreachable; | ||
| 20 | var docgen_cmd = b.addCommand(null, b.env_map, [][]const u8{ | 23 | var docgen_cmd = b.addCommand(null, b.env_map, [][]const u8{ |
| 21 | docgen_exe.getOutputPath(), | 24 | docgen_exe.getOutputPath(), |
| 22 | rel_zig_exe, | 25 | rel_zig_exe, |
| ... | @@ -125,13 +128,19 @@ fn dependOnLib(b: *Builder, lib_exe_obj: var, dep: LibraryDep) void { | ... | @@ -125,13 +128,19 @@ fn dependOnLib(b: *Builder, lib_exe_obj: var, dep: LibraryDep) void { |
| 125 | for (dep.libdirs.toSliceConst()) |lib_dir| { | 128 | for (dep.libdirs.toSliceConst()) |lib_dir| { |
| 126 | lib_exe_obj.addLibPath(lib_dir); | 129 | lib_exe_obj.addLibPath(lib_dir); |
| 127 | } | 130 | } |
| 128 | const lib_dir = os.path.join(b.allocator, dep.prefix, "lib") catch unreachable; | 131 | const lib_dir = os.path.join( |
| 132 | b.allocator, | ||
| 133 | [][]const u8{ dep.prefix, "lib" }, | ||
| 134 | ) catch unreachable; | ||
| 129 | for (dep.system_libs.toSliceConst()) |lib| { | 135 | for (dep.system_libs.toSliceConst()) |lib| { |
| 130 | const static_bare_name = if (mem.eql(u8, lib, "curses")) | 136 | const static_bare_name = if (mem.eql(u8, lib, "curses")) |
| 131 | ([]const u8)("libncurses.a") | 137 | ([]const u8)("libncurses.a") |
| 132 | else | 138 | else |
| 133 | b.fmt("lib{}.a", lib); | 139 | b.fmt("lib{}.a", lib); |
| 134 | const static_lib_name = os.path.join(b.allocator, lib_dir, static_bare_name) catch unreachable; | 140 | const static_lib_name = os.path.join( |
| 141 | b.allocator, | ||
| 142 | [][]const u8{ lib_dir, static_bare_name }, | ||
| 143 | ) catch unreachable; | ||
| 135 | const have_static = fileExists(static_lib_name) catch unreachable; | 144 | const have_static = fileExists(static_lib_name) catch unreachable; |
| 136 | if (have_static) { | 145 | if (have_static) { |
| 137 | lib_exe_obj.addObjectFile(static_lib_name); | 146 | lib_exe_obj.addObjectFile(static_lib_name); |
| ... | @@ -159,7 +168,11 @@ fn fileExists(filename: []const u8) !bool { | ... | @@ -159,7 +168,11 @@ fn fileExists(filename: []const u8) !bool { |
| 159 | 168 | ||
| 160 | fn addCppLib(b: *Builder, lib_exe_obj: var, cmake_binary_dir: []const u8, lib_name: []const u8) void { | 169 | fn addCppLib(b: *Builder, lib_exe_obj: var, cmake_binary_dir: []const u8, lib_name: []const u8) void { |
| 161 | const lib_prefix = if (lib_exe_obj.target.isWindows()) "" else "lib"; | 170 | const lib_prefix = if (lib_exe_obj.target.isWindows()) "" else "lib"; |
| 162 | lib_exe_obj.addObjectFile(os.path.join(b.allocator, cmake_binary_dir, "zig_cpp", b.fmt("{}{}{}", lib_prefix, lib_name, lib_exe_obj.target.libFileExt())) catch unreachable); | 171 | lib_exe_obj.addObjectFile(os.path.join(b.allocator, [][]const u8{ |
| 172 | cmake_binary_dir, | ||
| 173 | "zig_cpp", | ||
| 174 | b.fmt("{}{}{}", lib_prefix, lib_name, lib_exe_obj.target.libFileExt()), | ||
| 175 | }) catch unreachable); | ||
| 163 | } | 176 | } |
| 164 | 177 | ||
| 165 | const LibraryDep = struct { | 178 | const LibraryDep = struct { |
| ... | @@ -235,8 +248,11 @@ fn findLLVM(b: *Builder, llvm_config_exe: []const u8) !LibraryDep { | ... | @@ -235,8 +248,11 @@ fn findLLVM(b: *Builder, llvm_config_exe: []const u8) !LibraryDep { |
| 235 | pub fn installStdLib(b: *Builder, stdlib_files: []const u8) void { | 248 | pub fn installStdLib(b: *Builder, stdlib_files: []const u8) void { |
| 236 | var it = mem.tokenize(stdlib_files, ";"); | 249 | var it = mem.tokenize(stdlib_files, ";"); |
| 237 | while (it.next()) |stdlib_file| { | 250 | while (it.next()) |stdlib_file| { |
| 238 | const src_path = os.path.join(b.allocator, "std", stdlib_file) catch unreachable; | 251 | const src_path = os.path.join(b.allocator, [][]const u8{ "std", stdlib_file }) catch unreachable; |
| 239 | const dest_path = os.path.join(b.allocator, "lib", "zig", "std", stdlib_file) catch unreachable; | 252 | const dest_path = os.path.join( |
| 253 | b.allocator, | ||
| 254 | [][]const u8{ "lib", "zig", "std", stdlib_file }, | ||
| 255 | ) catch unreachable; | ||
| 240 | b.installFile(src_path, dest_path); | 256 | b.installFile(src_path, dest_path); |
| 241 | } | 257 | } |
| 242 | } | 258 | } |
| ... | @@ -244,8 +260,11 @@ pub fn installStdLib(b: *Builder, stdlib_files: []const u8) void { | ... | @@ -244,8 +260,11 @@ pub fn installStdLib(b: *Builder, stdlib_files: []const u8) void { |
| 244 | pub fn installCHeaders(b: *Builder, c_header_files: []const u8) void { | 260 | pub fn installCHeaders(b: *Builder, c_header_files: []const u8) void { |
| 245 | var it = mem.tokenize(c_header_files, ";"); | 261 | var it = mem.tokenize(c_header_files, ";"); |
| 246 | while (it.next()) |c_header_file| { | 262 | while (it.next()) |c_header_file| { |
| 247 | const src_path = os.path.join(b.allocator, "c_headers", c_header_file) catch unreachable; | 263 | const src_path = os.path.join(b.allocator, [][]const u8{ "c_headers", c_header_file }) catch unreachable; |
| 248 | const dest_path = os.path.join(b.allocator, "lib", "zig", "include", c_header_file) catch unreachable; | 264 | const dest_path = os.path.join( |
| 265 | b.allocator, | ||
| 266 | [][]const u8{ "lib", "zig", "include", c_header_file }, | ||
| 267 | ) catch unreachable; | ||
| 249 | b.installFile(src_path, dest_path); | 268 | b.installFile(src_path, dest_path); |
| 250 | } | 269 | } |
| 251 | } | 270 | } |
doc/docgen.zig+20-5| ... | @@ -990,13 +990,19 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var | ... | @@ -990,13 +990,19 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var |
| 990 | try tokenizeAndPrint(tokenizer, out, code.source_token); | 990 | try tokenizeAndPrint(tokenizer, out, code.source_token); |
| 991 | try out.write("</pre>"); | 991 | try out.write("</pre>"); |
| 992 | const name_plus_ext = try std.fmt.allocPrint(allocator, "{}.zig", code.name); | 992 | const name_plus_ext = try std.fmt.allocPrint(allocator, "{}.zig", code.name); |
| 993 | const tmp_source_file_name = try os.path.join(allocator, tmp_dir_name, name_plus_ext); | 993 | const tmp_source_file_name = try os.path.join( |
| 994 | allocator, | ||
| 995 | [][]const u8{ tmp_dir_name, name_plus_ext }, | ||
| 996 | ); | ||
| 994 | try io.writeFile(tmp_source_file_name, trimmed_raw_source); | 997 | try io.writeFile(tmp_source_file_name, trimmed_raw_source); |
| 995 | 998 | ||
| 996 | switch (code.id) { | 999 | switch (code.id) { |
| 997 | Code.Id.Exe => |expected_outcome| { | 1000 | Code.Id.Exe => |expected_outcome| { |
| 998 | const name_plus_bin_ext = try std.fmt.allocPrint(allocator, "{}{}", code.name, exe_ext); | 1001 | const name_plus_bin_ext = try std.fmt.allocPrint(allocator, "{}{}", code.name, exe_ext); |
| 999 | const tmp_bin_file_name = try os.path.join(allocator, tmp_dir_name, name_plus_bin_ext); | 1002 | const tmp_bin_file_name = try os.path.join( |
| 1003 | allocator, | ||
| 1004 | [][]const u8{ tmp_dir_name, name_plus_bin_ext }, | ||
| 1005 | ); | ||
| 1000 | var build_args = std.ArrayList([]const u8).init(allocator); | 1006 | var build_args = std.ArrayList([]const u8).init(allocator); |
| 1001 | defer build_args.deinit(); | 1007 | defer build_args.deinit(); |
| 1002 | try build_args.appendSlice([][]const u8{ | 1008 | try build_args.appendSlice([][]const u8{ |
| ... | @@ -1024,7 +1030,10 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var | ... | @@ -1024,7 +1030,10 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var |
| 1024 | } | 1030 | } |
| 1025 | for (code.link_objects) |link_object| { | 1031 | for (code.link_objects) |link_object| { |
| 1026 | const name_with_ext = try std.fmt.allocPrint(allocator, "{}{}", link_object, obj_ext); | 1032 | const name_with_ext = try std.fmt.allocPrint(allocator, "{}{}", link_object, obj_ext); |
| 1027 | const full_path_object = try os.path.join(allocator, tmp_dir_name, name_with_ext); | 1033 | const full_path_object = try os.path.join( |
| 1034 | allocator, | ||
| 1035 | [][]const u8{ tmp_dir_name, name_with_ext }, | ||
| 1036 | ); | ||
| 1028 | try build_args.append("--object"); | 1037 | try build_args.append("--object"); |
| 1029 | try build_args.append(full_path_object); | 1038 | try build_args.append(full_path_object); |
| 1030 | try out.print(" --object {}", name_with_ext); | 1039 | try out.print(" --object {}", name_with_ext); |
| ... | @@ -1216,12 +1225,18 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var | ... | @@ -1216,12 +1225,18 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var |
| 1216 | }, | 1225 | }, |
| 1217 | Code.Id.Obj => |maybe_error_match| { | 1226 | Code.Id.Obj => |maybe_error_match| { |
| 1218 | const name_plus_obj_ext = try std.fmt.allocPrint(allocator, "{}{}", code.name, obj_ext); | 1227 | const name_plus_obj_ext = try std.fmt.allocPrint(allocator, "{}{}", code.name, obj_ext); |
| 1219 | const tmp_obj_file_name = try os.path.join(allocator, tmp_dir_name, name_plus_obj_ext); | 1228 | const tmp_obj_file_name = try os.path.join( |
| 1229 | allocator, | ||
| 1230 | [][]const u8{ tmp_dir_name, name_plus_obj_ext }, | ||
| 1231 | ); | ||
| 1220 | var build_args = std.ArrayList([]const u8).init(allocator); | 1232 | var build_args = std.ArrayList([]const u8).init(allocator); |
| 1221 | defer build_args.deinit(); | 1233 | defer build_args.deinit(); |
| 1222 | 1234 | ||
| 1223 | const name_plus_h_ext = try std.fmt.allocPrint(allocator, "{}.h", code.name); | 1235 | const name_plus_h_ext = try std.fmt.allocPrint(allocator, "{}.h", code.name); |
| 1224 | const output_h_file_name = try os.path.join(allocator, tmp_dir_name, name_plus_h_ext); | 1236 | const output_h_file_name = try os.path.join( |
| 1237 | allocator, | ||
| 1238 | [][]const u8{ tmp_dir_name, name_plus_h_ext }, | ||
| 1239 | ); | ||
| 1225 | 1240 | ||
| 1226 | try build_args.appendSlice([][]const u8{ | 1241 | try build_args.appendSlice([][]const u8{ |
| 1227 | zig_exe, | 1242 | zig_exe, |
doc/langref.html.in+15-6| ... | @@ -3192,7 +3192,16 @@ fn foo() void { } | ... | @@ -3192,7 +3192,16 @@ fn foo() void { } |
| 3192 | {#code_end#} | 3192 | {#code_end#} |
| 3193 | {#header_open|Pass-by-value Parameters#} | 3193 | {#header_open|Pass-by-value Parameters#} |
| 3194 | <p> | 3194 | <p> |
| 3195 | In Zig, structs, unions, and enums with payloads can be passed directly to a function: | 3195 | Primitive types such as {#link|Integers#} and {#link|Floats#} passed as parameters |
| 3196 | are copied, and then the copy is available in the function body. This is called "passing by value". | ||
| 3197 | Copying a primitive type is essentially free and typically involves nothing more than | ||
| 3198 | setting a register. | ||
| 3199 | </p> | ||
| 3200 | <p> | ||
| 3201 | Structs, unions, and arrays can sometimes be more efficiently passed as a reference, since a copy | ||
| 3202 | could be arbitrarily expensive depending on the size. When these types are passed | ||
| 3203 | as parameters, Zig may choose to copy and pass by value, or pass by reference, whichever way | ||
| 3204 | Zig decides will be faster. This is made possible, in part, by the fact that parameters are immutable. | ||
| 3196 | </p> | 3205 | </p> |
| 3197 | {#code_begin|test#} | 3206 | {#code_begin|test#} |
| 3198 | const Point = struct { | 3207 | const Point = struct { |
| ... | @@ -3201,20 +3210,20 @@ const Point = struct { | ... | @@ -3201,20 +3210,20 @@ const Point = struct { |
| 3201 | }; | 3210 | }; |
| 3202 | 3211 | ||
| 3203 | fn foo(point: Point) i32 { | 3212 | fn foo(point: Point) i32 { |
| 3213 | // Here, `point` could be a reference, or a copy. The function body | ||
| 3214 | // can ignore the difference and treat it as a value. Be very careful | ||
| 3215 | // taking the address of the parameter - it should be treated as if | ||
| 3216 | // the address will become invalid when the function returns. | ||
| 3204 | return point.x + point.y; | 3217 | return point.x + point.y; |
| 3205 | } | 3218 | } |
| 3206 | 3219 | ||
| 3207 | const assert = @import("std").debug.assert; | 3220 | const assert = @import("std").debug.assert; |
| 3208 | 3221 | ||
| 3209 | test "pass aggregate type by non-copy value to function" { | 3222 | test "pass struct to function" { |
| 3210 | assert(foo(Point{ .x = 1, .y = 2 }) == 3); | 3223 | assert(foo(Point{ .x = 1, .y = 2 }) == 3); |
| 3211 | } | 3224 | } |
| 3212 | {#code_end#} | 3225 | {#code_end#} |
| 3213 | <p> | 3226 | <p> |
| 3214 | In this case, the value may be passed by reference, or by value, whichever way | ||
| 3215 | Zig decides will be faster. | ||
| 3216 | </p> | ||
| 3217 | <p> | ||
| 3218 | For extern functions, Zig follows the C ABI for passing structs and unions by value. | 3227 | For extern functions, Zig follows the C ABI for passing structs and unions by value. |
| 3219 | </p> | 3228 | </p> |
| 3220 | {#header_close#} | 3229 | {#header_close#} |
doc/targets.md deleted-15| ... | @@ -1,15 +0,0 @@ | ||
| 1 | # How to Add Support For More Targets | ||
| 2 | |||
| 3 | Create bootstrap code in std/bootstrap.zig and add conditional compilation | ||
| 4 | logic. This code is responsible for the real executable entry point, calling | ||
| 5 | main() and making the exit syscall when main returns. | ||
| 6 | |||
| 7 | How to pass a byvalue struct parameter in the C calling convention is | ||
| 8 | target-specific. Add logic for how to do function prototypes and function calls | ||
| 9 | for the target when an exported or external function has a byvalue struct. | ||
| 10 | |||
| 11 | Write the target-specific code in the standard library. | ||
| 12 | |||
| 13 | Update the C integer types to be the correct size for the target. | ||
| 14 | |||
| 15 | Make sure that `c_longdouble` codegens the correct floating point value. | ||
src-self-hosted/compilation.zig+3-3| ... | @@ -487,7 +487,7 @@ pub const Compilation = struct { | ... | @@ -487,7 +487,7 @@ pub const Compilation = struct { |
| 487 | comp.name = try Buffer.init(comp.arena(), name); | 487 | comp.name = try Buffer.init(comp.arena(), name); |
| 488 | comp.llvm_triple = try target.getTriple(comp.arena()); | 488 | comp.llvm_triple = try target.getTriple(comp.arena()); |
| 489 | comp.llvm_target = try Target.llvmTargetFromTriple(comp.llvm_triple); | 489 | comp.llvm_target = try Target.llvmTargetFromTriple(comp.llvm_triple); |
| 490 | comp.zig_std_dir = try std.os.path.join(comp.arena(), zig_lib_dir, "std"); | 490 | comp.zig_std_dir = try std.os.path.join(comp.arena(), [][]const u8{ zig_lib_dir, "std" }); |
| 491 | 491 | ||
| 492 | const opt_level = switch (build_mode) { | 492 | const opt_level = switch (build_mode) { |
| 493 | builtin.Mode.Debug => llvm.CodeGenLevelNone, | 493 | builtin.Mode.Debug => llvm.CodeGenLevelNone, |
| ... | @@ -1198,7 +1198,7 @@ pub const Compilation = struct { | ... | @@ -1198,7 +1198,7 @@ pub const Compilation = struct { |
| 1198 | const file_name = try std.fmt.allocPrint(self.gpa(), "{}{}", file_prefix[0..], suffix); | 1198 | const file_name = try std.fmt.allocPrint(self.gpa(), "{}{}", file_prefix[0..], suffix); |
| 1199 | defer self.gpa().free(file_name); | 1199 | defer self.gpa().free(file_name); |
| 1200 | 1200 | ||
| 1201 | const full_path = try os.path.join(self.gpa(), tmp_dir, file_name[0..]); | 1201 | const full_path = try os.path.join(self.gpa(), [][]const u8{ tmp_dir, file_name[0..] }); |
| 1202 | errdefer self.gpa().free(full_path); | 1202 | errdefer self.gpa().free(full_path); |
| 1203 | 1203 | ||
| 1204 | return Buffer.fromOwnedSlice(self.gpa(), full_path); | 1204 | return Buffer.fromOwnedSlice(self.gpa(), full_path); |
| ... | @@ -1219,7 +1219,7 @@ pub const Compilation = struct { | ... | @@ -1219,7 +1219,7 @@ pub const Compilation = struct { |
| 1219 | const zig_dir_path = try getZigDir(self.gpa()); | 1219 | const zig_dir_path = try getZigDir(self.gpa()); |
| 1220 | defer self.gpa().free(zig_dir_path); | 1220 | defer self.gpa().free(zig_dir_path); |
| 1221 | 1221 | ||
| 1222 | const tmp_dir = try os.path.join(self.arena(), zig_dir_path, comp_dir_name[0..]); | 1222 | const tmp_dir = try os.path.join(self.arena(), [][]const u8{ zig_dir_path, comp_dir_name[0..] }); |
| 1223 | try os.makePath(self.gpa(), tmp_dir); | 1223 | try os.makePath(self.gpa(), tmp_dir); |
| 1224 | return tmp_dir; | 1224 | return tmp_dir; |
| 1225 | } | 1225 | } |
src-self-hosted/introspect.zig+2-2| ... | @@ -8,10 +8,10 @@ const warn = std.debug.warn; | ... | @@ -8,10 +8,10 @@ const warn = std.debug.warn; |
| 8 | 8 | ||
| 9 | /// Caller must free result | 9 | /// Caller must free result |
| 10 | pub fn testZigInstallPrefix(allocator: *mem.Allocator, test_path: []const u8) ![]u8 { | 10 | pub fn testZigInstallPrefix(allocator: *mem.Allocator, test_path: []const u8) ![]u8 { |
| 11 | const test_zig_dir = try os.path.join(allocator, test_path, "lib", "zig"); | 11 | const test_zig_dir = try os.path.join(allocator, [][]const u8{ test_path, "lib", "zig" }); |
| 12 | errdefer allocator.free(test_zig_dir); | 12 | errdefer allocator.free(test_zig_dir); |
| 13 | 13 | ||
| 14 | const test_index_file = try os.path.join(allocator, test_zig_dir, "std", "index.zig"); | 14 | const test_index_file = try os.path.join(allocator, [][]const u8{ test_zig_dir, "std", "index.zig" }); |
| 15 | defer allocator.free(test_index_file); | 15 | defer allocator.free(test_index_file); |
| 16 | 16 | ||
| 17 | var file = try os.File.openRead(test_index_file); | 17 | var file = try os.File.openRead(test_index_file); |
src-self-hosted/libc_installation.zig+13-4| ... | @@ -230,7 +230,7 @@ pub const LibCInstallation = struct { | ... | @@ -230,7 +230,7 @@ pub const LibCInstallation = struct { |
| 230 | while (path_i < search_paths.len) : (path_i += 1) { | 230 | while (path_i < search_paths.len) : (path_i += 1) { |
| 231 | const search_path_untrimmed = search_paths.at(search_paths.len - path_i - 1); | 231 | const search_path_untrimmed = search_paths.at(search_paths.len - path_i - 1); |
| 232 | const search_path = std.mem.trimLeft(u8, search_path_untrimmed, " "); | 232 | const search_path = std.mem.trimLeft(u8, search_path_untrimmed, " "); |
| 233 | const stdlib_path = try std.os.path.join(loop.allocator, search_path, "stdlib.h"); | 233 | const stdlib_path = try std.os.path.join(loop.allocator, [][]const u8{ search_path, "stdlib.h" }); |
| 234 | defer loop.allocator.free(stdlib_path); | 234 | defer loop.allocator.free(stdlib_path); |
| 235 | 235 | ||
| 236 | if (try fileExists(stdlib_path)) { | 236 | if (try fileExists(stdlib_path)) { |
| ... | @@ -254,7 +254,10 @@ pub const LibCInstallation = struct { | ... | @@ -254,7 +254,10 @@ pub const LibCInstallation = struct { |
| 254 | const stream = &std.io.BufferOutStream.init(&result_buf).stream; | 254 | const stream = &std.io.BufferOutStream.init(&result_buf).stream; |
| 255 | try stream.print("{}\\Include\\{}\\ucrt", search.path, search.version); | 255 | try stream.print("{}\\Include\\{}\\ucrt", search.path, search.version); |
| 256 | 256 | ||
| 257 | const stdlib_path = try std.os.path.join(loop.allocator, result_buf.toSliceConst(), "stdlib.h"); | 257 | const stdlib_path = try std.os.path.join( |
| 258 | loop.allocator, | ||
| 259 | [][]const u8{ result_buf.toSliceConst(), "stdlib.h" }, | ||
| 260 | ); | ||
| 258 | defer loop.allocator.free(stdlib_path); | 261 | defer loop.allocator.free(stdlib_path); |
| 259 | 262 | ||
| 260 | if (try fileExists(stdlib_path)) { | 263 | if (try fileExists(stdlib_path)) { |
| ... | @@ -283,7 +286,10 @@ pub const LibCInstallation = struct { | ... | @@ -283,7 +286,10 @@ pub const LibCInstallation = struct { |
| 283 | builtin.Arch.aarch64v8 => try stream.write("arm"), | 286 | builtin.Arch.aarch64v8 => try stream.write("arm"), |
| 284 | else => return error.UnsupportedArchitecture, | 287 | else => return error.UnsupportedArchitecture, |
| 285 | } | 288 | } |
| 286 | const ucrt_lib_path = try std.os.path.join(loop.allocator, result_buf.toSliceConst(), "ucrt.lib"); | 289 | const ucrt_lib_path = try std.os.path.join( |
| 290 | loop.allocator, | ||
| 291 | [][]const u8{ result_buf.toSliceConst(), "ucrt.lib" }, | ||
| 292 | ); | ||
| 287 | defer loop.allocator.free(ucrt_lib_path); | 293 | defer loop.allocator.free(ucrt_lib_path); |
| 288 | if (try fileExists(ucrt_lib_path)) { | 294 | if (try fileExists(ucrt_lib_path)) { |
| 289 | self.lib_dir = result_buf.toOwnedSlice(); | 295 | self.lib_dir = result_buf.toOwnedSlice(); |
| ... | @@ -358,7 +364,10 @@ pub const LibCInstallation = struct { | ... | @@ -358,7 +364,10 @@ pub const LibCInstallation = struct { |
| 358 | builtin.Arch.aarch64v8 => try stream.write("arm\\"), | 364 | builtin.Arch.aarch64v8 => try stream.write("arm\\"), |
| 359 | else => return error.UnsupportedArchitecture, | 365 | else => return error.UnsupportedArchitecture, |
| 360 | } | 366 | } |
| 361 | const kernel32_path = try std.os.path.join(loop.allocator, result_buf.toSliceConst(), "kernel32.lib"); | 367 | const kernel32_path = try std.os.path.join( |
| 368 | loop.allocator, | ||
| 369 | [][]const u8{ result_buf.toSliceConst(), "kernel32.lib" }, | ||
| 370 | ); | ||
| 362 | defer loop.allocator.free(kernel32_path); | 371 | defer loop.allocator.free(kernel32_path); |
| 363 | if (try fileExists(kernel32_path)) { | 372 | if (try fileExists(kernel32_path)) { |
| 364 | self.kernel32_lib_dir = result_buf.toOwnedSlice(); | 373 | self.kernel32_lib_dir = result_buf.toOwnedSlice(); |
src-self-hosted/link.zig+1-1| ... | @@ -315,7 +315,7 @@ fn constructLinkerArgsElf(ctx: *Context) !void { | ... | @@ -315,7 +315,7 @@ fn constructLinkerArgsElf(ctx: *Context) !void { |
| 315 | } | 315 | } |
| 316 | 316 | ||
| 317 | fn addPathJoin(ctx: *Context, dirname: []const u8, basename: []const u8) !void { | 317 | fn addPathJoin(ctx: *Context, dirname: []const u8, basename: []const u8) !void { |
| 318 | const full_path = try std.os.path.join(&ctx.arena.allocator, dirname, basename); | 318 | const full_path = try std.os.path.join(&ctx.arena.allocator, [][]const u8{ dirname, basename }); |
| 319 | const full_path_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, full_path); | 319 | const full_path_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, full_path); |
| 320 | try ctx.args.append(full_path_with_null.ptr); | 320 | try ctx.args.append(full_path_with_null.ptr); |
| 321 | } | 321 | } |
src-self-hosted/main.zig+1-1| ... | @@ -757,7 +757,7 @@ async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtErro | ... | @@ -757,7 +757,7 @@ async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtErro |
| 757 | var group = event.Group(FmtError!void).init(fmt.loop); | 757 | var group = event.Group(FmtError!void).init(fmt.loop); |
| 758 | while (try dir.next()) |entry| { | 758 | while (try dir.next()) |entry| { |
| 759 | if (entry.kind == std.os.Dir.Entry.Kind.Directory or mem.endsWith(u8, entry.name, ".zig")) { | 759 | if (entry.kind == std.os.Dir.Entry.Kind.Directory or mem.endsWith(u8, entry.name, ".zig")) { |
| 760 | const full_path = try os.path.join(fmt.loop.allocator, file_path, entry.name); | 760 | const full_path = try os.path.join(fmt.loop.allocator, [][]const u8{ file_path, entry.name }); |
| 761 | try group.call(fmtPath, fmt, full_path, check_mode); | 761 | try group.call(fmtPath, fmt, full_path, check_mode); |
| 762 | } | 762 | } |
| 763 | } | 763 | } |
src-self-hosted/test.zig+2-2| ... | @@ -87,7 +87,7 @@ pub const TestContext = struct { | ... | @@ -87,7 +87,7 @@ pub const TestContext = struct { |
| 87 | ) !void { | 87 | ) !void { |
| 88 | var file_index_buf: [20]u8 = undefined; | 88 | var file_index_buf: [20]u8 = undefined; |
| 89 | const file_index = try std.fmt.bufPrint(file_index_buf[0..], "{}", self.file_index.incr()); | 89 | const file_index = try std.fmt.bufPrint(file_index_buf[0..], "{}", self.file_index.incr()); |
| 90 | const file1_path = try std.os.path.join(allocator, tmp_dir_name, file_index, file1); | 90 | const file1_path = try std.os.path.join(allocator, [][]const u8{ tmp_dir_name, file_index, file1 }); |
| 91 | 91 | ||
| 92 | if (std.os.path.dirname(file1_path)) |dirname| { | 92 | if (std.os.path.dirname(file1_path)) |dirname| { |
| 93 | try std.os.makePath(allocator, dirname); | 93 | try std.os.makePath(allocator, dirname); |
| ... | @@ -120,7 +120,7 @@ pub const TestContext = struct { | ... | @@ -120,7 +120,7 @@ pub const TestContext = struct { |
| 120 | ) !void { | 120 | ) !void { |
| 121 | var file_index_buf: [20]u8 = undefined; | 121 | var file_index_buf: [20]u8 = undefined; |
| 122 | const file_index = try std.fmt.bufPrint(file_index_buf[0..], "{}", self.file_index.incr()); | 122 | const file_index = try std.fmt.bufPrint(file_index_buf[0..], "{}", self.file_index.incr()); |
| 123 | const file1_path = try std.os.path.join(allocator, tmp_dir_name, file_index, file1); | 123 | const file1_path = try std.os.path.join(allocator, [][]const u8{ tmp_dir_name, file_index, file1 }); |
| 124 | 124 | ||
| 125 | const output_file = try std.fmt.allocPrint(allocator, "{}-out{}", file1_path, Target(Target.Native).exeFileExt()); | 125 | const output_file = try std.fmt.allocPrint(allocator, "{}-out{}", file1_path, Target(Target.Native).exeFileExt()); |
| 126 | if (std.os.path.dirname(file1_path)) |dirname| { | 126 | if (std.os.path.dirname(file1_path)) |dirname| { |
src/all_types.hpp+8-5| ... | @@ -544,12 +544,7 @@ struct AstNodeDefer { | ... | @@ -544,12 +544,7 @@ struct AstNodeDefer { |
| 544 | }; | 544 | }; |
| 545 | 545 | ||
| 546 | struct AstNodeVariableDeclaration { | 546 | struct AstNodeVariableDeclaration { |
| 547 | VisibMod visib_mod; | ||
| 548 | Buf *symbol; | 547 | Buf *symbol; |
| 549 | bool is_const; | ||
| 550 | bool is_comptime; | ||
| 551 | bool is_export; | ||
| 552 | bool is_extern; | ||
| 553 | // one or both of type and expr will be non null | 548 | // one or both of type and expr will be non null |
| 554 | AstNode *type; | 549 | AstNode *type; |
| 555 | AstNode *expr; | 550 | AstNode *expr; |
| ... | @@ -559,6 +554,13 @@ struct AstNodeVariableDeclaration { | ... | @@ -559,6 +554,13 @@ struct AstNodeVariableDeclaration { |
| 559 | AstNode *align_expr; | 554 | AstNode *align_expr; |
| 560 | // populated if the "section(S)" is present | 555 | // populated if the "section(S)" is present |
| 561 | AstNode *section_expr; | 556 | AstNode *section_expr; |
| 557 | Token *threadlocal_tok; | ||
| 558 | |||
| 559 | VisibMod visib_mod; | ||
| 560 | bool is_const; | ||
| 561 | bool is_comptime; | ||
| 562 | bool is_export; | ||
| 563 | bool is_extern; | ||
| 562 | }; | 564 | }; |
| 563 | 565 | ||
| 564 | struct AstNodeTestDecl { | 566 | struct AstNodeTestDecl { |
| ... | @@ -1873,6 +1875,7 @@ struct ZigVar { | ... | @@ -1873,6 +1875,7 @@ struct ZigVar { |
| 1873 | bool shadowable; | 1875 | bool shadowable; |
| 1874 | bool src_is_const; | 1876 | bool src_is_const; |
| 1875 | bool gen_is_const; | 1877 | bool gen_is_const; |
| 1878 | bool is_thread_local; | ||
| 1876 | }; | 1879 | }; |
| 1877 | 1880 | ||
| 1878 | struct ErrorTableEntry { | 1881 | struct ErrorTableEntry { |
src/analyze.cpp+45-24| ... | @@ -28,28 +28,10 @@ static Error ATTRIBUTE_MUST_USE resolve_enum_zero_bits(CodeGen *g, ZigType *enum | ... | @@ -28,28 +28,10 @@ static Error ATTRIBUTE_MUST_USE resolve_enum_zero_bits(CodeGen *g, ZigType *enum |
| 28 | static Error ATTRIBUTE_MUST_USE resolve_union_zero_bits(CodeGen *g, ZigType *union_type); | 28 | static Error ATTRIBUTE_MUST_USE resolve_union_zero_bits(CodeGen *g, ZigType *union_type); |
| 29 | static void analyze_fn_body(CodeGen *g, ZigFn *fn_table_entry); | 29 | static void analyze_fn_body(CodeGen *g, ZigFn *fn_table_entry); |
| 30 | 30 | ||
| 31 | ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg) { | 31 | static ErrorMsg *add_error_note_token(CodeGen *g, ErrorMsg *parent_msg, ImportTableEntry *owner, Token *token, |
| 32 | if (node->owner->c_import_node != nullptr) { | 32 | Buf *msg) |
| 33 | // if this happens, then translate_c generated code that | 33 | { |
| 34 | // failed semantic analysis, which isn't supposed to happen | 34 | if (owner->c_import_node != nullptr) { |
| 35 | ErrorMsg *err = add_node_error(g, node->owner->c_import_node, | ||
| 36 | buf_sprintf("compiler bug: @cImport generated invalid zig code")); | ||
| 37 | |||
| 38 | add_error_note(g, err, node, msg); | ||
| 39 | |||
| 40 | g->errors.append(err); | ||
| 41 | return err; | ||
| 42 | } | ||
| 43 | |||
| 44 | ErrorMsg *err = err_msg_create_with_line(node->owner->path, node->line, node->column, | ||
| 45 | node->owner->source_code, node->owner->line_offsets, msg); | ||
| 46 | |||
| 47 | g->errors.append(err); | ||
| 48 | return err; | ||
| 49 | } | ||
| 50 | |||
| 51 | ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *msg) { | ||
| 52 | if (node->owner->c_import_node != nullptr) { | ||
| 53 | // if this happens, then translate_c generated code that | 35 | // if this happens, then translate_c generated code that |
| 54 | // failed semantic analysis, which isn't supposed to happen | 36 | // failed semantic analysis, which isn't supposed to happen |
| 55 | 37 | ||
| ... | @@ -64,13 +46,46 @@ ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *m | ... | @@ -64,13 +46,46 @@ ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *m |
| 64 | return note; | 46 | return note; |
| 65 | } | 47 | } |
| 66 | 48 | ||
| 67 | ErrorMsg *err = err_msg_create_with_line(node->owner->path, node->line, node->column, | 49 | ErrorMsg *err = err_msg_create_with_line(owner->path, token->start_line, token->start_column, |
| 68 | node->owner->source_code, node->owner->line_offsets, msg); | 50 | owner->source_code, owner->line_offsets, msg); |
| 69 | 51 | ||
| 70 | err_msg_add_note(parent_msg, err); | 52 | err_msg_add_note(parent_msg, err); |
| 71 | return err; | 53 | return err; |
| 72 | } | 54 | } |
| 73 | 55 | ||
| 56 | ErrorMsg *add_token_error(CodeGen *g, ImportTableEntry *owner, Token *token, Buf *msg) { | ||
| 57 | if (owner->c_import_node != nullptr) { | ||
| 58 | // if this happens, then translate_c generated code that | ||
| 59 | // failed semantic analysis, which isn't supposed to happen | ||
| 60 | ErrorMsg *err = add_node_error(g, owner->c_import_node, | ||
| 61 | buf_sprintf("compiler bug: @cImport generated invalid zig code")); | ||
| 62 | |||
| 63 | add_error_note_token(g, err, owner, token, msg); | ||
| 64 | |||
| 65 | g->errors.append(err); | ||
| 66 | return err; | ||
| 67 | } | ||
| 68 | ErrorMsg *err = err_msg_create_with_line(owner->path, token->start_line, token->start_column, | ||
| 69 | owner->source_code, owner->line_offsets, msg); | ||
| 70 | |||
| 71 | g->errors.append(err); | ||
| 72 | return err; | ||
| 73 | } | ||
| 74 | |||
| 75 | ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg) { | ||
| 76 | Token fake_token; | ||
| 77 | fake_token.start_line = node->line; | ||
| 78 | fake_token.start_column = node->column; | ||
| 79 | return add_token_error(g, node->owner, &fake_token, msg); | ||
| 80 | } | ||
| 81 | |||
| 82 | ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *msg) { | ||
| 83 | Token fake_token; | ||
| 84 | fake_token.start_line = node->line; | ||
| 85 | fake_token.start_column = node->column; | ||
| 86 | return add_error_note_token(g, parent_msg, node->owner, &fake_token, msg); | ||
| 87 | } | ||
| 88 | |||
| 74 | ZigType *new_type_table_entry(ZigTypeId id) { | 89 | ZigType *new_type_table_entry(ZigTypeId id) { |
| 75 | ZigType *entry = allocate<ZigType>(1); | 90 | ZigType *entry = allocate<ZigType>(1); |
| 76 | entry->id = id; | 91 | entry->id = id; |
| ... | @@ -3668,6 +3683,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) { | ... | @@ -3668,6 +3683,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) { |
| 3668 | bool is_const = var_decl->is_const; | 3683 | bool is_const = var_decl->is_const; |
| 3669 | bool is_extern = var_decl->is_extern; | 3684 | bool is_extern = var_decl->is_extern; |
| 3670 | bool is_export = var_decl->is_export; | 3685 | bool is_export = var_decl->is_export; |
| 3686 | bool is_thread_local = var_decl->threadlocal_tok != nullptr; | ||
| 3671 | 3687 | ||
| 3672 | ZigType *explicit_type = nullptr; | 3688 | ZigType *explicit_type = nullptr; |
| 3673 | if (var_decl->type) { | 3689 | if (var_decl->type) { |
| ... | @@ -3727,6 +3743,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) { | ... | @@ -3727,6 +3743,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) { |
| 3727 | tld_var->var = add_variable(g, source_node, tld_var->base.parent_scope, var_decl->symbol, | 3743 | tld_var->var = add_variable(g, source_node, tld_var->base.parent_scope, var_decl->symbol, |
| 3728 | is_const, init_val, &tld_var->base, type); | 3744 | is_const, init_val, &tld_var->base, type); |
| 3729 | tld_var->var->linkage = linkage; | 3745 | tld_var->var->linkage = linkage; |
| 3746 | tld_var->var->is_thread_local = is_thread_local; | ||
| 3730 | 3747 | ||
| 3731 | if (implicit_type != nullptr && type_is_invalid(implicit_type)) { | 3748 | if (implicit_type != nullptr && type_is_invalid(implicit_type)) { |
| 3732 | tld_var->var->var_type = g->builtin_types.entry_invalid; | 3749 | tld_var->var->var_type = g->builtin_types.entry_invalid; |
| ... | @@ -3747,6 +3764,10 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) { | ... | @@ -3747,6 +3764,10 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) { |
| 3747 | } | 3764 | } |
| 3748 | } | 3765 | } |
| 3749 | 3766 | ||
| 3767 | if (is_thread_local && is_const) { | ||
| 3768 | add_node_error(g, source_node, buf_sprintf("threadlocal variable cannot be constant")); | ||
| 3769 | } | ||
| 3770 | |||
| 3750 | g->global_vars.append(tld_var); | 3771 | g->global_vars.append(tld_var); |
| 3751 | } | 3772 | } |
| 3752 | 3773 |
src/analyze.hpp+1| ... | @@ -12,6 +12,7 @@ | ... | @@ -12,6 +12,7 @@ |
| 12 | 12 | ||
| 13 | void semantic_analyze(CodeGen *g); | 13 | void semantic_analyze(CodeGen *g); |
| 14 | ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg); | 14 | ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg); |
| 15 | ErrorMsg *add_token_error(CodeGen *g, ImportTableEntry *owner, Token *token, Buf *msg); | ||
| 15 | ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *msg); | 16 | ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *msg); |
| 16 | ZigType *new_type_table_entry(ZigTypeId id); | 17 | ZigType *new_type_table_entry(ZigTypeId id); |
| 17 | ZigType *get_pointer_to_type(CodeGen *g, ZigType *child_type, bool is_const); | 18 | ZigType *get_pointer_to_type(CodeGen *g, ZigType *child_type, bool is_const); |
src/ast_render.cpp+6-1| ... | @@ -132,6 +132,10 @@ static const char *const_or_var_string(bool is_const) { | ... | @@ -132,6 +132,10 @@ static const char *const_or_var_string(bool is_const) { |
| 132 | return is_const ? "const" : "var"; | 132 | return is_const ? "const" : "var"; |
| 133 | } | 133 | } |
| 134 | 134 | ||
| 135 | static const char *thread_local_string(Token *tok) { | ||
| 136 | return (tok == nullptr) ? "" : "threadlocal "; | ||
| 137 | } | ||
| 138 | |||
| 135 | const char *container_string(ContainerKind kind) { | 139 | const char *container_string(ContainerKind kind) { |
| 136 | switch (kind) { | 140 | switch (kind) { |
| 137 | case ContainerKindEnum: return "enum"; | 141 | case ContainerKindEnum: return "enum"; |
| ... | @@ -554,8 +558,9 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { | ... | @@ -554,8 +558,9 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { |
| 554 | { | 558 | { |
| 555 | const char *pub_str = visib_mod_string(node->data.variable_declaration.visib_mod); | 559 | const char *pub_str = visib_mod_string(node->data.variable_declaration.visib_mod); |
| 556 | const char *extern_str = extern_string(node->data.variable_declaration.is_extern); | 560 | const char *extern_str = extern_string(node->data.variable_declaration.is_extern); |
| 561 | const char *thread_local_str = thread_local_string(node->data.variable_declaration.threadlocal_tok); | ||
| 557 | const char *const_or_var = const_or_var_string(node->data.variable_declaration.is_const); | 562 | const char *const_or_var = const_or_var_string(node->data.variable_declaration.is_const); |
| 558 | fprintf(ar->f, "%s%s%s ", pub_str, extern_str, const_or_var); | 563 | fprintf(ar->f, "%s%s%s%s ", pub_str, extern_str, thread_local_str, const_or_var); |
| 559 | print_symbol(ar, node->data.variable_declaration.symbol); | 564 | print_symbol(ar, node->data.variable_declaration.symbol); |
| 560 | 565 | ||
| 561 | if (node->data.variable_declaration.type) { | 566 | if (node->data.variable_declaration.type) { |
src/codegen.cpp+80-77| ... | @@ -88,7 +88,7 @@ static const char *symbols_that_llvm_depends_on[] = { | ... | @@ -88,7 +88,7 @@ static const char *symbols_that_llvm_depends_on[] = { |
| 88 | }; | 88 | }; |
| 89 | 89 | ||
| 90 | CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out_type, BuildMode build_mode, | 90 | CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out_type, BuildMode build_mode, |
| 91 | Buf *zig_lib_dir) | 91 | Buf *zig_lib_dir, Buf *override_std_dir) |
| 92 | { | 92 | { |
| 93 | CodeGen *g = allocate<CodeGen>(1); | 93 | CodeGen *g = allocate<CodeGen>(1); |
| 94 | 94 | ||
| ... | @@ -96,8 +96,12 @@ CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out | ... | @@ -96,8 +96,12 @@ CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out |
| 96 | 96 | ||
| 97 | g->zig_lib_dir = zig_lib_dir; | 97 | g->zig_lib_dir = zig_lib_dir; |
| 98 | 98 | ||
| 99 | g->zig_std_dir = buf_alloc(); | 99 | if (override_std_dir == nullptr) { |
| 100 | os_path_join(zig_lib_dir, buf_create_from_str("std"), g->zig_std_dir); | 100 | g->zig_std_dir = buf_alloc(); |
| 101 | os_path_join(zig_lib_dir, buf_create_from_str("std"), g->zig_std_dir); | ||
| 102 | } else { | ||
| 103 | g->zig_std_dir = override_std_dir; | ||
| 104 | } | ||
| 101 | 105 | ||
| 102 | g->zig_c_headers_dir = buf_alloc(); | 106 | g->zig_c_headers_dir = buf_alloc(); |
| 103 | os_path_join(zig_lib_dir, buf_create_from_str("include"), g->zig_c_headers_dir); | 107 | os_path_join(zig_lib_dir, buf_create_from_str("include"), g->zig_c_headers_dir); |
| ... | @@ -2582,6 +2586,8 @@ static LLVMValueRef gen_rem(CodeGen *g, bool want_runtime_safety, bool want_fast | ... | @@ -2582,6 +2586,8 @@ static LLVMValueRef gen_rem(CodeGen *g, bool want_runtime_safety, bool want_fast |
| 2582 | 2586 | ||
| 2583 | } | 2587 | } |
| 2584 | 2588 | ||
| 2589 | typedef LLVMValueRef (*BuildBinOpFunc)(LLVMBuilderRef, LLVMValueRef, LLVMValueRef, const char *); | ||
| 2590 | |||
| 2585 | static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, | 2591 | static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, |
| 2586 | IrInstructionBinOp *bin_op_instruction) | 2592 | IrInstructionBinOp *bin_op_instruction) |
| 2587 | { | 2593 | { |
| ... | @@ -2640,50 +2646,71 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, | ... | @@ -2640,50 +2646,71 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, |
| 2640 | } else { | 2646 | } else { |
| 2641 | zig_unreachable(); | 2647 | zig_unreachable(); |
| 2642 | } | 2648 | } |
| 2649 | case IrBinOpMult: | ||
| 2650 | case IrBinOpMultWrap: | ||
| 2643 | case IrBinOpAdd: | 2651 | case IrBinOpAdd: |
| 2644 | case IrBinOpAddWrap: | 2652 | case IrBinOpAddWrap: |
| 2653 | case IrBinOpSub: | ||
| 2654 | case IrBinOpSubWrap: { | ||
| 2655 | // These are lookup table using the AddSubMul enum as the lookup. | ||
| 2656 | // If AddSubMul ever changes, then these tables will be out of | ||
| 2657 | // date. | ||
| 2658 | static const BuildBinOpFunc float_op[3] = { LLVMBuildFAdd, LLVMBuildFSub, LLVMBuildFMul }; | ||
| 2659 | static const BuildBinOpFunc wrap_op[3] = { LLVMBuildAdd, LLVMBuildSub, LLVMBuildMul }; | ||
| 2660 | static const BuildBinOpFunc signed_op[3] = { LLVMBuildNSWAdd, LLVMBuildNSWSub, LLVMBuildNSWMul }; | ||
| 2661 | static const BuildBinOpFunc unsigned_op[3] = { LLVMBuildNUWAdd, LLVMBuildNUWSub, LLVMBuildNUWMul }; | ||
| 2662 | |||
| 2663 | bool is_vector = type_entry->id == ZigTypeIdVector; | ||
| 2664 | bool is_wrapping = (op_id == IrBinOpSubWrap || op_id == IrBinOpAddWrap || op_id == IrBinOpMultWrap); | ||
| 2665 | AddSubMul add_sub_mul = | ||
| 2666 | op_id == IrBinOpAdd || op_id == IrBinOpAddWrap ? AddSubMulAdd : | ||
| 2667 | op_id == IrBinOpSub || op_id == IrBinOpSubWrap ? AddSubMulSub : | ||
| 2668 | AddSubMulMul; | ||
| 2669 | |||
| 2670 | // The code that is generated for vectors and scalars are the same, | ||
| 2671 | // so we can just set type_entry to the vectors elem_type an avoid | ||
| 2672 | // a lot of repeated code. | ||
| 2673 | if (is_vector) | ||
| 2674 | type_entry = type_entry->data.vector.elem_type; | ||
| 2675 | |||
| 2645 | if (type_entry->id == ZigTypeIdPointer) { | 2676 | if (type_entry->id == ZigTypeIdPointer) { |
| 2646 | assert(type_entry->data.pointer.ptr_len == PtrLenUnknown); | 2677 | assert(type_entry->data.pointer.ptr_len == PtrLenUnknown); |
| 2678 | LLVMValueRef subscript_value; | ||
| 2679 | if (is_vector) | ||
| 2680 | zig_panic("TODO: Implement vector operations on pointers."); | ||
| 2681 | |||
| 2682 | switch (add_sub_mul) { | ||
| 2683 | case AddSubMulAdd: | ||
| 2684 | subscript_value = op2_value; | ||
| 2685 | break; | ||
| 2686 | case AddSubMulSub: | ||
| 2687 | subscript_value = LLVMBuildNeg(g->builder, op2_value, ""); | ||
| 2688 | break; | ||
| 2689 | case AddSubMulMul: | ||
| 2690 | zig_unreachable(); | ||
| 2691 | } | ||
| 2692 | |||
| 2647 | // TODO runtime safety | 2693 | // TODO runtime safety |
| 2648 | return LLVMBuildInBoundsGEP(g->builder, op1_value, &op2_value, 1, ""); | 2694 | return LLVMBuildInBoundsGEP(g->builder, op1_value, &subscript_value, 1, ""); |
| 2649 | } else if (type_entry->id == ZigTypeIdFloat) { | 2695 | } else if (type_entry->id == ZigTypeIdFloat) { |
| 2650 | ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base)); | 2696 | ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base)); |
| 2651 | return LLVMBuildFAdd(g->builder, op1_value, op2_value, ""); | 2697 | return float_op[add_sub_mul](g->builder, op1_value, op2_value, ""); |
| 2652 | } else if (type_entry->id == ZigTypeIdInt) { | 2698 | } else if (type_entry->id == ZigTypeIdInt) { |
| 2653 | bool is_wrapping = (op_id == IrBinOpAddWrap); | ||
| 2654 | if (is_wrapping) { | 2699 | if (is_wrapping) { |
| 2655 | return LLVMBuildAdd(g->builder, op1_value, op2_value, ""); | 2700 | return wrap_op[add_sub_mul](g->builder, op1_value, op2_value, ""); |
| 2656 | } else if (want_runtime_safety) { | 2701 | } else if (want_runtime_safety) { |
| 2657 | return gen_overflow_op(g, type_entry, AddSubMulAdd, op1_value, op2_value); | 2702 | if (is_vector) |
| 2703 | zig_panic("TODO: Implement runtime safety vector operations."); | ||
| 2704 | return gen_overflow_op(g, type_entry, add_sub_mul, op1_value, op2_value); | ||
| 2658 | } else if (type_entry->data.integral.is_signed) { | 2705 | } else if (type_entry->data.integral.is_signed) { |
| 2659 | return LLVMBuildNSWAdd(g->builder, op1_value, op2_value, ""); | 2706 | return signed_op[add_sub_mul](g->builder, op1_value, op2_value, ""); |
| 2660 | } else { | 2707 | } else { |
| 2661 | return LLVMBuildNUWAdd(g->builder, op1_value, op2_value, ""); | 2708 | return unsigned_op[add_sub_mul](g->builder, op1_value, op2_value, ""); |
| 2662 | } | ||
| 2663 | } else if (type_entry->id == ZigTypeIdVector) { | ||
| 2664 | ZigType *elem_type = type_entry->data.vector.elem_type; | ||
| 2665 | if (elem_type->id == ZigTypeIdFloat) { | ||
| 2666 | ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base)); | ||
| 2667 | return LLVMBuildFAdd(g->builder, op1_value, op2_value, ""); | ||
| 2668 | } else if (elem_type->id == ZigTypeIdPointer) { | ||
| 2669 | zig_panic("TODO codegen for pointers in vectors"); | ||
| 2670 | } else if (elem_type->id == ZigTypeIdInt) { | ||
| 2671 | bool is_wrapping = (op_id == IrBinOpAddWrap); | ||
| 2672 | if (is_wrapping) { | ||
| 2673 | return LLVMBuildAdd(g->builder, op1_value, op2_value, ""); | ||
| 2674 | } else if (want_runtime_safety) { | ||
| 2675 | zig_panic("TODO runtime safety for vector integer addition"); | ||
| 2676 | } else if (elem_type->data.integral.is_signed) { | ||
| 2677 | return LLVMBuildNSWAdd(g->builder, op1_value, op2_value, ""); | ||
| 2678 | } else { | ||
| 2679 | return LLVMBuildNUWAdd(g->builder, op1_value, op2_value, ""); | ||
| 2680 | } | ||
| 2681 | } else { | ||
| 2682 | zig_unreachable(); | ||
| 2683 | } | 2709 | } |
| 2684 | } else { | 2710 | } else { |
| 2685 | zig_unreachable(); | 2711 | zig_unreachable(); |
| 2686 | } | 2712 | } |
| 2713 | } | ||
| 2687 | case IrBinOpBinOr: | 2714 | case IrBinOpBinOr: |
| 2688 | return LLVMBuildOr(g->builder, op1_value, op2_value, ""); | 2715 | return LLVMBuildOr(g->builder, op1_value, op2_value, ""); |
| 2689 | case IrBinOpBinXor: | 2716 | case IrBinOpBinXor: |
| ... | @@ -2728,49 +2755,6 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, | ... | @@ -2728,49 +2755,6 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, |
| 2728 | return ZigLLVMBuildLShrExact(g->builder, op1_value, op2_casted, ""); | 2755 | return ZigLLVMBuildLShrExact(g->builder, op1_value, op2_casted, ""); |
| 2729 | } | 2756 | } |
| 2730 | } | 2757 | } |
| 2731 | case IrBinOpSub: | ||
| 2732 | case IrBinOpSubWrap: | ||
| 2733 | if (type_entry->id == ZigTypeIdPointer) { | ||
| 2734 | assert(type_entry->data.pointer.ptr_len == PtrLenUnknown); | ||
| 2735 | // TODO runtime safety | ||
| 2736 | LLVMValueRef subscript_value = LLVMBuildNeg(g->builder, op2_value, ""); | ||
| 2737 | return LLVMBuildInBoundsGEP(g->builder, op1_value, &subscript_value, 1, ""); | ||
| 2738 | } else if (type_entry->id == ZigTypeIdFloat) { | ||
| 2739 | ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base)); | ||
| 2740 | return LLVMBuildFSub(g->builder, op1_value, op2_value, ""); | ||
| 2741 | } else if (type_entry->id == ZigTypeIdInt) { | ||
| 2742 | bool is_wrapping = (op_id == IrBinOpSubWrap); | ||
| 2743 | if (is_wrapping) { | ||
| 2744 | return LLVMBuildSub(g->builder, op1_value, op2_value, ""); | ||
| 2745 | } else if (want_runtime_safety) { | ||
| 2746 | return gen_overflow_op(g, type_entry, AddSubMulSub, op1_value, op2_value); | ||
| 2747 | } else if (type_entry->data.integral.is_signed) { | ||
| 2748 | return LLVMBuildNSWSub(g->builder, op1_value, op2_value, ""); | ||
| 2749 | } else { | ||
| 2750 | return LLVMBuildNUWSub(g->builder, op1_value, op2_value, ""); | ||
| 2751 | } | ||
| 2752 | } else { | ||
| 2753 | zig_unreachable(); | ||
| 2754 | } | ||
| 2755 | case IrBinOpMult: | ||
| 2756 | case IrBinOpMultWrap: | ||
| 2757 | if (type_entry->id == ZigTypeIdFloat) { | ||
| 2758 | ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base)); | ||
| 2759 | return LLVMBuildFMul(g->builder, op1_value, op2_value, ""); | ||
| 2760 | } else if (type_entry->id == ZigTypeIdInt) { | ||
| 2761 | bool is_wrapping = (op_id == IrBinOpMultWrap); | ||
| 2762 | if (is_wrapping) { | ||
| 2763 | return LLVMBuildMul(g->builder, op1_value, op2_value, ""); | ||
| 2764 | } else if (want_runtime_safety) { | ||
| 2765 | return gen_overflow_op(g, type_entry, AddSubMulMul, op1_value, op2_value); | ||
| 2766 | } else if (type_entry->data.integral.is_signed) { | ||
| 2767 | return LLVMBuildNSWMul(g->builder, op1_value, op2_value, ""); | ||
| 2768 | } else { | ||
| 2769 | return LLVMBuildNUWMul(g->builder, op1_value, op2_value, ""); | ||
| 2770 | } | ||
| 2771 | } else { | ||
| 2772 | zig_unreachable(); | ||
| 2773 | } | ||
| 2774 | case IrBinOpDivUnspecified: | 2758 | case IrBinOpDivUnspecified: |
| 2775 | return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), | 2759 | return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), |
| 2776 | op1_value, op2_value, type_entry, DivKindFloat); | 2760 | op1_value, op2_value, type_entry, DivKindFloat); |
| ... | @@ -6361,6 +6345,12 @@ static void validate_inline_fns(CodeGen *g) { | ... | @@ -6361,6 +6345,12 @@ static void validate_inline_fns(CodeGen *g) { |
| 6361 | report_errors_and_maybe_exit(g); | 6345 | report_errors_and_maybe_exit(g); |
| 6362 | } | 6346 | } |
| 6363 | 6347 | ||
| 6348 | static void set_global_tls(CodeGen *g, ZigVar *var, LLVMValueRef global_value) { | ||
| 6349 | if (var->is_thread_local && !g->is_single_threaded) { | ||
| 6350 | LLVMSetThreadLocalMode(global_value, LLVMGeneralDynamicTLSModel); | ||
| 6351 | } | ||
| 6352 | } | ||
| 6353 | |||
| 6364 | static void do_code_gen(CodeGen *g) { | 6354 | static void do_code_gen(CodeGen *g) { |
| 6365 | assert(!g->errors.length); | 6355 | assert(!g->errors.length); |
| 6366 | 6356 | ||
| ... | @@ -6445,6 +6435,7 @@ static void do_code_gen(CodeGen *g) { | ... | @@ -6445,6 +6435,7 @@ static void do_code_gen(CodeGen *g) { |
| 6445 | maybe_import_dll(g, global_value, GlobalLinkageIdStrong); | 6435 | maybe_import_dll(g, global_value, GlobalLinkageIdStrong); |
| 6446 | LLVMSetAlignment(global_value, var->align_bytes); | 6436 | LLVMSetAlignment(global_value, var->align_bytes); |
| 6447 | LLVMSetGlobalConstant(global_value, var->gen_is_const); | 6437 | LLVMSetGlobalConstant(global_value, var->gen_is_const); |
| 6438 | set_global_tls(g, var, global_value); | ||
| 6448 | } | 6439 | } |
| 6449 | } else { | 6440 | } else { |
| 6450 | bool exported = (var->linkage == VarLinkageExport); | 6441 | bool exported = (var->linkage == VarLinkageExport); |
| ... | @@ -6470,6 +6461,7 @@ static void do_code_gen(CodeGen *g) { | ... | @@ -6470,6 +6461,7 @@ static void do_code_gen(CodeGen *g) { |
| 6470 | } | 6461 | } |
| 6471 | 6462 | ||
| 6472 | LLVMSetGlobalConstant(global_value, var->gen_is_const); | 6463 | LLVMSetGlobalConstant(global_value, var->gen_is_const); |
| 6464 | set_global_tls(g, var, global_value); | ||
| 6473 | } | 6465 | } |
| 6474 | 6466 | ||
| 6475 | var->value_ref = global_value; | 6467 | var->value_ref = global_value; |
| ... | @@ -7520,6 +7512,7 @@ static Error define_builtin_compile_vars(CodeGen *g) { | ... | @@ -7520,6 +7512,7 @@ static Error define_builtin_compile_vars(CodeGen *g) { |
| 7520 | g->compile_var_package = new_package(buf_ptr(this_dir), builtin_zig_basename); | 7512 | g->compile_var_package = new_package(buf_ptr(this_dir), builtin_zig_basename); |
| 7521 | g->root_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package); | 7513 | g->root_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package); |
| 7522 | g->std_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package); | 7514 | g->std_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package); |
| 7515 | g->std_package->package_table.put(buf_create_from_str("std"), g->std_package); | ||
| 7523 | g->compile_var_import = add_source_file(g, g->compile_var_package, builtin_zig_path, contents); | 7516 | g->compile_var_import = add_source_file(g, g->compile_var_package, builtin_zig_path, contents); |
| 7524 | scan_import(g, g->compile_var_import); | 7517 | scan_import(g, g->compile_var_import); |
| 7525 | 7518 | ||
| ... | @@ -7560,7 +7553,13 @@ static void init(CodeGen *g) { | ... | @@ -7560,7 +7553,13 @@ static void init(CodeGen *g) { |
| 7560 | LLVMTargetRef target_ref; | 7553 | LLVMTargetRef target_ref; |
| 7561 | char *err_msg = nullptr; | 7554 | char *err_msg = nullptr; |
| 7562 | if (LLVMGetTargetFromTriple(buf_ptr(&g->triple_str), &target_ref, &err_msg)) { | 7555 | if (LLVMGetTargetFromTriple(buf_ptr(&g->triple_str), &target_ref, &err_msg)) { |
| 7563 | zig_panic("unable to create target based on: %s", buf_ptr(&g->triple_str)); | 7556 | fprintf(stderr, |
| 7557 | "Zig is expecting LLVM to understand this target: '%s'\n" | ||
| 7558 | "However LLVM responded with: \"%s\"\n" | ||
| 7559 | "Zig is unable to continue. This is a bug in Zig:\n" | ||
| 7560 | "https://github.com/ziglang/zig/issues/438\n" | ||
| 7561 | , buf_ptr(&g->triple_str), err_msg); | ||
| 7562 | exit(1); | ||
| 7564 | } | 7563 | } |
| 7565 | 7564 | ||
| 7566 | bool is_optimized = g->build_mode != BuildModeDebug; | 7565 | bool is_optimized = g->build_mode != BuildModeDebug; |
| ... | @@ -8349,8 +8348,12 @@ static void add_cache_pkg(CodeGen *g, CacheHash *ch, PackageTableEntry *pkg) { | ... | @@ -8349,8 +8348,12 @@ static void add_cache_pkg(CodeGen *g, CacheHash *ch, PackageTableEntry *pkg) { |
| 8349 | if (!entry) | 8348 | if (!entry) |
| 8350 | break; | 8349 | break; |
| 8351 | 8350 | ||
| 8352 | cache_buf(ch, entry->key); | 8351 | // TODO: I think we need a more sophisticated detection of |
| 8353 | add_cache_pkg(g, ch, entry->value); | 8352 | // packages we have already seen |
| 8353 | if (entry->value != pkg) { | ||
| 8354 | cache_buf(ch, entry->key); | ||
| 8355 | add_cache_pkg(g, ch, entry->value); | ||
| 8356 | } | ||
| 8354 | } | 8357 | } |
| 8355 | } | 8358 | } |
| 8356 | 8359 |
src/codegen.hpp+1-1| ... | @@ -15,7 +15,7 @@ | ... | @@ -15,7 +15,7 @@ |
| 15 | #include <stdio.h> | 15 | #include <stdio.h> |
| 16 | 16 | ||
| 17 | CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out_type, BuildMode build_mode, | 17 | CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out_type, BuildMode build_mode, |
| 18 | Buf *zig_lib_dir); | 18 | Buf *zig_lib_dir, Buf *override_std_dir); |
| 19 | 19 | ||
| 20 | void codegen_set_clang_argv(CodeGen *codegen, const char **args, size_t len); | 20 | void codegen_set_clang_argv(CodeGen *codegen, const char **args, size_t len); |
| 21 | void codegen_set_llvm_argv(CodeGen *codegen, const char **args, size_t len); | 21 | void codegen_set_llvm_argv(CodeGen *codegen, const char **args, size_t len); |
src/ir.cpp+4| ... | @@ -5204,6 +5204,10 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod | ... | @@ -5204,6 +5204,10 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod |
| 5204 | add_node_error(irb->codegen, variable_declaration->section_expr, | 5204 | add_node_error(irb->codegen, variable_declaration->section_expr, |
| 5205 | buf_sprintf("cannot set section of local variable '%s'", buf_ptr(variable_declaration->symbol))); | 5205 | buf_sprintf("cannot set section of local variable '%s'", buf_ptr(variable_declaration->symbol))); |
| 5206 | } | 5206 | } |
| 5207 | if (variable_declaration->threadlocal_tok != nullptr) { | ||
| 5208 | add_token_error(irb->codegen, node->owner, variable_declaration->threadlocal_tok, | ||
| 5209 | buf_sprintf("function-local variable '%s' cannot be threadlocal", buf_ptr(variable_declaration->symbol))); | ||
| 5210 | } | ||
| 5207 | 5211 | ||
| 5208 | // Temporarily set the name of the IrExecutable to the VariableDeclaration | 5212 | // Temporarily set the name of the IrExecutable to the VariableDeclaration |
| 5209 | // so that the struct or enum from the init expression inherits the name. | 5213 | // so that the struct or enum from the init expression inherits the name. |
src/link.cpp+1-1| ... | @@ -42,7 +42,7 @@ static Buf *build_a_raw(CodeGen *parent_gen, const char *aname, Buf *full_path) | ... | @@ -42,7 +42,7 @@ static Buf *build_a_raw(CodeGen *parent_gen, const char *aname, Buf *full_path) |
| 42 | } | 42 | } |
| 43 | 43 | ||
| 44 | CodeGen *child_gen = codegen_create(full_path, child_target, child_out_type, | 44 | CodeGen *child_gen = codegen_create(full_path, child_target, child_out_type, |
| 45 | parent_gen->build_mode, parent_gen->zig_lib_dir); | 45 | parent_gen->build_mode, parent_gen->zig_lib_dir, parent_gen->zig_std_dir); |
| 46 | 46 | ||
| 47 | child_gen->out_h_path = nullptr; | 47 | child_gen->out_h_path = nullptr; |
| 48 | child_gen->verbose_tokenize = parent_gen->verbose_tokenize; | 48 | child_gen->verbose_tokenize = parent_gen->verbose_tokenize; |
src/main.cpp+9-3| ... | @@ -74,6 +74,7 @@ static int print_full_usage(const char *arg0) { | ... | @@ -74,6 +74,7 @@ static int print_full_usage(const char *arg0) { |
| 74 | " -dirafter [dir] same as -isystem but do it last\n" | 74 | " -dirafter [dir] same as -isystem but do it last\n" |
| 75 | " -isystem [dir] add additional search path for other .h files\n" | 75 | " -isystem [dir] add additional search path for other .h files\n" |
| 76 | " -mllvm [arg] forward an arg to LLVM's option processing\n" | 76 | " -mllvm [arg] forward an arg to LLVM's option processing\n" |
| 77 | " --override-std-dir [arg] use an alternate Zig standard library\n" | ||
| 77 | "\n" | 78 | "\n" |
| 78 | "Link Options:\n" | 79 | "Link Options:\n" |
| 79 | " --dynamic-linker [path] set the path to ld.so\n" | 80 | " --dynamic-linker [path] set the path to ld.so\n" |
| ... | @@ -395,6 +396,7 @@ int main(int argc, char **argv) { | ... | @@ -395,6 +396,7 @@ int main(int argc, char **argv) { |
| 395 | bool system_linker_hack = false; | 396 | bool system_linker_hack = false; |
| 396 | TargetSubsystem subsystem = TargetSubsystemAuto; | 397 | TargetSubsystem subsystem = TargetSubsystemAuto; |
| 397 | bool is_single_threaded = false; | 398 | bool is_single_threaded = false; |
| 399 | Buf *override_std_dir = nullptr; | ||
| 398 | 400 | ||
| 399 | if (argc >= 2 && strcmp(argv[1], "build") == 0) { | 401 | if (argc >= 2 && strcmp(argv[1], "build") == 0) { |
| 400 | Buf zig_exe_path_buf = BUF_INIT; | 402 | Buf zig_exe_path_buf = BUF_INIT; |
| ... | @@ -430,7 +432,8 @@ int main(int argc, char **argv) { | ... | @@ -430,7 +432,8 @@ int main(int argc, char **argv) { |
| 430 | Buf *build_runner_path = buf_alloc(); | 432 | Buf *build_runner_path = buf_alloc(); |
| 431 | os_path_join(get_zig_special_dir(), buf_create_from_str("build_runner.zig"), build_runner_path); | 433 | os_path_join(get_zig_special_dir(), buf_create_from_str("build_runner.zig"), build_runner_path); |
| 432 | 434 | ||
| 433 | CodeGen *g = codegen_create(build_runner_path, nullptr, OutTypeExe, BuildModeDebug, get_zig_lib_dir()); | 435 | CodeGen *g = codegen_create(build_runner_path, nullptr, OutTypeExe, BuildModeDebug, get_zig_lib_dir(), |
| 436 | override_std_dir); | ||
| 434 | g->enable_time_report = timing_info; | 437 | g->enable_time_report = timing_info; |
| 435 | buf_init_from_str(&g->cache_dir, cache_dir ? cache_dir : default_zig_cache_name); | 438 | buf_init_from_str(&g->cache_dir, cache_dir ? cache_dir : default_zig_cache_name); |
| 436 | codegen_set_out_name(g, buf_create_from_str("build")); | 439 | codegen_set_out_name(g, buf_create_from_str("build")); |
| ... | @@ -645,6 +648,8 @@ int main(int argc, char **argv) { | ... | @@ -645,6 +648,8 @@ int main(int argc, char **argv) { |
| 645 | clang_argv.append(argv[i]); | 648 | clang_argv.append(argv[i]); |
| 646 | 649 | ||
| 647 | llvm_argv.append(argv[i]); | 650 | llvm_argv.append(argv[i]); |
| 651 | } else if (strcmp(arg, "--override-std-dir") == 0) { | ||
| 652 | override_std_dir = buf_create_from_str(argv[i]); | ||
| 648 | } else if (strcmp(arg, "--library-path") == 0 || strcmp(arg, "-L") == 0) { | 653 | } else if (strcmp(arg, "--library-path") == 0 || strcmp(arg, "-L") == 0) { |
| 649 | lib_dirs.append(argv[i]); | 654 | lib_dirs.append(argv[i]); |
| 650 | } else if (strcmp(arg, "--library") == 0) { | 655 | } else if (strcmp(arg, "--library") == 0) { |
| ... | @@ -819,7 +824,7 @@ int main(int argc, char **argv) { | ... | @@ -819,7 +824,7 @@ int main(int argc, char **argv) { |
| 819 | 824 | ||
| 820 | switch (cmd) { | 825 | switch (cmd) { |
| 821 | case CmdBuiltin: { | 826 | case CmdBuiltin: { |
| 822 | CodeGen *g = codegen_create(nullptr, target, out_type, build_mode, get_zig_lib_dir()); | 827 | CodeGen *g = codegen_create(nullptr, target, out_type, build_mode, get_zig_lib_dir(), override_std_dir); |
| 823 | g->is_single_threaded = is_single_threaded; | 828 | g->is_single_threaded = is_single_threaded; |
| 824 | Buf *builtin_source = codegen_generate_builtin_source(g); | 829 | Buf *builtin_source = codegen_generate_builtin_source(g); |
| 825 | if (fwrite(buf_ptr(builtin_source), 1, buf_len(builtin_source), stdout) != buf_len(builtin_source)) { | 830 | if (fwrite(buf_ptr(builtin_source), 1, buf_len(builtin_source), stdout) != buf_len(builtin_source)) { |
| ... | @@ -878,7 +883,8 @@ int main(int argc, char **argv) { | ... | @@ -878,7 +883,8 @@ int main(int argc, char **argv) { |
| 878 | if (cmd == CmdRun && buf_out_name == nullptr) { | 883 | if (cmd == CmdRun && buf_out_name == nullptr) { |
| 879 | buf_out_name = buf_create_from_str("run"); | 884 | buf_out_name = buf_create_from_str("run"); |
| 880 | } | 885 | } |
| 881 | CodeGen *g = codegen_create(zig_root_source_file, target, out_type, build_mode, get_zig_lib_dir()); | 886 | CodeGen *g = codegen_create(zig_root_source_file, target, out_type, build_mode, get_zig_lib_dir(), |
| 887 | override_std_dir); | ||
| 882 | g->subsystem = subsystem; | 888 | g->subsystem = subsystem; |
| 883 | 889 | ||
| 884 | if (disable_pic) { | 890 | if (disable_pic) { |
src/parser.cpp+14-8| ... | @@ -844,12 +844,17 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc) { | ... | @@ -844,12 +844,17 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc) { |
| 844 | 844 | ||
| 845 | // VarDecl <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? LinkSection? (EQUAL Expr)? SEMICOLON | 845 | // VarDecl <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? LinkSection? (EQUAL Expr)? SEMICOLON |
| 846 | static AstNode *ast_parse_var_decl(ParseContext *pc) { | 846 | static AstNode *ast_parse_var_decl(ParseContext *pc) { |
| 847 | Token *first = eat_token_if(pc, TokenIdKeywordConst); | 847 | Token *thread_local_kw = eat_token_if(pc, TokenIdKeywordThreadLocal); |
| 848 | if (first == nullptr) | 848 | Token *mut_kw = eat_token_if(pc, TokenIdKeywordConst); |
| 849 | first = eat_token_if(pc, TokenIdKeywordVar); | 849 | if (mut_kw == nullptr) |
| 850 | if (first == nullptr) | 850 | mut_kw = eat_token_if(pc, TokenIdKeywordVar); |
| 851 | return nullptr; | 851 | if (mut_kw == nullptr) { |
| 852 | 852 | if (thread_local_kw == nullptr) { | |
| 853 | return nullptr; | ||
| 854 | } else { | ||
| 855 | ast_invalid_token_error(pc, peek_token(pc)); | ||
| 856 | } | ||
| 857 | } | ||
| 853 | Token *identifier = expect_token(pc, TokenIdSymbol); | 858 | Token *identifier = expect_token(pc, TokenIdSymbol); |
| 854 | AstNode *type_expr = nullptr; | 859 | AstNode *type_expr = nullptr; |
| 855 | if (eat_token_if(pc, TokenIdColon) != nullptr) | 860 | if (eat_token_if(pc, TokenIdColon) != nullptr) |
| ... | @@ -863,8 +868,9 @@ static AstNode *ast_parse_var_decl(ParseContext *pc) { | ... | @@ -863,8 +868,9 @@ static AstNode *ast_parse_var_decl(ParseContext *pc) { |
| 863 | 868 | ||
| 864 | expect_token(pc, TokenIdSemicolon); | 869 | expect_token(pc, TokenIdSemicolon); |
| 865 | 870 | ||
| 866 | AstNode *res = ast_create_node(pc, NodeTypeVariableDeclaration, first); | 871 | AstNode *res = ast_create_node(pc, NodeTypeVariableDeclaration, mut_kw); |
| 867 | res->data.variable_declaration.is_const = first->id == TokenIdKeywordConst; | 872 | res->data.variable_declaration.threadlocal_tok = thread_local_kw; |
| 873 | res->data.variable_declaration.is_const = mut_kw->id == TokenIdKeywordConst; | ||
| 868 | res->data.variable_declaration.symbol = token_buf(identifier); | 874 | res->data.variable_declaration.symbol = token_buf(identifier); |
| 869 | res->data.variable_declaration.type = type_expr; | 875 | res->data.variable_declaration.type = type_expr; |
| 870 | res->data.variable_declaration.align_expr = align_expr; | 876 | res->data.variable_declaration.align_expr = align_expr; |
src/tokenizer.cpp+2| ... | @@ -146,6 +146,7 @@ static const struct ZigKeyword zig_keywords[] = { | ... | @@ -146,6 +146,7 @@ static const struct ZigKeyword zig_keywords[] = { |
| 146 | {"suspend", TokenIdKeywordSuspend}, | 146 | {"suspend", TokenIdKeywordSuspend}, |
| 147 | {"switch", TokenIdKeywordSwitch}, | 147 | {"switch", TokenIdKeywordSwitch}, |
| 148 | {"test", TokenIdKeywordTest}, | 148 | {"test", TokenIdKeywordTest}, |
| 149 | {"threadlocal", TokenIdKeywordThreadLocal}, | ||
| 149 | {"true", TokenIdKeywordTrue}, | 150 | {"true", TokenIdKeywordTrue}, |
| 150 | {"try", TokenIdKeywordTry}, | 151 | {"try", TokenIdKeywordTry}, |
| 151 | {"undefined", TokenIdKeywordUndefined}, | 152 | {"undefined", TokenIdKeywordUndefined}, |
| ... | @@ -1586,6 +1587,7 @@ const char * token_name(TokenId id) { | ... | @@ -1586,6 +1587,7 @@ const char * token_name(TokenId id) { |
| 1586 | case TokenIdKeywordStruct: return "struct"; | 1587 | case TokenIdKeywordStruct: return "struct"; |
| 1587 | case TokenIdKeywordSwitch: return "switch"; | 1588 | case TokenIdKeywordSwitch: return "switch"; |
| 1588 | case TokenIdKeywordTest: return "test"; | 1589 | case TokenIdKeywordTest: return "test"; |
| 1590 | case TokenIdKeywordThreadLocal: return "threadlocal"; | ||
| 1589 | case TokenIdKeywordTrue: return "true"; | 1591 | case TokenIdKeywordTrue: return "true"; |
| 1590 | case TokenIdKeywordTry: return "try"; | 1592 | case TokenIdKeywordTry: return "try"; |
| 1591 | case TokenIdKeywordUndefined: return "undefined"; | 1593 | case TokenIdKeywordUndefined: return "undefined"; |
src/tokenizer.hpp+1| ... | @@ -88,6 +88,7 @@ enum TokenId { | ... | @@ -88,6 +88,7 @@ enum TokenId { |
| 88 | TokenIdKeywordSuspend, | 88 | TokenIdKeywordSuspend, |
| 89 | TokenIdKeywordSwitch, | 89 | TokenIdKeywordSwitch, |
| 90 | TokenIdKeywordTest, | 90 | TokenIdKeywordTest, |
| 91 | TokenIdKeywordThreadLocal, | ||
| 91 | TokenIdKeywordTrue, | 92 | TokenIdKeywordTrue, |
| 92 | TokenIdKeywordTry, | 93 | TokenIdKeywordTry, |
| 93 | TokenIdKeywordUndefined, | 94 | TokenIdKeywordUndefined, |
std/build.zig+68-19| ... | @@ -145,8 +145,8 @@ pub const Builder = struct { | ... | @@ -145,8 +145,8 @@ pub const Builder = struct { |
| 145 | 145 | ||
| 146 | pub fn setInstallPrefix(self: *Builder, maybe_prefix: ?[]const u8) void { | 146 | pub fn setInstallPrefix(self: *Builder, maybe_prefix: ?[]const u8) void { |
| 147 | self.prefix = maybe_prefix orelse "/usr/local"; // TODO better default | 147 | self.prefix = maybe_prefix orelse "/usr/local"; // TODO better default |
| 148 | self.lib_dir = os.path.join(self.allocator, self.prefix, "lib") catch unreachable; | 148 | self.lib_dir = os.path.join(self.allocator, [][]const u8{ self.prefix, "lib" }) catch unreachable; |
| 149 | self.exe_dir = os.path.join(self.allocator, self.prefix, "bin") catch unreachable; | 149 | self.exe_dir = os.path.join(self.allocator, [][]const u8{ self.prefix, "bin" }) catch unreachable; |
| 150 | } | 150 | } |
| 151 | 151 | ||
| 152 | pub fn addExecutable(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { | 152 | pub fn addExecutable(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { |
| ... | @@ -618,7 +618,10 @@ pub const Builder = struct { | ... | @@ -618,7 +618,10 @@ pub const Builder = struct { |
| 618 | 618 | ||
| 619 | ///::dest_rel_path is relative to prefix path or it can be an absolute path | 619 | ///::dest_rel_path is relative to prefix path or it can be an absolute path |
| 620 | pub fn addInstallFile(self: *Builder, src_path: []const u8, dest_rel_path: []const u8) *InstallFileStep { | 620 | pub fn addInstallFile(self: *Builder, src_path: []const u8, dest_rel_path: []const u8) *InstallFileStep { |
| 621 | const full_dest_path = os.path.resolve(self.allocator, self.prefix, dest_rel_path) catch unreachable; | 621 | const full_dest_path = os.path.resolve( |
| 622 | self.allocator, | ||
| 623 | [][]const u8{ self.prefix, dest_rel_path }, | ||
| 624 | ) catch unreachable; | ||
| 622 | self.pushInstalledFile(full_dest_path); | 625 | self.pushInstalledFile(full_dest_path); |
| 623 | 626 | ||
| 624 | const install_step = self.allocator.create(InstallFileStep) catch unreachable; | 627 | const install_step = self.allocator.create(InstallFileStep) catch unreachable; |
| ... | @@ -653,7 +656,7 @@ pub const Builder = struct { | ... | @@ -653,7 +656,7 @@ pub const Builder = struct { |
| 653 | } | 656 | } |
| 654 | 657 | ||
| 655 | fn pathFromRoot(self: *Builder, rel_path: []const u8) []u8 { | 658 | fn pathFromRoot(self: *Builder, rel_path: []const u8) []u8 { |
| 656 | return os.path.resolve(self.allocator, self.build_root, rel_path) catch unreachable; | 659 | return os.path.resolve(self.allocator, [][]const u8{ self.build_root, rel_path }) catch unreachable; |
| 657 | } | 660 | } |
| 658 | 661 | ||
| 659 | pub fn fmt(self: *Builder, comptime format: []const u8, args: ...) []u8 { | 662 | pub fn fmt(self: *Builder, comptime format: []const u8, args: ...) []u8 { |
| ... | @@ -676,7 +679,7 @@ pub const Builder = struct { | ... | @@ -676,7 +679,7 @@ pub const Builder = struct { |
| 676 | if (os.path.isAbsolute(name)) { | 679 | if (os.path.isAbsolute(name)) { |
| 677 | return name; | 680 | return name; |
| 678 | } | 681 | } |
| 679 | const full_path = try os.path.join(self.allocator, search_prefix, "bin", self.fmt("{}{}", name, exe_extension)); | 682 | const full_path = try os.path.join(self.allocator, [][]const u8{ search_prefix, "bin", self.fmt("{}{}", name, exe_extension) }); |
| 680 | if (os.path.real(self.allocator, full_path)) |real_path| { | 683 | if (os.path.real(self.allocator, full_path)) |real_path| { |
| 681 | return real_path; | 684 | return real_path; |
| 682 | } else |_| { | 685 | } else |_| { |
| ... | @@ -691,7 +694,7 @@ pub const Builder = struct { | ... | @@ -691,7 +694,7 @@ pub const Builder = struct { |
| 691 | } | 694 | } |
| 692 | var it = mem.tokenize(PATH, []u8{os.path.delimiter}); | 695 | var it = mem.tokenize(PATH, []u8{os.path.delimiter}); |
| 693 | while (it.next()) |path| { | 696 | while (it.next()) |path| { |
| 694 | const full_path = try os.path.join(self.allocator, path, self.fmt("{}{}", name, exe_extension)); | 697 | const full_path = try os.path.join(self.allocator, [][]const u8{ path, self.fmt("{}{}", name, exe_extension) }); |
| 695 | if (os.path.real(self.allocator, full_path)) |real_path| { | 698 | if (os.path.real(self.allocator, full_path)) |real_path| { |
| 696 | return real_path; | 699 | return real_path; |
| 697 | } else |_| { | 700 | } else |_| { |
| ... | @@ -705,7 +708,7 @@ pub const Builder = struct { | ... | @@ -705,7 +708,7 @@ pub const Builder = struct { |
| 705 | return name; | 708 | return name; |
| 706 | } | 709 | } |
| 707 | for (paths) |path| { | 710 | for (paths) |path| { |
| 708 | const full_path = try os.path.join(self.allocator, path, self.fmt("{}{}", name, exe_extension)); | 711 | const full_path = try os.path.join(self.allocator, [][]const u8{ path, self.fmt("{}{}", name, exe_extension) }); |
| 709 | if (os.path.real(self.allocator, full_path)) |real_path| { | 712 | if (os.path.real(self.allocator, full_path)) |real_path| { |
| 710 | return real_path; | 713 | return real_path; |
| 711 | } else |_| { | 714 | } else |_| { |
| ... | @@ -1113,7 +1116,10 @@ pub const LibExeObjStep = struct { | ... | @@ -1113,7 +1116,10 @@ pub const LibExeObjStep = struct { |
| 1113 | } | 1116 | } |
| 1114 | 1117 | ||
| 1115 | pub fn getOutputPath(self: *LibExeObjStep) []const u8 { | 1118 | pub fn getOutputPath(self: *LibExeObjStep) []const u8 { |
| 1116 | return if (self.output_path) |output_path| output_path else os.path.join(self.builder.allocator, self.builder.cache_root, self.out_filename) catch unreachable; | 1119 | return if (self.output_path) |output_path| output_path else os.path.join( |
| 1120 | self.builder.allocator, | ||
| 1121 | [][]const u8{ self.builder.cache_root, self.out_filename }, | ||
| 1122 | ) catch unreachable; | ||
| 1117 | } | 1123 | } |
| 1118 | 1124 | ||
| 1119 | pub fn setOutputHPath(self: *LibExeObjStep, file_path: []const u8) void { | 1125 | pub fn setOutputHPath(self: *LibExeObjStep, file_path: []const u8) void { |
| ... | @@ -1126,7 +1132,10 @@ pub const LibExeObjStep = struct { | ... | @@ -1126,7 +1132,10 @@ pub const LibExeObjStep = struct { |
| 1126 | } | 1132 | } |
| 1127 | 1133 | ||
| 1128 | pub fn getOutputHPath(self: *LibExeObjStep) []const u8 { | 1134 | pub fn getOutputHPath(self: *LibExeObjStep) []const u8 { |
| 1129 | return if (self.output_h_path) |output_h_path| output_h_path else os.path.join(self.builder.allocator, self.builder.cache_root, self.out_h_filename) catch unreachable; | 1135 | return if (self.output_h_path) |output_h_path| output_h_path else os.path.join( |
| 1136 | self.builder.allocator, | ||
| 1137 | [][]const u8{ self.builder.cache_root, self.out_h_filename }, | ||
| 1138 | ) catch unreachable; | ||
| 1130 | } | 1139 | } |
| 1131 | 1140 | ||
| 1132 | pub fn addAssemblyFile(self: *LibExeObjStep, path: []const u8) void { | 1141 | pub fn addAssemblyFile(self: *LibExeObjStep, path: []const u8) void { |
| ... | @@ -1226,7 +1235,10 @@ pub const LibExeObjStep = struct { | ... | @@ -1226,7 +1235,10 @@ pub const LibExeObjStep = struct { |
| 1226 | } | 1235 | } |
| 1227 | 1236 | ||
| 1228 | if (self.build_options_contents.len() > 0) { | 1237 | if (self.build_options_contents.len() > 0) { |
| 1229 | const build_options_file = try os.path.join(builder.allocator, builder.cache_root, builder.fmt("{}_build_options.zig", self.name)); | 1238 | const build_options_file = try os.path.join( |
| 1239 | builder.allocator, | ||
| 1240 | [][]const u8{ builder.cache_root, builder.fmt("{}_build_options.zig", self.name) }, | ||
| 1241 | ); | ||
| 1230 | try std.io.writeFile(build_options_file, self.build_options_contents.toSliceConst()); | 1242 | try std.io.writeFile(build_options_file, self.build_options_contents.toSliceConst()); |
| 1231 | try zig_args.append("--pkg-begin"); | 1243 | try zig_args.append("--pkg-begin"); |
| 1232 | try zig_args.append("build_options"); | 1244 | try zig_args.append("build_options"); |
| ... | @@ -1476,7 +1488,10 @@ pub const LibExeObjStep = struct { | ... | @@ -1476,7 +1488,10 @@ pub const LibExeObjStep = struct { |
| 1476 | cc_args.append("-c") catch unreachable; | 1488 | cc_args.append("-c") catch unreachable; |
| 1477 | cc_args.append(abs_source_file) catch unreachable; | 1489 | cc_args.append(abs_source_file) catch unreachable; |
| 1478 | 1490 | ||
| 1479 | const cache_o_src = os.path.join(builder.allocator, builder.cache_root, source_file) catch unreachable; | 1491 | const cache_o_src = os.path.join( |
| 1492 | builder.allocator, | ||
| 1493 | [][]const u8{ builder.cache_root, source_file }, | ||
| 1494 | ) catch unreachable; | ||
| 1480 | if (os.path.dirname(cache_o_src)) |cache_o_dir| { | 1495 | if (os.path.dirname(cache_o_src)) |cache_o_dir| { |
| 1481 | try builder.makePath(cache_o_dir); | 1496 | try builder.makePath(cache_o_dir); |
| 1482 | } | 1497 | } |
| ... | @@ -1528,7 +1543,10 @@ pub const LibExeObjStep = struct { | ... | @@ -1528,7 +1543,10 @@ pub const LibExeObjStep = struct { |
| 1528 | cc_args.append("-current_version") catch unreachable; | 1543 | cc_args.append("-current_version") catch unreachable; |
| 1529 | cc_args.append(builder.fmt("{}.{}.{}", self.version.major, self.version.minor, self.version.patch)) catch unreachable; | 1544 | cc_args.append(builder.fmt("{}.{}.{}", self.version.major, self.version.minor, self.version.patch)) catch unreachable; |
| 1530 | 1545 | ||
| 1531 | const install_name = builder.pathFromRoot(os.path.join(builder.allocator, builder.cache_root, self.major_only_filename) catch unreachable); | 1546 | const install_name = builder.pathFromRoot(os.path.join( |
| 1547 | builder.allocator, | ||
| 1548 | [][]const u8{ builder.cache_root, self.major_only_filename }, | ||
| 1549 | ) catch unreachable); | ||
| 1532 | cc_args.append("-install_name") catch unreachable; | 1550 | cc_args.append("-install_name") catch unreachable; |
| 1533 | cc_args.append(install_name) catch unreachable; | 1551 | cc_args.append(install_name) catch unreachable; |
| 1534 | } else { | 1552 | } else { |
| ... | @@ -1594,7 +1612,10 @@ pub const LibExeObjStep = struct { | ... | @@ -1594,7 +1612,10 @@ pub const LibExeObjStep = struct { |
| 1594 | cc_args.append("-c") catch unreachable; | 1612 | cc_args.append("-c") catch unreachable; |
| 1595 | cc_args.append(abs_source_file) catch unreachable; | 1613 | cc_args.append(abs_source_file) catch unreachable; |
| 1596 | 1614 | ||
| 1597 | const cache_o_src = os.path.join(builder.allocator, builder.cache_root, source_file) catch unreachable; | 1615 | const cache_o_src = os.path.join( |
| 1616 | builder.allocator, | ||
| 1617 | [][]const u8{ builder.cache_root, source_file }, | ||
| 1618 | ) catch unreachable; | ||
| 1598 | if (os.path.dirname(cache_o_src)) |cache_o_dir| { | 1619 | if (os.path.dirname(cache_o_src)) |cache_o_dir| { |
| 1599 | try builder.makePath(cache_o_dir); | 1620 | try builder.makePath(cache_o_dir); |
| 1600 | } | 1621 | } |
| ... | @@ -1686,6 +1707,7 @@ pub const TestStep = struct { | ... | @@ -1686,6 +1707,7 @@ pub const TestStep = struct { |
| 1686 | no_rosegment: bool, | 1707 | no_rosegment: bool, |
| 1687 | output_path: ?[]const u8, | 1708 | output_path: ?[]const u8, |
| 1688 | system_linker_hack: bool, | 1709 | system_linker_hack: bool, |
| 1710 | override_std_dir: ?[]const u8, | ||
| 1689 | 1711 | ||
| 1690 | pub fn init(builder: *Builder, root_src: []const u8) TestStep { | 1712 | pub fn init(builder: *Builder, root_src: []const u8) TestStep { |
| 1691 | const step_name = builder.fmt("test {}", root_src); | 1713 | const step_name = builder.fmt("test {}", root_src); |
| ... | @@ -1707,6 +1729,7 @@ pub const TestStep = struct { | ... | @@ -1707,6 +1729,7 @@ pub const TestStep = struct { |
| 1707 | .no_rosegment = false, | 1729 | .no_rosegment = false, |
| 1708 | .output_path = null, | 1730 | .output_path = null, |
| 1709 | .system_linker_hack = false, | 1731 | .system_linker_hack = false, |
| 1732 | .override_std_dir = null, | ||
| 1710 | }; | 1733 | }; |
| 1711 | } | 1734 | } |
| 1712 | 1735 | ||
| ... | @@ -1737,6 +1760,10 @@ pub const TestStep = struct { | ... | @@ -1737,6 +1760,10 @@ pub const TestStep = struct { |
| 1737 | self.build_mode = mode; | 1760 | self.build_mode = mode; |
| 1738 | } | 1761 | } |
| 1739 | 1762 | ||
| 1763 | pub fn overrideStdDir(self: *TestStep, dir_path: []const u8) void { | ||
| 1764 | self.override_std_dir = dir_path; | ||
| 1765 | } | ||
| 1766 | |||
| 1740 | pub fn setOutputPath(self: *TestStep, file_path: []const u8) void { | 1767 | pub fn setOutputPath(self: *TestStep, file_path: []const u8) void { |
| 1741 | self.output_path = file_path; | 1768 | self.output_path = file_path; |
| 1742 | 1769 | ||
| ... | @@ -1751,7 +1778,10 @@ pub const TestStep = struct { | ... | @@ -1751,7 +1778,10 @@ pub const TestStep = struct { |
| 1751 | return output_path; | 1778 | return output_path; |
| 1752 | } else { | 1779 | } else { |
| 1753 | const basename = self.builder.fmt("test{}", self.target.exeFileExt()); | 1780 | const basename = self.builder.fmt("test{}", self.target.exeFileExt()); |
| 1754 | return os.path.join(self.builder.allocator, self.builder.cache_root, basename) catch unreachable; | 1781 | return os.path.join( |
| 1782 | self.builder.allocator, | ||
| 1783 | [][]const u8{ self.builder.cache_root, basename }, | ||
| 1784 | ) catch unreachable; | ||
| 1755 | } | 1785 | } |
| 1756 | } | 1786 | } |
| 1757 | 1787 | ||
| ... | @@ -1914,6 +1944,10 @@ pub const TestStep = struct { | ... | @@ -1914,6 +1944,10 @@ pub const TestStep = struct { |
| 1914 | if (self.system_linker_hack) { | 1944 | if (self.system_linker_hack) { |
| 1915 | try zig_args.append("--system-linker-hack"); | 1945 | try zig_args.append("--system-linker-hack"); |
| 1916 | } | 1946 | } |
| 1947 | if (self.override_std_dir) |dir| { | ||
| 1948 | try zig_args.append("--override-std-dir"); | ||
| 1949 | try zig_args.append(builder.pathFromRoot(dir)); | ||
| 1950 | } | ||
| 1917 | 1951 | ||
| 1918 | try builder.spawnChild(zig_args.toSliceConst()); | 1952 | try builder.spawnChild(zig_args.toSliceConst()); |
| 1919 | } | 1953 | } |
| ... | @@ -1969,13 +2003,22 @@ const InstallArtifactStep = struct { | ... | @@ -1969,13 +2003,22 @@ const InstallArtifactStep = struct { |
| 1969 | .builder = builder, | 2003 | .builder = builder, |
| 1970 | .step = Step.init(builder.fmt("install {}", artifact.step.name), builder.allocator, make), | 2004 | .step = Step.init(builder.fmt("install {}", artifact.step.name), builder.allocator, make), |
| 1971 | .artifact = artifact, | 2005 | .artifact = artifact, |
| 1972 | .dest_file = os.path.join(builder.allocator, dest_dir, artifact.out_filename) catch unreachable, | 2006 | .dest_file = os.path.join( |
| 2007 | builder.allocator, | ||
| 2008 | [][]const u8{ dest_dir, artifact.out_filename }, | ||
| 2009 | ) catch unreachable, | ||
| 1973 | }; | 2010 | }; |
| 1974 | self.step.dependOn(&artifact.step); | 2011 | self.step.dependOn(&artifact.step); |
| 1975 | builder.pushInstalledFile(self.dest_file); | 2012 | builder.pushInstalledFile(self.dest_file); |
| 1976 | if (self.artifact.kind == LibExeObjStep.Kind.Lib and !self.artifact.static) { | 2013 | if (self.artifact.kind == LibExeObjStep.Kind.Lib and !self.artifact.static) { |
| 1977 | builder.pushInstalledFile(os.path.join(builder.allocator, builder.lib_dir, artifact.major_only_filename) catch unreachable); | 2014 | builder.pushInstalledFile(os.path.join( |
| 1978 | builder.pushInstalledFile(os.path.join(builder.allocator, builder.lib_dir, artifact.name_only_filename) catch unreachable); | 2015 | builder.allocator, |
| 2016 | [][]const u8{ builder.lib_dir, artifact.major_only_filename }, | ||
| 2017 | ) catch unreachable); | ||
| 2018 | builder.pushInstalledFile(os.path.join( | ||
| 2019 | builder.allocator, | ||
| 2020 | [][]const u8{ builder.lib_dir, artifact.name_only_filename }, | ||
| 2021 | ) catch unreachable); | ||
| 1979 | } | 2022 | } |
| 1980 | return self; | 2023 | return self; |
| 1981 | } | 2024 | } |
| ... | @@ -2131,13 +2174,19 @@ fn doAtomicSymLinks(allocator: *Allocator, output_path: []const u8, filename_maj | ... | @@ -2131,13 +2174,19 @@ fn doAtomicSymLinks(allocator: *Allocator, output_path: []const u8, filename_maj |
| 2131 | const out_dir = os.path.dirname(output_path) orelse "."; | 2174 | const out_dir = os.path.dirname(output_path) orelse "."; |
| 2132 | const out_basename = os.path.basename(output_path); | 2175 | const out_basename = os.path.basename(output_path); |
| 2133 | // sym link for libfoo.so.1 to libfoo.so.1.2.3 | 2176 | // sym link for libfoo.so.1 to libfoo.so.1.2.3 |
| 2134 | const major_only_path = os.path.join(allocator, out_dir, filename_major_only) catch unreachable; | 2177 | const major_only_path = os.path.join( |
| 2178 | allocator, | ||
| 2179 | [][]const u8{ out_dir, filename_major_only }, | ||
| 2180 | ) catch unreachable; | ||
| 2135 | os.atomicSymLink(allocator, out_basename, major_only_path) catch |err| { | 2181 | os.atomicSymLink(allocator, out_basename, major_only_path) catch |err| { |
| 2136 | warn("Unable to symlink {} -> {}\n", major_only_path, out_basename); | 2182 | warn("Unable to symlink {} -> {}\n", major_only_path, out_basename); |
| 2137 | return err; | 2183 | return err; |
| 2138 | }; | 2184 | }; |
| 2139 | // sym link for libfoo.so to libfoo.so.1 | 2185 | // sym link for libfoo.so to libfoo.so.1 |
| 2140 | const name_only_path = os.path.join(allocator, out_dir, filename_name_only) catch unreachable; | 2186 | const name_only_path = os.path.join( |
| 2187 | allocator, | ||
| 2188 | [][]const u8{ out_dir, filename_name_only }, | ||
| 2189 | ) catch unreachable; | ||
| 2141 | os.atomicSymLink(allocator, filename_major_only, name_only_path) catch |err| { | 2190 | os.atomicSymLink(allocator, filename_major_only, name_only_path) catch |err| { |
| 2142 | warn("Unable to symlink {} -> {}\n", name_only_path, filename_major_only); | 2191 | warn("Unable to symlink {} -> {}\n", name_only_path, filename_major_only); |
| 2143 | return err; | 2192 | return err; |
std/debug/index.zig+2-3| ... | @@ -37,7 +37,6 @@ const Module = struct { | ... | @@ -37,7 +37,6 @@ const Module = struct { |
| 37 | var stderr_file: os.File = undefined; | 37 | var stderr_file: os.File = undefined; |
| 38 | var stderr_file_out_stream: os.File.OutStream = undefined; | 38 | var stderr_file_out_stream: os.File.OutStream = undefined; |
| 39 | 39 | ||
| 40 | /// TODO multithreaded awareness | ||
| 41 | var stderr_stream: ?*io.OutStream(os.File.WriteError) = null; | 40 | var stderr_stream: ?*io.OutStream(os.File.WriteError) = null; |
| 42 | var stderr_mutex = std.Mutex.init(); | 41 | var stderr_mutex = std.Mutex.init(); |
| 43 | pub fn warn(comptime fmt: []const u8, args: ...) void { | 42 | pub fn warn(comptime fmt: []const u8, args: ...) void { |
| ... | @@ -775,7 +774,7 @@ fn openSelfDebugInfoWindows(allocator: *mem.Allocator) !DebugInfo { | ... | @@ -775,7 +774,7 @@ fn openSelfDebugInfoWindows(allocator: *mem.Allocator) !DebugInfo { |
| 775 | const len = try di.coff.getPdbPath(path_buf[0..]); | 774 | const len = try di.coff.getPdbPath(path_buf[0..]); |
| 776 | const raw_path = path_buf[0..len]; | 775 | const raw_path = path_buf[0..len]; |
| 777 | 776 | ||
| 778 | const path = try os.path.resolve(allocator, raw_path); | 777 | const path = try os.path.resolve(allocator, [][]const u8{raw_path}); |
| 779 | 778 | ||
| 780 | try di.pdb.openFile(di.coff, path); | 779 | try di.pdb.openFile(di.coff, path); |
| 781 | 780 | ||
| ... | @@ -1353,7 +1352,7 @@ const LineNumberProgram = struct { | ... | @@ -1353,7 +1352,7 @@ const LineNumberProgram = struct { |
| 1353 | return error.InvalidDebugInfo; | 1352 | return error.InvalidDebugInfo; |
| 1354 | } else | 1353 | } else |
| 1355 | self.include_dirs[file_entry.dir_index]; | 1354 | self.include_dirs[file_entry.dir_index]; |
| 1356 | const file_name = try os.path.join(self.file_entries.allocator, dir_name, file_entry.file_name); | 1355 | const file_name = try os.path.join(self.file_entries.allocator, [][]const u8{ dir_name, file_entry.file_name }); |
| 1357 | errdefer self.file_entries.allocator.free(file_name); | 1356 | errdefer self.file_entries.allocator.free(file_name); |
| 1358 | return LineInfo{ | 1357 | return LineInfo{ |
| 1359 | .line = if (self.prev_line >= 0) @intCast(usize, self.prev_line) else 0, | 1358 | .line = if (self.prev_line >= 0) @intCast(usize, self.prev_line) else 0, |
std/event/fs.zig+2-2| ... | @@ -871,7 +871,7 @@ pub fn Watch(comptime V: type) type { | ... | @@ -871,7 +871,7 @@ pub fn Watch(comptime V: type) type { |
| 871 | } | 871 | } |
| 872 | 872 | ||
| 873 | async fn addFileKEvent(self: *Self, file_path: []const u8, value: V) !?V { | 873 | async fn addFileKEvent(self: *Self, file_path: []const u8, value: V) !?V { |
| 874 | const resolved_path = try os.path.resolve(self.channel.loop.allocator, file_path); | 874 | const resolved_path = try os.path.resolve(self.channel.loop.allocator, [][]const u8{file_path}); |
| 875 | var resolved_path_consumed = false; | 875 | var resolved_path_consumed = false; |
| 876 | defer if (!resolved_path_consumed) self.channel.loop.allocator.free(resolved_path); | 876 | defer if (!resolved_path_consumed) self.channel.loop.allocator.free(resolved_path); |
| 877 | 877 | ||
| ... | @@ -1336,7 +1336,7 @@ async fn testFsWatchCantFail(loop: *Loop, result: *(anyerror!void)) void { | ... | @@ -1336,7 +1336,7 @@ async fn testFsWatchCantFail(loop: *Loop, result: *(anyerror!void)) void { |
| 1336 | } | 1336 | } |
| 1337 | 1337 | ||
| 1338 | async fn testFsWatch(loop: *Loop) !void { | 1338 | async fn testFsWatch(loop: *Loop) !void { |
| 1339 | const file_path = try os.path.join(loop.allocator, test_tmp_dir, "file.txt"); | 1339 | const file_path = try os.path.join(loop.allocator, [][]const u8{ test_tmp_dir, "file.txt" }); |
| 1340 | defer loop.allocator.free(file_path); | 1340 | defer loop.allocator.free(file_path); |
| 1341 | 1341 | ||
| 1342 | const contents = | 1342 | const contents = |
std/heap.zig+2-5| ... | @@ -106,9 +106,7 @@ pub const DirectAllocator = struct { | ... | @@ -106,9 +106,7 @@ pub const DirectAllocator = struct { |
| 106 | }; | 106 | }; |
| 107 | const ptr = os.windows.HeapAlloc(heap_handle, 0, amt) orelse return error.OutOfMemory; | 107 | const ptr = os.windows.HeapAlloc(heap_handle, 0, amt) orelse return error.OutOfMemory; |
| 108 | const root_addr = @ptrToInt(ptr); | 108 | const root_addr = @ptrToInt(ptr); |
| 109 | const rem = @rem(root_addr, alignment); | 109 | const adjusted_addr = mem.alignForward(root_addr, alignment); |
| 110 | const march_forward_bytes = if (rem == 0) 0 else (alignment - rem); | ||
| 111 | const adjusted_addr = root_addr + march_forward_bytes; | ||
| 112 | const record_addr = adjusted_addr + n; | 110 | const record_addr = adjusted_addr + n; |
| 113 | @intToPtr(*align(1) usize, record_addr).* = root_addr; | 111 | @intToPtr(*align(1) usize, record_addr).* = root_addr; |
| 114 | return @intToPtr([*]u8, adjusted_addr)[0..n]; | 112 | return @intToPtr([*]u8, adjusted_addr)[0..n]; |
| ... | @@ -126,8 +124,7 @@ pub const DirectAllocator = struct { | ... | @@ -126,8 +124,7 @@ pub const DirectAllocator = struct { |
| 126 | const base_addr = @ptrToInt(old_mem.ptr); | 124 | const base_addr = @ptrToInt(old_mem.ptr); |
| 127 | const old_addr_end = base_addr + old_mem.len; | 125 | const old_addr_end = base_addr + old_mem.len; |
| 128 | const new_addr_end = base_addr + new_size; | 126 | const new_addr_end = base_addr + new_size; |
| 129 | const rem = @rem(new_addr_end, os.page_size); | 127 | const new_addr_end_rounded = mem.alignForward(new_addr_end, os.page_size); |
| 130 | const new_addr_end_rounded = new_addr_end + if (rem == 0) 0 else (os.page_size - rem); | ||
| 131 | if (old_addr_end > new_addr_end_rounded) { | 128 | if (old_addr_end > new_addr_end_rounded) { |
| 132 | _ = os.posix.munmap(new_addr_end_rounded, old_addr_end - new_addr_end_rounded); | 129 | _ = os.posix.munmap(new_addr_end_rounded, old_addr_end - new_addr_end_rounded); |
| 133 | } | 130 | } |
std/index.zig+1-1| ... | @@ -33,8 +33,8 @@ pub const io = @import("io.zig"); | ... | @@ -33,8 +33,8 @@ pub const io = @import("io.zig"); |
| 33 | pub const json = @import("json.zig"); | 33 | pub const json = @import("json.zig"); |
| 34 | pub const macho = @import("macho.zig"); | 34 | pub const macho = @import("macho.zig"); |
| 35 | pub const math = @import("math/index.zig"); | 35 | pub const math = @import("math/index.zig"); |
| 36 | pub const meta = @import("meta/index.zig"); | ||
| 37 | pub const mem = @import("mem.zig"); | 36 | pub const mem = @import("mem.zig"); |
| 37 | pub const meta = @import("meta/index.zig"); | ||
| 38 | pub const net = @import("net.zig"); | 38 | pub const net = @import("net.zig"); |
| 39 | pub const os = @import("os/index.zig"); | 39 | pub const os = @import("os/index.zig"); |
| 40 | pub const pdb = @import("pdb.zig"); | 40 | pub const pdb = @import("pdb.zig"); |
std/io.zig+5-5| ... | @@ -912,7 +912,7 @@ pub fn BitOutStream(endian: builtin.Endian, comptime Error: type) type { | ... | @@ -912,7 +912,7 @@ pub fn BitOutStream(endian: builtin.Endian, comptime Error: type) type { |
| 912 | } | 912 | } |
| 913 | 913 | ||
| 914 | /// Flush any remaining bits to the stream. | 914 | /// Flush any remaining bits to the stream. |
| 915 | pub fn flushBits(self: *Self) !void { | 915 | pub fn flushBits(self: *Self) Error!void { |
| 916 | if (self.bit_count == 0) return; | 916 | if (self.bit_count == 0) return; |
| 917 | try self.out_stream.writeByte(self.bit_buffer); | 917 | try self.out_stream.writeByte(self.bit_buffer); |
| 918 | self.bit_buffer = 0; | 918 | self.bit_buffer = 0; |
| ... | @@ -1079,7 +1079,7 @@ pub fn Deserializer(comptime endian: builtin.Endian, is_packed: bool, comptime E | ... | @@ -1079,7 +1079,7 @@ pub fn Deserializer(comptime endian: builtin.Endian, is_packed: bool, comptime E |
| 1079 | } | 1079 | } |
| 1080 | 1080 | ||
| 1081 | //@BUG: inferred error issue. See: #1386 | 1081 | //@BUG: inferred error issue. See: #1386 |
| 1082 | fn deserializeInt(self: *Self, comptime T: type) (Stream.Error || error{EndOfStream})!T { | 1082 | fn deserializeInt(self: *Self, comptime T: type) (Error || error{EndOfStream})!T { |
| 1083 | comptime assert(trait.is(builtin.TypeId.Int)(T) or trait.is(builtin.TypeId.Float)(T)); | 1083 | comptime assert(trait.is(builtin.TypeId.Int)(T) or trait.is(builtin.TypeId.Float)(T)); |
| 1084 | 1084 | ||
| 1085 | const u8_bit_count = 8; | 1085 | const u8_bit_count = 8; |
| ... | @@ -1287,11 +1287,11 @@ pub fn Serializer(comptime endian: builtin.Endian, comptime is_packed: bool, com | ... | @@ -1287,11 +1287,11 @@ pub fn Serializer(comptime endian: builtin.Endian, comptime is_packed: bool, com |
| 1287 | } | 1287 | } |
| 1288 | 1288 | ||
| 1289 | /// Flushes any unwritten bits to the stream | 1289 | /// Flushes any unwritten bits to the stream |
| 1290 | pub fn flush(self: *Self) Stream.Error!void { | 1290 | pub fn flush(self: *Self) Error!void { |
| 1291 | if (is_packed) return self.out_stream.flushBits(); | 1291 | if (is_packed) return self.out_stream.flushBits(); |
| 1292 | } | 1292 | } |
| 1293 | 1293 | ||
| 1294 | fn serializeInt(self: *Self, value: var) !void { | 1294 | fn serializeInt(self: *Self, value: var) Error!void { |
| 1295 | const T = @typeOf(value); | 1295 | const T = @typeOf(value); |
| 1296 | comptime assert(trait.is(builtin.TypeId.Int)(T) or trait.is(builtin.TypeId.Float)(T)); | 1296 | comptime assert(trait.is(builtin.TypeId.Int)(T) or trait.is(builtin.TypeId.Float)(T)); |
| 1297 | 1297 | ||
| ... | @@ -1323,7 +1323,7 @@ pub fn Serializer(comptime endian: builtin.Endian, comptime is_packed: bool, com | ... | @@ -1323,7 +1323,7 @@ pub fn Serializer(comptime endian: builtin.Endian, comptime is_packed: bool, com |
| 1323 | } | 1323 | } |
| 1324 | 1324 | ||
| 1325 | /// Serializes the passed value into the stream | 1325 | /// Serializes the passed value into the stream |
| 1326 | pub fn serialize(self: *Self, value: var) !void { | 1326 | pub fn serialize(self: *Self, value: var) Error!void { |
| 1327 | const T = comptime @typeOf(value); | 1327 | const T = comptime @typeOf(value); |
| 1328 | 1328 | ||
| 1329 | if (comptime trait.isIndexable(T)) { | 1329 | if (comptime trait.isIndexable(T)) { |
std/io_test.zig+10-1| ... | @@ -357,6 +357,15 @@ fn testIntSerializerDeserializer(comptime endian: builtin.Endian, comptime is_pa | ... | @@ -357,6 +357,15 @@ fn testIntSerializerDeserializer(comptime endian: builtin.Endian, comptime is_pa |
| 357 | const total_packed_bytes = (total_bits / u8_bit_count) + extra_packed_byte; | 357 | const total_packed_bytes = (total_bits / u8_bit_count) + extra_packed_byte; |
| 358 | 358 | ||
| 359 | assert(in.pos == if (is_packed) total_packed_bytes else total_bytes); | 359 | assert(in.pos == if (is_packed) total_packed_bytes else total_bytes); |
| 360 | |||
| 361 | //Verify that empty error set works with serializer. | ||
| 362 | //deserializer is covered by SliceInStream | ||
| 363 | const NullError = io.NullOutStream.Error; | ||
| 364 | var null_out = io.NullOutStream.init(); | ||
| 365 | var null_out_stream = &null_out.stream; | ||
| 366 | var null_serializer = io.Serializer(endian, is_packed, NullError).init(null_out_stream); | ||
| 367 | try null_serializer.serialize(data_mem[0..]); | ||
| 368 | try null_serializer.flush(); | ||
| 360 | } | 369 | } |
| 361 | 370 | ||
| 362 | test "Serializer/Deserializer Int" { | 371 | test "Serializer/Deserializer Int" { |
| ... | @@ -568,4 +577,4 @@ test "Deserializer bad data" { | ... | @@ -568,4 +577,4 @@ test "Deserializer bad data" { |
| 568 | try testBadData(builtin.Endian.Little, false); | 577 | try testBadData(builtin.Endian.Little, false); |
| 569 | try testBadData(builtin.Endian.Big, true); | 578 | try testBadData(builtin.Endian.Big, true); |
| 570 | try testBadData(builtin.Endian.Little, true); | 579 | try testBadData(builtin.Endian.Little, true); |
| 571 | } | ||
| \ No newline at end of file | |||
| 580 | } | ||
std/mem.zig+45-27| ... | @@ -882,42 +882,40 @@ pub const SplitIterator = struct { | ... | @@ -882,42 +882,40 @@ pub const SplitIterator = struct { |
| 882 | } | 882 | } |
| 883 | }; | 883 | }; |
| 884 | 884 | ||
| 885 | /// Naively combines a series of strings with a separator. | 885 | /// Naively combines a series of slices with a separator. |
| 886 | /// Allocates memory for the result, which must be freed by the caller. | 886 | /// Allocates memory for the result, which must be freed by the caller. |
| 887 | pub fn join(allocator: *Allocator, sep: u8, strings: ...) ![]u8 { | 887 | pub fn join(allocator: *Allocator, separator: []const u8, slices: []const []const u8) ![]u8 { |
| 888 | comptime assert(strings.len >= 1); | 888 | if (slices.len == 0) return (([*]u8)(undefined))[0..0]; |
| 889 | var total_strings_len: usize = strings.len; // 1 sep per string | 889 | |
| 890 | { | 890 | const total_len = blk: { |
| 891 | comptime var string_i = 0; | 891 | var sum: usize = separator.len * (slices.len - 1); |
| 892 | inline while (string_i < strings.len) : (string_i += 1) { | 892 | for (slices) |slice| |
| 893 | const arg = ([]const u8)(strings[string_i]); | 893 | sum += slice.len; |
| 894 | total_strings_len += arg.len; | 894 | break :blk sum; |
| 895 | } | 895 | }; |
| 896 | } | ||
| 897 | 896 | ||
| 898 | const buf = try allocator.alloc(u8, total_strings_len); | 897 | const buf = try allocator.alloc(u8, total_len); |
| 899 | errdefer allocator.free(buf); | 898 | errdefer allocator.free(buf); |
| 900 | 899 | ||
| 901 | var buf_index: usize = 0; | 900 | copy(u8, buf, slices[0]); |
| 902 | comptime var string_i = 0; | 901 | var buf_index: usize = slices[0].len; |
| 903 | inline while (true) { | 902 | for (slices[1..]) |slice| { |
| 904 | const arg = ([]const u8)(strings[string_i]); | 903 | copy(u8, buf[buf_index..], separator); |
| 905 | string_i += 1; | 904 | buf_index += separator.len; |
| 906 | copy(u8, buf[buf_index..], arg); | 905 | copy(u8, buf[buf_index..], slice); |
| 907 | buf_index += arg.len; | 906 | buf_index += slice.len; |
| 908 | if (string_i >= strings.len) break; | ||
| 909 | if (buf[buf_index - 1] != sep) { | ||
| 910 | buf[buf_index] = sep; | ||
| 911 | buf_index += 1; | ||
| 912 | } | ||
| 913 | } | 907 | } |
| 914 | 908 | ||
| 915 | return allocator.shrink(u8, buf, buf_index); | 909 | // No need for shrink since buf is exactly the correct size. |
| 910 | return buf; | ||
| 916 | } | 911 | } |
| 917 | 912 | ||
| 918 | test "mem.join" { | 913 | test "mem.join" { |
| 919 | assert(eql(u8, try join(debug.global_allocator, ',', "a", "b", "c"), "a,b,c")); | 914 | var buf: [1024]u8 = undefined; |
| 920 | assert(eql(u8, try join(debug.global_allocator, ',', "a"), "a")); | 915 | const a = &std.heap.FixedBufferAllocator.init(&buf).allocator; |
| 916 | assert(eql(u8, try join(a, ",", [][]const u8{ "a", "b", "c" }), "a,b,c")); | ||
| 917 | assert(eql(u8, try join(a, ",", [][]const u8{"a"}), "a")); | ||
| 918 | assert(eql(u8, try join(a, ",", [][]const u8{ "a", "", "b", "", "c" }), "a,,b,,c")); | ||
| 921 | } | 919 | } |
| 922 | 920 | ||
| 923 | test "testStringEquality" { | 921 | test "testStringEquality" { |
| ... | @@ -1366,3 +1364,23 @@ test "std.mem.subArrayPtr" { | ... | @@ -1366,3 +1364,23 @@ test "std.mem.subArrayPtr" { |
| 1366 | sub2[1] = 'X'; | 1364 | sub2[1] = 'X'; |
| 1367 | debug.assert(std.mem.eql(u8, a2, "abcXef")); | 1365 | debug.assert(std.mem.eql(u8, a2, "abcXef")); |
| 1368 | } | 1366 | } |
| 1367 | |||
| 1368 | /// Round an address up to the nearest aligned address | ||
| 1369 | pub fn alignForward(addr: usize, alignment: usize) usize { | ||
| 1370 | return (addr + alignment - 1) & ~(alignment - 1); | ||
| 1371 | } | ||
| 1372 | |||
| 1373 | test "std.mem.alignForward" { | ||
| 1374 | debug.assertOrPanic(alignForward(1, 1) == 1); | ||
| 1375 | debug.assertOrPanic(alignForward(2, 1) == 2); | ||
| 1376 | debug.assertOrPanic(alignForward(1, 2) == 2); | ||
| 1377 | debug.assertOrPanic(alignForward(2, 2) == 2); | ||
| 1378 | debug.assertOrPanic(alignForward(3, 2) == 4); | ||
| 1379 | debug.assertOrPanic(alignForward(4, 2) == 4); | ||
| 1380 | debug.assertOrPanic(alignForward(7, 8) == 8); | ||
| 1381 | debug.assertOrPanic(alignForward(8, 8) == 8); | ||
| 1382 | debug.assertOrPanic(alignForward(9, 8) == 16); | ||
| 1383 | debug.assertOrPanic(alignForward(15, 8) == 16); | ||
| 1384 | debug.assertOrPanic(alignForward(16, 8) == 16); | ||
| 1385 | debug.assertOrPanic(alignForward(17, 8) == 24); | ||
| 1386 | } |
std/os/child_process.zig+6-3| ... | @@ -574,7 +574,7 @@ pub const ChildProcess = struct { | ... | @@ -574,7 +574,7 @@ pub const ChildProcess = struct { |
| 574 | // to match posix semantics | 574 | // to match posix semantics |
| 575 | const app_name = x: { | 575 | const app_name = x: { |
| 576 | if (self.cwd) |cwd| { | 576 | if (self.cwd) |cwd| { |
| 577 | const resolved = try os.path.resolve(self.allocator, cwd, self.argv[0]); | 577 | const resolved = try os.path.resolve(self.allocator, [][]const u8{ cwd, self.argv[0] }); |
| 578 | defer self.allocator.free(resolved); | 578 | defer self.allocator.free(resolved); |
| 579 | break :x try cstr.addNullByte(self.allocator, resolved); | 579 | break :x try cstr.addNullByte(self.allocator, resolved); |
| 580 | } else { | 580 | } else { |
| ... | @@ -597,10 +597,10 @@ pub const ChildProcess = struct { | ... | @@ -597,10 +597,10 @@ pub const ChildProcess = struct { |
| 597 | 597 | ||
| 598 | var it = mem.tokenize(PATH, ";"); | 598 | var it = mem.tokenize(PATH, ";"); |
| 599 | while (it.next()) |search_path| { | 599 | while (it.next()) |search_path| { |
| 600 | const joined_path = try os.path.join(self.allocator, search_path, app_name); | 600 | const joined_path = try os.path.join(self.allocator, [][]const u8{ search_path, app_name }); |
| 601 | defer self.allocator.free(joined_path); | 601 | defer self.allocator.free(joined_path); |
| 602 | 602 | ||
| 603 | const joined_path_w = try unicode.utf8ToUtf16LeWithNull(self.allocator, app_name); | 603 | const joined_path_w = try unicode.utf8ToUtf16LeWithNull(self.allocator, joined_path); |
| 604 | defer self.allocator.free(joined_path_w); | 604 | defer self.allocator.free(joined_path_w); |
| 605 | 605 | ||
| 606 | if (windowsCreateProcess(joined_path_w.ptr, cmd_line_w.ptr, envp_ptr, cwd_w_ptr, &siStartInfo, &piProcInfo)) |_| { | 606 | if (windowsCreateProcess(joined_path_w.ptr, cmd_line_w.ptr, envp_ptr, cwd_w_ptr, &siStartInfo, &piProcInfo)) |_| { |
| ... | @@ -610,6 +610,9 @@ pub const ChildProcess = struct { | ... | @@ -610,6 +610,9 @@ pub const ChildProcess = struct { |
| 610 | } else { | 610 | } else { |
| 611 | return err; | 611 | return err; |
| 612 | } | 612 | } |
| 613 | } else { | ||
| 614 | // Every other error would have been returned earlier. | ||
| 615 | return error.FileNotFound; | ||
| 613 | } | 616 | } |
| 614 | }; | 617 | }; |
| 615 | 618 |
std/os/get_app_data_dir.zig+3-4| ... | @@ -30,7 +30,7 @@ pub fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataD | ... | @@ -30,7 +30,7 @@ pub fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataD |
| 30 | error.OutOfMemory => return error.OutOfMemory, | 30 | error.OutOfMemory => return error.OutOfMemory, |
| 31 | }; | 31 | }; |
| 32 | defer allocator.free(global_dir); | 32 | defer allocator.free(global_dir); |
| 33 | return os.path.join(allocator, global_dir, appname); | 33 | return os.path.join(allocator, [][]const u8{ global_dir, appname }); |
| 34 | }, | 34 | }, |
| 35 | os.windows.E_OUTOFMEMORY => return error.OutOfMemory, | 35 | os.windows.E_OUTOFMEMORY => return error.OutOfMemory, |
| 36 | else => return error.AppDataDirUnavailable, | 36 | else => return error.AppDataDirUnavailable, |
| ... | @@ -41,14 +41,14 @@ pub fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataD | ... | @@ -41,14 +41,14 @@ pub fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataD |
| 41 | // TODO look in /etc/passwd | 41 | // TODO look in /etc/passwd |
| 42 | return error.AppDataDirUnavailable; | 42 | return error.AppDataDirUnavailable; |
| 43 | }; | 43 | }; |
| 44 | return os.path.join(allocator, home_dir, "Library", "Application Support", appname); | 44 | return os.path.join(allocator, [][]const u8{ home_dir, "Library", "Application Support", appname }); |
| 45 | }, | 45 | }, |
| 46 | builtin.Os.linux, builtin.Os.freebsd => { | 46 | builtin.Os.linux, builtin.Os.freebsd => { |
| 47 | const home_dir = os.getEnvPosix("HOME") orelse { | 47 | const home_dir = os.getEnvPosix("HOME") orelse { |
| 48 | // TODO look in /etc/passwd | 48 | // TODO look in /etc/passwd |
| 49 | return error.AppDataDirUnavailable; | 49 | return error.AppDataDirUnavailable; |
| 50 | }; | 50 | }; |
| 51 | return os.path.join(allocator, home_dir, ".local", "share", appname); | 51 | return os.path.join(allocator, [][]const u8{ home_dir, ".local", "share", appname }); |
| 52 | }, | 52 | }, |
| 53 | else => @compileError("Unsupported OS"), | 53 | else => @compileError("Unsupported OS"), |
| 54 | } | 54 | } |
| ... | @@ -67,4 +67,3 @@ test "std.os.getAppDataDir" { | ... | @@ -67,4 +67,3 @@ test "std.os.getAppDataDir" { |
| 67 | // We can't actually validate the result | 67 | // We can't actually validate the result |
| 68 | _ = getAppDataDir(allocator, "zig") catch return; | 68 | _ = getAppDataDir(allocator, "zig") catch return; |
| 69 | } | 69 | } |
| 70 |
std/os/index.zig+68-45| ... | @@ -8,6 +8,10 @@ const is_posix = switch (builtin.os) { | ... | @@ -8,6 +8,10 @@ const is_posix = switch (builtin.os) { |
| 8 | }; | 8 | }; |
| 9 | const os = @This(); | 9 | const os = @This(); |
| 10 | 10 | ||
| 11 | comptime { | ||
| 12 | assert(@import("std") == std); // You have to run the std lib tests with --override-std-dir | ||
| 13 | } | ||
| 14 | |||
| 11 | test "std.os" { | 15 | test "std.os" { |
| 12 | _ = @import("child_process.zig"); | 16 | _ = @import("child_process.zig"); |
| 13 | _ = @import("darwin.zig"); | 17 | _ = @import("darwin.zig"); |
| ... | @@ -692,12 +696,7 @@ pub fn getBaseAddress() usize { | ... | @@ -692,12 +696,7 @@ pub fn getBaseAddress() usize { |
| 692 | return base; | 696 | return base; |
| 693 | } | 697 | } |
| 694 | const phdr = linuxGetAuxVal(std.elf.AT_PHDR); | 698 | const phdr = linuxGetAuxVal(std.elf.AT_PHDR); |
| 695 | const ElfHeader = switch (@sizeOf(usize)) { | 699 | return phdr - @sizeOf(std.elf.Ehdr); |
| 696 | 4 => std.elf.Elf32_Ehdr, | ||
| 697 | 8 => std.elf.Elf64_Ehdr, | ||
| 698 | else => @compileError("Unsupported architecture"), | ||
| 699 | }; | ||
| 700 | return phdr - @sizeOf(ElfHeader); | ||
| 701 | }, | 700 | }, |
| 702 | builtin.Os.macosx, builtin.Os.freebsd => return @ptrToInt(&std.c._mh_execute_header), | 701 | builtin.Os.macosx, builtin.Os.freebsd => return @ptrToInt(&std.c._mh_execute_header), |
| 703 | builtin.Os.windows => return @ptrToInt(windows.GetModuleHandleW(null)), | 702 | builtin.Os.windows => return @ptrToInt(windows.GetModuleHandleW(null)), |
| ... | @@ -1285,7 +1284,7 @@ pub fn makeDirPosix(dir_path: []const u8) !void { | ... | @@ -1285,7 +1284,7 @@ pub fn makeDirPosix(dir_path: []const u8) !void { |
| 1285 | /// already exists and is a directory. | 1284 | /// already exists and is a directory. |
| 1286 | /// TODO determine if we can remove the allocator requirement from this function | 1285 | /// TODO determine if we can remove the allocator requirement from this function |
| 1287 | pub fn makePath(allocator: *Allocator, full_path: []const u8) !void { | 1286 | pub fn makePath(allocator: *Allocator, full_path: []const u8) !void { |
| 1288 | const resolved_path = try path.resolve(allocator, full_path); | 1287 | const resolved_path = try path.resolve(allocator, [][]const u8{full_path}); |
| 1289 | defer allocator.free(resolved_path); | 1288 | defer allocator.free(resolved_path); |
| 1290 | 1289 | ||
| 1291 | var end_index: usize = resolved_path.len; | 1290 | var end_index: usize = resolved_path.len; |
| ... | @@ -2305,18 +2304,17 @@ pub fn selfExePath(out_buffer: *[MAX_PATH_BYTES]u8) ![]u8 { | ... | @@ -2305,18 +2304,17 @@ pub fn selfExePath(out_buffer: *[MAX_PATH_BYTES]u8) ![]u8 { |
| 2305 | switch (builtin.os) { | 2304 | switch (builtin.os) { |
| 2306 | Os.linux => return readLink(out_buffer, "/proc/self/exe"), | 2305 | Os.linux => return readLink(out_buffer, "/proc/self/exe"), |
| 2307 | Os.freebsd => { | 2306 | Os.freebsd => { |
| 2308 | var mib = [4]c_int{ posix.CTL_KERN, posix.KERN_PROC, posix.KERN_PROC_PATHNAME, -1}; | 2307 | var mib = [4]c_int{ posix.CTL_KERN, posix.KERN_PROC, posix.KERN_PROC_PATHNAME, -1 }; |
| 2309 | var out_len: usize = out_buffer.len; | 2308 | var out_len: usize = out_buffer.len; |
| 2310 | const err = posix.getErrno(posix.sysctl(&mib, 4, out_buffer, &out_len, null, 0)); | 2309 | const err = posix.getErrno(posix.sysctl(&mib, 4, out_buffer, &out_len, null, 0)); |
| 2311 | 2310 | ||
| 2312 | if (err == 0 ) return mem.toSlice(u8, out_buffer); | 2311 | if (err == 0) return mem.toSlice(u8, out_buffer); |
| 2313 | 2312 | ||
| 2314 | return switch (err) { | 2313 | return switch (err) { |
| 2315 | posix.EFAULT => error.BadAdress, | 2314 | posix.EFAULT => error.BadAdress, |
| 2316 | posix.EPERM => error.PermissionDenied, | 2315 | posix.EPERM => error.PermissionDenied, |
| 2317 | else => unexpectedErrorPosix(err), | 2316 | else => unexpectedErrorPosix(err), |
| 2318 | }; | 2317 | }; |
| 2319 | |||
| 2320 | }, | 2318 | }, |
| 2321 | Os.windows => { | 2319 | Os.windows => { |
| 2322 | var utf16le_buf: [windows_util.PATH_MAX_WIDE]u16 = undefined; | 2320 | var utf16le_buf: [windows_util.PATH_MAX_WIDE]u16 = undefined; |
| ... | @@ -2908,14 +2906,15 @@ pub const Thread = struct { | ... | @@ -2908,14 +2906,15 @@ pub const Thread = struct { |
| 2908 | pub const Data = if (use_pthreads) | 2906 | pub const Data = if (use_pthreads) |
| 2909 | struct { | 2907 | struct { |
| 2910 | handle: Thread.Handle, | 2908 | handle: Thread.Handle, |
| 2911 | stack_addr: usize, | 2909 | mmap_addr: usize, |
| 2912 | stack_len: usize, | 2910 | mmap_len: usize, |
| 2913 | } | 2911 | } |
| 2914 | else switch (builtin.os) { | 2912 | else switch (builtin.os) { |
| 2915 | builtin.Os.linux => struct { | 2913 | builtin.Os.linux => struct { |
| 2916 | handle: Thread.Handle, | 2914 | handle: Thread.Handle, |
| 2917 | stack_addr: usize, | 2915 | mmap_addr: usize, |
| 2918 | stack_len: usize, | 2916 | mmap_len: usize, |
| 2917 | tls_end_addr: usize, | ||
| 2919 | }, | 2918 | }, |
| 2920 | builtin.Os.windows => struct { | 2919 | builtin.Os.windows => struct { |
| 2921 | handle: Thread.Handle, | 2920 | handle: Thread.Handle, |
| ... | @@ -2955,7 +2954,7 @@ pub const Thread = struct { | ... | @@ -2955,7 +2954,7 @@ pub const Thread = struct { |
| 2955 | posix.EDEADLK => unreachable, | 2954 | posix.EDEADLK => unreachable, |
| 2956 | else => unreachable, | 2955 | else => unreachable, |
| 2957 | } | 2956 | } |
| 2958 | assert(posix.munmap(self.data.stack_addr, self.data.stack_len) == 0); | 2957 | assert(posix.munmap(self.data.mmap_addr, self.data.mmap_len) == 0); |
| 2959 | } else switch (builtin.os) { | 2958 | } else switch (builtin.os) { |
| 2960 | builtin.Os.linux => { | 2959 | builtin.Os.linux => { |
| 2961 | while (true) { | 2960 | while (true) { |
| ... | @@ -2969,7 +2968,7 @@ pub const Thread = struct { | ... | @@ -2969,7 +2968,7 @@ pub const Thread = struct { |
| 2969 | else => unreachable, | 2968 | else => unreachable, |
| 2970 | } | 2969 | } |
| 2971 | } | 2970 | } |
| 2972 | assert(posix.munmap(self.data.stack_addr, self.data.stack_len) == 0); | 2971 | assert(posix.munmap(self.data.mmap_addr, self.data.mmap_len) == 0); |
| 2973 | }, | 2972 | }, |
| 2974 | builtin.Os.windows => { | 2973 | builtin.Os.windows => { |
| 2975 | assert(windows.WaitForSingleObject(self.data.handle, windows.INFINITE) == windows.WAIT_OBJECT_0); | 2974 | assert(windows.WaitForSingleObject(self.data.handle, windows.INFINITE) == windows.WAIT_OBJECT_0); |
| ... | @@ -3008,6 +3007,9 @@ pub const SpawnThreadError = error{ | ... | @@ -3008,6 +3007,9 @@ pub const SpawnThreadError = error{ |
| 3008 | Unexpected, | 3007 | Unexpected, |
| 3009 | }; | 3008 | }; |
| 3010 | 3009 | ||
| 3010 | pub var linux_tls_phdr: ?*std.elf.Phdr = null; | ||
| 3011 | pub var linux_tls_img_src: [*]const u8 = undefined; // defined if linux_tls_phdr is | ||
| 3012 | |||
| 3011 | /// caller must call wait on the returned thread | 3013 | /// caller must call wait on the returned thread |
| 3012 | /// fn startFn(@typeOf(context)) T | 3014 | /// fn startFn(@typeOf(context)) T |
| 3013 | /// where T is u8, noreturn, void, or !void | 3015 | /// where T is u8, noreturn, void, or !void |
| ... | @@ -3097,42 +3099,56 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread | ... | @@ -3097,42 +3099,56 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread |
| 3097 | 3099 | ||
| 3098 | const MAP_GROWSDOWN = if (builtin.os == builtin.Os.linux) linux.MAP_GROWSDOWN else 0; | 3100 | const MAP_GROWSDOWN = if (builtin.os == builtin.Os.linux) linux.MAP_GROWSDOWN else 0; |
| 3099 | 3101 | ||
| 3100 | const mmap_len = default_stack_size; | 3102 | var stack_end_offset: usize = undefined; |
| 3101 | const stack_addr = posix.mmap(null, mmap_len, posix.PROT_READ | posix.PROT_WRITE, posix.MAP_PRIVATE | posix.MAP_ANONYMOUS | MAP_GROWSDOWN, -1, 0); | 3103 | var thread_start_offset: usize = undefined; |
| 3102 | if (stack_addr == posix.MAP_FAILED) return error.OutOfMemory; | 3104 | var context_start_offset: usize = undefined; |
| 3103 | errdefer assert(posix.munmap(stack_addr, mmap_len) == 0); | 3105 | var tls_start_offset: usize = undefined; |
| 3106 | const mmap_len = blk: { | ||
| 3107 | // First in memory will be the stack, which grows downwards. | ||
| 3108 | var l: usize = mem.alignForward(default_stack_size, os.page_size); | ||
| 3109 | stack_end_offset = l; | ||
| 3110 | // Above the stack, so that it can be in the same mmap call, put the Thread object. | ||
| 3111 | l = mem.alignForward(l, @alignOf(Thread)); | ||
| 3112 | thread_start_offset = l; | ||
| 3113 | l += @sizeOf(Thread); | ||
| 3114 | // Next, the Context object. | ||
| 3115 | if (@sizeOf(Context) != 0) { | ||
| 3116 | l = mem.alignForward(l, @alignOf(Context)); | ||
| 3117 | context_start_offset = l; | ||
| 3118 | l += @sizeOf(Context); | ||
| 3119 | } | ||
| 3120 | // Finally, the Thread Local Storage, if any. | ||
| 3121 | if (!Thread.use_pthreads) { | ||
| 3122 | if (linux_tls_phdr) |tls_phdr| { | ||
| 3123 | l = mem.alignForward(l, tls_phdr.p_align); | ||
| 3124 | tls_start_offset = l; | ||
| 3125 | l += tls_phdr.p_memsz; | ||
| 3126 | } | ||
| 3127 | } | ||
| 3128 | break :blk l; | ||
| 3129 | }; | ||
| 3130 | const mmap_addr = posix.mmap(null, mmap_len, posix.PROT_READ | posix.PROT_WRITE, posix.MAP_PRIVATE | posix.MAP_ANONYMOUS | MAP_GROWSDOWN, -1, 0); | ||
| 3131 | if (mmap_addr == posix.MAP_FAILED) return error.OutOfMemory; | ||
| 3132 | errdefer assert(posix.munmap(mmap_addr, mmap_len) == 0); | ||
| 3133 | |||
| 3134 | const thread_ptr = @alignCast(@alignOf(Thread), @intToPtr(*Thread, mmap_addr + thread_start_offset)); | ||
| 3135 | thread_ptr.data.mmap_addr = mmap_addr; | ||
| 3136 | thread_ptr.data.mmap_len = mmap_len; | ||
| 3104 | 3137 | ||
| 3105 | var stack_end: usize = stack_addr + mmap_len; | ||
| 3106 | var arg: usize = undefined; | 3138 | var arg: usize = undefined; |
| 3107 | if (@sizeOf(Context) != 0) { | 3139 | if (@sizeOf(Context) != 0) { |
| 3108 | stack_end -= @sizeOf(Context); | 3140 | arg = mmap_addr + context_start_offset; |
| 3109 | stack_end -= stack_end % @alignOf(Context); | 3141 | const context_ptr = @alignCast(@alignOf(Context), @intToPtr(*Context, arg)); |
| 3110 | assert(stack_end >= stack_addr); | ||
| 3111 | const context_ptr = @alignCast(@alignOf(Context), @intToPtr(*Context, stack_end)); | ||
| 3112 | context_ptr.* = context; | 3142 | context_ptr.* = context; |
| 3113 | arg = stack_end; | ||
| 3114 | } | 3143 | } |
| 3115 | 3144 | ||
| 3116 | stack_end -= @sizeOf(Thread); | 3145 | if (Thread.use_pthreads) { |
| 3117 | stack_end -= stack_end % @alignOf(Thread); | ||
| 3118 | assert(stack_end >= stack_addr); | ||
| 3119 | const thread_ptr = @alignCast(@alignOf(Thread), @intToPtr(*Thread, stack_end)); | ||
| 3120 | |||
| 3121 | thread_ptr.data.stack_addr = stack_addr; | ||
| 3122 | thread_ptr.data.stack_len = mmap_len; | ||
| 3123 | |||
| 3124 | if (builtin.os == builtin.Os.windows) { | ||
| 3125 | // use windows API directly | ||
| 3126 | @compileError("TODO support spawnThread for Windows"); | ||
| 3127 | } else if (Thread.use_pthreads) { | ||
| 3128 | // use pthreads | 3146 | // use pthreads |
| 3129 | var attr: c.pthread_attr_t = undefined; | 3147 | var attr: c.pthread_attr_t = undefined; |
| 3130 | if (c.pthread_attr_init(&attr) != 0) return SpawnThreadError.SystemResources; | 3148 | if (c.pthread_attr_init(&attr) != 0) return SpawnThreadError.SystemResources; |
| 3131 | defer assert(c.pthread_attr_destroy(&attr) == 0); | 3149 | defer assert(c.pthread_attr_destroy(&attr) == 0); |
| 3132 | 3150 | ||
| 3133 | // align to page | 3151 | assert(c.pthread_attr_setstack(&attr, @intToPtr(*c_void, mmap_addr), stack_end_offset) == 0); |
| 3134 | stack_end -= stack_end % os.page_size; | ||
| 3135 | assert(c.pthread_attr_setstack(&attr, @intToPtr(*c_void, stack_addr), stack_end - stack_addr) == 0); | ||
| 3136 | 3152 | ||
| 3137 | const err = c.pthread_create(&thread_ptr.data.handle, &attr, MainFuncs.posixThreadMain, @intToPtr(*c_void, arg)); | 3153 | const err = c.pthread_create(&thread_ptr.data.handle, &attr, MainFuncs.posixThreadMain, @intToPtr(*c_void, arg)); |
| 3138 | switch (err) { | 3154 | switch (err) { |
| ... | @@ -3143,10 +3159,17 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread | ... | @@ -3143,10 +3159,17 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread |
| 3143 | else => return unexpectedErrorPosix(@intCast(usize, err)), | 3159 | else => return unexpectedErrorPosix(@intCast(usize, err)), |
| 3144 | } | 3160 | } |
| 3145 | } else if (builtin.os == builtin.Os.linux) { | 3161 | } else if (builtin.os == builtin.Os.linux) { |
| 3146 | // use linux API directly. TODO use posix.CLONE_SETTLS and initialize thread local storage correctly | 3162 | var flags: u32 = posix.CLONE_VM | posix.CLONE_FS | posix.CLONE_FILES | posix.CLONE_SIGHAND | |
| 3147 | const flags = posix.CLONE_VM | posix.CLONE_FS | posix.CLONE_FILES | posix.CLONE_SIGHAND | posix.CLONE_THREAD | posix.CLONE_SYSVSEM | posix.CLONE_PARENT_SETTID | posix.CLONE_CHILD_CLEARTID | posix.CLONE_DETACHED; | 3163 | posix.CLONE_THREAD | posix.CLONE_SYSVSEM | posix.CLONE_PARENT_SETTID | posix.CLONE_CHILD_CLEARTID | |
| 3148 | const newtls: usize = 0; | 3164 | posix.CLONE_DETACHED; |
| 3149 | const rc = posix.clone(MainFuncs.linuxThreadMain, stack_end, flags, arg, &thread_ptr.data.handle, newtls, &thread_ptr.data.handle); | 3165 | var newtls: usize = undefined; |
| 3166 | if (linux_tls_phdr) |tls_phdr| { | ||
| 3167 | @memcpy(@intToPtr([*]u8, mmap_addr + tls_start_offset), linux_tls_img_src, tls_phdr.p_filesz); | ||
| 3168 | thread_ptr.data.tls_end_addr = mmap_addr + mmap_len; | ||
| 3169 | newtls = @ptrToInt(&thread_ptr.data.tls_end_addr); | ||
| 3170 | flags |= posix.CLONE_SETTLS; | ||
| 3171 | } | ||
| 3172 | const rc = posix.clone(MainFuncs.linuxThreadMain, mmap_addr + stack_end_offset, flags, arg, &thread_ptr.data.handle, newtls, &thread_ptr.data.handle); | ||
| 3150 | const err = posix.getErrno(rc); | 3173 | const err = posix.getErrno(rc); |
| 3151 | switch (err) { | 3174 | switch (err) { |
| 3152 | 0 => return thread_ptr, | 3175 | 0 => return thread_ptr, |
std/os/path.zig+92-33| ... | @@ -33,40 +33,103 @@ pub fn isSep(byte: u8) bool { | ... | @@ -33,40 +33,103 @@ pub fn isSep(byte: u8) bool { |
| 33 | } | 33 | } |
| 34 | } | 34 | } |
| 35 | 35 | ||
| 36 | /// This is different from mem.join in that the separator will not be repeated if | ||
| 37 | /// it is found at the end or beginning of a pair of consecutive paths. | ||
| 38 | fn joinSep(allocator: *Allocator, separator: u8, paths: []const []const u8) ![]u8 { | ||
| 39 | if (paths.len == 0) return (([*]u8)(undefined))[0..0]; | ||
| 40 | |||
| 41 | const total_len = blk: { | ||
| 42 | var sum: usize = paths[0].len; | ||
| 43 | var i: usize = 1; | ||
| 44 | while (i < paths.len) : (i += 1) { | ||
| 45 | const prev_path = paths[i - 1]; | ||
| 46 | const this_path = paths[i]; | ||
| 47 | const prev_sep = (prev_path.len != 0 and prev_path[prev_path.len - 1] == separator); | ||
| 48 | const this_sep = (this_path.len != 0 and this_path[0] == separator); | ||
| 49 | sum += @boolToInt(!prev_sep and !this_sep); | ||
| 50 | sum += if (prev_sep and this_sep) this_path.len - 1 else this_path.len; | ||
| 51 | } | ||
| 52 | break :blk sum; | ||
| 53 | }; | ||
| 54 | |||
| 55 | const buf = try allocator.alloc(u8, total_len); | ||
| 56 | errdefer allocator.free(buf); | ||
| 57 | |||
| 58 | mem.copy(u8, buf, paths[0]); | ||
| 59 | var buf_index: usize = paths[0].len; | ||
| 60 | var i: usize = 1; | ||
| 61 | while (i < paths.len) : (i += 1) { | ||
| 62 | const prev_path = paths[i - 1]; | ||
| 63 | const this_path = paths[i]; | ||
| 64 | const prev_sep = (prev_path.len != 0 and prev_path[prev_path.len - 1] == separator); | ||
| 65 | const this_sep = (this_path.len != 0 and this_path[0] == separator); | ||
| 66 | if (!prev_sep and !this_sep) { | ||
| 67 | buf[buf_index] = separator; | ||
| 68 | buf_index += 1; | ||
| 69 | } | ||
| 70 | const adjusted_path = if (prev_sep and this_sep) this_path[1..] else this_path; | ||
| 71 | mem.copy(u8, buf[buf_index..], adjusted_path); | ||
| 72 | buf_index += adjusted_path.len; | ||
| 73 | } | ||
| 74 | |||
| 75 | // No need for shrink since buf is exactly the correct size. | ||
| 76 | return buf; | ||
| 77 | } | ||
| 78 | |||
| 79 | pub const join = if (is_windows) joinWindows else joinPosix; | ||
| 80 | |||
| 36 | /// Naively combines a series of paths with the native path seperator. | 81 | /// Naively combines a series of paths with the native path seperator. |
| 37 | /// Allocates memory for the result, which must be freed by the caller. | 82 | /// Allocates memory for the result, which must be freed by the caller. |
| 38 | pub fn join(allocator: *Allocator, paths: ...) ![]u8 { | 83 | pub fn joinWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 { |
| 39 | if (is_windows) { | 84 | return joinSep(allocator, sep_windows, paths); |
| 40 | return joinWindows(allocator, paths); | 85 | } |
| 41 | } else { | 86 | |
| 42 | return joinPosix(allocator, paths); | 87 | /// Naively combines a series of paths with the native path seperator. |
| 43 | } | 88 | /// Allocates memory for the result, which must be freed by the caller. |
| 89 | pub fn joinPosix(allocator: *Allocator, paths: []const []const u8) ![]u8 { | ||
| 90 | return joinSep(allocator, sep_posix, paths); | ||
| 44 | } | 91 | } |
| 45 | 92 | ||
| 46 | pub fn joinWindows(allocator: *Allocator, paths: ...) ![]u8 { | 93 | fn testJoinWindows(paths: []const []const u8, expected: []const u8) void { |
| 47 | return mem.join(allocator, sep_windows, paths); | 94 | var buf: [1024]u8 = undefined; |
| 95 | const a = &std.heap.FixedBufferAllocator.init(&buf).allocator; | ||
| 96 | const actual = joinWindows(a, paths) catch @panic("fail"); | ||
| 97 | debug.assertOrPanic(mem.eql(u8, actual, expected)); | ||
| 48 | } | 98 | } |
| 49 | 99 | ||
| 50 | pub fn joinPosix(allocator: *Allocator, paths: ...) ![]u8 { | 100 | fn testJoinPosix(paths: []const []const u8, expected: []const u8) void { |
| 51 | return mem.join(allocator, sep_posix, paths); | 101 | var buf: [1024]u8 = undefined; |
| 102 | const a = &std.heap.FixedBufferAllocator.init(&buf).allocator; | ||
| 103 | const actual = joinPosix(a, paths) catch @panic("fail"); | ||
| 104 | debug.assertOrPanic(mem.eql(u8, actual, expected)); | ||
| 52 | } | 105 | } |
| 53 | 106 | ||
| 54 | test "os.path.join" { | 107 | test "os.path.join" { |
| 55 | assert(mem.eql(u8, try joinWindows(debug.global_allocator, "c:\\a\\b", "c"), "c:\\a\\b\\c")); | 108 | testJoinWindows([][]const u8{ "c:\\a\\b", "c" }, "c:\\a\\b\\c"); |
| 56 | assert(mem.eql(u8, try joinWindows(debug.global_allocator, "c:\\a\\b\\", "c"), "c:\\a\\b\\c")); | 109 | testJoinWindows([][]const u8{ "c:\\a\\b", "c" }, "c:\\a\\b\\c"); |
| 110 | testJoinWindows([][]const u8{ "c:\\a\\b\\", "c" }, "c:\\a\\b\\c"); | ||
| 57 | 111 | ||
| 58 | assert(mem.eql(u8, try joinWindows(debug.global_allocator, "c:\\", "a", "b\\", "c"), "c:\\a\\b\\c")); | 112 | testJoinWindows([][]const u8{ "c:\\", "a", "b\\", "c" }, "c:\\a\\b\\c"); |
| 59 | assert(mem.eql(u8, try joinWindows(debug.global_allocator, "c:\\a\\", "b\\", "c"), "c:\\a\\b\\c")); | 113 | testJoinWindows([][]const u8{ "c:\\a\\", "b\\", "c" }, "c:\\a\\b\\c"); |
| 60 | 114 | ||
| 61 | assert(mem.eql(u8, try joinWindows(debug.global_allocator, "c:\\home\\andy\\dev\\zig\\build\\lib\\zig\\std", "io.zig"), "c:\\home\\andy\\dev\\zig\\build\\lib\\zig\\std\\io.zig")); | 115 | testJoinWindows( |
| 116 | [][]const u8{ "c:\\home\\andy\\dev\\zig\\build\\lib\\zig\\std", "io.zig" }, | ||
| 117 | "c:\\home\\andy\\dev\\zig\\build\\lib\\zig\\std\\io.zig", | ||
| 118 | ); | ||
| 62 | 119 | ||
| 63 | assert(mem.eql(u8, try joinPosix(debug.global_allocator, "/a/b", "c"), "/a/b/c")); | 120 | testJoinPosix([][]const u8{ "/a/b", "c" }, "/a/b/c"); |
| 64 | assert(mem.eql(u8, try joinPosix(debug.global_allocator, "/a/b/", "c"), "/a/b/c")); | 121 | testJoinPosix([][]const u8{ "/a/b/", "c" }, "/a/b/c"); |
| 65 | 122 | ||
| 66 | assert(mem.eql(u8, try joinPosix(debug.global_allocator, "/", "a", "b/", "c"), "/a/b/c")); | 123 | testJoinPosix([][]const u8{ "/", "a", "b/", "c" }, "/a/b/c"); |
| 67 | assert(mem.eql(u8, try joinPosix(debug.global_allocator, "/a/", "b/", "c"), "/a/b/c")); | 124 | testJoinPosix([][]const u8{ "/a/", "b/", "c" }, "/a/b/c"); |
| 68 | 125 | ||
| 69 | assert(mem.eql(u8, try joinPosix(debug.global_allocator, "/home/andy/dev/zig/build/lib/zig/std", "io.zig"), "/home/andy/dev/zig/build/lib/zig/std/io.zig")); | 126 | testJoinPosix( |
| 127 | [][]const u8{ "/home/andy/dev/zig/build/lib/zig/std", "io.zig" }, | ||
| 128 | "/home/andy/dev/zig/build/lib/zig/std/io.zig", | ||
| 129 | ); | ||
| 130 | |||
| 131 | testJoinPosix([][]const u8{ "a", "/c" }, "a/c"); | ||
| 132 | testJoinPosix([][]const u8{ "a/", "/c" }, "a/c"); | ||
| 70 | } | 133 | } |
| 71 | 134 | ||
| 72 | pub fn isAbsolute(path: []const u8) bool { | 135 | pub fn isAbsolute(path: []const u8) bool { |
| ... | @@ -312,18 +375,8 @@ fn asciiEqlIgnoreCase(s1: []const u8, s2: []const u8) bool { | ... | @@ -312,18 +375,8 @@ fn asciiEqlIgnoreCase(s1: []const u8, s2: []const u8) bool { |
| 312 | return true; | 375 | return true; |
| 313 | } | 376 | } |
| 314 | 377 | ||
| 315 | /// Converts the command line arguments into a slice and calls `resolveSlice`. | ||
| 316 | pub fn resolve(allocator: *Allocator, args: ...) ![]u8 { | ||
| 317 | var paths: [args.len][]const u8 = undefined; | ||
| 318 | comptime var arg_i = 0; | ||
| 319 | inline while (arg_i < args.len) : (arg_i += 1) { | ||
| 320 | paths[arg_i] = args[arg_i]; | ||
| 321 | } | ||
| 322 | return resolveSlice(allocator, paths); | ||
| 323 | } | ||
| 324 | |||
| 325 | /// On Windows, this calls `resolveWindows` and on POSIX it calls `resolvePosix`. | 378 | /// On Windows, this calls `resolveWindows` and on POSIX it calls `resolvePosix`. |
| 326 | pub fn resolveSlice(allocator: *Allocator, paths: []const []const u8) ![]u8 { | 379 | pub fn resolve(allocator: *Allocator, paths: []const []const u8) ![]u8 { |
| 327 | if (is_windows) { | 380 | if (is_windows) { |
| 328 | return resolveWindows(allocator, paths); | 381 | return resolveWindows(allocator, paths); |
| 329 | } else { | 382 | } else { |
| ... | @@ -602,7 +655,10 @@ test "os.path.resolveWindows" { | ... | @@ -602,7 +655,10 @@ test "os.path.resolveWindows" { |
| 602 | const parsed_cwd = windowsParsePath(cwd); | 655 | const parsed_cwd = windowsParsePath(cwd); |
| 603 | { | 656 | { |
| 604 | const result = testResolveWindows([][]const u8{ "/usr/local", "lib\\zig\\std\\array_list.zig" }); | 657 | const result = testResolveWindows([][]const u8{ "/usr/local", "lib\\zig\\std\\array_list.zig" }); |
| 605 | const expected = try join(debug.global_allocator, parsed_cwd.disk_designator, "usr\\local\\lib\\zig\\std\\array_list.zig"); | 658 | const expected = try join(debug.global_allocator, [][]const u8{ |
| 659 | parsed_cwd.disk_designator, | ||
| 660 | "usr\\local\\lib\\zig\\std\\array_list.zig", | ||
| 661 | }); | ||
| 606 | if (parsed_cwd.kind == WindowsPath.Kind.Drive) { | 662 | if (parsed_cwd.kind == WindowsPath.Kind.Drive) { |
| 607 | expected[0] = asciiUpper(parsed_cwd.disk_designator[0]); | 663 | expected[0] = asciiUpper(parsed_cwd.disk_designator[0]); |
| 608 | } | 664 | } |
| ... | @@ -610,7 +666,10 @@ test "os.path.resolveWindows" { | ... | @@ -610,7 +666,10 @@ test "os.path.resolveWindows" { |
| 610 | } | 666 | } |
| 611 | { | 667 | { |
| 612 | const result = testResolveWindows([][]const u8{ "usr/local", "lib\\zig" }); | 668 | const result = testResolveWindows([][]const u8{ "usr/local", "lib\\zig" }); |
| 613 | const expected = try join(debug.global_allocator, cwd, "usr\\local\\lib\\zig"); | 669 | const expected = try join(debug.global_allocator, [][]const u8{ |
| 670 | cwd, | ||
| 671 | "usr\\local\\lib\\zig", | ||
| 672 | }); | ||
| 614 | if (parsed_cwd.kind == WindowsPath.Kind.Drive) { | 673 | if (parsed_cwd.kind == WindowsPath.Kind.Drive) { |
| 615 | expected[0] = asciiUpper(parsed_cwd.disk_designator[0]); | 674 | expected[0] = asciiUpper(parsed_cwd.disk_designator[0]); |
| 616 | } | 675 | } |
std/os/test.zig+16| ... | @@ -105,3 +105,19 @@ test "AtomicFile" { | ... | @@ -105,3 +105,19 @@ test "AtomicFile" { |
| 105 | 105 | ||
| 106 | try os.deleteFile(test_out_file); | 106 | try os.deleteFile(test_out_file); |
| 107 | } | 107 | } |
| 108 | |||
| 109 | test "thread local storage" { | ||
| 110 | if (builtin.single_threaded) return error.SkipZigTest; | ||
| 111 | const thread1 = try std.os.spawnThread({}, testTls); | ||
| 112 | const thread2 = try std.os.spawnThread({}, testTls); | ||
| 113 | testTls({}); | ||
| 114 | thread1.wait(); | ||
| 115 | thread2.wait(); | ||
| 116 | } | ||
| 117 | |||
| 118 | threadlocal var x: i32 = 1234; | ||
| 119 | fn testTls(context: void) void { | ||
| 120 | if (x != 1234) @panic("bad start value"); | ||
| 121 | x += 1; | ||
| 122 | if (x != 1235) @panic("bad end value"); | ||
| 123 | } |
std/os/windows/index.zig+18-1| ... | @@ -49,7 +49,10 @@ pub const UNICODE = false; | ... | @@ -49,7 +49,10 @@ pub const UNICODE = false; |
| 49 | pub const WCHAR = u16; | 49 | pub const WCHAR = u16; |
| 50 | pub const WORD = u16; | 50 | pub const WORD = u16; |
| 51 | pub const LARGE_INTEGER = i64; | 51 | pub const LARGE_INTEGER = i64; |
| 52 | pub const LONG = c_long; | 52 | pub const ULONG = u32; |
| 53 | pub const LONG = i32; | ||
| 54 | pub const ULONGLONG = u64; | ||
| 55 | pub const LONGLONG = i64; | ||
| 53 | 56 | ||
| 54 | pub const TRUE = 1; | 57 | pub const TRUE = 1; |
| 55 | pub const FALSE = 0; | 58 | pub const FALSE = 0; |
| ... | @@ -380,3 +383,17 @@ pub const COORD = extern struct { | ... | @@ -380,3 +383,17 @@ pub const COORD = extern struct { |
| 380 | }; | 383 | }; |
| 381 | 384 | ||
| 382 | pub const CREATE_UNICODE_ENVIRONMENT = 1024; | 385 | pub const CREATE_UNICODE_ENVIRONMENT = 1024; |
| 386 | |||
| 387 | pub const TLS_OUT_OF_INDEXES = 4294967295; | ||
| 388 | pub const IMAGE_TLS_DIRECTORY = extern struct { | ||
| 389 | StartAddressOfRawData: usize, | ||
| 390 | EndAddressOfRawData: usize, | ||
| 391 | AddressOfIndex: usize, | ||
| 392 | AddressOfCallBacks: usize, | ||
| 393 | SizeOfZeroFill: u32, | ||
| 394 | Characteristics: u32, | ||
| 395 | }; | ||
| 396 | pub const IMAGE_TLS_DIRECTORY64 = IMAGE_TLS_DIRECTORY; | ||
| 397 | pub const IMAGE_TLS_DIRECTORY32 = IMAGE_TLS_DIRECTORY; | ||
| 398 | |||
| 399 | pub const PIMAGE_TLS_CALLBACK = ?extern fn(PVOID, DWORD, PVOID) void; |
std/os/windows/kernel32.zig+4| ... | @@ -164,6 +164,10 @@ pub extern "kernel32" stdcallcc fn Sleep(dwMilliseconds: DWORD) void; | ... | @@ -164,6 +164,10 @@ pub extern "kernel32" stdcallcc fn Sleep(dwMilliseconds: DWORD) void; |
| 164 | 164 | ||
| 165 | pub extern "kernel32" stdcallcc fn TerminateProcess(hProcess: HANDLE, uExitCode: UINT) BOOL; | 165 | pub extern "kernel32" stdcallcc fn TerminateProcess(hProcess: HANDLE, uExitCode: UINT) BOOL; |
| 166 | 166 | ||
| 167 | pub extern "kernel32" stdcallcc fn TlsAlloc() DWORD; | ||
| 168 | |||
| 169 | pub extern "kernel32" stdcallcc fn TlsFree(dwTlsIndex: DWORD) BOOL; | ||
| 170 | |||
| 167 | pub extern "kernel32" stdcallcc fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) DWORD; | 171 | pub extern "kernel32" stdcallcc fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) DWORD; |
| 168 | 172 | ||
| 169 | pub extern "kernel32" stdcallcc fn WriteFile( | 173 | pub extern "kernel32" stdcallcc fn WriteFile( |
std/os/windows/tls.zig created+36| ... | @@ -0,0 +1,36 @@ | ||
| 1 | const std = @import("../../index.zig"); | ||
| 2 | |||
| 3 | export var _tls_index: u32 = std.os.windows.TLS_OUT_OF_INDEXES; | ||
| 4 | export var _tls_start: u8 linksection(".tls") = 0; | ||
| 5 | export var _tls_end: u8 linksection(".tls$ZZZ") = 0; | ||
| 6 | export var __xl_a: std.os.windows.PIMAGE_TLS_CALLBACK linksection(".CRT$XLA") = null; | ||
| 7 | export var __xl_z: std.os.windows.PIMAGE_TLS_CALLBACK linksection(".CRT$XLZ") = null; | ||
| 8 | |||
| 9 | // TODO this is how I would like it to be expressed | ||
| 10 | // TODO also note, ReactOS has a +1 on StartAddressOfRawData and AddressOfCallBacks. Investigate | ||
| 11 | // why they do that. | ||
| 12 | //export const _tls_used linksection(".rdata$T") = std.os.windows.IMAGE_TLS_DIRECTORY { | ||
| 13 | // .StartAddressOfRawData = @ptrToInt(&_tls_start), | ||
| 14 | // .EndAddressOfRawData = @ptrToInt(&_tls_end), | ||
| 15 | // .AddressOfIndex = @ptrToInt(&_tls_index), | ||
| 16 | // .AddressOfCallBacks = @ptrToInt(__xl_a), | ||
| 17 | // .SizeOfZeroFill = 0, | ||
| 18 | // .Characteristics = 0, | ||
| 19 | //}; | ||
| 20 | // This is the workaround because we can't do @ptrToInt at comptime like that. | ||
| 21 | pub const IMAGE_TLS_DIRECTORY = extern struct { | ||
| 22 | StartAddressOfRawData: *c_void, | ||
| 23 | EndAddressOfRawData: *c_void, | ||
| 24 | AddressOfIndex: *c_void, | ||
| 25 | AddressOfCallBacks: *c_void, | ||
| 26 | SizeOfZeroFill: u32, | ||
| 27 | Characteristics: u32, | ||
| 28 | }; | ||
| 29 | export const _tls_used linksection(".rdata$T") = IMAGE_TLS_DIRECTORY { | ||
| 30 | .StartAddressOfRawData = &_tls_start, | ||
| 31 | .EndAddressOfRawData = &_tls_end, | ||
| 32 | .AddressOfIndex = &_tls_index, | ||
| 33 | .AddressOfCallBacks = &__xl_a, | ||
| 34 | .SizeOfZeroFill = 0, | ||
| 35 | .Characteristics = 0, | ||
| 36 | }; | ||
std/special/bootstrap.zig+59-4| ... | @@ -4,6 +4,7 @@ | ... | @@ -4,6 +4,7 @@ |
| 4 | const root = @import("@root"); | 4 | const root = @import("@root"); |
| 5 | const std = @import("std"); | 5 | const std = @import("std"); |
| 6 | const builtin = @import("builtin"); | 6 | const builtin = @import("builtin"); |
| 7 | const assert = std.debug.assert; | ||
| 7 | 8 | ||
| 8 | var argc_ptr: [*]usize = undefined; | 9 | var argc_ptr: [*]usize = undefined; |
| 9 | 10 | ||
| ... | @@ -44,7 +45,9 @@ nakedcc fn _start() noreturn { | ... | @@ -44,7 +45,9 @@ nakedcc fn _start() noreturn { |
| 44 | 45 | ||
| 45 | extern fn WinMainCRTStartup() noreturn { | 46 | extern fn WinMainCRTStartup() noreturn { |
| 46 | @setAlignStack(16); | 47 | @setAlignStack(16); |
| 47 | 48 | if (!builtin.single_threaded) { | |
| 49 | _ = @import("../os/windows/tls.zig"); | ||
| 50 | } | ||
| 48 | std.os.windows.ExitProcess(callMain()); | 51 | std.os.windows.ExitProcess(callMain()); |
| 49 | } | 52 | } |
| 50 | 53 | ||
| ... | @@ -61,9 +64,23 @@ fn posixCallMainAndExit() noreturn { | ... | @@ -61,9 +64,23 @@ fn posixCallMainAndExit() noreturn { |
| 61 | while (envp_optional[envp_count]) |_| : (envp_count += 1) {} | 64 | while (envp_optional[envp_count]) |_| : (envp_count += 1) {} |
| 62 | const envp = @ptrCast([*][*]u8, envp_optional)[0..envp_count]; | 65 | const envp = @ptrCast([*][*]u8, envp_optional)[0..envp_count]; |
| 63 | if (builtin.os == builtin.Os.linux) { | 66 | if (builtin.os == builtin.Os.linux) { |
| 64 | const auxv = @ptrCast([*]usize, envp.ptr + envp_count + 1); | 67 | // Scan auxiliary vector. |
| 65 | std.os.linux_elf_aux_maybe = @ptrCast([*]std.elf.Auxv, auxv); | 68 | const auxv = @ptrCast([*]std.elf.Auxv, envp.ptr + envp_count + 1); |
| 66 | std.debug.assert(std.os.linuxGetAuxVal(std.elf.AT_PAGESZ) == std.os.page_size); | 69 | std.os.linux_elf_aux_maybe = auxv; |
| 70 | var i: usize = 0; | ||
| 71 | var at_phdr: usize = 0; | ||
| 72 | var at_phnum: usize = 0; | ||
| 73 | var at_phent: usize = 0; | ||
| 74 | while (auxv[i].a_un.a_val != 0) : (i += 1) { | ||
| 75 | switch (auxv[i].a_type) { | ||
| 76 | std.elf.AT_PAGESZ => assert(auxv[i].a_un.a_val == std.os.page_size), | ||
| 77 | std.elf.AT_PHDR => at_phdr = auxv[i].a_un.a_val, | ||
| 78 | std.elf.AT_PHNUM => at_phnum = auxv[i].a_un.a_val, | ||
| 79 | std.elf.AT_PHENT => at_phent = auxv[i].a_un.a_val, | ||
| 80 | else => {}, | ||
| 81 | } | ||
| 82 | } | ||
| 83 | if (!builtin.single_threaded) linuxInitializeThreadLocalStorage(at_phdr, at_phnum, at_phent); | ||
| 67 | } | 84 | } |
| 68 | 85 | ||
| 69 | std.os.posix.exit(callMainWithArgs(argc, argv, envp)); | 86 | std.os.posix.exit(callMainWithArgs(argc, argv, envp)); |
| ... | @@ -116,3 +133,41 @@ inline fn callMain() u8 { | ... | @@ -116,3 +133,41 @@ inline fn callMain() u8 { |
| 116 | else => @compileError("expected return type of main to be 'u8', 'noreturn', 'void', or '!void'"), | 133 | else => @compileError("expected return type of main to be 'u8', 'noreturn', 'void', or '!void'"), |
| 117 | } | 134 | } |
| 118 | } | 135 | } |
| 136 | |||
| 137 | var tls_end_addr: usize = undefined; | ||
| 138 | const main_thread_tls_align = 32; | ||
| 139 | var main_thread_tls_bytes: [64]u8 align(main_thread_tls_align) = [1]u8{0} ** 64; | ||
| 140 | |||
| 141 | fn linuxInitializeThreadLocalStorage(at_phdr: usize, at_phnum: usize, at_phent: usize) void { | ||
| 142 | var phdr_addr = at_phdr; | ||
| 143 | var n = at_phnum; | ||
| 144 | var base: usize = 0; | ||
| 145 | while (n != 0) : ({n -= 1; phdr_addr += at_phent;}) { | ||
| 146 | const phdr = @intToPtr(*std.elf.Phdr, phdr_addr); | ||
| 147 | // TODO look for PT_DYNAMIC when we have https://github.com/ziglang/zig/issues/1917 | ||
| 148 | switch (phdr.p_type) { | ||
| 149 | std.elf.PT_PHDR => base = at_phdr - phdr.p_vaddr, | ||
| 150 | std.elf.PT_TLS => std.os.linux_tls_phdr = phdr, | ||
| 151 | else => continue, | ||
| 152 | } | ||
| 153 | } | ||
| 154 | const tls_phdr = std.os.linux_tls_phdr orelse return; | ||
| 155 | std.os.linux_tls_img_src = @intToPtr([*]const u8, base + tls_phdr.p_vaddr); | ||
| 156 | assert(main_thread_tls_bytes.len >= tls_phdr.p_memsz); // not enough preallocated Thread Local Storage | ||
| 157 | assert(main_thread_tls_align >= tls_phdr.p_align); // preallocated Thread Local Storage not aligned enough | ||
| 158 | @memcpy(&main_thread_tls_bytes, std.os.linux_tls_img_src, tls_phdr.p_filesz); | ||
| 159 | tls_end_addr = @ptrToInt(&main_thread_tls_bytes) + tls_phdr.p_memsz; | ||
| 160 | linuxSetThreadArea(@ptrToInt(&tls_end_addr)); | ||
| 161 | } | ||
| 162 | |||
| 163 | fn linuxSetThreadArea(addr: usize) void { | ||
| 164 | switch (builtin.arch) { | ||
| 165 | builtin.Arch.x86_64 => { | ||
| 166 | const ARCH_SET_FS = 0x1002; | ||
| 167 | const rc = std.os.linux.syscall2(std.os.linux.SYS_arch_prctl, ARCH_SET_FS, addr); | ||
| 168 | // acrh_prctl is documented to never fail | ||
| 169 | assert(rc == 0); | ||
| 170 | }, | ||
| 171 | else => @compileError("Unsupported architecture"), | ||
| 172 | } | ||
| 173 | } |
test/cli.zig+4-4| ... | @@ -27,9 +27,9 @@ pub fn main() !void { | ... | @@ -27,9 +27,9 @@ pub fn main() !void { |
| 27 | std.debug.warn("Expected second argument to be cache root directory path\n"); | 27 | std.debug.warn("Expected second argument to be cache root directory path\n"); |
| 28 | return error.InvalidArgs; | 28 | return error.InvalidArgs; |
| 29 | }); | 29 | }); |
| 30 | const zig_exe = try os.path.resolve(a, zig_exe_rel); | 30 | const zig_exe = try os.path.resolve(a, [][]const u8{zig_exe_rel}); |
| 31 | 31 | ||
| 32 | const dir_path = try os.path.join(a, cache_root, "clitest"); | 32 | const dir_path = try os.path.join(a, [][]const u8{ cache_root, "clitest" }); |
| 33 | const TestFn = fn ([]const u8, []const u8) anyerror!void; | 33 | const TestFn = fn ([]const u8, []const u8) anyerror!void; |
| 34 | const test_fns = []TestFn{ | 34 | const test_fns = []TestFn{ |
| 35 | testZigInitLib, | 35 | testZigInitLib, |
| ... | @@ -99,8 +99,8 @@ fn testZigInitExe(zig_exe: []const u8, dir_path: []const u8) !void { | ... | @@ -99,8 +99,8 @@ fn testZigInitExe(zig_exe: []const u8, dir_path: []const u8) !void { |
| 99 | fn testGodboltApi(zig_exe: []const u8, dir_path: []const u8) anyerror!void { | 99 | fn testGodboltApi(zig_exe: []const u8, dir_path: []const u8) anyerror!void { |
| 100 | if (builtin.os != builtin.Os.linux or builtin.arch != builtin.Arch.x86_64) return; | 100 | if (builtin.os != builtin.Os.linux or builtin.arch != builtin.Arch.x86_64) return; |
| 101 | 101 | ||
| 102 | const example_zig_path = try os.path.join(a, dir_path, "example.zig"); | 102 | const example_zig_path = try os.path.join(a, [][]const u8{ dir_path, "example.zig" }); |
| 103 | const example_s_path = try os.path.join(a, dir_path, "example.s"); | 103 | const example_s_path = try os.path.join(a, [][]const u8{ dir_path, "example.s" }); |
| 104 | 104 | ||
| 105 | try std.io.writeFile(example_zig_path, | 105 | try std.io.writeFile(example_zig_path, |
| 106 | \\// Type your code here, or load an example. | 106 | \\// Type your code here, or load an example. |
test/compile_errors.zig+19| ... | @@ -1,6 +1,25 @@ | ... | @@ -1,6 +1,25 @@ |
| 1 | const tests = @import("tests.zig"); | 1 | const tests = @import("tests.zig"); |
| 2 | 2 | ||
| 3 | pub fn addCases(cases: *tests.CompileErrorContext) void { | 3 | pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4 | cases.add( | ||
| 5 | "threadlocal qualifier on const", | ||
| 6 | \\threadlocal const x: i32 = 1234; | ||
| 7 | \\export fn entry() i32 { | ||
| 8 | \\ return x; | ||
| 9 | \\} | ||
| 10 | , | ||
| 11 | ".tmp_source.zig:1:13: error: threadlocal variable cannot be constant", | ||
| 12 | ); | ||
| 13 | |||
| 14 | cases.add( | ||
| 15 | "threadlocal qualifier on local variable", | ||
| 16 | \\export fn entry() void { | ||
| 17 | \\ threadlocal var x: i32 = 1234; | ||
| 18 | \\} | ||
| 19 | , | ||
| 20 | ".tmp_source.zig:2:5: error: function-local variable 'x' cannot be threadlocal", | ||
| 21 | ); | ||
| 22 | |||
| 4 | cases.add( | 23 | cases.add( |
| 5 | "@bitCast same size but bit count mismatch", | 24 | "@bitCast same size but bit count mismatch", |
| 6 | \\export fn entry(byte: u8) void { | 25 | \\export fn entry(byte: u8) void { |
test/stage1/behavior/misc.zig+8| ... | @@ -685,3 +685,11 @@ test "fn call returning scalar optional in equality expression" { | ... | @@ -685,3 +685,11 @@ test "fn call returning scalar optional in equality expression" { |
| 685 | fn getNull() ?*i32 { | 685 | fn getNull() ?*i32 { |
| 686 | return null; | 686 | return null; |
| 687 | } | 687 | } |
| 688 | |||
| 689 | test "thread local variable" { | ||
| 690 | const S = struct { | ||
| 691 | threadlocal var t: i32 = 1234; | ||
| 692 | }; | ||
| 693 | S.t += 1; | ||
| 694 | assertOrPanic(S.t == 1235); | ||
| 695 | } |
test/stage1/behavior/vector.zig+7-10| ... | @@ -1,20 +1,17 @@ | ... | @@ -1,20 +1,17 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const mem = std.mem; | ||
| 2 | const assertOrPanic = std.debug.assertOrPanic; | 3 | const assertOrPanic = std.debug.assertOrPanic; |
| 3 | 4 | ||
| 4 | test "implicit array to vector and vector to array" { | 5 | test "vector wrap operators" { |
| 5 | const S = struct { | 6 | const S = struct { |
| 6 | fn doTheTest() void { | 7 | fn doTheTest() void { |
| 7 | var v: @Vector(4, i32) = [4]i32{10, 20, 30, 40}; | 8 | const v: @Vector(4, i32) = [4]i32{ 10, 20, 30, 40 }; |
| 8 | const x: @Vector(4, i32) = [4]i32{1, 2, 3, 4}; | 9 | const x: @Vector(4, i32) = [4]i32{ 1, 2, 3, 4 }; |
| 9 | v +%= x; | 10 | assertOrPanic(mem.eql(i32, ([4]i32)(v +% x), [4]i32{ 11, 22, 33, 44 })); |
| 10 | const result: [4]i32 = v; | 11 | assertOrPanic(mem.eql(i32, ([4]i32)(v -% x), [4]i32{ 9, 18, 27, 36 })); |
| 11 | assertOrPanic(result[0] == 11); | 12 | assertOrPanic(mem.eql(i32, ([4]i32)(v *% x), [4]i32{ 10, 40, 90, 160 })); |
| 12 | assertOrPanic(result[1] == 22); | ||
| 13 | assertOrPanic(result[2] == 33); | ||
| 14 | assertOrPanic(result[3] == 44); | ||
| 15 | } | 13 | } |
| 16 | }; | 14 | }; |
| 17 | S.doTheTest(); | 15 | S.doTheTest(); |
| 18 | comptime S.doTheTest(); | 16 | comptime S.doTheTest(); |
| 19 | } | 17 | } |
| 20 |
test/tests.zig+47-11| ... | @@ -194,6 +194,9 @@ pub fn addPkgTests(b: *build.Builder, test_filter: ?[]const u8, root_src: []cons | ... | @@ -194,6 +194,9 @@ pub fn addPkgTests(b: *build.Builder, test_filter: ?[]const u8, root_src: []cons |
| 194 | if (link_libc) { | 194 | if (link_libc) { |
| 195 | these_tests.linkSystemLibrary("c"); | 195 | these_tests.linkSystemLibrary("c"); |
| 196 | } | 196 | } |
| 197 | if (mem.eql(u8, name, "std")) { | ||
| 198 | these_tests.overrideStdDir("std"); | ||
| 199 | } | ||
| 197 | step.dependOn(&these_tests.step); | 200 | step.dependOn(&these_tests.step); |
| 198 | } | 201 | } |
| 199 | } | 202 | } |
| ... | @@ -436,7 +439,10 @@ pub const CompareOutputContext = struct { | ... | @@ -436,7 +439,10 @@ pub const CompareOutputContext = struct { |
| 436 | pub fn addCase(self: *CompareOutputContext, case: TestCase) void { | 439 | pub fn addCase(self: *CompareOutputContext, case: TestCase) void { |
| 437 | const b = self.b; | 440 | const b = self.b; |
| 438 | 441 | ||
| 439 | const root_src = os.path.join(b.allocator, b.cache_root, case.sources.items[0].filename) catch unreachable; | 442 | const root_src = os.path.join( |
| 443 | b.allocator, | ||
| 444 | [][]const u8{ b.cache_root, case.sources.items[0].filename }, | ||
| 445 | ) catch unreachable; | ||
| 440 | 446 | ||
| 441 | switch (case.special) { | 447 | switch (case.special) { |
| 442 | Special.Asm => { | 448 | Special.Asm => { |
| ... | @@ -449,7 +455,10 @@ pub const CompareOutputContext = struct { | ... | @@ -449,7 +455,10 @@ pub const CompareOutputContext = struct { |
| 449 | exe.addAssemblyFile(root_src); | 455 | exe.addAssemblyFile(root_src); |
| 450 | 456 | ||
| 451 | for (case.sources.toSliceConst()) |src_file| { | 457 | for (case.sources.toSliceConst()) |src_file| { |
| 452 | const expanded_src_path = os.path.join(b.allocator, b.cache_root, src_file.filename) catch unreachable; | 458 | const expanded_src_path = os.path.join( |
| 459 | b.allocator, | ||
| 460 | [][]const u8{ b.cache_root, src_file.filename }, | ||
| 461 | ) catch unreachable; | ||
| 453 | const write_src = b.addWriteFile(expanded_src_path, src_file.source); | 462 | const write_src = b.addWriteFile(expanded_src_path, src_file.source); |
| 454 | exe.step.dependOn(&write_src.step); | 463 | exe.step.dependOn(&write_src.step); |
| 455 | } | 464 | } |
| ... | @@ -473,7 +482,10 @@ pub const CompareOutputContext = struct { | ... | @@ -473,7 +482,10 @@ pub const CompareOutputContext = struct { |
| 473 | } | 482 | } |
| 474 | 483 | ||
| 475 | for (case.sources.toSliceConst()) |src_file| { | 484 | for (case.sources.toSliceConst()) |src_file| { |
| 476 | const expanded_src_path = os.path.join(b.allocator, b.cache_root, src_file.filename) catch unreachable; | 485 | const expanded_src_path = os.path.join( |
| 486 | b.allocator, | ||
| 487 | [][]const u8{ b.cache_root, src_file.filename }, | ||
| 488 | ) catch unreachable; | ||
| 477 | const write_src = b.addWriteFile(expanded_src_path, src_file.source); | 489 | const write_src = b.addWriteFile(expanded_src_path, src_file.source); |
| 478 | exe.step.dependOn(&write_src.step); | 490 | exe.step.dependOn(&write_src.step); |
| 479 | } | 491 | } |
| ... | @@ -496,7 +508,10 @@ pub const CompareOutputContext = struct { | ... | @@ -496,7 +508,10 @@ pub const CompareOutputContext = struct { |
| 496 | } | 508 | } |
| 497 | 509 | ||
| 498 | for (case.sources.toSliceConst()) |src_file| { | 510 | for (case.sources.toSliceConst()) |src_file| { |
| 499 | const expanded_src_path = os.path.join(b.allocator, b.cache_root, src_file.filename) catch unreachable; | 511 | const expanded_src_path = os.path.join( |
| 512 | b.allocator, | ||
| 513 | [][]const u8{ b.cache_root, src_file.filename }, | ||
| 514 | ) catch unreachable; | ||
| 500 | const write_src = b.addWriteFile(expanded_src_path, src_file.source); | 515 | const write_src = b.addWriteFile(expanded_src_path, src_file.source); |
| 501 | exe.step.dependOn(&write_src.step); | 516 | exe.step.dependOn(&write_src.step); |
| 502 | } | 517 | } |
| ... | @@ -569,8 +584,14 @@ pub const CompileErrorContext = struct { | ... | @@ -569,8 +584,14 @@ pub const CompileErrorContext = struct { |
| 569 | const self = @fieldParentPtr(CompileCmpOutputStep, "step", step); | 584 | const self = @fieldParentPtr(CompileCmpOutputStep, "step", step); |
| 570 | const b = self.context.b; | 585 | const b = self.context.b; |
| 571 | 586 | ||
| 572 | const root_src = os.path.join(b.allocator, b.cache_root, self.case.sources.items[0].filename) catch unreachable; | 587 | const root_src = os.path.join( |
| 573 | const obj_path = os.path.join(b.allocator, b.cache_root, "test.o") catch unreachable; | 588 | b.allocator, |
| 589 | [][]const u8{ b.cache_root, self.case.sources.items[0].filename }, | ||
| 590 | ) catch unreachable; | ||
| 591 | const obj_path = os.path.join( | ||
| 592 | b.allocator, | ||
| 593 | [][]const u8{ b.cache_root, "test.o" }, | ||
| 594 | ) catch unreachable; | ||
| 574 | 595 | ||
| 575 | var zig_args = ArrayList([]const u8).init(b.allocator); | 596 | var zig_args = ArrayList([]const u8).init(b.allocator); |
| 576 | zig_args.append(b.zig_exe) catch unreachable; | 597 | zig_args.append(b.zig_exe) catch unreachable; |
| ... | @@ -718,7 +739,10 @@ pub const CompileErrorContext = struct { | ... | @@ -718,7 +739,10 @@ pub const CompileErrorContext = struct { |
| 718 | self.step.dependOn(&compile_and_cmp_errors.step); | 739 | self.step.dependOn(&compile_and_cmp_errors.step); |
| 719 | 740 | ||
| 720 | for (case.sources.toSliceConst()) |src_file| { | 741 | for (case.sources.toSliceConst()) |src_file| { |
| 721 | const expanded_src_path = os.path.join(b.allocator, b.cache_root, src_file.filename) catch unreachable; | 742 | const expanded_src_path = os.path.join( |
| 743 | b.allocator, | ||
| 744 | [][]const u8{ b.cache_root, src_file.filename }, | ||
| 745 | ) catch unreachable; | ||
| 722 | const write_src = b.addWriteFile(expanded_src_path, src_file.source); | 746 | const write_src = b.addWriteFile(expanded_src_path, src_file.source); |
| 723 | compile_and_cmp_errors.step.dependOn(&write_src.step); | 747 | compile_and_cmp_errors.step.dependOn(&write_src.step); |
| 724 | } | 748 | } |
| ... | @@ -849,7 +873,10 @@ pub const TranslateCContext = struct { | ... | @@ -849,7 +873,10 @@ pub const TranslateCContext = struct { |
| 849 | const self = @fieldParentPtr(TranslateCCmpOutputStep, "step", step); | 873 | const self = @fieldParentPtr(TranslateCCmpOutputStep, "step", step); |
| 850 | const b = self.context.b; | 874 | const b = self.context.b; |
| 851 | 875 | ||
| 852 | const root_src = os.path.join(b.allocator, b.cache_root, self.case.sources.items[0].filename) catch unreachable; | 876 | const root_src = os.path.join( |
| 877 | b.allocator, | ||
| 878 | [][]const u8{ b.cache_root, self.case.sources.items[0].filename }, | ||
| 879 | ) catch unreachable; | ||
| 853 | 880 | ||
| 854 | var zig_args = ArrayList([]const u8).init(b.allocator); | 881 | var zig_args = ArrayList([]const u8).init(b.allocator); |
| 855 | zig_args.append(b.zig_exe) catch unreachable; | 882 | zig_args.append(b.zig_exe) catch unreachable; |
| ... | @@ -983,7 +1010,10 @@ pub const TranslateCContext = struct { | ... | @@ -983,7 +1010,10 @@ pub const TranslateCContext = struct { |
| 983 | self.step.dependOn(&translate_c_and_cmp.step); | 1010 | self.step.dependOn(&translate_c_and_cmp.step); |
| 984 | 1011 | ||
| 985 | for (case.sources.toSliceConst()) |src_file| { | 1012 | for (case.sources.toSliceConst()) |src_file| { |
| 986 | const expanded_src_path = os.path.join(b.allocator, b.cache_root, src_file.filename) catch unreachable; | 1013 | const expanded_src_path = os.path.join( |
| 1014 | b.allocator, | ||
| 1015 | [][]const u8{ b.cache_root, src_file.filename }, | ||
| 1016 | ) catch unreachable; | ||
| 987 | const write_src = b.addWriteFile(expanded_src_path, src_file.source); | 1017 | const write_src = b.addWriteFile(expanded_src_path, src_file.source); |
| 988 | translate_c_and_cmp.step.dependOn(&write_src.step); | 1018 | translate_c_and_cmp.step.dependOn(&write_src.step); |
| 989 | } | 1019 | } |
| ... | @@ -1098,7 +1128,10 @@ pub const GenHContext = struct { | ... | @@ -1098,7 +1128,10 @@ pub const GenHContext = struct { |
| 1098 | 1128 | ||
| 1099 | pub fn addCase(self: *GenHContext, case: *const TestCase) void { | 1129 | pub fn addCase(self: *GenHContext, case: *const TestCase) void { |
| 1100 | const b = self.b; | 1130 | const b = self.b; |
| 1101 | const root_src = os.path.join(b.allocator, b.cache_root, case.sources.items[0].filename) catch unreachable; | 1131 | const root_src = os.path.join( |
| 1132 | b.allocator, | ||
| 1133 | [][]const u8{ b.cache_root, case.sources.items[0].filename }, | ||
| 1134 | ) catch unreachable; | ||
| 1102 | 1135 | ||
| 1103 | const mode = builtin.Mode.Debug; | 1136 | const mode = builtin.Mode.Debug; |
| 1104 | const annotated_case_name = fmt.allocPrint(self.b.allocator, "gen-h {} ({})", case.name, @tagName(mode)) catch unreachable; | 1137 | const annotated_case_name = fmt.allocPrint(self.b.allocator, "gen-h {} ({})", case.name, @tagName(mode)) catch unreachable; |
| ... | @@ -1110,7 +1143,10 @@ pub const GenHContext = struct { | ... | @@ -1110,7 +1143,10 @@ pub const GenHContext = struct { |
| 1110 | obj.setBuildMode(mode); | 1143 | obj.setBuildMode(mode); |
| 1111 | 1144 | ||
| 1112 | for (case.sources.toSliceConst()) |src_file| { | 1145 | for (case.sources.toSliceConst()) |src_file| { |
| 1113 | const expanded_src_path = os.path.join(b.allocator, b.cache_root, src_file.filename) catch unreachable; | 1146 | const expanded_src_path = os.path.join( |
| 1147 | b.allocator, | ||
| 1148 | [][]const u8{ b.cache_root, src_file.filename }, | ||
| 1149 | ) catch unreachable; | ||
| 1114 | const write_src = b.addWriteFile(expanded_src_path, src_file.source); | 1150 | const write_src = b.addWriteFile(expanded_src_path, src_file.source); |
| 1115 | obj.step.dependOn(&write_src.step); | 1151 | obj.step.dependOn(&write_src.step); |
| 1116 | } | 1152 | } |