| author | |
| committer | |
| log | 574e31f0a046aa6e6fad73fff2cbbb3617fe1bae |
| tree | b83f2dfec8821055929b82b18c87ea6b4e8da78a |
| parent | 8fba0a6ae862993afa2aeca774347adc399b3605 |
* introduce std.atomic.Int
* add src-self-hosted/test.zig which is tested by the main test suite
- it fully utilizes the multithreaded async/await event loop so the
tests should Go Fast
* `stage2/bin/zig build-obj test.zig` is able to spit out an error if 2 exported
functions collide
* ability for `zig test` to accept `--object` and `--assembly`
arguments
* std.build: TestStep supports addLibPath and addObjectFile11 files changed, 432 insertions(+), 107 deletions(-)
CMakeLists.txt+1| ... | ... | @@ -431,6 +431,7 @@ set(ZIG_CPP_SOURCES |
| 431 | 431 | set(ZIG_STD_FILES |
| 432 | 432 | "array_list.zig" |
| 433 | 433 | "atomic/index.zig" |
| 434 | "atomic/int.zig" | |
| 434 | 435 | "atomic/queue_mpmc.zig" |
| 435 | 436 | "atomic/queue_mpsc.zig" |
| 436 | 437 | "atomic/stack.zig" |
build.zig+89-63| ... | ... | @@ -35,70 +35,27 @@ pub fn build(b: *Builder) !void { |
| 35 | 35 | "BUILD_INFO", |
| 36 | 36 | }); |
| 37 | 37 | var index: usize = 0; |
| 38 | const cmake_binary_dir = nextValue(&index, build_info); | |
| 39 | const cxx_compiler = nextValue(&index, build_info); | |
| 40 | const llvm_config_exe = nextValue(&index, build_info); | |
| 41 | const lld_include_dir = nextValue(&index, build_info); | |
| 42 | const lld_libraries = nextValue(&index, build_info); | |
| 43 | const std_files = nextValue(&index, build_info); | |
| 44 | const c_header_files = nextValue(&index, build_info); | |
| 45 | const dia_guids_lib = nextValue(&index, build_info); | |
| 38 | var ctx = Context{ | |
| 39 | .cmake_binary_dir = nextValue(&index, build_info), | |
| 40 | .cxx_compiler = nextValue(&index, build_info), | |
| 41 | .llvm_config_exe = nextValue(&index, build_info), | |
| 42 | .lld_include_dir = nextValue(&index, build_info), | |
| 43 | .lld_libraries = nextValue(&index, build_info), | |
| 44 | .std_files = nextValue(&index, build_info), | |
| 45 | .c_header_files = nextValue(&index, build_info), | |
| 46 | .dia_guids_lib = nextValue(&index, build_info), | |
| 47 | .llvm = undefined, | |
| 48 | }; | |
| 49 | ctx.llvm = try findLLVM(b, ctx.llvm_config_exe); | |
| 46 | 50 | |
| 47 | const llvm = findLLVM(b, llvm_config_exe) catch unreachable; | |
| 51 | var test_stage2 = b.addTest("src-self-hosted/test.zig"); | |
| 52 | test_stage2.setBuildMode(builtin.Mode.Debug); | |
| 48 | 53 | |
| 49 | 54 | var exe = b.addExecutable("zig", "src-self-hosted/main.zig"); |
| 50 | 55 | exe.setBuildMode(mode); |
| 51 | 56 | |
| 52 | // This is for finding /lib/libz.a on alpine linux. | |
| 53 | // TODO turn this into -Dextra-lib-path=/lib option | |
| 54 | exe.addLibPath("/lib"); | |
| 55 | ||
| 56 | exe.addIncludeDir("src"); | |
| 57 | exe.addIncludeDir(cmake_binary_dir); | |
| 58 | addCppLib(b, exe, cmake_binary_dir, "zig_cpp"); | |
| 59 | if (lld_include_dir.len != 0) { | |
| 60 | exe.addIncludeDir(lld_include_dir); | |
| 61 | var it = mem.split(lld_libraries, ";"); | |
| 62 | while (it.next()) |lib| { | |
| 63 | exe.addObjectFile(lib); | |
| 64 | } | |
| 65 | } else { | |
| 66 | addCppLib(b, exe, cmake_binary_dir, "embedded_lld_wasm"); | |
| 67 | addCppLib(b, exe, cmake_binary_dir, "embedded_lld_elf"); | |
| 68 | addCppLib(b, exe, cmake_binary_dir, "embedded_lld_coff"); | |
| 69 | addCppLib(b, exe, cmake_binary_dir, "embedded_lld_lib"); | |
| 70 | } | |
| 71 | dependOnLib(exe, llvm); | |
| 72 | ||
| 73 | if (exe.target.getOs() == builtin.Os.linux) { | |
| 74 | const libstdcxx_path_padded = try b.exec([][]const u8{ | |
| 75 | cxx_compiler, | |
| 76 | "-print-file-name=libstdc++.a", | |
| 77 | }); | |
| 78 | const libstdcxx_path = mem.split(libstdcxx_path_padded, "\r\n").next().?; | |
| 79 | if (mem.eql(u8, libstdcxx_path, "libstdc++.a")) { | |
| 80 | warn( | |
| 81 | \\Unable to determine path to libstdc++.a | |
| 82 | \\On Fedora, install libstdc++-static and try again. | |
| 83 | \\ | |
| 84 | ); | |
| 85 | return error.RequiredLibraryNotFound; | |
| 86 | } | |
| 87 | exe.addObjectFile(libstdcxx_path); | |
| 88 | ||
| 89 | exe.linkSystemLibrary("pthread"); | |
| 90 | } else if (exe.target.isDarwin()) { | |
| 91 | exe.linkSystemLibrary("c++"); | |
| 92 | } | |
| 93 | ||
| 94 | if (dia_guids_lib.len != 0) { | |
| 95 | exe.addObjectFile(dia_guids_lib); | |
| 96 | } | |
| 97 | ||
| 98 | if (exe.target.getOs() != builtin.Os.windows) { | |
| 99 | exe.linkSystemLibrary("xml2"); | |
| 100 | } | |
| 101 | exe.linkSystemLibrary("c"); | |
| 57 | try configureStage2(b, test_stage2, ctx); | |
| 58 | try configureStage2(b, exe, ctx); | |
| 102 | 59 | |
| 103 | 60 | b.default_step.dependOn(&exe.step); |
| 104 | 61 | |
| ... | ... | @@ -110,12 +67,16 @@ pub fn build(b: *Builder) !void { |
| 110 | 67 | exe.setVerboseLink(verbose_link_exe); |
| 111 | 68 | |
| 112 | 69 | b.installArtifact(exe); |
| 113 | installStdLib(b, std_files); | |
| 114 | installCHeaders(b, c_header_files); | |
| 70 | installStdLib(b, ctx.std_files); | |
| 71 | installCHeaders(b, ctx.c_header_files); | |
| 115 | 72 | |
| 116 | 73 | const test_filter = b.option([]const u8, "test-filter", "Skip tests that do not match filter"); |
| 117 | 74 | const with_lldb = b.option(bool, "with-lldb", "Run tests in LLDB to get a backtrace if one fails") orelse false; |
| 118 | 75 | |
| 76 | const test_stage2_step = b.step("test-stage2", "Run the stage2 compiler tests"); | |
| 77 | test_stage2_step.dependOn(&test_stage2.step); | |
| 78 | test_step.dependOn(test_stage2_step); | |
| 79 | ||
| 119 | 80 | test_step.dependOn(docs_step); |
| 120 | 81 | |
| 121 | 82 | test_step.dependOn(tests.addPkgTests(b, test_filter, "test/behavior.zig", "behavior", "Run the behavior tests", with_lldb)); |
| ... | ... | @@ -133,7 +94,7 @@ pub fn build(b: *Builder) !void { |
| 133 | 94 | test_step.dependOn(tests.addGenHTests(b, test_filter)); |
| 134 | 95 | } |
| 135 | 96 | |
| 136 | fn dependOnLib(lib_exe_obj: *std.build.LibExeObjStep, dep: *const LibraryDep) void { | |
| 97 | fn dependOnLib(lib_exe_obj: var, dep: *const LibraryDep) void { | |
| 137 | 98 | for (dep.libdirs.toSliceConst()) |lib_dir| { |
| 138 | 99 | lib_exe_obj.addLibPath(lib_dir); |
| 139 | 100 | } |
| ... | ... | @@ -148,7 +109,7 @@ fn dependOnLib(lib_exe_obj: *std.build.LibExeObjStep, dep: *const LibraryDep) vo |
| 148 | 109 | } |
| 149 | 110 | } |
| 150 | 111 | |
| 151 | fn addCppLib(b: *Builder, lib_exe_obj: *std.build.LibExeObjStep, cmake_binary_dir: []const u8, lib_name: []const u8) void { | |
| 112 | fn addCppLib(b: *Builder, lib_exe_obj: var, cmake_binary_dir: []const u8, lib_name: []const u8) void { | |
| 152 | 113 | const lib_prefix = if (lib_exe_obj.target.isWindows()) "" else "lib"; |
| 153 | 114 | 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); |
| 154 | 115 | } |
| ... | ... | @@ -254,3 +215,68 @@ fn nextValue(index: *usize, build_info: []const u8) []const u8 { |
| 254 | 215 | } |
| 255 | 216 | } |
| 256 | 217 | } |
| 218 | ||
| 219 | fn configureStage2(b: *Builder, exe: var, ctx: Context) !void { | |
| 220 | // This is for finding /lib/libz.a on alpine linux. | |
| 221 | // TODO turn this into -Dextra-lib-path=/lib option | |
| 222 | exe.addLibPath("/lib"); | |
| 223 | ||
| 224 | exe.addIncludeDir("src"); | |
| 225 | exe.addIncludeDir(ctx.cmake_binary_dir); | |
| 226 | addCppLib(b, exe, ctx.cmake_binary_dir, "zig_cpp"); | |
| 227 | if (ctx.lld_include_dir.len != 0) { | |
| 228 | exe.addIncludeDir(ctx.lld_include_dir); | |
| 229 | var it = mem.split(ctx.lld_libraries, ";"); | |
| 230 | while (it.next()) |lib| { | |
| 231 | exe.addObjectFile(lib); | |
| 232 | } | |
| 233 | } else { | |
| 234 | addCppLib(b, exe, ctx.cmake_binary_dir, "embedded_lld_wasm"); | |
| 235 | addCppLib(b, exe, ctx.cmake_binary_dir, "embedded_lld_elf"); | |
| 236 | addCppLib(b, exe, ctx.cmake_binary_dir, "embedded_lld_coff"); | |
| 237 | addCppLib(b, exe, ctx.cmake_binary_dir, "embedded_lld_lib"); | |
| 238 | } | |
| 239 | dependOnLib(exe, ctx.llvm); | |
| 240 | ||
| 241 | if (exe.target.getOs() == builtin.Os.linux) { | |
| 242 | const libstdcxx_path_padded = try b.exec([][]const u8{ | |
| 243 | ctx.cxx_compiler, | |
| 244 | "-print-file-name=libstdc++.a", | |
| 245 | }); | |
| 246 | const libstdcxx_path = mem.split(libstdcxx_path_padded, "\r\n").next().?; | |
| 247 | if (mem.eql(u8, libstdcxx_path, "libstdc++.a")) { | |
| 248 | warn( | |
| 249 | \\Unable to determine path to libstdc++.a | |
| 250 | \\On Fedora, install libstdc++-static and try again. | |
| 251 | \\ | |
| 252 | ); | |
| 253 | return error.RequiredLibraryNotFound; | |
| 254 | } | |
| 255 | exe.addObjectFile(libstdcxx_path); | |
| 256 | ||
| 257 | exe.linkSystemLibrary("pthread"); | |
| 258 | } else if (exe.target.isDarwin()) { | |
| 259 | exe.linkSystemLibrary("c++"); | |
| 260 | } | |
| 261 | ||
| 262 | if (ctx.dia_guids_lib.len != 0) { | |
| 263 | exe.addObjectFile(ctx.dia_guids_lib); | |
| 264 | } | |
| 265 | ||
| 266 | if (exe.target.getOs() != builtin.Os.windows) { | |
| 267 | exe.linkSystemLibrary("xml2"); | |
| 268 | } | |
| 269 | exe.linkSystemLibrary("c"); | |
| 270 | } | |
| 271 | ||
| 272 | const Context = struct { | |
| 273 | cmake_binary_dir: []const u8, | |
| 274 | cxx_compiler: []const u8, | |
| 275 | llvm_config_exe: []const u8, | |
| 276 | lld_include_dir: []const u8, | |
| 277 | lld_libraries: []const u8, | |
| 278 | std_files: []const u8, | |
| 279 | c_header_files: []const u8, | |
| 280 | dia_guids_lib: []const u8, | |
| 281 | llvm: LibraryDep, | |
| 282 | }; |
src-self-hosted/errmsg.zig+12-6| ... | ... | @@ -11,11 +11,15 @@ pub const Color = enum { |
| 11 | 11 | On, |
| 12 | 12 | }; |
| 13 | 13 | |
| 14 | pub const Span = struct { | |
| 15 | first: ast.TokenIndex, | |
| 16 | last: ast.TokenIndex, | |
| 17 | }; | |
| 18 | ||
| 14 | 19 | pub const Msg = struct { |
| 15 | 20 | path: []const u8, |
| 16 | 21 | text: []u8, |
| 17 | first_token: TokenIndex, | |
| 18 | last_token: TokenIndex, | |
| 22 | span: Span, | |
| 19 | 23 | tree: *ast.Tree, |
| 20 | 24 | }; |
| 21 | 25 | |
| ... | ... | @@ -39,8 +43,10 @@ pub fn createFromParseError( |
| 39 | 43 | .tree = tree, |
| 40 | 44 | .path = path, |
| 41 | 45 | .text = text_buf.toOwnedSlice(), |
| 42 | .first_token = loc_token, | |
| 43 | .last_token = loc_token, | |
| 46 | .span = Span{ | |
| 47 | .first = loc_token, | |
| 48 | .last = loc_token, | |
| 49 | }, | |
| 44 | 50 | }); |
| 45 | 51 | errdefer allocator.destroy(msg); |
| 46 | 52 | |
| ... | ... | @@ -48,8 +54,8 @@ pub fn createFromParseError( |
| 48 | 54 | } |
| 49 | 55 | |
| 50 | 56 | pub fn printToStream(stream: var, msg: *const Msg, color_on: bool) !void { |
| 51 | const first_token = msg.tree.tokens.at(msg.first_token); | |
| 52 | const last_token = msg.tree.tokens.at(msg.last_token); | |
| 57 | const first_token = msg.tree.tokens.at(msg.span.first); | |
| 58 | const last_token = msg.tree.tokens.at(msg.span.last); | |
| 53 | 59 | const start_loc = msg.tree.tokenLocationPtr(0, first_token); |
| 54 | 60 | const end_loc = msg.tree.tokenLocationPtr(first_token.end, last_token); |
| 55 | 61 | if (!color_on) { |
src-self-hosted/introspect.zig+5| ... | ... | @@ -53,3 +53,8 @@ pub fn resolveZigLibDir(allocator: *mem.Allocator) ![]u8 { |
| 53 | 53 | return error.ZigLibDirNotFound; |
| 54 | 54 | }; |
| 55 | 55 | } |
| 56 | ||
| 57 | /// Caller must free result | |
| 58 | pub fn resolveZigCacheDir(allocator: *mem.Allocator) ![]u8 { | |
| 59 | return std.mem.dupe(allocator, u8, "zig-cache"); | |
| 60 | } |
src-self-hosted/main.zig+18-18| ... | ... | @@ -481,29 +481,29 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo |
| 481 | 481 | module.link_out_file = flags.single("out-file"); |
| 482 | 482 | |
| 483 | 483 | try module.build(); |
| 484 | const process_build_events_handle = try async<loop.allocator> processBuildEvents(module, true); | |
| 484 | const process_build_events_handle = try async<loop.allocator> processBuildEvents(module, color); | |
| 485 | 485 | defer cancel process_build_events_handle; |
| 486 | 486 | loop.run(); |
| 487 | 487 | } |
| 488 | 488 | |
| 489 | async fn processBuildEvents(module: *Module, watch: bool) void { | |
| 490 | while (watch) { | |
| 491 | // TODO directly awaiting async should guarantee memory allocation elision | |
| 492 | const build_event = await (async module.events.get() catch unreachable); | |
| 489 | async fn processBuildEvents(module: *Module, color: errmsg.Color) void { | |
| 490 | // TODO directly awaiting async should guarantee memory allocation elision | |
| 491 | const build_event = await (async module.events.get() catch unreachable); | |
| 493 | 492 | |
| 494 | switch (build_event) { | |
| 495 | Module.Event.Ok => { | |
| 496 | std.debug.warn("Build succeeded\n"); | |
| 497 | return; | |
| 498 | }, | |
| 499 | Module.Event.Error => |err| { | |
| 500 | std.debug.warn("build failed: {}\n", @errorName(err)); | |
| 501 | @panic("TODO error return trace"); | |
| 502 | }, | |
| 503 | Module.Event.Fail => |errs| { | |
| 504 | @panic("TODO print compile error messages"); | |
| 505 | }, | |
| 506 | } | |
| 493 | switch (build_event) { | |
| 494 | Module.Event.Ok => { | |
| 495 | std.debug.warn("Build succeeded\n"); | |
| 496 | return; | |
| 497 | }, | |
| 498 | Module.Event.Error => |err| { | |
| 499 | std.debug.warn("build failed: {}\n", @errorName(err)); | |
| 500 | @panic("TODO error return trace"); | |
| 501 | }, | |
| 502 | Module.Event.Fail => |msgs| { | |
| 503 | for (msgs) |msg| { | |
| 504 | errmsg.printToFile(&stderr_file, msg, color) catch os.exit(1); | |
| 505 | } | |
| 506 | }, | |
| 507 | 507 | } |
| 508 | 508 | } |
| 509 | 509 |
src-self-hosted/module.zig+81-17| ... | ... | @@ -89,12 +89,9 @@ pub const Module = struct { |
| 89 | 89 | /// the build is complete. |
| 90 | 90 | build_group: event.Group(BuildError!void), |
| 91 | 91 | |
| 92 | const BuildErrorsList = std.SegmentedList(BuildErrorDesc, 1); | |
| 92 | compile_errors: event.Locked(CompileErrList), | |
| 93 | 93 | |
| 94 | pub const BuildErrorDesc = struct { | |
| 95 | code: BuildError, | |
| 96 | text: []const u8, | |
| 97 | }; | |
| 94 | const CompileErrList = std.ArrayList(*errmsg.Msg); | |
| 98 | 95 | |
| 99 | 96 | // TODO handle some of these earlier and report them in a way other than error codes |
| 100 | 97 | pub const BuildError = error{ |
| ... | ... | @@ -131,11 +128,12 @@ pub const Module = struct { |
| 131 | 128 | NoStdHandles, |
| 132 | 129 | Overflow, |
| 133 | 130 | NotSupported, |
| 131 | BufferTooSmall, | |
| 134 | 132 | }; |
| 135 | 133 | |
| 136 | 134 | pub const Event = union(enum) { |
| 137 | 135 | Ok, |
| 138 | Fail: []errmsg.Msg, | |
| 136 | Fail: []*errmsg.Msg, | |
| 139 | 137 | Error: BuildError, |
| 140 | 138 | }; |
| 141 | 139 | |
| ... | ... | @@ -249,6 +247,7 @@ pub const Module = struct { |
| 249 | 247 | .link_out_file = null, |
| 250 | 248 | .exported_symbol_names = event.Locked(Decl.Table).init(loop, Decl.Table.init(loop.allocator)), |
| 251 | 249 | .build_group = event.Group(BuildError!void).init(loop), |
| 250 | .compile_errors = event.Locked(CompileErrList).init(loop, CompileErrList.init(loop.allocator)), | |
| 252 | 251 | }); |
| 253 | 252 | } |
| 254 | 253 | |
| ... | ... | @@ -288,7 +287,17 @@ pub const Module = struct { |
| 288 | 287 | await (async self.events.put(Event{ .Error = err }) catch unreachable); |
| 289 | 288 | return; |
| 290 | 289 | }; |
| 291 | await (async self.events.put(Event.Ok) catch unreachable); | |
| 290 | const compile_errors = blk: { | |
| 291 | const held = await (async self.compile_errors.acquire() catch unreachable); | |
| 292 | defer held.release(); | |
| 293 | break :blk held.value.toOwnedSlice(); | |
| 294 | }; | |
| 295 | ||
| 296 | if (compile_errors.len == 0) { | |
| 297 | await (async self.events.put(Event.Ok) catch unreachable); | |
| 298 | } else { | |
| 299 | await (async self.events.put(Event{ .Fail = compile_errors }) catch unreachable); | |
| 300 | } | |
| 292 | 301 | // for now we stop after 1 |
| 293 | 302 | return; |
| 294 | 303 | } |
| ... | ... | @@ -310,10 +319,13 @@ pub const Module = struct { |
| 310 | 319 | }; |
| 311 | 320 | errdefer self.a().free(source_code); |
| 312 | 321 | |
| 313 | var parsed_file = ParsedFile{ | |
| 314 | .tree = try std.zig.parse(self.a(), source_code), | |
| 322 | const parsed_file = try self.a().create(ParsedFile{ | |
| 323 | .tree = undefined, | |
| 315 | 324 | .realpath = root_src_real_path, |
| 316 | }; | |
| 325 | }); | |
| 326 | errdefer self.a().destroy(parsed_file); | |
| 327 | ||
| 328 | parsed_file.tree = try std.zig.parse(self.a(), source_code); | |
| 317 | 329 | errdefer parsed_file.tree.deinit(); |
| 318 | 330 | |
| 319 | 331 | const tree = &parsed_file.tree; |
| ... | ... | @@ -337,7 +349,7 @@ pub const Module = struct { |
| 337 | 349 | const name = if (fn_proto.name_token) |name_token| tree.tokenSlice(name_token) else { |
| 338 | 350 | @panic("TODO add compile error"); |
| 339 | 351 | //try self.addCompileError( |
| 340 | // &parsed_file, | |
| 352 | // parsed_file, | |
| 341 | 353 | // fn_proto.fn_token, |
| 342 | 354 | // fn_proto.fn_token + 1, |
| 343 | 355 | // "missing function name", |
| ... | ... | @@ -357,7 +369,7 @@ pub const Module = struct { |
| 357 | 369 | }); |
| 358 | 370 | errdefer self.a().destroy(fn_decl); |
| 359 | 371 | |
| 360 | try decl_group.call(addTopLevelDecl, self, tree, &fn_decl.base); | |
| 372 | try decl_group.call(addTopLevelDecl, self, parsed_file, &fn_decl.base); | |
| 361 | 373 | }, |
| 362 | 374 | ast.Node.Id.TestDecl => @panic("TODO"), |
| 363 | 375 | else => unreachable, |
| ... | ... | @@ -367,20 +379,56 @@ pub const Module = struct { |
| 367 | 379 | try await (async self.build_group.wait() catch unreachable); |
| 368 | 380 | } |
| 369 | 381 | |
| 370 | async fn addTopLevelDecl(self: *Module, tree: *ast.Tree, decl: *Decl) !void { | |
| 371 | const is_export = decl.isExported(tree); | |
| 382 | async fn addTopLevelDecl(self: *Module, parsed_file: *ParsedFile, decl: *Decl) !void { | |
| 383 | const is_export = decl.isExported(&parsed_file.tree); | |
| 372 | 384 | |
| 373 | 385 | if (is_export) { |
| 374 | try self.build_group.call(verifyUniqueSymbol, self, decl); | |
| 386 | try self.build_group.call(verifyUniqueSymbol, self, parsed_file, decl); | |
| 375 | 387 | } |
| 376 | 388 | } |
| 377 | 389 | |
| 378 | async fn verifyUniqueSymbol(self: *Module, decl: *Decl) !void { | |
| 390 | fn addCompileError(self: *Module, parsed_file: *ParsedFile, span: errmsg.Span, comptime fmt: []const u8, args: ...) !void { | |
| 391 | const text = try std.fmt.allocPrint(self.loop.allocator, fmt, args); | |
| 392 | errdefer self.loop.allocator.free(text); | |
| 393 | ||
| 394 | try self.build_group.call(addCompileErrorAsync, self, parsed_file, span.first, span.last, text); | |
| 395 | } | |
| 396 | ||
| 397 | async fn addCompileErrorAsync( | |
| 398 | self: *Module, | |
| 399 | parsed_file: *ParsedFile, | |
| 400 | first_token: ast.TokenIndex, | |
| 401 | last_token: ast.TokenIndex, | |
| 402 | text: []u8, | |
| 403 | ) !void { | |
| 404 | const msg = try self.loop.allocator.create(errmsg.Msg{ | |
| 405 | .path = parsed_file.realpath, | |
| 406 | .text = text, | |
| 407 | .span = errmsg.Span{ | |
| 408 | .first = first_token, | |
| 409 | .last = last_token, | |
| 410 | }, | |
| 411 | .tree = &parsed_file.tree, | |
| 412 | }); | |
| 413 | errdefer self.loop.allocator.destroy(msg); | |
| 414 | ||
| 415 | const compile_errors = await (async self.compile_errors.acquire() catch unreachable); | |
| 416 | defer compile_errors.release(); | |
| 417 | ||
| 418 | try compile_errors.value.append(msg); | |
| 419 | } | |
| 420 | ||
| 421 | async fn verifyUniqueSymbol(self: *Module, parsed_file: *ParsedFile, decl: *Decl) !void { | |
| 379 | 422 | const exported_symbol_names = await (async self.exported_symbol_names.acquire() catch unreachable); |
| 380 | 423 | defer exported_symbol_names.release(); |
| 381 | 424 | |
| 382 | 425 | if (try exported_symbol_names.value.put(decl.name, decl)) |other_decl| { |
| 383 | @panic("TODO report compile error"); | |
| 426 | try self.addCompileError( | |
| 427 | parsed_file, | |
| 428 | decl.getSpan(), | |
| 429 | "exported symbol collision: '{}'", | |
| 430 | decl.name, | |
| 431 | ); | |
| 384 | 432 | } |
| 385 | 433 | } |
| 386 | 434 | |
| ... | ... | @@ -503,6 +551,22 @@ pub const Decl = struct { |
| 503 | 551 | } |
| 504 | 552 | } |
| 505 | 553 | |
| 554 | pub fn getSpan(base: *const Decl) errmsg.Span { | |
| 555 | switch (base.id) { | |
| 556 | Id.Fn => { | |
| 557 | const fn_decl = @fieldParentPtr(Fn, "base", base); | |
| 558 | const fn_proto = fn_decl.fn_proto; | |
| 559 | const start = fn_proto.fn_token; | |
| 560 | const end = fn_proto.name_token orelse start; | |
| 561 | return errmsg.Span{ | |
| 562 | .first = start, | |
| 563 | .last = end + 1, | |
| 564 | }; | |
| 565 | }, | |
| 566 | else => @panic("TODO"), | |
| 567 | } | |
| 568 | } | |
| 569 | ||
| 506 | 570 | pub const Resolution = enum { |
| 507 | 571 | Unresolved, |
| 508 | 572 | InProgress, |
src-self-hosted/test.zig created+176| ... | ... | @@ -0,0 +1,176 @@ |
| 1 | const std = @import("std"); | |
| 2 | const mem = std.mem; | |
| 3 | const builtin = @import("builtin"); | |
| 4 | const Target = @import("target.zig").Target; | |
| 5 | const Module = @import("module.zig").Module; | |
| 6 | const introspect = @import("introspect.zig"); | |
| 7 | const assertOrPanic = std.debug.assertOrPanic; | |
| 8 | const errmsg = @import("errmsg.zig"); | |
| 9 | ||
| 10 | test "compile errors" { | |
| 11 | var ctx: TestContext = undefined; | |
| 12 | try ctx.init(); | |
| 13 | defer ctx.deinit(); | |
| 14 | ||
| 15 | try ctx.testCompileError( | |
| 16 | \\export fn entry() void {} | |
| 17 | \\export fn entry() void {} | |
| 18 | , file1, 2, 8, "exported symbol collision: 'entry'"); | |
| 19 | ||
| 20 | try ctx.run(); | |
| 21 | } | |
| 22 | ||
| 23 | const file1 = "1.zig"; | |
| 24 | ||
| 25 | const TestContext = struct { | |
| 26 | loop: std.event.Loop, | |
| 27 | zig_lib_dir: []u8, | |
| 28 | direct_allocator: std.heap.DirectAllocator, | |
| 29 | arena: std.heap.ArenaAllocator, | |
| 30 | zig_cache_dir: []u8, | |
| 31 | file_index: std.atomic.Int(usize), | |
| 32 | group: std.event.Group(error!void), | |
| 33 | any_err: error!void, | |
| 34 | ||
| 35 | const tmp_dir_name = "stage2_test_tmp"; | |
| 36 | ||
| 37 | fn init(self: *TestContext) !void { | |
| 38 | self.* = TestContext{ | |
| 39 | .any_err = {}, | |
| 40 | .direct_allocator = undefined, | |
| 41 | .arena = undefined, | |
| 42 | .loop = undefined, | |
| 43 | .zig_lib_dir = undefined, | |
| 44 | .zig_cache_dir = undefined, | |
| 45 | .group = undefined, | |
| 46 | .file_index = std.atomic.Int(usize).init(0), | |
| 47 | }; | |
| 48 | ||
| 49 | self.direct_allocator = std.heap.DirectAllocator.init(); | |
| 50 | errdefer self.direct_allocator.deinit(); | |
| 51 | ||
| 52 | self.arena = std.heap.ArenaAllocator.init(&self.direct_allocator.allocator); | |
| 53 | errdefer self.arena.deinit(); | |
| 54 | ||
| 55 | // TODO faster allocator for coroutines that is thread-safe/lock-free | |
| 56 | try self.loop.initMultiThreaded(&self.direct_allocator.allocator); | |
| 57 | errdefer self.loop.deinit(); | |
| 58 | ||
| 59 | self.group = std.event.Group(error!void).init(&self.loop); | |
| 60 | errdefer self.group.cancelAll(); | |
| 61 | ||
| 62 | self.zig_lib_dir = try introspect.resolveZigLibDir(&self.arena.allocator); | |
| 63 | errdefer self.arena.allocator.free(self.zig_lib_dir); | |
| 64 | ||
| 65 | self.zig_cache_dir = try introspect.resolveZigCacheDir(&self.arena.allocator); | |
| 66 | errdefer self.arena.allocator.free(self.zig_cache_dir); | |
| 67 | ||
| 68 | try std.os.makePath(&self.arena.allocator, tmp_dir_name); | |
| 69 | errdefer std.os.deleteTree(&self.arena.allocator, tmp_dir_name) catch {}; | |
| 70 | } | |
| 71 | ||
| 72 | fn deinit(self: *TestContext) void { | |
| 73 | std.os.deleteTree(&self.arena.allocator, tmp_dir_name) catch {}; | |
| 74 | self.arena.allocator.free(self.zig_cache_dir); | |
| 75 | self.arena.allocator.free(self.zig_lib_dir); | |
| 76 | self.loop.deinit(); | |
| 77 | self.arena.deinit(); | |
| 78 | self.direct_allocator.deinit(); | |
| 79 | } | |
| 80 | ||
| 81 | fn run(self: *TestContext) !void { | |
| 82 | const handle = try self.loop.call(waitForGroup, self); | |
| 83 | defer cancel handle; | |
| 84 | self.loop.run(); | |
| 85 | return self.any_err; | |
| 86 | } | |
| 87 | ||
| 88 | async fn waitForGroup(self: *TestContext) void { | |
| 89 | self.any_err = await (async self.group.wait() catch unreachable); | |
| 90 | } | |
| 91 | ||
| 92 | fn testCompileError( | |
| 93 | self: *TestContext, | |
| 94 | source: []const u8, | |
| 95 | path: []const u8, | |
| 96 | line: usize, | |
| 97 | column: usize, | |
| 98 | msg: []const u8, | |
| 99 | ) !void { | |
| 100 | var file_index_buf: [20]u8 = undefined; | |
| 101 | const file_index = try std.fmt.bufPrint(file_index_buf[0..], "{}", self.file_index.next()); | |
| 102 | const file1_path = try std.os.path.join(&self.arena.allocator, tmp_dir_name, file_index, file1); | |
| 103 | ||
| 104 | if (std.os.path.dirname(file1_path)) |dirname| { | |
| 105 | try std.os.makePath(&self.arena.allocator, dirname); | |
| 106 | } | |
| 107 | ||
| 108 | // TODO async I/O | |
| 109 | try std.io.writeFile(&self.arena.allocator, file1_path, source); | |
| 110 | ||
| 111 | var module = try Module.create( | |
| 112 | &self.loop, | |
| 113 | "test", | |
| 114 | file1_path, | |
| 115 | Target.Native, | |
| 116 | Module.Kind.Obj, | |
| 117 | builtin.Mode.Debug, | |
| 118 | self.zig_lib_dir, | |
| 119 | self.zig_cache_dir, | |
| 120 | ); | |
| 121 | errdefer module.destroy(); | |
| 122 | ||
| 123 | try module.build(); | |
| 124 | ||
| 125 | try self.group.call(getModuleEvent, module, source, path, line, column, msg); | |
| 126 | } | |
| 127 | ||
| 128 | async fn getModuleEvent( | |
| 129 | module: *Module, | |
| 130 | source: []const u8, | |
| 131 | path: []const u8, | |
| 132 | line: usize, | |
| 133 | column: usize, | |
| 134 | text: []const u8, | |
| 135 | ) !void { | |
| 136 | defer module.destroy(); | |
| 137 | const build_event = await (async module.events.get() catch unreachable); | |
| 138 | ||
| 139 | switch (build_event) { | |
| 140 | Module.Event.Ok => { | |
| 141 | @panic("build incorrectly succeeded"); | |
| 142 | }, | |
| 143 | Module.Event.Error => |err| { | |
| 144 | @panic("build incorrectly failed"); | |
| 145 | }, | |
| 146 | Module.Event.Fail => |msgs| { | |
| 147 | assertOrPanic(msgs.len != 0); | |
| 148 | for (msgs) |msg| { | |
| 149 | if (mem.endsWith(u8, msg.path, path) and mem.eql(u8, msg.text, text)) { | |
| 150 | const first_token = msg.tree.tokens.at(msg.span.first); | |
| 151 | const last_token = msg.tree.tokens.at(msg.span.first); | |
| 152 | const start_loc = msg.tree.tokenLocationPtr(0, first_token); | |
| 153 | if (start_loc.line + 1 == line and start_loc.column + 1 == column) { | |
| 154 | return; | |
| 155 | } | |
| 156 | } | |
| 157 | } | |
| 158 | std.debug.warn( | |
| 159 | "\n=====source:=======\n{}\n====expected:========\n{}:{}:{}: error: {}\n", | |
| 160 | source, | |
| 161 | path, | |
| 162 | line, | |
| 163 | column, | |
| 164 | text, | |
| 165 | ); | |
| 166 | std.debug.warn("\n====found:========\n"); | |
| 167 | var stderr = try std.io.getStdErr(); | |
| 168 | for (msgs) |msg| { | |
| 169 | try errmsg.printToFile(&stderr, msg, errmsg.Color.Auto); | |
| 170 | } | |
| 171 | std.debug.warn("============\n"); | |
| 172 | return error.TestFailed; | |
| 173 | }, | |
| 174 | } | |
| 175 | } | |
| 176 | }; |
src/main.cpp+7-3| ... | ... | @@ -891,15 +891,19 @@ int main(int argc, char **argv) { |
| 891 | 891 | |
| 892 | 892 | add_package(g, cur_pkg, g->root_package); |
| 893 | 893 | |
| 894 | if (cmd == CmdBuild || cmd == CmdRun) { | |
| 895 | codegen_set_emit_file_type(g, emit_file_type); | |
| 896 | ||
| 894 | if (cmd == CmdBuild || cmd == CmdRun || cmd == CmdTest) { | |
| 897 | 895 | for (size_t i = 0; i < objects.length; i += 1) { |
| 898 | 896 | codegen_add_object(g, buf_create_from_str(objects.at(i))); |
| 899 | 897 | } |
| 900 | 898 | for (size_t i = 0; i < asm_files.length; i += 1) { |
| 901 | 899 | codegen_add_assembly(g, buf_create_from_str(asm_files.at(i))); |
| 902 | 900 | } |
| 901 | } | |
| 902 | ||
| 903 | ||
| 904 | if (cmd == CmdBuild || cmd == CmdRun) { | |
| 905 | codegen_set_emit_file_type(g, emit_file_type); | |
| 906 | ||
| 903 | 907 | codegen_build(g); |
| 904 | 908 | codegen_link(g, out_file); |
| 905 | 909 | if (timing_info) |
std/atomic/index.zig+2| ... | ... | @@ -1,9 +1,11 @@ |
| 1 | 1 | pub const Stack = @import("stack.zig").Stack; |
| 2 | 2 | pub const QueueMpsc = @import("queue_mpsc.zig").QueueMpsc; |
| 3 | 3 | pub const QueueMpmc = @import("queue_mpmc.zig").QueueMpmc; |
| 4 | pub const Int = @import("int.zig").Int; | |
| 4 | 5 | |
| 5 | 6 | test "std.atomic" { |
| 6 | 7 | _ = @import("stack.zig"); |
| 7 | 8 | _ = @import("queue_mpsc.zig"); |
| 8 | 9 | _ = @import("queue_mpmc.zig"); |
| 10 | _ = @import("int.zig"); | |
| 9 | 11 | } |
std/atomic/int.zig created+19| ... | ... | @@ -0,0 +1,19 @@ |
| 1 | const builtin = @import("builtin"); | |
| 2 | const AtomicOrder = builtin.AtomicOrder; | |
| 3 | ||
| 4 | /// Thread-safe, lock-free integer | |
| 5 | pub fn Int(comptime T: type) type { | |
| 6 | return struct { | |
| 7 | value: T, | |
| 8 | ||
| 9 | pub const Self = this; | |
| 10 | ||
| 11 | pub fn init(init_val: T) Self { | |
| 12 | return Self{ .value = init_val }; | |
| 13 | } | |
| 14 | ||
| 15 | pub fn next(self: *Self) T { | |
| 16 | return @atomicRmw(T, &self.value, builtin.AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); | |
| 17 | } | |
| 18 | }; | |
| 19 | } |
std/build.zig+22| ... | ... | @@ -1596,6 +1596,8 @@ pub const TestStep = struct { |
| 1596 | 1596 | target: Target, |
| 1597 | 1597 | exec_cmd_args: ?[]const ?[]const u8, |
| 1598 | 1598 | include_dirs: ArrayList([]const u8), |
| 1599 | lib_paths: ArrayList([]const u8), | |
| 1600 | object_files: ArrayList([]const u8), | |
| 1599 | 1601 | |
| 1600 | 1602 | pub fn init(builder: *Builder, root_src: []const u8) TestStep { |
| 1601 | 1603 | const step_name = builder.fmt("test {}", root_src); |
| ... | ... | @@ -1611,9 +1613,15 @@ pub const TestStep = struct { |
| 1611 | 1613 | .target = Target{ .Native = {} }, |
| 1612 | 1614 | .exec_cmd_args = null, |
| 1613 | 1615 | .include_dirs = ArrayList([]const u8).init(builder.allocator), |
| 1616 | .lib_paths = ArrayList([]const u8).init(builder.allocator), | |
| 1617 | .object_files = ArrayList([]const u8).init(builder.allocator), | |
| 1614 | 1618 | }; |
| 1615 | 1619 | } |
| 1616 | 1620 | |
| 1621 | pub fn addLibPath(self: *TestStep, path: []const u8) void { | |
| 1622 | self.lib_paths.append(path) catch unreachable; | |
| 1623 | } | |
| 1624 | ||
| 1617 | 1625 | pub fn setVerbose(self: *TestStep, value: bool) void { |
| 1618 | 1626 | self.verbose = value; |
| 1619 | 1627 | } |
| ... | ... | @@ -1638,6 +1646,10 @@ pub const TestStep = struct { |
| 1638 | 1646 | self.filter = text; |
| 1639 | 1647 | } |
| 1640 | 1648 | |
| 1649 | pub fn addObjectFile(self: *TestStep, path: []const u8) void { | |
| 1650 | self.object_files.append(path) catch unreachable; | |
| 1651 | } | |
| 1652 | ||
| 1641 | 1653 | pub fn setTarget(self: *TestStep, target_arch: builtin.Arch, target_os: builtin.Os, target_environ: builtin.Environ) void { |
| 1642 | 1654 | self.target = Target{ |
| 1643 | 1655 | .Cross = CrossTarget{ |
| ... | ... | @@ -1699,6 +1711,11 @@ pub const TestStep = struct { |
| 1699 | 1711 | try zig_args.append(self.name_prefix); |
| 1700 | 1712 | } |
| 1701 | 1713 | |
| 1714 | for (self.object_files.toSliceConst()) |object_file| { | |
| 1715 | try zig_args.append("--object"); | |
| 1716 | try zig_args.append(builder.pathFromRoot(object_file)); | |
| 1717 | } | |
| 1718 | ||
| 1702 | 1719 | { |
| 1703 | 1720 | var it = self.link_libs.iterator(); |
| 1704 | 1721 | while (true) { |
| ... | ... | @@ -1734,6 +1751,11 @@ pub const TestStep = struct { |
| 1734 | 1751 | try zig_args.append(rpath); |
| 1735 | 1752 | } |
| 1736 | 1753 | |
| 1754 | for (self.lib_paths.toSliceConst()) |lib_path| { | |
| 1755 | try zig_args.append("--library-path"); | |
| 1756 | try zig_args.append(lib_path); | |
| 1757 | } | |
| 1758 | ||
| 1737 | 1759 | for (builder.lib_paths.toSliceConst()) |lib_path| { |
| 1738 | 1760 | try zig_args.append("--library-path"); |
| 1739 | 1761 | try zig_args.append(lib_path); |