| author | |
| committer | |
| log | bd242ce1ce9ef6ffb1af4432d892bf582dcdba8a |
| tree | 34caaa3f320e8830a5f1f1c93a4b5d70c0d192a8 |
| parent | a2c6ecd6dc0bdbe2396be9b055852324f16d34c9 |
| parent | 7177b3994626114e57bf8df36ca84fd942bac282 |
| signature |
zig build: run steps in parallel235 files changed, 11850 insertions(+), 9055 deletions(-)
CMakeLists.txt+3-2| ... | @@ -506,7 +506,9 @@ set(ZIG_STAGE2_SOURCES | ... | @@ -506,7 +506,9 @@ set(ZIG_STAGE2_SOURCES |
| 506 | "${CMAKE_SOURCE_DIR}/lib/std/Thread.zig" | 506 | "${CMAKE_SOURCE_DIR}/lib/std/Thread.zig" |
| 507 | "${CMAKE_SOURCE_DIR}/lib/std/Thread/Futex.zig" | 507 | "${CMAKE_SOURCE_DIR}/lib/std/Thread/Futex.zig" |
| 508 | "${CMAKE_SOURCE_DIR}/lib/std/Thread/Mutex.zig" | 508 | "${CMAKE_SOURCE_DIR}/lib/std/Thread/Mutex.zig" |
| 509 | "${CMAKE_SOURCE_DIR}/lib/std/Thread/Pool.zig" | ||
| 509 | "${CMAKE_SOURCE_DIR}/lib/std/Thread/ResetEvent.zig" | 510 | "${CMAKE_SOURCE_DIR}/lib/std/Thread/ResetEvent.zig" |
| 511 | "${CMAKE_SOURCE_DIR}/lib/std/Thread/WaitGroup.zig" | ||
| 510 | "${CMAKE_SOURCE_DIR}/lib/std/time.zig" | 512 | "${CMAKE_SOURCE_DIR}/lib/std/time.zig" |
| 511 | "${CMAKE_SOURCE_DIR}/lib/std/treap.zig" | 513 | "${CMAKE_SOURCE_DIR}/lib/std/treap.zig" |
| 512 | "${CMAKE_SOURCE_DIR}/lib/std/unicode.zig" | 514 | "${CMAKE_SOURCE_DIR}/lib/std/unicode.zig" |
| ... | @@ -516,6 +518,7 @@ set(ZIG_STAGE2_SOURCES | ... | @@ -516,6 +518,7 @@ set(ZIG_STAGE2_SOURCES |
| 516 | "${CMAKE_SOURCE_DIR}/lib/std/zig/c_builtins.zig" | 518 | "${CMAKE_SOURCE_DIR}/lib/std/zig/c_builtins.zig" |
| 517 | "${CMAKE_SOURCE_DIR}/lib/std/zig/Parse.zig" | 519 | "${CMAKE_SOURCE_DIR}/lib/std/zig/Parse.zig" |
| 518 | "${CMAKE_SOURCE_DIR}/lib/std/zig/render.zig" | 520 | "${CMAKE_SOURCE_DIR}/lib/std/zig/render.zig" |
| 521 | "${CMAKE_SOURCE_DIR}/lib/std/zig/Server.zig" | ||
| 519 | "${CMAKE_SOURCE_DIR}/lib/std/zig/string_literal.zig" | 522 | "${CMAKE_SOURCE_DIR}/lib/std/zig/string_literal.zig" |
| 520 | "${CMAKE_SOURCE_DIR}/lib/std/zig/system.zig" | 523 | "${CMAKE_SOURCE_DIR}/lib/std/zig/system.zig" |
| 521 | "${CMAKE_SOURCE_DIR}/lib/std/zig/system/NativePaths.zig" | 524 | "${CMAKE_SOURCE_DIR}/lib/std/zig/system/NativePaths.zig" |
| ... | @@ -530,9 +533,7 @@ set(ZIG_STAGE2_SOURCES | ... | @@ -530,9 +533,7 @@ set(ZIG_STAGE2_SOURCES |
| 530 | "${CMAKE_SOURCE_DIR}/src/Package.zig" | 533 | "${CMAKE_SOURCE_DIR}/src/Package.zig" |
| 531 | "${CMAKE_SOURCE_DIR}/src/RangeSet.zig" | 534 | "${CMAKE_SOURCE_DIR}/src/RangeSet.zig" |
| 532 | "${CMAKE_SOURCE_DIR}/src/Sema.zig" | 535 | "${CMAKE_SOURCE_DIR}/src/Sema.zig" |
| 533 | "${CMAKE_SOURCE_DIR}/src/ThreadPool.zig" | ||
| 534 | "${CMAKE_SOURCE_DIR}/src/TypedValue.zig" | 536 | "${CMAKE_SOURCE_DIR}/src/TypedValue.zig" |
| 535 | "${CMAKE_SOURCE_DIR}/src/WaitGroup.zig" | ||
| 536 | "${CMAKE_SOURCE_DIR}/src/Zir.zig" | 537 | "${CMAKE_SOURCE_DIR}/src/Zir.zig" |
| 537 | "${CMAKE_SOURCE_DIR}/src/arch/aarch64/CodeGen.zig" | 538 | "${CMAKE_SOURCE_DIR}/src/arch/aarch64/CodeGen.zig" |
| 538 | "${CMAKE_SOURCE_DIR}/src/arch/aarch64/Emit.zig" | 539 | "${CMAKE_SOURCE_DIR}/src/arch/aarch64/Emit.zig" |
build.zig+109-108| ... | @@ -31,6 +31,11 @@ pub fn build(b: *std.Build) !void { | ... | @@ -31,6 +31,11 @@ pub fn build(b: *std.Build) !void { |
| 31 | const use_zig_libcxx = b.option(bool, "use-zig-libcxx", "If libc++ is needed, use zig's bundled version, don't try to integrate with the system") orelse false; | 31 | const use_zig_libcxx = b.option(bool, "use-zig-libcxx", "If libc++ is needed, use zig's bundled version, don't try to integrate with the system") orelse false; |
| 32 | 32 | ||
| 33 | const test_step = b.step("test", "Run all the tests"); | 33 | const test_step = b.step("test", "Run all the tests"); |
| 34 | const deprecated_skip_install_lib_files = b.option(bool, "skip-install-lib-files", "deprecated. see no-lib") orelse false; | ||
| 35 | if (deprecated_skip_install_lib_files) { | ||
| 36 | std.log.warn("-Dskip-install-lib-files is deprecated in favor of -Dno-lib", .{}); | ||
| 37 | } | ||
| 38 | const skip_install_lib_files = b.option(bool, "no-lib", "skip copying of lib/ files and langref to installation prefix. Useful for development") orelse deprecated_skip_install_lib_files; | ||
| 34 | 39 | ||
| 35 | const docgen_exe = b.addExecutable(.{ | 40 | const docgen_exe = b.addExecutable(.{ |
| 36 | .name = "docgen", | 41 | .name = "docgen", |
| ... | @@ -40,28 +45,32 @@ pub fn build(b: *std.Build) !void { | ... | @@ -40,28 +45,32 @@ pub fn build(b: *std.Build) !void { |
| 40 | }); | 45 | }); |
| 41 | docgen_exe.single_threaded = single_threaded; | 46 | docgen_exe.single_threaded = single_threaded; |
| 42 | 47 | ||
| 43 | const langref_out_path = try b.cache_root.join(b.allocator, &.{"langref.html"}); | 48 | const docgen_cmd = b.addRunArtifact(docgen_exe); |
| 44 | const docgen_cmd = docgen_exe.run(); | 49 | docgen_cmd.addArgs(&.{ "--zig", b.zig_exe }); |
| 45 | docgen_cmd.addArgs(&[_][]const u8{ | 50 | docgen_cmd.addFileSourceArg(.{ .path = "doc/langref.html.in" }); |
| 46 | "--zig", | 51 | const langref_file = docgen_cmd.addOutputFileArg("langref.html"); |
| 47 | b.zig_exe, | 52 | const install_langref = b.addInstallFileWithDir(langref_file, .prefix, "doc/langref.html"); |
| 48 | "doc" ++ fs.path.sep_str ++ "langref.html.in", | 53 | if (!skip_install_lib_files) { |
| 49 | langref_out_path, | 54 | b.getInstallStep().dependOn(&install_langref.step); |
| 50 | }); | 55 | } |
| 51 | docgen_cmd.step.dependOn(&docgen_exe.step); | ||
| 52 | 56 | ||
| 53 | const docs_step = b.step("docs", "Build documentation"); | 57 | const docs_step = b.step("docs", "Build documentation"); |
| 54 | docs_step.dependOn(&docgen_cmd.step); | 58 | docs_step.dependOn(&docgen_cmd.step); |
| 55 | 59 | ||
| 56 | const test_cases = b.addTest(.{ | 60 | // This is for legacy reasons, to be removed after our CI scripts are upgraded to use |
| 57 | .root_source_file = .{ .path = "src/test.zig" }, | 61 | // the file from the install prefix instead. |
| 62 | const legacy_write_to_cache = b.addWriteFiles(); | ||
| 63 | legacy_write_to_cache.addCopyFileToSource(langref_file, "zig-cache/langref.html"); | ||
| 64 | docs_step.dependOn(&legacy_write_to_cache.step); | ||
| 65 | |||
| 66 | const check_case_exe = b.addExecutable(.{ | ||
| 67 | .name = "check-case", | ||
| 68 | .root_source_file = .{ .path = "test/src/Cases.zig" }, | ||
| 58 | .optimize = optimize, | 69 | .optimize = optimize, |
| 59 | }); | 70 | }); |
| 60 | test_cases.main_pkg_path = "."; | 71 | check_case_exe.main_pkg_path = "."; |
| 61 | test_cases.stack_size = stack_size; | 72 | check_case_exe.stack_size = stack_size; |
| 62 | test_cases.single_threaded = single_threaded; | 73 | check_case_exe.single_threaded = single_threaded; |
| 63 | |||
| 64 | const fmt_build_zig = b.addFmt(&[_][]const u8{"build.zig"}); | ||
| 65 | 74 | ||
| 66 | const skip_debug = b.option(bool, "skip-debug", "Main test suite skips debug builds") orelse false; | 75 | const skip_debug = b.option(bool, "skip-debug", "Main test suite skips debug builds") orelse false; |
| 67 | const skip_release = b.option(bool, "skip-release", "Main test suite skips release builds") orelse false; | 76 | const skip_release = b.option(bool, "skip-release", "Main test suite skips release builds") orelse false; |
| ... | @@ -74,11 +83,6 @@ pub fn build(b: *std.Build) !void { | ... | @@ -74,11 +83,6 @@ pub fn build(b: *std.Build) !void { |
| 74 | const skip_stage1 = b.option(bool, "skip-stage1", "Main test suite skips stage1 compile error tests") orelse false; | 83 | const skip_stage1 = b.option(bool, "skip-stage1", "Main test suite skips stage1 compile error tests") orelse false; |
| 75 | const skip_run_translated_c = b.option(bool, "skip-run-translated-c", "Main test suite skips run-translated-c tests") orelse false; | 84 | const skip_run_translated_c = b.option(bool, "skip-run-translated-c", "Main test suite skips run-translated-c tests") orelse false; |
| 76 | const skip_stage2_tests = b.option(bool, "skip-stage2-tests", "Main test suite skips self-hosted compiler tests") orelse false; | 85 | const skip_stage2_tests = b.option(bool, "skip-stage2-tests", "Main test suite skips self-hosted compiler tests") orelse false; |
| 77 | const deprecated_skip_install_lib_files = b.option(bool, "skip-install-lib-files", "deprecated. see no-lib") orelse false; | ||
| 78 | if (deprecated_skip_install_lib_files) { | ||
| 79 | std.log.warn("-Dskip-install-lib-files is deprecated in favor of -Dno-lib", .{}); | ||
| 80 | } | ||
| 81 | const skip_install_lib_files = b.option(bool, "no-lib", "skip copying of lib/ files to installation prefix. Useful for development") orelse deprecated_skip_install_lib_files; | ||
| 82 | 86 | ||
| 83 | const only_install_lib_files = b.option(bool, "lib-files-only", "Only install library files") orelse false; | 87 | const only_install_lib_files = b.option(bool, "lib-files-only", "Only install library files") orelse false; |
| 84 | 88 | ||
| ... | @@ -175,13 +179,12 @@ pub fn build(b: *std.Build) !void { | ... | @@ -175,13 +179,12 @@ pub fn build(b: *std.Build) !void { |
| 175 | test_step.dependOn(&exe.step); | 179 | test_step.dependOn(&exe.step); |
| 176 | } | 180 | } |
| 177 | 181 | ||
| 178 | b.default_step.dependOn(&exe.step); | ||
| 179 | exe.single_threaded = single_threaded; | 182 | exe.single_threaded = single_threaded; |
| 180 | 183 | ||
| 181 | if (target.isWindows() and target.getAbi() == .gnu) { | 184 | if (target.isWindows() and target.getAbi() == .gnu) { |
| 182 | // LTO is currently broken on mingw, this can be removed when it's fixed. | 185 | // LTO is currently broken on mingw, this can be removed when it's fixed. |
| 183 | exe.want_lto = false; | 186 | exe.want_lto = false; |
| 184 | test_cases.want_lto = false; | 187 | check_case_exe.want_lto = false; |
| 185 | } | 188 | } |
| 186 | 189 | ||
| 187 | const exe_options = b.addOptions(); | 190 | const exe_options = b.addOptions(); |
| ... | @@ -195,11 +198,11 @@ pub fn build(b: *std.Build) !void { | ... | @@ -195,11 +198,11 @@ pub fn build(b: *std.Build) !void { |
| 195 | exe_options.addOption(bool, "llvm_has_arc", llvm_has_arc); | 198 | exe_options.addOption(bool, "llvm_has_arc", llvm_has_arc); |
| 196 | exe_options.addOption(bool, "force_gpa", force_gpa); | 199 | exe_options.addOption(bool, "force_gpa", force_gpa); |
| 197 | exe_options.addOption(bool, "only_c", only_c); | 200 | exe_options.addOption(bool, "only_c", only_c); |
| 198 | exe_options.addOption(bool, "omit_pkg_fetching_code", false); | 201 | exe_options.addOption(bool, "omit_pkg_fetching_code", only_c); |
| 199 | 202 | ||
| 200 | if (link_libc) { | 203 | if (link_libc) { |
| 201 | exe.linkLibC(); | 204 | exe.linkLibC(); |
| 202 | test_cases.linkLibC(); | 205 | check_case_exe.linkLibC(); |
| 203 | } | 206 | } |
| 204 | 207 | ||
| 205 | const is_debug = optimize == .Debug; | 208 | const is_debug = optimize == .Debug; |
| ... | @@ -285,14 +288,14 @@ pub fn build(b: *std.Build) !void { | ... | @@ -285,14 +288,14 @@ pub fn build(b: *std.Build) !void { |
| 285 | } | 288 | } |
| 286 | 289 | ||
| 287 | try addCmakeCfgOptionsToExe(b, cfg, exe, use_zig_libcxx); | 290 | try addCmakeCfgOptionsToExe(b, cfg, exe, use_zig_libcxx); |
| 288 | try addCmakeCfgOptionsToExe(b, cfg, test_cases, use_zig_libcxx); | 291 | try addCmakeCfgOptionsToExe(b, cfg, check_case_exe, use_zig_libcxx); |
| 289 | } else { | 292 | } else { |
| 290 | // Here we are -Denable-llvm but no cmake integration. | 293 | // Here we are -Denable-llvm but no cmake integration. |
| 291 | try addStaticLlvmOptionsToExe(exe); | 294 | try addStaticLlvmOptionsToExe(exe); |
| 292 | try addStaticLlvmOptionsToExe(test_cases); | 295 | try addStaticLlvmOptionsToExe(check_case_exe); |
| 293 | } | 296 | } |
| 294 | if (target.isWindows()) { | 297 | if (target.isWindows()) { |
| 295 | inline for (.{ exe, test_cases }) |artifact| { | 298 | inline for (.{ exe, check_case_exe }) |artifact| { |
| 296 | artifact.linkSystemLibrary("version"); | 299 | artifact.linkSystemLibrary("version"); |
| 297 | artifact.linkSystemLibrary("uuid"); | 300 | artifact.linkSystemLibrary("uuid"); |
| 298 | artifact.linkSystemLibrary("ole32"); | 301 | artifact.linkSystemLibrary("ole32"); |
| ... | @@ -337,8 +340,9 @@ pub fn build(b: *std.Build) !void { | ... | @@ -337,8 +340,9 @@ pub fn build(b: *std.Build) !void { |
| 337 | const test_filter = b.option([]const u8, "test-filter", "Skip tests that do not match filter"); | 340 | const test_filter = b.option([]const u8, "test-filter", "Skip tests that do not match filter"); |
| 338 | 341 | ||
| 339 | const test_cases_options = b.addOptions(); | 342 | const test_cases_options = b.addOptions(); |
| 340 | test_cases.addOptions("build_options", test_cases_options); | 343 | check_case_exe.addOptions("build_options", test_cases_options); |
| 341 | 344 | ||
| 345 | test_cases_options.addOption(bool, "enable_tracy", false); | ||
| 342 | test_cases_options.addOption(bool, "enable_logging", enable_logging); | 346 | test_cases_options.addOption(bool, "enable_logging", enable_logging); |
| 343 | test_cases_options.addOption(bool, "enable_link_snapshots", enable_link_snapshots); | 347 | test_cases_options.addOption(bool, "enable_link_snapshots", enable_link_snapshots); |
| 344 | test_cases_options.addOption(bool, "skip_non_native", skip_non_native); | 348 | test_cases_options.addOption(bool, "skip_non_native", skip_non_native); |
| ... | @@ -361,12 +365,6 @@ pub fn build(b: *std.Build) !void { | ... | @@ -361,12 +365,6 @@ pub fn build(b: *std.Build) !void { |
| 361 | test_cases_options.addOption(std.SemanticVersion, "semver", semver); | 365 | test_cases_options.addOption(std.SemanticVersion, "semver", semver); |
| 362 | test_cases_options.addOption(?[]const u8, "test_filter", test_filter); | 366 | test_cases_options.addOption(?[]const u8, "test_filter", test_filter); |
| 363 | 367 | ||
| 364 | const test_cases_step = b.step("test-cases", "Run the main compiler test cases"); | ||
| 365 | test_cases_step.dependOn(&test_cases.step); | ||
| 366 | if (!skip_stage2_tests) { | ||
| 367 | test_step.dependOn(test_cases_step); | ||
| 368 | } | ||
| 369 | |||
| 370 | var chosen_opt_modes_buf: [4]builtin.Mode = undefined; | 368 | var chosen_opt_modes_buf: [4]builtin.Mode = undefined; |
| 371 | var chosen_mode_index: usize = 0; | 369 | var chosen_mode_index: usize = 0; |
| 372 | if (!skip_debug) { | 370 | if (!skip_debug) { |
| ... | @@ -387,96 +385,101 @@ pub fn build(b: *std.Build) !void { | ... | @@ -387,96 +385,101 @@ pub fn build(b: *std.Build) !void { |
| 387 | } | 385 | } |
| 388 | const optimization_modes = chosen_opt_modes_buf[0..chosen_mode_index]; | 386 | const optimization_modes = chosen_opt_modes_buf[0..chosen_mode_index]; |
| 389 | 387 | ||
| 390 | // run stage1 `zig fmt` on this build.zig file just to make sure it works | 388 | const fmt_include_paths = &.{ "doc", "lib", "src", "test", "tools", "build.zig" }; |
| 391 | test_step.dependOn(&fmt_build_zig.step); | 389 | const fmt_exclude_paths = &.{"test/cases"}; |
| 392 | const fmt_step = b.step("test-fmt", "Run zig fmt against build.zig to make sure it works"); | 390 | const do_fmt = b.addFmt(.{ |
| 393 | fmt_step.dependOn(&fmt_build_zig.step); | 391 | .paths = fmt_include_paths, |
| 394 | 392 | .exclude_paths = fmt_exclude_paths, | |
| 395 | test_step.dependOn(tests.addPkgTests( | 393 | }); |
| 396 | b, | ||
| 397 | test_filter, | ||
| 398 | "test/behavior.zig", | ||
| 399 | "behavior", | ||
| 400 | "Run the behavior tests", | ||
| 401 | optimization_modes, | ||
| 402 | skip_single_threaded, | ||
| 403 | skip_non_native, | ||
| 404 | skip_libc, | ||
| 405 | skip_stage1, | ||
| 406 | skip_stage2_tests, | ||
| 407 | )); | ||
| 408 | 394 | ||
| 409 | test_step.dependOn(tests.addPkgTests( | 395 | b.step("test-fmt", "Check source files having conforming formatting").dependOn(&b.addFmt(.{ |
| 410 | b, | 396 | .paths = fmt_include_paths, |
| 411 | test_filter, | 397 | .exclude_paths = fmt_exclude_paths, |
| 412 | "lib/compiler_rt.zig", | 398 | .check = true, |
| 413 | "compiler-rt", | 399 | }).step); |
| 414 | "Run the compiler_rt tests", | ||
| 415 | optimization_modes, | ||
| 416 | true, // skip_single_threaded | ||
| 417 | skip_non_native, | ||
| 418 | true, // skip_libc | ||
| 419 | skip_stage1, | ||
| 420 | skip_stage2_tests or true, // TODO get these all passing | ||
| 421 | )); | ||
| 422 | 400 | ||
| 423 | test_step.dependOn(tests.addPkgTests( | 401 | const test_cases_step = b.step("test-cases", "Run the main compiler test cases"); |
| 424 | b, | 402 | try tests.addCases(b, test_cases_step, test_filter, check_case_exe); |
| 425 | test_filter, | 403 | if (!skip_stage2_tests) test_step.dependOn(test_cases_step); |
| 426 | "lib/c.zig", | 404 | |
| 427 | "universal-libc", | 405 | test_step.dependOn(tests.addModuleTests(b, .{ |
| 428 | "Run the universal libc tests", | 406 | .test_filter = test_filter, |
| 429 | optimization_modes, | 407 | .root_src = "test/behavior.zig", |
| 430 | true, // skip_single_threaded | 408 | .name = "behavior", |
| 431 | skip_non_native, | 409 | .desc = "Run the behavior tests", |
| 432 | true, // skip_libc | 410 | .optimize_modes = optimization_modes, |
| 433 | skip_stage1, | 411 | .skip_single_threaded = skip_single_threaded, |
| 434 | skip_stage2_tests or true, // TODO get these all passing | 412 | .skip_non_native = skip_non_native, |
| 435 | )); | 413 | .skip_libc = skip_libc, |
| 414 | .skip_stage1 = skip_stage1, | ||
| 415 | .skip_stage2 = skip_stage2_tests, | ||
| 416 | .max_rss = 1 * 1024 * 1024 * 1024, | ||
| 417 | })); | ||
| 418 | |||
| 419 | test_step.dependOn(tests.addModuleTests(b, .{ | ||
| 420 | .test_filter = test_filter, | ||
| 421 | .root_src = "lib/compiler_rt.zig", | ||
| 422 | .name = "compiler-rt", | ||
| 423 | .desc = "Run the compiler_rt tests", | ||
| 424 | .optimize_modes = optimization_modes, | ||
| 425 | .skip_single_threaded = true, | ||
| 426 | .skip_non_native = skip_non_native, | ||
| 427 | .skip_libc = true, | ||
| 428 | .skip_stage1 = skip_stage1, | ||
| 429 | .skip_stage2 = true, // TODO get all these passing | ||
| 430 | })); | ||
| 431 | |||
| 432 | test_step.dependOn(tests.addModuleTests(b, .{ | ||
| 433 | .test_filter = test_filter, | ||
| 434 | .root_src = "lib/c.zig", | ||
| 435 | .name = "universal-libc", | ||
| 436 | .desc = "Run the universal libc tests", | ||
| 437 | .optimize_modes = optimization_modes, | ||
| 438 | .skip_single_threaded = true, | ||
| 439 | .skip_non_native = skip_non_native, | ||
| 440 | .skip_libc = true, | ||
| 441 | .skip_stage1 = skip_stage1, | ||
| 442 | .skip_stage2 = true, // TODO get all these passing | ||
| 443 | })); | ||
| 436 | 444 | ||
| 437 | test_step.dependOn(tests.addCompareOutputTests(b, test_filter, optimization_modes)); | 445 | test_step.dependOn(tests.addCompareOutputTests(b, test_filter, optimization_modes)); |
| 438 | test_step.dependOn(tests.addStandaloneTests( | 446 | test_step.dependOn(tests.addStandaloneTests( |
| 439 | b, | 447 | b, |
| 440 | test_filter, | ||
| 441 | optimization_modes, | 448 | optimization_modes, |
| 442 | skip_non_native, | ||
| 443 | enable_macos_sdk, | 449 | enable_macos_sdk, |
| 444 | target, | ||
| 445 | skip_stage2_tests, | 450 | skip_stage2_tests, |
| 446 | b.enable_darling, | ||
| 447 | b.enable_qemu, | ||
| 448 | b.enable_rosetta, | ||
| 449 | b.enable_wasmtime, | ||
| 450 | b.enable_wine, | ||
| 451 | enable_symlinks_windows, | 451 | enable_symlinks_windows, |
| 452 | )); | 452 | )); |
| 453 | test_step.dependOn(tests.addCAbiTests(b, skip_non_native, skip_release)); | 453 | test_step.dependOn(tests.addCAbiTests(b, skip_non_native, skip_release)); |
| 454 | test_step.dependOn(tests.addLinkTests(b, test_filter, optimization_modes, enable_macos_sdk, skip_stage2_tests, enable_symlinks_windows)); | 454 | test_step.dependOn(tests.addLinkTests(b, enable_macos_sdk, skip_stage2_tests, enable_symlinks_windows)); |
| 455 | test_step.dependOn(tests.addStackTraceTests(b, test_filter, optimization_modes)); | 455 | test_step.dependOn(tests.addStackTraceTests(b, test_filter, optimization_modes)); |
| 456 | test_step.dependOn(tests.addCliTests(b, test_filter, optimization_modes)); | 456 | test_step.dependOn(tests.addCliTests(b)); |
| 457 | test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, optimization_modes)); | 457 | test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, optimization_modes)); |
| 458 | test_step.dependOn(tests.addTranslateCTests(b, test_filter)); | 458 | test_step.dependOn(tests.addTranslateCTests(b, test_filter)); |
| 459 | if (!skip_run_translated_c) { | 459 | if (!skip_run_translated_c) { |
| 460 | test_step.dependOn(tests.addRunTranslatedCTests(b, test_filter, target)); | 460 | test_step.dependOn(tests.addRunTranslatedCTests(b, test_filter, target)); |
| 461 | } | 461 | } |
| 462 | // tests for this feature are disabled until we have the self-hosted compiler available | ||
| 463 | // test_step.dependOn(tests.addGenHTests(b, test_filter)); | ||
| 464 | 462 | ||
| 465 | test_step.dependOn(tests.addPkgTests( | 463 | test_step.dependOn(tests.addModuleTests(b, .{ |
| 466 | b, | 464 | .test_filter = test_filter, |
| 467 | test_filter, | 465 | .root_src = "lib/std/std.zig", |
| 468 | "lib/std/std.zig", | 466 | .name = "std", |
| 469 | "std", | 467 | .desc = "Run the standard library tests", |
| 470 | "Run the standard library tests", | 468 | .optimize_modes = optimization_modes, |
| 471 | optimization_modes, | 469 | .skip_single_threaded = skip_single_threaded, |
| 472 | skip_single_threaded, | 470 | .skip_non_native = skip_non_native, |
| 473 | skip_non_native, | 471 | .skip_libc = skip_libc, |
| 474 | skip_libc, | 472 | .skip_stage1 = skip_stage1, |
| 475 | skip_stage1, | 473 | .skip_stage2 = true, // TODO get all these passing |
| 476 | true, // TODO get these all passing | 474 | // I observed a value of 3398275072 on my M1, and multiplied by 1.1 to |
| 477 | )); | 475 | // get this amount: |
| 476 | .max_rss = 3738102579, | ||
| 477 | })); | ||
| 478 | 478 | ||
| 479 | try addWasiUpdateStep(b, version); | 479 | try addWasiUpdateStep(b, version); |
| 480 | |||
| 481 | b.step("fmt", "Modify source files in place to have conforming formatting") | ||
| 482 | .dependOn(&do_fmt.step); | ||
| 480 | } | 483 | } |
| 481 | 484 | ||
| 482 | fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void { | 485 | fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void { |
| ... | @@ -505,6 +508,7 @@ fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void { | ... | @@ -505,6 +508,7 @@ fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void { |
| 505 | exe_options.addOption(bool, "enable_tracy_callstack", false); | 508 | exe_options.addOption(bool, "enable_tracy_callstack", false); |
| 506 | exe_options.addOption(bool, "enable_tracy_allocation", false); | 509 | exe_options.addOption(bool, "enable_tracy_allocation", false); |
| 507 | exe_options.addOption(bool, "value_tracing", false); | 510 | exe_options.addOption(bool, "value_tracing", false); |
| 511 | exe_options.addOption(bool, "omit_pkg_fetching_code", true); | ||
| 508 | 512 | ||
| 509 | const run_opt = b.addSystemCommand(&.{ "wasm-opt", "-Oz", "--enable-bulk-memory" }); | 513 | const run_opt = b.addSystemCommand(&.{ "wasm-opt", "-Oz", "--enable-bulk-memory" }); |
| 510 | run_opt.addArtifactArg(exe); | 514 | run_opt.addArtifactArg(exe); |
| ... | @@ -676,10 +680,7 @@ fn addCxxKnownPath( | ... | @@ -676,10 +680,7 @@ fn addCxxKnownPath( |
| 676 | ) !void { | 680 | ) !void { |
| 677 | if (!std.process.can_spawn) | 681 | if (!std.process.can_spawn) |
| 678 | return error.RequiredLibraryNotFound; | 682 | return error.RequiredLibraryNotFound; |
| 679 | const path_padded = try b.exec(&[_][]const u8{ | 683 | const path_padded = b.exec(&.{ ctx.cxx_compiler, b.fmt("-print-file-name={s}", .{objname}) }); |
| 680 | ctx.cxx_compiler, | ||
| 681 | b.fmt("-print-file-name={s}", .{objname}), | ||
| 682 | }); | ||
| 683 | var tokenizer = mem.tokenize(u8, path_padded, "\r\n"); | 684 | var tokenizer = mem.tokenize(u8, path_padded, "\r\n"); |
| 684 | const path_unpadded = tokenizer.next().?; | 685 | const path_unpadded = tokenizer.next().?; |
| 685 | if (mem.eql(u8, path_unpadded, objname)) { | 686 | if (mem.eql(u8, path_unpadded, objname)) { |
ci/aarch64-linux-debug.sh+1-1| ... | @@ -67,7 +67,7 @@ stage3-debug/bin/zig build test docs \ | ... | @@ -67,7 +67,7 @@ stage3-debug/bin/zig build test docs \ |
| 67 | --zig-lib-dir "$(pwd)/../lib" | 67 | --zig-lib-dir "$(pwd)/../lib" |
| 68 | 68 | ||
| 69 | # Look for HTML errors. | 69 | # Look for HTML errors. |
| 70 | tidy --drop-empty-elements no -qe "$ZIG_LOCAL_CACHE_DIR/langref.html" | 70 | tidy --drop-empty-elements no -qe "stage3-debug/doc/langref.html" |
| 71 | 71 | ||
| 72 | # Produce the experimental std lib documentation. | 72 | # Produce the experimental std lib documentation. |
| 73 | stage3-debug/bin/zig test ../lib/std/std.zig -femit-docs -fno-emit-bin --zig-lib-dir ../lib | 73 | stage3-debug/bin/zig test ../lib/std/std.zig -femit-docs -fno-emit-bin --zig-lib-dir ../lib |
ci/aarch64-linux-release.sh+1-1| ... | @@ -67,7 +67,7 @@ stage3-release/bin/zig build test docs \ | ... | @@ -67,7 +67,7 @@ stage3-release/bin/zig build test docs \ |
| 67 | --zig-lib-dir "$(pwd)/../lib" | 67 | --zig-lib-dir "$(pwd)/../lib" |
| 68 | 68 | ||
| 69 | # Look for HTML errors. | 69 | # Look for HTML errors. |
| 70 | tidy --drop-empty-elements no -qe "$ZIG_LOCAL_CACHE_DIR/langref.html" | 70 | tidy --drop-empty-elements no -qe "stage3-release/doc/langref.html" |
| 71 | 71 | ||
| 72 | # Produce the experimental std lib documentation. | 72 | # Produce the experimental std lib documentation. |
| 73 | stage3-release/bin/zig test ../lib/std/std.zig -femit-docs -fno-emit-bin --zig-lib-dir ../lib | 73 | stage3-release/bin/zig test ../lib/std/std.zig -femit-docs -fno-emit-bin --zig-lib-dir ../lib |
ci/x86_64-linux-debug.sh+1-1| ... | @@ -66,7 +66,7 @@ stage3-debug/bin/zig build test docs \ | ... | @@ -66,7 +66,7 @@ stage3-debug/bin/zig build test docs \ |
| 66 | --zig-lib-dir "$(pwd)/../lib" | 66 | --zig-lib-dir "$(pwd)/../lib" |
| 67 | 67 | ||
| 68 | # Look for HTML errors. | 68 | # Look for HTML errors. |
| 69 | tidy --drop-empty-elements no -qe "$ZIG_LOCAL_CACHE_DIR/langref.html" | 69 | tidy --drop-empty-elements no -qe "stage3-debug/doc/langref.html" |
| 70 | 70 | ||
| 71 | # Produce the experimental std lib documentation. | 71 | # Produce the experimental std lib documentation. |
| 72 | stage3-debug/bin/zig test ../lib/std/std.zig -femit-docs -fno-emit-bin --zig-lib-dir ../lib | 72 | stage3-debug/bin/zig test ../lib/std/std.zig -femit-docs -fno-emit-bin --zig-lib-dir ../lib |
ci/x86_64-linux-release.sh+1-1| ... | @@ -67,7 +67,7 @@ stage3-release/bin/zig build test docs \ | ... | @@ -67,7 +67,7 @@ stage3-release/bin/zig build test docs \ |
| 67 | --zig-lib-dir "$(pwd)/../lib" | 67 | --zig-lib-dir "$(pwd)/../lib" |
| 68 | 68 | ||
| 69 | # Look for HTML errors. | 69 | # Look for HTML errors. |
| 70 | tidy --drop-empty-elements no -qe "$ZIG_LOCAL_CACHE_DIR/langref.html" | 70 | tidy --drop-empty-elements no -qe "stage3-release/doc/langref.html" |
| 71 | 71 | ||
| 72 | # Produce the experimental std lib documentation. | 72 | # Produce the experimental std lib documentation. |
| 73 | stage3-release/bin/zig test ../lib/std/std.zig -femit-docs -fno-emit-bin --zig-lib-dir ../lib | 73 | stage3-release/bin/zig test ../lib/std/std.zig -femit-docs -fno-emit-bin --zig-lib-dir ../lib |
doc/docgen.zig+1-1| ... | @@ -1270,7 +1270,7 @@ fn genHtml( | ... | @@ -1270,7 +1270,7 @@ fn genHtml( |
| 1270 | zig_exe: []const u8, | 1270 | zig_exe: []const u8, |
| 1271 | do_code_tests: bool, | 1271 | do_code_tests: bool, |
| 1272 | ) !void { | 1272 | ) !void { |
| 1273 | var progress = Progress{}; | 1273 | var progress = Progress{ .dont_print_on_dumb = true }; |
| 1274 | const root_node = progress.start("Generating docgen examples", toc.nodes.len); | 1274 | const root_node = progress.start("Generating docgen examples", toc.nodes.len); |
| 1275 | defer root_node.end(); | 1275 | defer root_node.end(); |
| 1276 | 1276 |
lib/build_runner.zig+702-53| ... | @@ -1,12 +1,14 @@ | ... | @@ -1,12 +1,14 @@ |
| 1 | const root = @import("@build"); | 1 | const root = @import("@build"); |
| 2 | const std = @import("std"); | 2 | const std = @import("std"); |
| 3 | const builtin = @import("builtin"); | 3 | const builtin = @import("builtin"); |
| 4 | const assert = std.debug.assert; | ||
| 4 | const io = std.io; | 5 | const io = std.io; |
| 5 | const fmt = std.fmt; | 6 | const fmt = std.fmt; |
| 6 | const mem = std.mem; | 7 | const mem = std.mem; |
| 7 | const process = std.process; | 8 | const process = std.process; |
| 8 | const ArrayList = std.ArrayList; | 9 | const ArrayList = std.ArrayList; |
| 9 | const File = std.fs.File; | 10 | const File = std.fs.File; |
| 11 | const Step = std.Build.Step; | ||
| 10 | 12 | ||
| 11 | pub const dependencies = @import("@dependencies"); | 13 | pub const dependencies = @import("@dependencies"); |
| 12 | 14 | ||
| ... | @@ -14,12 +16,15 @@ pub fn main() !void { | ... | @@ -14,12 +16,15 @@ pub fn main() !void { |
| 14 | // Here we use an ArenaAllocator backed by a DirectAllocator because a build is a short-lived, | 16 | // Here we use an ArenaAllocator backed by a DirectAllocator because a build is a short-lived, |
| 15 | // one shot program. We don't need to waste time freeing memory and finding places to squish | 17 | // one shot program. We don't need to waste time freeing memory and finding places to squish |
| 16 | // bytes into. So we free everything all at once at the very end. | 18 | // bytes into. So we free everything all at once at the very end. |
| 17 | var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); | 19 | var single_threaded_arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); |
| 18 | defer arena.deinit(); | 20 | defer single_threaded_arena.deinit(); |
| 19 | 21 | ||
| 20 | const allocator = arena.allocator(); | 22 | var thread_safe_arena: std.heap.ThreadSafeAllocator = .{ |
| 21 | var args = try process.argsAlloc(allocator); | 23 | .child_allocator = single_threaded_arena.allocator(), |
| 22 | defer process.argsFree(allocator, args); | 24 | }; |
| 25 | const arena = thread_safe_arena.allocator(); | ||
| 26 | |||
| 27 | var args = try process.argsAlloc(arena); | ||
| 23 | 28 | ||
| 24 | // skip my own exe name | 29 | // skip my own exe name |
| 25 | var arg_idx: usize = 1; | 30 | var arg_idx: usize = 1; |
| ... | @@ -59,18 +64,17 @@ pub fn main() !void { | ... | @@ -59,18 +64,17 @@ pub fn main() !void { |
| 59 | }; | 64 | }; |
| 60 | 65 | ||
| 61 | var cache: std.Build.Cache = .{ | 66 | var cache: std.Build.Cache = .{ |
| 62 | .gpa = allocator, | 67 | .gpa = arena, |
| 63 | .manifest_dir = try local_cache_directory.handle.makeOpenPath("h", .{}), | 68 | .manifest_dir = try local_cache_directory.handle.makeOpenPath("h", .{}), |
| 64 | }; | 69 | }; |
| 65 | cache.addPrefix(.{ .path = null, .handle = std.fs.cwd() }); | 70 | cache.addPrefix(.{ .path = null, .handle = std.fs.cwd() }); |
| 66 | cache.addPrefix(build_root_directory); | 71 | cache.addPrefix(build_root_directory); |
| 67 | cache.addPrefix(local_cache_directory); | 72 | cache.addPrefix(local_cache_directory); |
| 68 | cache.addPrefix(global_cache_directory); | 73 | cache.addPrefix(global_cache_directory); |
| 69 | 74 | cache.hash.addBytes(builtin.zig_version_string); | |
| 70 | //cache.hash.addBytes(builtin.zig_version); | ||
| 71 | 75 | ||
| 72 | const builder = try std.Build.create( | 76 | const builder = try std.Build.create( |
| 73 | allocator, | 77 | arena, |
| 74 | zig_exe, | 78 | zig_exe, |
| 75 | build_root_directory, | 79 | build_root_directory, |
| 76 | local_cache_directory, | 80 | local_cache_directory, |
| ... | @@ -80,35 +84,34 @@ pub fn main() !void { | ... | @@ -80,35 +84,34 @@ pub fn main() !void { |
| 80 | ); | 84 | ); |
| 81 | defer builder.destroy(); | 85 | defer builder.destroy(); |
| 82 | 86 | ||
| 83 | var targets = ArrayList([]const u8).init(allocator); | 87 | var targets = ArrayList([]const u8).init(arena); |
| 84 | var debug_log_scopes = ArrayList([]const u8).init(allocator); | 88 | var debug_log_scopes = ArrayList([]const u8).init(arena); |
| 85 | 89 | var thread_pool_options: std.Thread.Pool.Options = .{ .allocator = arena }; | |
| 86 | const stderr_stream = io.getStdErr().writer(); | ||
| 87 | const stdout_stream = io.getStdOut().writer(); | ||
| 88 | 90 | ||
| 89 | var install_prefix: ?[]const u8 = null; | 91 | var install_prefix: ?[]const u8 = null; |
| 90 | var dir_list = std.Build.DirList{}; | 92 | var dir_list = std.Build.DirList{}; |
| 93 | var enable_summary: ?bool = null; | ||
| 94 | var max_rss: usize = 0; | ||
| 95 | var color: Color = .auto; | ||
| 91 | 96 | ||
| 92 | // before arg parsing, check for the NO_COLOR environment variable | 97 | const stderr_stream = io.getStdErr().writer(); |
| 93 | // if it exists, default the color setting to .off | 98 | const stdout_stream = io.getStdOut().writer(); |
| 94 | // explicit --color arguments will still override this setting. | ||
| 95 | builder.color = if (std.process.hasEnvVarConstant("NO_COLOR")) .off else .auto; | ||
| 96 | 99 | ||
| 97 | while (nextArg(args, &arg_idx)) |arg| { | 100 | while (nextArg(args, &arg_idx)) |arg| { |
| 98 | if (mem.startsWith(u8, arg, "-D")) { | 101 | if (mem.startsWith(u8, arg, "-D")) { |
| 99 | const option_contents = arg[2..]; | 102 | const option_contents = arg[2..]; |
| 100 | if (option_contents.len == 0) { | 103 | if (option_contents.len == 0) { |
| 101 | std.debug.print("Expected option name after '-D'\n\n", .{}); | 104 | std.debug.print("Expected option name after '-D'\n\n", .{}); |
| 102 | return usageAndErr(builder, false, stderr_stream); | 105 | usageAndErr(builder, false, stderr_stream); |
| 103 | } | 106 | } |
| 104 | if (mem.indexOfScalar(u8, option_contents, '=')) |name_end| { | 107 | if (mem.indexOfScalar(u8, option_contents, '=')) |name_end| { |
| 105 | const option_name = option_contents[0..name_end]; | 108 | const option_name = option_contents[0..name_end]; |
| 106 | const option_value = option_contents[name_end + 1 ..]; | 109 | const option_value = option_contents[name_end + 1 ..]; |
| 107 | if (try builder.addUserInputOption(option_name, option_value)) | 110 | if (try builder.addUserInputOption(option_name, option_value)) |
| 108 | return usageAndErr(builder, false, stderr_stream); | 111 | usageAndErr(builder, false, stderr_stream); |
| 109 | } else { | 112 | } else { |
| 110 | if (try builder.addUserInputFlag(option_contents)) | 113 | if (try builder.addUserInputFlag(option_contents)) |
| 111 | return usageAndErr(builder, false, stderr_stream); | 114 | usageAndErr(builder, false, stderr_stream); |
| 112 | } | 115 | } |
| 113 | } else if (mem.startsWith(u8, arg, "-")) { | 116 | } else if (mem.startsWith(u8, arg, "-")) { |
| 114 | if (mem.eql(u8, arg, "--verbose")) { | 117 | if (mem.eql(u8, arg, "--verbose")) { |
| ... | @@ -118,69 +121,83 @@ pub fn main() !void { | ... | @@ -118,69 +121,83 @@ pub fn main() !void { |
| 118 | } else if (mem.eql(u8, arg, "-p") or mem.eql(u8, arg, "--prefix")) { | 121 | } else if (mem.eql(u8, arg, "-p") or mem.eql(u8, arg, "--prefix")) { |
| 119 | install_prefix = nextArg(args, &arg_idx) orelse { | 122 | install_prefix = nextArg(args, &arg_idx) orelse { |
| 120 | std.debug.print("Expected argument after {s}\n\n", .{arg}); | 123 | std.debug.print("Expected argument after {s}\n\n", .{arg}); |
| 121 | return usageAndErr(builder, false, stderr_stream); | 124 | usageAndErr(builder, false, stderr_stream); |
| 122 | }; | 125 | }; |
| 123 | } else if (mem.eql(u8, arg, "-l") or mem.eql(u8, arg, "--list-steps")) { | 126 | } else if (mem.eql(u8, arg, "-l") or mem.eql(u8, arg, "--list-steps")) { |
| 124 | return steps(builder, false, stdout_stream); | 127 | return steps(builder, false, stdout_stream); |
| 125 | } else if (mem.eql(u8, arg, "--prefix-lib-dir")) { | 128 | } else if (mem.eql(u8, arg, "--prefix-lib-dir")) { |
| 126 | dir_list.lib_dir = nextArg(args, &arg_idx) orelse { | 129 | dir_list.lib_dir = nextArg(args, &arg_idx) orelse { |
| 127 | std.debug.print("Expected argument after {s}\n\n", .{arg}); | 130 | std.debug.print("Expected argument after {s}\n\n", .{arg}); |
| 128 | return usageAndErr(builder, false, stderr_stream); | 131 | usageAndErr(builder, false, stderr_stream); |
| 129 | }; | 132 | }; |
| 130 | } else if (mem.eql(u8, arg, "--prefix-exe-dir")) { | 133 | } else if (mem.eql(u8, arg, "--prefix-exe-dir")) { |
| 131 | dir_list.exe_dir = nextArg(args, &arg_idx) orelse { | 134 | dir_list.exe_dir = nextArg(args, &arg_idx) orelse { |
| 132 | std.debug.print("Expected argument after {s}\n\n", .{arg}); | 135 | std.debug.print("Expected argument after {s}\n\n", .{arg}); |
| 133 | return usageAndErr(builder, false, stderr_stream); | 136 | usageAndErr(builder, false, stderr_stream); |
| 134 | }; | 137 | }; |
| 135 | } else if (mem.eql(u8, arg, "--prefix-include-dir")) { | 138 | } else if (mem.eql(u8, arg, "--prefix-include-dir")) { |
| 136 | dir_list.include_dir = nextArg(args, &arg_idx) orelse { | 139 | dir_list.include_dir = nextArg(args, &arg_idx) orelse { |
| 137 | std.debug.print("Expected argument after {s}\n\n", .{arg}); | 140 | std.debug.print("Expected argument after {s}\n\n", .{arg}); |
| 138 | return usageAndErr(builder, false, stderr_stream); | 141 | usageAndErr(builder, false, stderr_stream); |
| 139 | }; | 142 | }; |
| 140 | } else if (mem.eql(u8, arg, "--sysroot")) { | 143 | } else if (mem.eql(u8, arg, "--sysroot")) { |
| 141 | const sysroot = nextArg(args, &arg_idx) orelse { | 144 | const sysroot = nextArg(args, &arg_idx) orelse { |
| 142 | std.debug.print("Expected argument after --sysroot\n\n", .{}); | 145 | std.debug.print("Expected argument after --sysroot\n\n", .{}); |
| 143 | return usageAndErr(builder, false, stderr_stream); | 146 | usageAndErr(builder, false, stderr_stream); |
| 144 | }; | 147 | }; |
| 145 | builder.sysroot = sysroot; | 148 | builder.sysroot = sysroot; |
| 149 | } else if (mem.eql(u8, arg, "--maxrss")) { | ||
| 150 | const max_rss_text = nextArg(args, &arg_idx) orelse { | ||
| 151 | std.debug.print("Expected argument after --sysroot\n\n", .{}); | ||
| 152 | usageAndErr(builder, false, stderr_stream); | ||
| 153 | }; | ||
| 154 | // TODO: support shorthand such as "2GiB", "2GB", or "2G" | ||
| 155 | max_rss = std.fmt.parseInt(usize, max_rss_text, 10) catch |err| { | ||
| 156 | std.debug.print("invalid byte size: '{s}': {s}\n", .{ | ||
| 157 | max_rss_text, @errorName(err), | ||
| 158 | }); | ||
| 159 | process.exit(1); | ||
| 160 | }; | ||
| 146 | } else if (mem.eql(u8, arg, "--search-prefix")) { | 161 | } else if (mem.eql(u8, arg, "--search-prefix")) { |
| 147 | const search_prefix = nextArg(args, &arg_idx) orelse { | 162 | const search_prefix = nextArg(args, &arg_idx) orelse { |
| 148 | std.debug.print("Expected argument after --search-prefix\n\n", .{}); | 163 | std.debug.print("Expected argument after --search-prefix\n\n", .{}); |
| 149 | return usageAndErr(builder, false, stderr_stream); | 164 | usageAndErr(builder, false, stderr_stream); |
| 150 | }; | 165 | }; |
| 151 | builder.addSearchPrefix(search_prefix); | 166 | builder.addSearchPrefix(search_prefix); |
| 152 | } else if (mem.eql(u8, arg, "--libc")) { | 167 | } else if (mem.eql(u8, arg, "--libc")) { |
| 153 | const libc_file = nextArg(args, &arg_idx) orelse { | 168 | const libc_file = nextArg(args, &arg_idx) orelse { |
| 154 | std.debug.print("Expected argument after --libc\n\n", .{}); | 169 | std.debug.print("Expected argument after --libc\n\n", .{}); |
| 155 | return usageAndErr(builder, false, stderr_stream); | 170 | usageAndErr(builder, false, stderr_stream); |
| 156 | }; | 171 | }; |
| 157 | builder.libc_file = libc_file; | 172 | builder.libc_file = libc_file; |
| 158 | } else if (mem.eql(u8, arg, "--color")) { | 173 | } else if (mem.eql(u8, arg, "--color")) { |
| 159 | const next_arg = nextArg(args, &arg_idx) orelse { | 174 | const next_arg = nextArg(args, &arg_idx) orelse { |
| 160 | std.debug.print("expected [auto|on|off] after --color", .{}); | 175 | std.debug.print("expected [auto|on|off] after --color", .{}); |
| 161 | return usageAndErr(builder, false, stderr_stream); | 176 | usageAndErr(builder, false, stderr_stream); |
| 162 | }; | 177 | }; |
| 163 | builder.color = std.meta.stringToEnum(@TypeOf(builder.color), next_arg) orelse { | 178 | color = std.meta.stringToEnum(Color, next_arg) orelse { |
| 164 | std.debug.print("expected [auto|on|off] after --color, found '{s}'", .{next_arg}); | 179 | std.debug.print("expected [auto|on|off] after --color, found '{s}'", .{next_arg}); |
| 165 | return usageAndErr(builder, false, stderr_stream); | 180 | usageAndErr(builder, false, stderr_stream); |
| 166 | }; | 181 | }; |
| 167 | } else if (mem.eql(u8, arg, "--zig-lib-dir")) { | 182 | } else if (mem.eql(u8, arg, "--zig-lib-dir")) { |
| 168 | builder.zig_lib_dir = nextArg(args, &arg_idx) orelse { | 183 | builder.zig_lib_dir = nextArg(args, &arg_idx) orelse { |
| 169 | std.debug.print("Expected argument after --zig-lib-dir\n\n", .{}); | 184 | std.debug.print("Expected argument after --zig-lib-dir\n\n", .{}); |
| 170 | return usageAndErr(builder, false, stderr_stream); | 185 | usageAndErr(builder, false, stderr_stream); |
| 171 | }; | 186 | }; |
| 172 | } else if (mem.eql(u8, arg, "--debug-log")) { | 187 | } else if (mem.eql(u8, arg, "--debug-log")) { |
| 173 | const next_arg = nextArg(args, &arg_idx) orelse { | 188 | const next_arg = nextArg(args, &arg_idx) orelse { |
| 174 | std.debug.print("Expected argument after {s}\n\n", .{arg}); | 189 | std.debug.print("Expected argument after {s}\n\n", .{arg}); |
| 175 | return usageAndErr(builder, false, stderr_stream); | 190 | usageAndErr(builder, false, stderr_stream); |
| 176 | }; | 191 | }; |
| 177 | try debug_log_scopes.append(next_arg); | 192 | try debug_log_scopes.append(next_arg); |
| 193 | } else if (mem.eql(u8, arg, "--debug-pkg-config")) { | ||
| 194 | builder.debug_pkg_config = true; | ||
| 178 | } else if (mem.eql(u8, arg, "--debug-compile-errors")) { | 195 | } else if (mem.eql(u8, arg, "--debug-compile-errors")) { |
| 179 | builder.debug_compile_errors = true; | 196 | builder.debug_compile_errors = true; |
| 180 | } else if (mem.eql(u8, arg, "--glibc-runtimes")) { | 197 | } else if (mem.eql(u8, arg, "--glibc-runtimes")) { |
| 181 | builder.glibc_runtimes_dir = nextArg(args, &arg_idx) orelse { | 198 | builder.glibc_runtimes_dir = nextArg(args, &arg_idx) orelse { |
| 182 | std.debug.print("Expected argument after --glibc-runtimes\n\n", .{}); | 199 | std.debug.print("Expected argument after --glibc-runtimes\n\n", .{}); |
| 183 | return usageAndErr(builder, false, stderr_stream); | 200 | usageAndErr(builder, false, stderr_stream); |
| 184 | }; | 201 | }; |
| 185 | } else if (mem.eql(u8, arg, "--verbose-link")) { | 202 | } else if (mem.eql(u8, arg, "--verbose-link")) { |
| 186 | builder.verbose_link = true; | 203 | builder.verbose_link = true; |
| ... | @@ -194,8 +211,6 @@ pub fn main() !void { | ... | @@ -194,8 +211,6 @@ pub fn main() !void { |
| 194 | builder.verbose_cc = true; | 211 | builder.verbose_cc = true; |
| 195 | } else if (mem.eql(u8, arg, "--verbose-llvm-cpu-features")) { | 212 | } else if (mem.eql(u8, arg, "--verbose-llvm-cpu-features")) { |
| 196 | builder.verbose_llvm_cpu_features = true; | 213 | builder.verbose_llvm_cpu_features = true; |
| 197 | } else if (mem.eql(u8, arg, "--prominent-compile-errors")) { | ||
| 198 | builder.prominent_compile_errors = true; | ||
| 199 | } else if (mem.eql(u8, arg, "-fwine")) { | 214 | } else if (mem.eql(u8, arg, "-fwine")) { |
| 200 | builder.enable_wine = true; | 215 | builder.enable_wine = true; |
| 201 | } else if (mem.eql(u8, arg, "-fno-wine")) { | 216 | } else if (mem.eql(u8, arg, "-fno-wine")) { |
| ... | @@ -216,6 +231,10 @@ pub fn main() !void { | ... | @@ -216,6 +231,10 @@ pub fn main() !void { |
| 216 | builder.enable_darling = true; | 231 | builder.enable_darling = true; |
| 217 | } else if (mem.eql(u8, arg, "-fno-darling")) { | 232 | } else if (mem.eql(u8, arg, "-fno-darling")) { |
| 218 | builder.enable_darling = false; | 233 | builder.enable_darling = false; |
| 234 | } else if (mem.eql(u8, arg, "-fsummary")) { | ||
| 235 | enable_summary = true; | ||
| 236 | } else if (mem.eql(u8, arg, "-fno-summary")) { | ||
| 237 | enable_summary = false; | ||
| 219 | } else if (mem.eql(u8, arg, "-freference-trace")) { | 238 | } else if (mem.eql(u8, arg, "-freference-trace")) { |
| 220 | builder.reference_trace = 256; | 239 | builder.reference_trace = 256; |
| 221 | } else if (mem.startsWith(u8, arg, "-freference-trace=")) { | 240 | } else if (mem.startsWith(u8, arg, "-freference-trace=")) { |
| ... | @@ -226,39 +245,639 @@ pub fn main() !void { | ... | @@ -226,39 +245,639 @@ pub fn main() !void { |
| 226 | }; | 245 | }; |
| 227 | } else if (mem.eql(u8, arg, "-fno-reference-trace")) { | 246 | } else if (mem.eql(u8, arg, "-fno-reference-trace")) { |
| 228 | builder.reference_trace = null; | 247 | builder.reference_trace = null; |
| 248 | } else if (mem.startsWith(u8, arg, "-j")) { | ||
| 249 | const num = arg["-j".len..]; | ||
| 250 | const n_jobs = std.fmt.parseUnsigned(u32, num, 10) catch |err| { | ||
| 251 | std.debug.print("unable to parse jobs count '{s}': {s}", .{ | ||
| 252 | num, @errorName(err), | ||
| 253 | }); | ||
| 254 | process.exit(1); | ||
| 255 | }; | ||
| 256 | if (n_jobs < 1) { | ||
| 257 | std.debug.print("number of jobs must be at least 1\n", .{}); | ||
| 258 | process.exit(1); | ||
| 259 | } | ||
| 260 | thread_pool_options.n_jobs = n_jobs; | ||
| 229 | } else if (mem.eql(u8, arg, "--")) { | 261 | } else if (mem.eql(u8, arg, "--")) { |
| 230 | builder.args = argsRest(args, arg_idx); | 262 | builder.args = argsRest(args, arg_idx); |
| 231 | break; | 263 | break; |
| 232 | } else { | 264 | } else { |
| 233 | std.debug.print("Unrecognized argument: {s}\n\n", .{arg}); | 265 | std.debug.print("Unrecognized argument: {s}\n\n", .{arg}); |
| 234 | return usageAndErr(builder, false, stderr_stream); | 266 | usageAndErr(builder, false, stderr_stream); |
| 235 | } | 267 | } |
| 236 | } else { | 268 | } else { |
| 237 | try targets.append(arg); | 269 | try targets.append(arg); |
| 238 | } | 270 | } |
| 239 | } | 271 | } |
| 240 | 272 | ||
| 273 | const stderr = std.io.getStdErr(); | ||
| 274 | const ttyconf = get_tty_conf(color, stderr); | ||
| 275 | switch (ttyconf) { | ||
| 276 | .no_color => try builder.env_map.put("NO_COLOR", "1"), | ||
| 277 | .escape_codes => try builder.env_map.put("ZIG_DEBUG_COLOR", "1"), | ||
| 278 | .windows_api => {}, | ||
| 279 | } | ||
| 280 | |||
| 281 | var progress: std.Progress = .{ .dont_print_on_dumb = true }; | ||
| 282 | const main_progress_node = progress.start("", 0); | ||
| 283 | |||
| 241 | builder.debug_log_scopes = debug_log_scopes.items; | 284 | builder.debug_log_scopes = debug_log_scopes.items; |
| 242 | builder.resolveInstallPrefix(install_prefix, dir_list); | 285 | builder.resolveInstallPrefix(install_prefix, dir_list); |
| 243 | try builder.runBuild(root); | 286 | { |
| 287 | var prog_node = main_progress_node.start("user build.zig logic", 0); | ||
| 288 | defer prog_node.end(); | ||
| 289 | try builder.runBuild(root); | ||
| 290 | } | ||
| 244 | 291 | ||
| 245 | if (builder.validateUserInputDidItFail()) | 292 | if (builder.validateUserInputDidItFail()) |
| 246 | return usageAndErr(builder, true, stderr_stream); | 293 | usageAndErr(builder, true, stderr_stream); |
| 247 | 294 | ||
| 248 | builder.make(targets.items) catch |err| { | 295 | var run: Run = .{ |
| 249 | switch (err) { | 296 | .max_rss = max_rss, |
| 250 | error.InvalidStepName => { | 297 | .max_rss_is_default = false, |
| 251 | return usageAndErr(builder, true, stderr_stream); | 298 | .max_rss_mutex = .{}, |
| 299 | .memory_blocked_steps = std.ArrayList(*Step).init(arena), | ||
| 300 | |||
| 301 | .claimed_rss = 0, | ||
| 302 | .enable_summary = enable_summary, | ||
| 303 | .ttyconf = ttyconf, | ||
| 304 | .stderr = stderr, | ||
| 305 | }; | ||
| 306 | |||
| 307 | if (run.max_rss == 0) { | ||
| 308 | run.max_rss = process.totalSystemMemory() catch std.math.maxInt(usize); | ||
| 309 | run.max_rss_is_default = true; | ||
| 310 | } | ||
| 311 | |||
| 312 | runStepNames( | ||
| 313 | arena, | ||
| 314 | builder, | ||
| 315 | targets.items, | ||
| 316 | main_progress_node, | ||
| 317 | thread_pool_options, | ||
| 318 | &run, | ||
| 319 | ) catch |err| switch (err) { | ||
| 320 | error.UncleanExit => process.exit(1), | ||
| 321 | else => return err, | ||
| 322 | }; | ||
| 323 | } | ||
| 324 | |||
| 325 | const Run = struct { | ||
| 326 | max_rss: usize, | ||
| 327 | max_rss_is_default: bool, | ||
| 328 | max_rss_mutex: std.Thread.Mutex, | ||
| 329 | memory_blocked_steps: std.ArrayList(*Step), | ||
| 330 | |||
| 331 | claimed_rss: usize, | ||
| 332 | enable_summary: ?bool, | ||
| 333 | ttyconf: std.debug.TTY.Config, | ||
| 334 | stderr: std.fs.File, | ||
| 335 | }; | ||
| 336 | |||
| 337 | fn runStepNames( | ||
| 338 | arena: std.mem.Allocator, | ||
| 339 | b: *std.Build, | ||
| 340 | step_names: []const []const u8, | ||
| 341 | parent_prog_node: *std.Progress.Node, | ||
| 342 | thread_pool_options: std.Thread.Pool.Options, | ||
| 343 | run: *Run, | ||
| 344 | ) !void { | ||
| 345 | const gpa = b.allocator; | ||
| 346 | var step_stack: std.AutoArrayHashMapUnmanaged(*Step, void) = .{}; | ||
| 347 | defer step_stack.deinit(gpa); | ||
| 348 | |||
| 349 | if (step_names.len == 0) { | ||
| 350 | try step_stack.put(gpa, b.default_step, {}); | ||
| 351 | } else { | ||
| 352 | try step_stack.ensureUnusedCapacity(gpa, step_names.len); | ||
| 353 | for (0..step_names.len) |i| { | ||
| 354 | const step_name = step_names[step_names.len - i - 1]; | ||
| 355 | const s = b.top_level_steps.get(step_name) orelse { | ||
| 356 | std.debug.print("no step named '{s}'. Access the help menu with 'zig build -h'\n", .{step_name}); | ||
| 357 | process.exit(1); | ||
| 358 | }; | ||
| 359 | step_stack.putAssumeCapacity(&s.step, {}); | ||
| 360 | } | ||
| 361 | } | ||
| 362 | |||
| 363 | const starting_steps = try arena.dupe(*Step, step_stack.keys()); | ||
| 364 | for (starting_steps) |s| { | ||
| 365 | checkForDependencyLoop(b, s, &step_stack) catch |err| switch (err) { | ||
| 366 | error.DependencyLoopDetected => return error.UncleanExit, | ||
| 367 | else => |e| return e, | ||
| 368 | }; | ||
| 369 | } | ||
| 370 | |||
| 371 | { | ||
| 372 | // Check that we have enough memory to complete the build. | ||
| 373 | var any_problems = false; | ||
| 374 | for (step_stack.keys()) |s| { | ||
| 375 | if (s.max_rss == 0) continue; | ||
| 376 | if (s.max_rss > run.max_rss) { | ||
| 377 | std.debug.print("{s}{s}: this step declares an upper bound of {d} bytes of memory, exceeding the available {d} bytes of memory\n", .{ | ||
| 378 | s.owner.dep_prefix, s.name, s.max_rss, run.max_rss, | ||
| 379 | }); | ||
| 380 | any_problems = true; | ||
| 381 | } | ||
| 382 | } | ||
| 383 | if (any_problems) { | ||
| 384 | if (run.max_rss_is_default) { | ||
| 385 | std.debug.print("note: use --maxrss to override the default", .{}); | ||
| 386 | } | ||
| 387 | return error.UncleanExit; | ||
| 388 | } | ||
| 389 | } | ||
| 390 | |||
| 391 | var thread_pool: std.Thread.Pool = undefined; | ||
| 392 | try thread_pool.init(thread_pool_options); | ||
| 393 | defer thread_pool.deinit(); | ||
| 394 | |||
| 395 | { | ||
| 396 | defer parent_prog_node.end(); | ||
| 397 | |||
| 398 | var step_prog = parent_prog_node.start("steps", step_stack.count()); | ||
| 399 | defer step_prog.end(); | ||
| 400 | |||
| 401 | var wait_group: std.Thread.WaitGroup = .{}; | ||
| 402 | defer wait_group.wait(); | ||
| 403 | |||
| 404 | // Here we spawn the initial set of tasks with a nice heuristic - | ||
| 405 | // dependency order. Each worker when it finishes a step will then | ||
| 406 | // check whether it should run any dependants. | ||
| 407 | const steps_slice = step_stack.keys(); | ||
| 408 | for (0..steps_slice.len) |i| { | ||
| 409 | const step = steps_slice[steps_slice.len - i - 1]; | ||
| 410 | |||
| 411 | wait_group.start(); | ||
| 412 | thread_pool.spawn(workerMakeOneStep, .{ | ||
| 413 | &wait_group, &thread_pool, b, step, &step_prog, run, | ||
| 414 | }) catch @panic("OOM"); | ||
| 415 | } | ||
| 416 | } | ||
| 417 | assert(run.memory_blocked_steps.items.len == 0); | ||
| 418 | |||
| 419 | var test_skip_count: usize = 0; | ||
| 420 | var test_fail_count: usize = 0; | ||
| 421 | var test_pass_count: usize = 0; | ||
| 422 | var test_leak_count: usize = 0; | ||
| 423 | var test_count: usize = 0; | ||
| 424 | |||
| 425 | var success_count: usize = 0; | ||
| 426 | var skipped_count: usize = 0; | ||
| 427 | var failure_count: usize = 0; | ||
| 428 | var pending_count: usize = 0; | ||
| 429 | var total_compile_errors: usize = 0; | ||
| 430 | var compile_error_steps: std.ArrayListUnmanaged(*Step) = .{}; | ||
| 431 | defer compile_error_steps.deinit(gpa); | ||
| 432 | |||
| 433 | for (step_stack.keys()) |s| { | ||
| 434 | test_fail_count += s.test_results.fail_count; | ||
| 435 | test_skip_count += s.test_results.skip_count; | ||
| 436 | test_leak_count += s.test_results.leak_count; | ||
| 437 | test_pass_count += s.test_results.passCount(); | ||
| 438 | test_count += s.test_results.test_count; | ||
| 439 | |||
| 440 | switch (s.state) { | ||
| 441 | .precheck_unstarted => unreachable, | ||
| 442 | .precheck_started => unreachable, | ||
| 443 | .running => unreachable, | ||
| 444 | .precheck_done => { | ||
| 445 | // precheck_done is equivalent to dependency_failure in the case of | ||
| 446 | // transitive dependencies. For example: | ||
| 447 | // A -> B -> C (failure) | ||
| 448 | // B will be marked as dependency_failure, while A may never be queued, and thus | ||
| 449 | // remain in the initial state of precheck_done. | ||
| 450 | s.state = .dependency_failure; | ||
| 451 | pending_count += 1; | ||
| 452 | }, | ||
| 453 | .dependency_failure => pending_count += 1, | ||
| 454 | .success => success_count += 1, | ||
| 455 | .skipped => skipped_count += 1, | ||
| 456 | .failure => { | ||
| 457 | failure_count += 1; | ||
| 458 | const compile_errors_len = s.result_error_bundle.errorMessageCount(); | ||
| 459 | if (compile_errors_len > 0) { | ||
| 460 | total_compile_errors += compile_errors_len; | ||
| 461 | try compile_error_steps.append(gpa, s); | ||
| 462 | } | ||
| 252 | }, | 463 | }, |
| 253 | error.UncleanExit => process.exit(1), | ||
| 254 | // This error is intended to indicate that the step has already | ||
| 255 | // logged an error message and so printing the error return trace | ||
| 256 | // here would be unwanted extra information, unless the user opts | ||
| 257 | // into it with a debug flag. | ||
| 258 | error.StepFailed => process.exit(1), | ||
| 259 | else => return err, | ||
| 260 | } | 464 | } |
| 261 | }; | 465 | } |
| 466 | |||
| 467 | // A proper command line application defaults to silently succeeding. | ||
| 468 | // The user may request verbose mode if they have a different preference. | ||
| 469 | if (failure_count == 0 and run.enable_summary != true) return cleanExit(); | ||
| 470 | |||
| 471 | const ttyconf = run.ttyconf; | ||
| 472 | const stderr = run.stderr; | ||
| 473 | |||
| 474 | if (run.enable_summary != false) { | ||
| 475 | const total_count = success_count + failure_count + pending_count + skipped_count; | ||
| 476 | ttyconf.setColor(stderr, .Cyan) catch {}; | ||
| 477 | stderr.writeAll("Build Summary:") catch {}; | ||
| 478 | ttyconf.setColor(stderr, .Reset) catch {}; | ||
| 479 | stderr.writer().print(" {d}/{d} steps succeeded", .{ success_count, total_count }) catch {}; | ||
| 480 | if (skipped_count > 0) stderr.writer().print("; {d} skipped", .{skipped_count}) catch {}; | ||
| 481 | if (failure_count > 0) stderr.writer().print("; {d} failed", .{failure_count}) catch {}; | ||
| 482 | |||
| 483 | if (test_count > 0) stderr.writer().print("; {d}/{d} tests passed", .{ test_pass_count, test_count }) catch {}; | ||
| 484 | if (test_skip_count > 0) stderr.writer().print("; {d} skipped", .{test_skip_count}) catch {}; | ||
| 485 | if (test_fail_count > 0) stderr.writer().print("; {d} failed", .{test_fail_count}) catch {}; | ||
| 486 | if (test_leak_count > 0) stderr.writer().print("; {d} leaked", .{test_leak_count}) catch {}; | ||
| 487 | |||
| 488 | if (run.enable_summary == null) { | ||
| 489 | ttyconf.setColor(stderr, .Dim) catch {}; | ||
| 490 | stderr.writeAll(" (disable with -fno-summary)") catch {}; | ||
| 491 | ttyconf.setColor(stderr, .Reset) catch {}; | ||
| 492 | } | ||
| 493 | stderr.writeAll("\n") catch {}; | ||
| 494 | |||
| 495 | // Print a fancy tree with build results. | ||
| 496 | var print_node: PrintNode = .{ .parent = null }; | ||
| 497 | if (step_names.len == 0) { | ||
| 498 | print_node.last = true; | ||
| 499 | printTreeStep(b, b.default_step, stderr, ttyconf, &print_node, &step_stack) catch {}; | ||
| 500 | } else { | ||
| 501 | for (step_names, 0..) |step_name, i| { | ||
| 502 | const tls = b.top_level_steps.get(step_name).?; | ||
| 503 | print_node.last = i + 1 == b.top_level_steps.count(); | ||
| 504 | printTreeStep(b, &tls.step, stderr, ttyconf, &print_node, &step_stack) catch {}; | ||
| 505 | } | ||
| 506 | } | ||
| 507 | } | ||
| 508 | |||
| 509 | if (failure_count == 0) return cleanExit(); | ||
| 510 | |||
| 511 | // Finally, render compile errors at the bottom of the terminal. | ||
| 512 | // We use a separate compile_error_steps array list because step_stack is destructively | ||
| 513 | // mutated in printTreeStep above. | ||
| 514 | if (total_compile_errors > 0) { | ||
| 515 | for (compile_error_steps.items) |s| { | ||
| 516 | if (s.result_error_bundle.errorMessageCount() > 0) { | ||
| 517 | s.result_error_bundle.renderToStdErr(renderOptions(ttyconf)); | ||
| 518 | } | ||
| 519 | } | ||
| 520 | |||
| 521 | // Signal to parent process that we have printed compile errors. The | ||
| 522 | // parent process may choose to omit the "following command failed" | ||
| 523 | // line in this case. | ||
| 524 | process.exit(2); | ||
| 525 | } | ||
| 526 | |||
| 527 | process.exit(1); | ||
| 528 | } | ||
| 529 | |||
| 530 | const PrintNode = struct { | ||
| 531 | parent: ?*PrintNode, | ||
| 532 | last: bool = false, | ||
| 533 | }; | ||
| 534 | |||
| 535 | fn printPrefix(node: *PrintNode, stderr: std.fs.File, ttyconf: std.debug.TTY.Config) !void { | ||
| 536 | const parent = node.parent orelse return; | ||
| 537 | if (parent.parent == null) return; | ||
| 538 | try printPrefix(parent, stderr, ttyconf); | ||
| 539 | if (parent.last) { | ||
| 540 | try stderr.writeAll(" "); | ||
| 541 | } else { | ||
| 542 | try stderr.writeAll(switch (ttyconf) { | ||
| 543 | .no_color, .windows_api => "| ", | ||
| 544 | .escape_codes => "\x1B\x28\x30\x78\x1B\x28\x42 ", // │ | ||
| 545 | }); | ||
| 546 | } | ||
| 547 | } | ||
| 548 | |||
| 549 | fn printTreeStep( | ||
| 550 | b: *std.Build, | ||
| 551 | s: *Step, | ||
| 552 | stderr: std.fs.File, | ||
| 553 | ttyconf: std.debug.TTY.Config, | ||
| 554 | parent_node: *PrintNode, | ||
| 555 | step_stack: *std.AutoArrayHashMapUnmanaged(*Step, void), | ||
| 556 | ) !void { | ||
| 557 | const first = step_stack.swapRemove(s); | ||
| 558 | try printPrefix(parent_node, stderr, ttyconf); | ||
| 559 | |||
| 560 | if (!first) try ttyconf.setColor(stderr, .Dim); | ||
| 561 | if (parent_node.parent != null) { | ||
| 562 | if (parent_node.last) { | ||
| 563 | try stderr.writeAll(switch (ttyconf) { | ||
| 564 | .no_color, .windows_api => "+- ", | ||
| 565 | .escape_codes => "\x1B\x28\x30\x6d\x71\x1B\x28\x42 ", // └─ | ||
| 566 | }); | ||
| 567 | } else { | ||
| 568 | try stderr.writeAll(switch (ttyconf) { | ||
| 569 | .no_color, .windows_api => "+- ", | ||
| 570 | .escape_codes => "\x1B\x28\x30\x74\x71\x1B\x28\x42 ", // ├─ | ||
| 571 | }); | ||
| 572 | } | ||
| 573 | } | ||
| 574 | |||
| 575 | // dep_prefix omitted here because it is redundant with the tree. | ||
| 576 | try stderr.writeAll(s.name); | ||
| 577 | |||
| 578 | if (first) { | ||
| 579 | switch (s.state) { | ||
| 580 | .precheck_unstarted => unreachable, | ||
| 581 | .precheck_started => unreachable, | ||
| 582 | .precheck_done => unreachable, | ||
| 583 | .running => unreachable, | ||
| 584 | |||
| 585 | .dependency_failure => { | ||
| 586 | try ttyconf.setColor(stderr, .Dim); | ||
| 587 | try stderr.writeAll(" transitive failure\n"); | ||
| 588 | try ttyconf.setColor(stderr, .Reset); | ||
| 589 | }, | ||
| 590 | |||
| 591 | .success => { | ||
| 592 | try ttyconf.setColor(stderr, .Green); | ||
| 593 | if (s.result_cached) { | ||
| 594 | try stderr.writeAll(" cached"); | ||
| 595 | } else if (s.test_results.test_count > 0) { | ||
| 596 | const pass_count = s.test_results.passCount(); | ||
| 597 | try stderr.writer().print(" {d} passed", .{pass_count}); | ||
| 598 | if (s.test_results.skip_count > 0) { | ||
| 599 | try ttyconf.setColor(stderr, .Yellow); | ||
| 600 | try stderr.writer().print(" {d} skipped", .{s.test_results.skip_count}); | ||
| 601 | } | ||
| 602 | } else { | ||
| 603 | try stderr.writeAll(" success"); | ||
| 604 | } | ||
| 605 | try ttyconf.setColor(stderr, .Reset); | ||
| 606 | if (s.result_duration_ns) |ns| { | ||
| 607 | try ttyconf.setColor(stderr, .Dim); | ||
| 608 | if (ns >= std.time.ns_per_min) { | ||
| 609 | try stderr.writer().print(" {d}m", .{ns / std.time.ns_per_min}); | ||
| 610 | } else if (ns >= std.time.ns_per_s) { | ||
| 611 | try stderr.writer().print(" {d}s", .{ns / std.time.ns_per_s}); | ||
| 612 | } else if (ns >= std.time.ns_per_ms) { | ||
| 613 | try stderr.writer().print(" {d}ms", .{ns / std.time.ns_per_ms}); | ||
| 614 | } else if (ns >= std.time.ns_per_us) { | ||
| 615 | try stderr.writer().print(" {d}us", .{ns / std.time.ns_per_us}); | ||
| 616 | } else { | ||
| 617 | try stderr.writer().print(" {d}ns", .{ns}); | ||
| 618 | } | ||
| 619 | try ttyconf.setColor(stderr, .Reset); | ||
| 620 | } | ||
| 621 | if (s.result_peak_rss != 0) { | ||
| 622 | const rss = s.result_peak_rss; | ||
| 623 | try ttyconf.setColor(stderr, .Dim); | ||
| 624 | if (rss >= 1000_000_000) { | ||
| 625 | try stderr.writer().print(" MaxRSS:{d}G", .{rss / 1000_000_000}); | ||
| 626 | } else if (rss >= 1000_000) { | ||
| 627 | try stderr.writer().print(" MaxRSS:{d}M", .{rss / 1000_000}); | ||
| 628 | } else if (rss >= 1000) { | ||
| 629 | try stderr.writer().print(" MaxRSS:{d}K", .{rss / 1000}); | ||
| 630 | } else { | ||
| 631 | try stderr.writer().print(" MaxRSS:{d}B", .{rss}); | ||
| 632 | } | ||
| 633 | try ttyconf.setColor(stderr, .Reset); | ||
| 634 | } | ||
| 635 | try stderr.writeAll("\n"); | ||
| 636 | }, | ||
| 637 | |||
| 638 | .skipped => { | ||
| 639 | try ttyconf.setColor(stderr, .Yellow); | ||
| 640 | try stderr.writeAll(" skipped\n"); | ||
| 641 | try ttyconf.setColor(stderr, .Reset); | ||
| 642 | }, | ||
| 643 | |||
| 644 | .failure => { | ||
| 645 | if (s.result_error_bundle.errorMessageCount() > 0) { | ||
| 646 | try ttyconf.setColor(stderr, .Red); | ||
| 647 | try stderr.writer().print(" {d} errors\n", .{ | ||
| 648 | s.result_error_bundle.errorMessageCount(), | ||
| 649 | }); | ||
| 650 | try ttyconf.setColor(stderr, .Reset); | ||
| 651 | } else if (!s.test_results.isSuccess()) { | ||
| 652 | try stderr.writer().print(" {d}/{d} passed", .{ | ||
| 653 | s.test_results.passCount(), s.test_results.test_count, | ||
| 654 | }); | ||
| 655 | if (s.test_results.fail_count > 0) { | ||
| 656 | try stderr.writeAll(", "); | ||
| 657 | try ttyconf.setColor(stderr, .Red); | ||
| 658 | try stderr.writer().print("{d} failed", .{ | ||
| 659 | s.test_results.fail_count, | ||
| 660 | }); | ||
| 661 | try ttyconf.setColor(stderr, .Reset); | ||
| 662 | } | ||
| 663 | if (s.test_results.skip_count > 0) { | ||
| 664 | try stderr.writeAll(", "); | ||
| 665 | try ttyconf.setColor(stderr, .Yellow); | ||
| 666 | try stderr.writer().print("{d} skipped", .{ | ||
| 667 | s.test_results.skip_count, | ||
| 668 | }); | ||
| 669 | try ttyconf.setColor(stderr, .Reset); | ||
| 670 | } | ||
| 671 | if (s.test_results.leak_count > 0) { | ||
| 672 | try stderr.writeAll(", "); | ||
| 673 | try ttyconf.setColor(stderr, .Red); | ||
| 674 | try stderr.writer().print("{d} leaked", .{ | ||
| 675 | s.test_results.leak_count, | ||
| 676 | }); | ||
| 677 | try ttyconf.setColor(stderr, .Reset); | ||
| 678 | } | ||
| 679 | try stderr.writeAll("\n"); | ||
| 680 | } else { | ||
| 681 | try ttyconf.setColor(stderr, .Red); | ||
| 682 | try stderr.writeAll(" failure\n"); | ||
| 683 | try ttyconf.setColor(stderr, .Reset); | ||
| 684 | } | ||
| 685 | }, | ||
| 686 | } | ||
| 687 | |||
| 688 | for (s.dependencies.items, 0..) |dep, i| { | ||
| 689 | var print_node: PrintNode = .{ | ||
| 690 | .parent = parent_node, | ||
| 691 | .last = i == s.dependencies.items.len - 1, | ||
| 692 | }; | ||
| 693 | try printTreeStep(b, dep, stderr, ttyconf, &print_node, step_stack); | ||
| 694 | } | ||
| 695 | } else { | ||
| 696 | if (s.dependencies.items.len == 0) { | ||
| 697 | try stderr.writeAll(" (reused)\n"); | ||
| 698 | } else { | ||
| 699 | try stderr.writer().print(" (+{d} more reused dependencies)\n", .{ | ||
| 700 | s.dependencies.items.len, | ||
| 701 | }); | ||
| 702 | } | ||
| 703 | try ttyconf.setColor(stderr, .Reset); | ||
| 704 | } | ||
| 705 | } | ||
| 706 | |||
| 707 | fn checkForDependencyLoop( | ||
| 708 | b: *std.Build, | ||
| 709 | s: *Step, | ||
| 710 | step_stack: *std.AutoArrayHashMapUnmanaged(*Step, void), | ||
| 711 | ) !void { | ||
| 712 | switch (s.state) { | ||
| 713 | .precheck_started => { | ||
| 714 | std.debug.print("dependency loop detected:\n {s}\n", .{s.name}); | ||
| 715 | return error.DependencyLoopDetected; | ||
| 716 | }, | ||
| 717 | .precheck_unstarted => { | ||
| 718 | s.state = .precheck_started; | ||
| 719 | |||
| 720 | try step_stack.ensureUnusedCapacity(b.allocator, s.dependencies.items.len); | ||
| 721 | for (s.dependencies.items) |dep| { | ||
| 722 | try step_stack.put(b.allocator, dep, {}); | ||
| 723 | try dep.dependants.append(b.allocator, s); | ||
| 724 | checkForDependencyLoop(b, dep, step_stack) catch |err| { | ||
| 725 | if (err == error.DependencyLoopDetected) { | ||
| 726 | std.debug.print(" {s}\n", .{s.name}); | ||
| 727 | } | ||
| 728 | return err; | ||
| 729 | }; | ||
| 730 | } | ||
| 731 | |||
| 732 | s.state = .precheck_done; | ||
| 733 | }, | ||
| 734 | .precheck_done => {}, | ||
| 735 | |||
| 736 | // These don't happen until we actually run the step graph. | ||
| 737 | .dependency_failure => unreachable, | ||
| 738 | .running => unreachable, | ||
| 739 | .success => unreachable, | ||
| 740 | .failure => unreachable, | ||
| 741 | .skipped => unreachable, | ||
| 742 | } | ||
| 743 | } | ||
| 744 | |||
| 745 | fn workerMakeOneStep( | ||
| 746 | wg: *std.Thread.WaitGroup, | ||
| 747 | thread_pool: *std.Thread.Pool, | ||
| 748 | b: *std.Build, | ||
| 749 | s: *Step, | ||
| 750 | prog_node: *std.Progress.Node, | ||
| 751 | run: *Run, | ||
| 752 | ) void { | ||
| 753 | defer wg.finish(); | ||
| 754 | |||
| 755 | // First, check the conditions for running this step. If they are not met, | ||
| 756 | // then we return without doing the step, relying on another worker to | ||
| 757 | // queue this step up again when dependencies are met. | ||
| 758 | for (s.dependencies.items) |dep| { | ||
| 759 | switch (@atomicLoad(Step.State, &dep.state, .SeqCst)) { | ||
| 760 | .success, .skipped => continue, | ||
| 761 | .failure, .dependency_failure => { | ||
| 762 | @atomicStore(Step.State, &s.state, .dependency_failure, .SeqCst); | ||
| 763 | return; | ||
| 764 | }, | ||
| 765 | .precheck_done, .running => { | ||
| 766 | // dependency is not finished yet. | ||
| 767 | return; | ||
| 768 | }, | ||
| 769 | .precheck_unstarted => unreachable, | ||
| 770 | .precheck_started => unreachable, | ||
| 771 | } | ||
| 772 | } | ||
| 773 | |||
| 774 | if (s.max_rss != 0) { | ||
| 775 | run.max_rss_mutex.lock(); | ||
| 776 | defer run.max_rss_mutex.unlock(); | ||
| 777 | |||
| 778 | // Avoid running steps twice. | ||
| 779 | if (s.state != .precheck_done) { | ||
| 780 | // Another worker got the job. | ||
| 781 | return; | ||
| 782 | } | ||
| 783 | |||
| 784 | const new_claimed_rss = run.claimed_rss + s.max_rss; | ||
| 785 | if (new_claimed_rss > run.max_rss) { | ||
| 786 | // Running this step right now could possibly exceed the allotted RSS. | ||
| 787 | // Add this step to the queue of memory-blocked steps. | ||
| 788 | run.memory_blocked_steps.append(s) catch @panic("OOM"); | ||
| 789 | return; | ||
| 790 | } | ||
| 791 | |||
| 792 | run.claimed_rss = new_claimed_rss; | ||
| 793 | s.state = .running; | ||
| 794 | } else { | ||
| 795 | // Avoid running steps twice. | ||
| 796 | if (@cmpxchgStrong(Step.State, &s.state, .precheck_done, .running, .SeqCst, .SeqCst) != null) { | ||
| 797 | // Another worker got the job. | ||
| 798 | return; | ||
| 799 | } | ||
| 800 | } | ||
| 801 | |||
| 802 | var sub_prog_node = prog_node.start(s.name, 0); | ||
| 803 | sub_prog_node.activate(); | ||
| 804 | defer sub_prog_node.end(); | ||
| 805 | |||
| 806 | const make_result = s.make(&sub_prog_node); | ||
| 807 | |||
| 808 | // No matter the result, we want to display error/warning messages. | ||
| 809 | if (s.result_error_msgs.items.len > 0) { | ||
| 810 | sub_prog_node.context.lock_stderr(); | ||
| 811 | defer sub_prog_node.context.unlock_stderr(); | ||
| 812 | |||
| 813 | const stderr = run.stderr; | ||
| 814 | const ttyconf = run.ttyconf; | ||
| 815 | |||
| 816 | for (s.result_error_msgs.items) |msg| { | ||
| 817 | // Sometimes it feels like you just can't catch a break. Finally, | ||
| 818 | // with Zig, you can. | ||
| 819 | ttyconf.setColor(stderr, .Bold) catch break; | ||
| 820 | stderr.writeAll(s.owner.dep_prefix) catch break; | ||
| 821 | stderr.writeAll(s.name) catch break; | ||
| 822 | stderr.writeAll(": ") catch break; | ||
| 823 | ttyconf.setColor(stderr, .Red) catch break; | ||
| 824 | stderr.writeAll("error: ") catch break; | ||
| 825 | ttyconf.setColor(stderr, .Reset) catch break; | ||
| 826 | stderr.writeAll(msg) catch break; | ||
| 827 | stderr.writeAll("\n") catch break; | ||
| 828 | } | ||
| 829 | } | ||
| 830 | |||
| 831 | handle_result: { | ||
| 832 | if (make_result) |_| { | ||
| 833 | @atomicStore(Step.State, &s.state, .success, .SeqCst); | ||
| 834 | } else |err| switch (err) { | ||
| 835 | error.MakeFailed => { | ||
| 836 | @atomicStore(Step.State, &s.state, .failure, .SeqCst); | ||
| 837 | break :handle_result; | ||
| 838 | }, | ||
| 839 | error.MakeSkipped => @atomicStore(Step.State, &s.state, .skipped, .SeqCst), | ||
| 840 | } | ||
| 841 | |||
| 842 | // Successful completion of a step, so we queue up its dependants as well. | ||
| 843 | for (s.dependants.items) |dep| { | ||
| 844 | wg.start(); | ||
| 845 | thread_pool.spawn(workerMakeOneStep, .{ | ||
| 846 | wg, thread_pool, b, dep, prog_node, run, | ||
| 847 | }) catch @panic("OOM"); | ||
| 848 | } | ||
| 849 | } | ||
| 850 | |||
| 851 | // If this is a step that claims resources, we must now queue up other | ||
| 852 | // steps that are waiting for resources. | ||
| 853 | if (s.max_rss != 0) { | ||
| 854 | run.max_rss_mutex.lock(); | ||
| 855 | defer run.max_rss_mutex.unlock(); | ||
| 856 | |||
| 857 | // Give the memory back to the scheduler. | ||
| 858 | run.claimed_rss -= s.max_rss; | ||
| 859 | // Avoid kicking off too many tasks that we already know will not have | ||
| 860 | // enough resources. | ||
| 861 | var remaining = run.max_rss - run.claimed_rss; | ||
| 862 | var i: usize = 0; | ||
| 863 | var j: usize = 0; | ||
| 864 | while (j < run.memory_blocked_steps.items.len) : (j += 1) { | ||
| 865 | const dep = run.memory_blocked_steps.items[j]; | ||
| 866 | assert(dep.max_rss != 0); | ||
| 867 | if (dep.max_rss <= remaining) { | ||
| 868 | remaining -= dep.max_rss; | ||
| 869 | |||
| 870 | wg.start(); | ||
| 871 | thread_pool.spawn(workerMakeOneStep, .{ | ||
| 872 | wg, thread_pool, b, dep, prog_node, run, | ||
| 873 | }) catch @panic("OOM"); | ||
| 874 | } else { | ||
| 875 | run.memory_blocked_steps.items[i] = dep; | ||
| 876 | i += 1; | ||
| 877 | } | ||
| 878 | } | ||
| 879 | run.memory_blocked_steps.shrinkRetainingCapacity(i); | ||
| 880 | } | ||
| 262 | } | 881 | } |
| 263 | 882 | ||
| 264 | fn steps(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !void { | 883 | fn steps(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !void { |
| ... | @@ -269,7 +888,7 @@ fn steps(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !voi | ... | @@ -269,7 +888,7 @@ fn steps(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !voi |
| 269 | } | 888 | } |
| 270 | 889 | ||
| 271 | const allocator = builder.allocator; | 890 | const allocator = builder.allocator; |
| 272 | for (builder.top_level_steps.items) |top_level_step| { | 891 | for (builder.top_level_steps.values()) |top_level_step| { |
| 273 | const name = if (&top_level_step.step == builder.default_step) | 892 | const name = if (&top_level_step.step == builder.default_step) |
| 274 | try fmt.allocPrint(allocator, "{s} (default)", .{top_level_step.step.name}) | 893 | try fmt.allocPrint(allocator, "{s} (default)", .{top_level_step.step.name}) |
| 275 | else | 894 | else |
| ... | @@ -327,6 +946,10 @@ fn usage(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !voi | ... | @@ -327,6 +946,10 @@ fn usage(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !voi |
| 327 | \\ --verbose Print commands before executing them | 946 | \\ --verbose Print commands before executing them |
| 328 | \\ --color [auto|off|on] Enable or disable colored error messages | 947 | \\ --color [auto|off|on] Enable or disable colored error messages |
| 329 | \\ --prominent-compile-errors Output compile errors formatted for a human to read | 948 | \\ --prominent-compile-errors Output compile errors formatted for a human to read |
| 949 | \\ -fsummary Print the build summary, even on success | ||
| 950 | \\ -fno-summary Omit the build summary, even on failure | ||
| 951 | \\ -j<N> Limit concurrent jobs (default is to use all CPU cores) | ||
| 952 | \\ --maxrss <bytes> Limit memory usage (default is to use available memory) | ||
| 330 | \\ | 953 | \\ |
| 331 | \\Project-Specific Options: | 954 | \\Project-Specific Options: |
| 332 | \\ | 955 | \\ |
| ... | @@ -364,6 +987,7 @@ fn usage(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !voi | ... | @@ -364,6 +987,7 @@ fn usage(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !voi |
| 364 | \\ --zig-lib-dir [arg] Override path to Zig lib directory | 987 | \\ --zig-lib-dir [arg] Override path to Zig lib directory |
| 365 | \\ --build-runner [file] Override path to build runner | 988 | \\ --build-runner [file] Override path to build runner |
| 366 | \\ --debug-log [scope] Enable debugging the compiler | 989 | \\ --debug-log [scope] Enable debugging the compiler |
| 990 | \\ --debug-pkg-config Fail if unknown pkg-config flags encountered | ||
| 367 | \\ --verbose-link Enable compiler debug output for linking | 991 | \\ --verbose-link Enable compiler debug output for linking |
| 368 | \\ --verbose-air Enable compiler debug output for Zig AIR | 992 | \\ --verbose-air Enable compiler debug output for Zig AIR |
| 369 | \\ --verbose-llvm-ir Enable compiler debug output for LLVM IR | 993 | \\ --verbose-llvm-ir Enable compiler debug output for LLVM IR |
| ... | @@ -374,7 +998,7 @@ fn usage(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !voi | ... | @@ -374,7 +998,7 @@ fn usage(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !voi |
| 374 | ); | 998 | ); |
| 375 | } | 999 | } |
| 376 | 1000 | ||
| 377 | fn usageAndErr(builder: *std.Build, already_ran_build: bool, out_stream: anytype) void { | 1001 | fn usageAndErr(builder: *std.Build, already_ran_build: bool, out_stream: anytype) noreturn { |
| 378 | usage(builder, already_ran_build, out_stream) catch {}; | 1002 | usage(builder, already_ran_build, out_stream) catch {}; |
| 379 | process.exit(1); | 1003 | process.exit(1); |
| 380 | } | 1004 | } |
| ... | @@ -389,3 +1013,28 @@ fn argsRest(args: [][]const u8, idx: usize) ?[][]const u8 { | ... | @@ -389,3 +1013,28 @@ fn argsRest(args: [][]const u8, idx: usize) ?[][]const u8 { |
| 389 | if (idx >= args.len) return null; | 1013 | if (idx >= args.len) return null; |
| 390 | return args[idx..]; | 1014 | return args[idx..]; |
| 391 | } | 1015 | } |
| 1016 | |||
| 1017 | fn cleanExit() void { | ||
| 1018 | // Perhaps in the future there could be an Advanced Options flag such as | ||
| 1019 | // --debug-build-runner-leaks which would make this function return instead | ||
| 1020 | // of calling exit. | ||
| 1021 | process.exit(0); | ||
| 1022 | } | ||
| 1023 | |||
| 1024 | const Color = enum { auto, off, on }; | ||
| 1025 | |||
| 1026 | fn get_tty_conf(color: Color, stderr: std.fs.File) std.debug.TTY.Config { | ||
| 1027 | return switch (color) { | ||
| 1028 | .auto => std.debug.detectTTYConfig(stderr), | ||
| 1029 | .on => .escape_codes, | ||
| 1030 | .off => .no_color, | ||
| 1031 | }; | ||
| 1032 | } | ||
| 1033 | |||
| 1034 | fn renderOptions(ttyconf: std.debug.TTY.Config) std.zig.ErrorBundle.RenderOptions { | ||
| 1035 | return .{ | ||
| 1036 | .ttyconf = ttyconf, | ||
| 1037 | .include_source_line = ttyconf != .no_color, | ||
| 1038 | .include_reference_trace = ttyconf != .no_color, | ||
| 1039 | }; | ||
| 1040 | } |
lib/std/Build.zig+233-232| ... | @@ -32,14 +32,12 @@ pub const Step = @import("Build/Step.zig"); | ... | @@ -32,14 +32,12 @@ pub const Step = @import("Build/Step.zig"); |
| 32 | pub const CheckFileStep = @import("Build/CheckFileStep.zig"); | 32 | pub const CheckFileStep = @import("Build/CheckFileStep.zig"); |
| 33 | pub const CheckObjectStep = @import("Build/CheckObjectStep.zig"); | 33 | pub const CheckObjectStep = @import("Build/CheckObjectStep.zig"); |
| 34 | pub const ConfigHeaderStep = @import("Build/ConfigHeaderStep.zig"); | 34 | pub const ConfigHeaderStep = @import("Build/ConfigHeaderStep.zig"); |
| 35 | pub const EmulatableRunStep = @import("Build/EmulatableRunStep.zig"); | ||
| 36 | pub const FmtStep = @import("Build/FmtStep.zig"); | 35 | pub const FmtStep = @import("Build/FmtStep.zig"); |
| 37 | pub const InstallArtifactStep = @import("Build/InstallArtifactStep.zig"); | 36 | pub const InstallArtifactStep = @import("Build/InstallArtifactStep.zig"); |
| 38 | pub const InstallDirStep = @import("Build/InstallDirStep.zig"); | 37 | pub const InstallDirStep = @import("Build/InstallDirStep.zig"); |
| 39 | pub const InstallFileStep = @import("Build/InstallFileStep.zig"); | 38 | pub const InstallFileStep = @import("Build/InstallFileStep.zig"); |
| 40 | pub const ObjCopyStep = @import("Build/ObjCopyStep.zig"); | 39 | pub const ObjCopyStep = @import("Build/ObjCopyStep.zig"); |
| 41 | pub const CompileStep = @import("Build/CompileStep.zig"); | 40 | pub const CompileStep = @import("Build/CompileStep.zig"); |
| 42 | pub const LogStep = @import("Build/LogStep.zig"); | ||
| 43 | pub const OptionsStep = @import("Build/OptionsStep.zig"); | 41 | pub const OptionsStep = @import("Build/OptionsStep.zig"); |
| 44 | pub const RemoveDirStep = @import("Build/RemoveDirStep.zig"); | 42 | pub const RemoveDirStep = @import("Build/RemoveDirStep.zig"); |
| 45 | pub const RunStep = @import("Build/RunStep.zig"); | 43 | pub const RunStep = @import("Build/RunStep.zig"); |
| ... | @@ -59,15 +57,12 @@ verbose_air: bool, | ... | @@ -59,15 +57,12 @@ verbose_air: bool, |
| 59 | verbose_llvm_ir: bool, | 57 | verbose_llvm_ir: bool, |
| 60 | verbose_cimport: bool, | 58 | verbose_cimport: bool, |
| 61 | verbose_llvm_cpu_features: bool, | 59 | verbose_llvm_cpu_features: bool, |
| 62 | /// The purpose of executing the command is for a human to read compile errors from the terminal | ||
| 63 | prominent_compile_errors: bool, | ||
| 64 | color: enum { auto, on, off } = .auto, | ||
| 65 | reference_trace: ?u32 = null, | 60 | reference_trace: ?u32 = null, |
| 66 | invalid_user_input: bool, | 61 | invalid_user_input: bool, |
| 67 | zig_exe: []const u8, | 62 | zig_exe: []const u8, |
| 68 | default_step: *Step, | 63 | default_step: *Step, |
| 69 | env_map: *EnvMap, | 64 | env_map: *EnvMap, |
| 70 | top_level_steps: ArrayList(*TopLevelStep), | 65 | top_level_steps: std.StringArrayHashMapUnmanaged(*TopLevelStep), |
| 71 | install_prefix: []const u8, | 66 | install_prefix: []const u8, |
| 72 | dest_dir: ?[]const u8, | 67 | dest_dir: ?[]const u8, |
| 73 | lib_dir: []const u8, | 68 | lib_dir: []const u8, |
| ... | @@ -90,6 +85,7 @@ pkg_config_pkg_list: ?(PkgConfigError![]const PkgConfigPkg) = null, | ... | @@ -90,6 +85,7 @@ pkg_config_pkg_list: ?(PkgConfigError![]const PkgConfigPkg) = null, |
| 90 | args: ?[][]const u8 = null, | 85 | args: ?[][]const u8 = null, |
| 91 | debug_log_scopes: []const []const u8 = &.{}, | 86 | debug_log_scopes: []const []const u8 = &.{}, |
| 92 | debug_compile_errors: bool = false, | 87 | debug_compile_errors: bool = false, |
| 88 | debug_pkg_config: bool = false, | ||
| 93 | 89 | ||
| 94 | /// Experimental. Use system Darling installation to run cross compiled macOS build artifacts. | 90 | /// Experimental. Use system Darling installation to run cross compiled macOS build artifacts. |
| 95 | enable_darling: bool = false, | 91 | enable_darling: bool = false, |
| ... | @@ -198,7 +194,7 @@ pub fn create( | ... | @@ -198,7 +194,7 @@ pub fn create( |
| 198 | env_map.* = try process.getEnvMap(allocator); | 194 | env_map.* = try process.getEnvMap(allocator); |
| 199 | 195 | ||
| 200 | const self = try allocator.create(Build); | 196 | const self = try allocator.create(Build); |
| 201 | self.* = Build{ | 197 | self.* = .{ |
| 202 | .zig_exe = zig_exe, | 198 | .zig_exe = zig_exe, |
| 203 | .build_root = build_root, | 199 | .build_root = build_root, |
| 204 | .cache_root = cache_root, | 200 | .cache_root = cache_root, |
| ... | @@ -211,13 +207,12 @@ pub fn create( | ... | @@ -211,13 +207,12 @@ pub fn create( |
| 211 | .verbose_llvm_ir = false, | 207 | .verbose_llvm_ir = false, |
| 212 | .verbose_cimport = false, | 208 | .verbose_cimport = false, |
| 213 | .verbose_llvm_cpu_features = false, | 209 | .verbose_llvm_cpu_features = false, |
| 214 | .prominent_compile_errors = false, | ||
| 215 | .invalid_user_input = false, | 210 | .invalid_user_input = false, |
| 216 | .allocator = allocator, | 211 | .allocator = allocator, |
| 217 | .user_input_options = UserInputOptionsMap.init(allocator), | 212 | .user_input_options = UserInputOptionsMap.init(allocator), |
| 218 | .available_options_map = AvailableOptionsMap.init(allocator), | 213 | .available_options_map = AvailableOptionsMap.init(allocator), |
| 219 | .available_options_list = ArrayList(AvailableOption).init(allocator), | 214 | .available_options_list = ArrayList(AvailableOption).init(allocator), |
| 220 | .top_level_steps = ArrayList(*TopLevelStep).init(allocator), | 215 | .top_level_steps = .{}, |
| 221 | .default_step = undefined, | 216 | .default_step = undefined, |
| 222 | .env_map = env_map, | 217 | .env_map = env_map, |
| 223 | .search_prefixes = ArrayList([]const u8).init(allocator), | 218 | .search_prefixes = ArrayList([]const u8).init(allocator), |
| ... | @@ -227,12 +222,21 @@ pub fn create( | ... | @@ -227,12 +222,21 @@ pub fn create( |
| 227 | .h_dir = undefined, | 222 | .h_dir = undefined, |
| 228 | .dest_dir = env_map.get("DESTDIR"), | 223 | .dest_dir = env_map.get("DESTDIR"), |
| 229 | .installed_files = ArrayList(InstalledFile).init(allocator), | 224 | .installed_files = ArrayList(InstalledFile).init(allocator), |
| 230 | .install_tls = TopLevelStep{ | 225 | .install_tls = .{ |
| 231 | .step = Step.initNoOp(.top_level, "install", allocator), | 226 | .step = Step.init(.{ |
| 227 | .id = .top_level, | ||
| 228 | .name = "install", | ||
| 229 | .owner = self, | ||
| 230 | }), | ||
| 232 | .description = "Copy build artifacts to prefix path", | 231 | .description = "Copy build artifacts to prefix path", |
| 233 | }, | 232 | }, |
| 234 | .uninstall_tls = TopLevelStep{ | 233 | .uninstall_tls = .{ |
| 235 | .step = Step.init(.top_level, "uninstall", allocator, makeUninstall), | 234 | .step = Step.init(.{ |
| 235 | .id = .top_level, | ||
| 236 | .name = "uninstall", | ||
| 237 | .owner = self, | ||
| 238 | .makeFn = makeUninstall, | ||
| 239 | }), | ||
| 236 | .description = "Remove build artifacts from prefix path", | 240 | .description = "Remove build artifacts from prefix path", |
| 237 | }, | 241 | }, |
| 238 | .zig_lib_dir = null, | 242 | .zig_lib_dir = null, |
| ... | @@ -241,8 +245,8 @@ pub fn create( | ... | @@ -241,8 +245,8 @@ pub fn create( |
| 241 | .host = host, | 245 | .host = host, |
| 242 | .modules = std.StringArrayHashMap(*Module).init(allocator), | 246 | .modules = std.StringArrayHashMap(*Module).init(allocator), |
| 243 | }; | 247 | }; |
| 244 | try self.top_level_steps.append(&self.install_tls); | 248 | try self.top_level_steps.put(allocator, self.install_tls.step.name, &self.install_tls); |
| 245 | try self.top_level_steps.append(&self.uninstall_tls); | 249 | try self.top_level_steps.put(allocator, self.uninstall_tls.step.name, &self.uninstall_tls); |
| 246 | self.default_step = &self.install_tls.step; | 250 | self.default_step = &self.install_tls.step; |
| 247 | return self; | 251 | return self; |
| 248 | } | 252 | } |
| ... | @@ -264,11 +268,20 @@ fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: Cache.Direc | ... | @@ -264,11 +268,20 @@ fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: Cache.Direc |
| 264 | child.* = .{ | 268 | child.* = .{ |
| 265 | .allocator = allocator, | 269 | .allocator = allocator, |
| 266 | .install_tls = .{ | 270 | .install_tls = .{ |
| 267 | .step = Step.initNoOp(.top_level, "install", allocator), | 271 | .step = Step.init(.{ |
| 272 | .id = .top_level, | ||
| 273 | .name = "install", | ||
| 274 | .owner = child, | ||
| 275 | }), | ||
| 268 | .description = "Copy build artifacts to prefix path", | 276 | .description = "Copy build artifacts to prefix path", |
| 269 | }, | 277 | }, |
| 270 | .uninstall_tls = .{ | 278 | .uninstall_tls = .{ |
| 271 | .step = Step.init(.top_level, "uninstall", allocator, makeUninstall), | 279 | .step = Step.init(.{ |
| 280 | .id = .top_level, | ||
| 281 | .name = "uninstall", | ||
| 282 | .owner = child, | ||
| 283 | .makeFn = makeUninstall, | ||
| 284 | }), | ||
| 272 | .description = "Remove build artifacts from prefix path", | 285 | .description = "Remove build artifacts from prefix path", |
| 273 | }, | 286 | }, |
| 274 | .user_input_options = UserInputOptionsMap.init(allocator), | 287 | .user_input_options = UserInputOptionsMap.init(allocator), |
| ... | @@ -281,14 +294,12 @@ fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: Cache.Direc | ... | @@ -281,14 +294,12 @@ fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: Cache.Direc |
| 281 | .verbose_llvm_ir = parent.verbose_llvm_ir, | 294 | .verbose_llvm_ir = parent.verbose_llvm_ir, |
| 282 | .verbose_cimport = parent.verbose_cimport, | 295 | .verbose_cimport = parent.verbose_cimport, |
| 283 | .verbose_llvm_cpu_features = parent.verbose_llvm_cpu_features, | 296 | .verbose_llvm_cpu_features = parent.verbose_llvm_cpu_features, |
| 284 | .prominent_compile_errors = parent.prominent_compile_errors, | ||
| 285 | .color = parent.color, | ||
| 286 | .reference_trace = parent.reference_trace, | 297 | .reference_trace = parent.reference_trace, |
| 287 | .invalid_user_input = false, | 298 | .invalid_user_input = false, |
| 288 | .zig_exe = parent.zig_exe, | 299 | .zig_exe = parent.zig_exe, |
| 289 | .default_step = undefined, | 300 | .default_step = undefined, |
| 290 | .env_map = parent.env_map, | 301 | .env_map = parent.env_map, |
| 291 | .top_level_steps = ArrayList(*TopLevelStep).init(allocator), | 302 | .top_level_steps = .{}, |
| 292 | .install_prefix = undefined, | 303 | .install_prefix = undefined, |
| 293 | .dest_dir = parent.dest_dir, | 304 | .dest_dir = parent.dest_dir, |
| 294 | .lib_dir = parent.lib_dir, | 305 | .lib_dir = parent.lib_dir, |
| ... | @@ -306,6 +317,7 @@ fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: Cache.Direc | ... | @@ -306,6 +317,7 @@ fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: Cache.Direc |
| 306 | .zig_lib_dir = parent.zig_lib_dir, | 317 | .zig_lib_dir = parent.zig_lib_dir, |
| 307 | .debug_log_scopes = parent.debug_log_scopes, | 318 | .debug_log_scopes = parent.debug_log_scopes, |
| 308 | .debug_compile_errors = parent.debug_compile_errors, | 319 | .debug_compile_errors = parent.debug_compile_errors, |
| 320 | .debug_pkg_config = parent.debug_pkg_config, | ||
| 309 | .enable_darling = parent.enable_darling, | 321 | .enable_darling = parent.enable_darling, |
| 310 | .enable_qemu = parent.enable_qemu, | 322 | .enable_qemu = parent.enable_qemu, |
| 311 | .enable_rosetta = parent.enable_rosetta, | 323 | .enable_rosetta = parent.enable_rosetta, |
| ... | @@ -316,8 +328,8 @@ fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: Cache.Direc | ... | @@ -316,8 +328,8 @@ fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: Cache.Direc |
| 316 | .dep_prefix = parent.fmt("{s}{s}.", .{ parent.dep_prefix, dep_name }), | 328 | .dep_prefix = parent.fmt("{s}{s}.", .{ parent.dep_prefix, dep_name }), |
| 317 | .modules = std.StringArrayHashMap(*Module).init(allocator), | 329 | .modules = std.StringArrayHashMap(*Module).init(allocator), |
| 318 | }; | 330 | }; |
| 319 | try child.top_level_steps.append(&child.install_tls); | 331 | try child.top_level_steps.put(allocator, child.install_tls.step.name, &child.install_tls); |
| 320 | try child.top_level_steps.append(&child.uninstall_tls); | 332 | try child.top_level_steps.put(allocator, child.uninstall_tls.step.name, &child.uninstall_tls); |
| 321 | child.default_step = &child.install_tls.step; | 333 | child.default_step = &child.install_tls.step; |
| 322 | return child; | 334 | return child; |
| 323 | } | 335 | } |
| ... | @@ -372,27 +384,24 @@ fn applyArgs(b: *Build, args: anytype) !void { | ... | @@ -372,27 +384,24 @@ fn applyArgs(b: *Build, args: anytype) !void { |
| 372 | }, | 384 | }, |
| 373 | } | 385 | } |
| 374 | } | 386 | } |
| 375 | const Hasher = std.crypto.auth.siphash.SipHash128(1, 3); | 387 | |
| 388 | // Create an installation directory local to this package. This will be used when | ||
| 389 | // dependant packages require a standard prefix, such as include directories for C headers. | ||
| 390 | var hash = b.cache.hash; | ||
| 376 | // Random bytes to make unique. Refresh this with new random bytes when | 391 | // Random bytes to make unique. Refresh this with new random bytes when |
| 377 | // implementation is modified in a non-backwards-compatible way. | 392 | // implementation is modified in a non-backwards-compatible way. |
| 378 | var hash = Hasher.init("ZaEsvQ5ClaA2IdH9"); | 393 | hash.add(@as(u32, 0xd8cb0055)); |
| 379 | hash.update(b.dep_prefix); | 394 | hash.addBytes(b.dep_prefix); |
| 380 | // TODO additionally update the hash with `args`. | 395 | // TODO additionally update the hash with `args`. |
| 381 | 396 | const digest = hash.final(); | |
| 382 | var digest: [16]u8 = undefined; | 397 | const install_prefix = try b.cache_root.join(b.allocator, &.{ "i", &digest }); |
| 383 | hash.final(&digest); | ||
| 384 | var hash_basename: [digest.len * 2]u8 = undefined; | ||
| 385 | _ = std.fmt.bufPrint(&hash_basename, "{s}", .{std.fmt.fmtSliceHexLower(&digest)}) catch | ||
| 386 | unreachable; | ||
| 387 | |||
| 388 | const install_prefix = try b.cache_root.join(b.allocator, &.{ "i", &hash_basename }); | ||
| 389 | b.resolveInstallPrefix(install_prefix, .{}); | 398 | b.resolveInstallPrefix(install_prefix, .{}); |
| 390 | } | 399 | } |
| 391 | 400 | ||
| 392 | pub fn destroy(self: *Build) void { | 401 | pub fn destroy(b: *Build) void { |
| 393 | self.env_map.deinit(); | 402 | b.env_map.deinit(); |
| 394 | self.top_level_steps.deinit(); | 403 | b.top_level_steps.deinit(b.allocator); |
| 395 | self.allocator.destroy(self); | 404 | b.allocator.destroy(b); |
| 396 | } | 405 | } |
| 397 | 406 | ||
| 398 | /// This function is intended to be called by lib/build_runner.zig, not a build.zig file. | 407 | /// This function is intended to be called by lib/build_runner.zig, not a build.zig file. |
| ... | @@ -441,6 +450,7 @@ pub const ExecutableOptions = struct { | ... | @@ -441,6 +450,7 @@ pub const ExecutableOptions = struct { |
| 441 | target: CrossTarget = .{}, | 450 | target: CrossTarget = .{}, |
| 442 | optimize: std.builtin.Mode = .Debug, | 451 | optimize: std.builtin.Mode = .Debug, |
| 443 | linkage: ?CompileStep.Linkage = null, | 452 | linkage: ?CompileStep.Linkage = null, |
| 453 | max_rss: usize = 0, | ||
| 444 | }; | 454 | }; |
| 445 | 455 | ||
| 446 | pub fn addExecutable(b: *Build, options: ExecutableOptions) *CompileStep { | 456 | pub fn addExecutable(b: *Build, options: ExecutableOptions) *CompileStep { |
| ... | @@ -452,6 +462,7 @@ pub fn addExecutable(b: *Build, options: ExecutableOptions) *CompileStep { | ... | @@ -452,6 +462,7 @@ pub fn addExecutable(b: *Build, options: ExecutableOptions) *CompileStep { |
| 452 | .optimize = options.optimize, | 462 | .optimize = options.optimize, |
| 453 | .kind = .exe, | 463 | .kind = .exe, |
| 454 | .linkage = options.linkage, | 464 | .linkage = options.linkage, |
| 465 | .max_rss = options.max_rss, | ||
| 455 | }); | 466 | }); |
| 456 | } | 467 | } |
| 457 | 468 | ||
| ... | @@ -460,6 +471,7 @@ pub const ObjectOptions = struct { | ... | @@ -460,6 +471,7 @@ pub const ObjectOptions = struct { |
| 460 | root_source_file: ?FileSource = null, | 471 | root_source_file: ?FileSource = null, |
| 461 | target: CrossTarget, | 472 | target: CrossTarget, |
| 462 | optimize: std.builtin.Mode, | 473 | optimize: std.builtin.Mode, |
| 474 | max_rss: usize = 0, | ||
| 463 | }; | 475 | }; |
| 464 | 476 | ||
| 465 | pub fn addObject(b: *Build, options: ObjectOptions) *CompileStep { | 477 | pub fn addObject(b: *Build, options: ObjectOptions) *CompileStep { |
| ... | @@ -469,6 +481,7 @@ pub fn addObject(b: *Build, options: ObjectOptions) *CompileStep { | ... | @@ -469,6 +481,7 @@ pub fn addObject(b: *Build, options: ObjectOptions) *CompileStep { |
| 469 | .target = options.target, | 481 | .target = options.target, |
| 470 | .optimize = options.optimize, | 482 | .optimize = options.optimize, |
| 471 | .kind = .obj, | 483 | .kind = .obj, |
| 484 | .max_rss = options.max_rss, | ||
| 472 | }); | 485 | }); |
| 473 | } | 486 | } |
| 474 | 487 | ||
| ... | @@ -478,6 +491,7 @@ pub const SharedLibraryOptions = struct { | ... | @@ -478,6 +491,7 @@ pub const SharedLibraryOptions = struct { |
| 478 | version: ?std.builtin.Version = null, | 491 | version: ?std.builtin.Version = null, |
| 479 | target: CrossTarget, | 492 | target: CrossTarget, |
| 480 | optimize: std.builtin.Mode, | 493 | optimize: std.builtin.Mode, |
| 494 | max_rss: usize = 0, | ||
| 481 | }; | 495 | }; |
| 482 | 496 | ||
| 483 | pub fn addSharedLibrary(b: *Build, options: SharedLibraryOptions) *CompileStep { | 497 | pub fn addSharedLibrary(b: *Build, options: SharedLibraryOptions) *CompileStep { |
| ... | @@ -489,6 +503,7 @@ pub fn addSharedLibrary(b: *Build, options: SharedLibraryOptions) *CompileStep { | ... | @@ -489,6 +503,7 @@ pub fn addSharedLibrary(b: *Build, options: SharedLibraryOptions) *CompileStep { |
| 489 | .version = options.version, | 503 | .version = options.version, |
| 490 | .target = options.target, | 504 | .target = options.target, |
| 491 | .optimize = options.optimize, | 505 | .optimize = options.optimize, |
| 506 | .max_rss = options.max_rss, | ||
| 492 | }); | 507 | }); |
| 493 | } | 508 | } |
| 494 | 509 | ||
| ... | @@ -498,6 +513,7 @@ pub const StaticLibraryOptions = struct { | ... | @@ -498,6 +513,7 @@ pub const StaticLibraryOptions = struct { |
| 498 | target: CrossTarget, | 513 | target: CrossTarget, |
| 499 | optimize: std.builtin.Mode, | 514 | optimize: std.builtin.Mode, |
| 500 | version: ?std.builtin.Version = null, | 515 | version: ?std.builtin.Version = null, |
| 516 | max_rss: usize = 0, | ||
| 501 | }; | 517 | }; |
| 502 | 518 | ||
| 503 | pub fn addStaticLibrary(b: *Build, options: StaticLibraryOptions) *CompileStep { | 519 | pub fn addStaticLibrary(b: *Build, options: StaticLibraryOptions) *CompileStep { |
| ... | @@ -509,25 +525,27 @@ pub fn addStaticLibrary(b: *Build, options: StaticLibraryOptions) *CompileStep { | ... | @@ -509,25 +525,27 @@ pub fn addStaticLibrary(b: *Build, options: StaticLibraryOptions) *CompileStep { |
| 509 | .version = options.version, | 525 | .version = options.version, |
| 510 | .target = options.target, | 526 | .target = options.target, |
| 511 | .optimize = options.optimize, | 527 | .optimize = options.optimize, |
| 528 | .max_rss = options.max_rss, | ||
| 512 | }); | 529 | }); |
| 513 | } | 530 | } |
| 514 | 531 | ||
| 515 | pub const TestOptions = struct { | 532 | pub const TestOptions = struct { |
| 516 | name: []const u8 = "test", | 533 | name: []const u8 = "test", |
| 517 | kind: CompileStep.Kind = .@"test", | ||
| 518 | root_source_file: FileSource, | 534 | root_source_file: FileSource, |
| 519 | target: CrossTarget = .{}, | 535 | target: CrossTarget = .{}, |
| 520 | optimize: std.builtin.Mode = .Debug, | 536 | optimize: std.builtin.Mode = .Debug, |
| 521 | version: ?std.builtin.Version = null, | 537 | version: ?std.builtin.Version = null, |
| 538 | max_rss: usize = 0, | ||
| 522 | }; | 539 | }; |
| 523 | 540 | ||
| 524 | pub fn addTest(b: *Build, options: TestOptions) *CompileStep { | 541 | pub fn addTest(b: *Build, options: TestOptions) *CompileStep { |
| 525 | return CompileStep.create(b, .{ | 542 | return CompileStep.create(b, .{ |
| 526 | .name = options.name, | 543 | .name = options.name, |
| 527 | .kind = options.kind, | 544 | .kind = .@"test", |
| 528 | .root_source_file = options.root_source_file, | 545 | .root_source_file = options.root_source_file, |
| 529 | .target = options.target, | 546 | .target = options.target, |
| 530 | .optimize = options.optimize, | 547 | .optimize = options.optimize, |
| 548 | .max_rss = options.max_rss, | ||
| 531 | }); | 549 | }); |
| 532 | } | 550 | } |
| 533 | 551 | ||
| ... | @@ -536,6 +554,7 @@ pub const AssemblyOptions = struct { | ... | @@ -536,6 +554,7 @@ pub const AssemblyOptions = struct { |
| 536 | source_file: FileSource, | 554 | source_file: FileSource, |
| 537 | target: CrossTarget, | 555 | target: CrossTarget, |
| 538 | optimize: std.builtin.Mode, | 556 | optimize: std.builtin.Mode, |
| 557 | max_rss: usize = 0, | ||
| 539 | }; | 558 | }; |
| 540 | 559 | ||
| 541 | pub fn addAssembly(b: *Build, options: AssemblyOptions) *CompileStep { | 560 | pub fn addAssembly(b: *Build, options: AssemblyOptions) *CompileStep { |
| ... | @@ -545,6 +564,7 @@ pub fn addAssembly(b: *Build, options: AssemblyOptions) *CompileStep { | ... | @@ -545,6 +564,7 @@ pub fn addAssembly(b: *Build, options: AssemblyOptions) *CompileStep { |
| 545 | .root_source_file = null, | 564 | .root_source_file = null, |
| 546 | .target = options.target, | 565 | .target = options.target, |
| 547 | .optimize = options.optimize, | 566 | .optimize = options.optimize, |
| 567 | .max_rss = options.max_rss, | ||
| 548 | }); | 568 | }); |
| 549 | obj_step.addAssemblyFileSource(options.source_file.dupe(b)); | 569 | obj_step.addAssemblyFileSource(options.source_file.dupe(b)); |
| 550 | return obj_step; | 570 | return obj_step; |
| ... | @@ -605,16 +625,15 @@ pub fn addSystemCommand(self: *Build, argv: []const []const u8) *RunStep { | ... | @@ -605,16 +625,15 @@ pub fn addSystemCommand(self: *Build, argv: []const []const u8) *RunStep { |
| 605 | /// Creates a `RunStep` with an executable built with `addExecutable`. | 625 | /// Creates a `RunStep` with an executable built with `addExecutable`. |
| 606 | /// Add command line arguments with methods of `RunStep`. | 626 | /// Add command line arguments with methods of `RunStep`. |
| 607 | pub fn addRunArtifact(b: *Build, exe: *CompileStep) *RunStep { | 627 | pub fn addRunArtifact(b: *Build, exe: *CompileStep) *RunStep { |
| 608 | assert(exe.kind == .exe or exe.kind == .test_exe); | ||
| 609 | |||
| 610 | // It doesn't have to be native. We catch that if you actually try to run it. | 628 | // It doesn't have to be native. We catch that if you actually try to run it. |
| 611 | // Consider that this is declarative; the run step may not be run unless a user | 629 | // Consider that this is declarative; the run step may not be run unless a user |
| 612 | // option is supplied. | 630 | // option is supplied. |
| 613 | const run_step = RunStep.create(b, b.fmt("run {s}", .{exe.step.name})); | 631 | const run_step = RunStep.create(b, b.fmt("run {s}", .{exe.name})); |
| 614 | run_step.addArtifactArg(exe); | 632 | run_step.addArtifactArg(exe); |
| 615 | 633 | ||
| 616 | if (exe.kind == .test_exe) { | 634 | if (exe.kind == .@"test") { |
| 617 | run_step.addArg(b.zig_exe); | 635 | run_step.stdio = .zig_test; |
| 636 | run_step.addArgs(&.{"--listen=-"}); | ||
| 618 | } | 637 | } |
| 619 | 638 | ||
| 620 | if (exe.vcpkg_bin_path) |path| { | 639 | if (exe.vcpkg_bin_path) |path| { |
| ... | @@ -634,7 +653,11 @@ pub fn addConfigHeader( | ... | @@ -634,7 +653,11 @@ pub fn addConfigHeader( |
| 634 | options: ConfigHeaderStep.Options, | 653 | options: ConfigHeaderStep.Options, |
| 635 | values: anytype, | 654 | values: anytype, |
| 636 | ) *ConfigHeaderStep { | 655 | ) *ConfigHeaderStep { |
| 637 | const config_header_step = ConfigHeaderStep.create(b, options); | 656 | var options_copy = options; |
| 657 | if (options_copy.first_ret_addr == null) | ||
| 658 | options_copy.first_ret_addr = @returnAddress(); | ||
| 659 | |||
| 660 | const config_header_step = ConfigHeaderStep.create(b, options_copy); | ||
| 638 | config_header_step.addValues(values); | 661 | config_header_step.addValues(values); |
| 639 | return config_header_step; | 662 | return config_header_step; |
| 640 | } | 663 | } |
| ... | @@ -671,17 +694,8 @@ pub fn addWriteFile(self: *Build, file_path: []const u8, data: []const u8) *Writ | ... | @@ -671,17 +694,8 @@ pub fn addWriteFile(self: *Build, file_path: []const u8, data: []const u8) *Writ |
| 671 | return write_file_step; | 694 | return write_file_step; |
| 672 | } | 695 | } |
| 673 | 696 | ||
| 674 | pub fn addWriteFiles(self: *Build) *WriteFileStep { | 697 | pub fn addWriteFiles(b: *Build) *WriteFileStep { |
| 675 | const write_file_step = self.allocator.create(WriteFileStep) catch @panic("OOM"); | 698 | return WriteFileStep.create(b); |
| 676 | write_file_step.* = WriteFileStep.init(self); | ||
| 677 | return write_file_step; | ||
| 678 | } | ||
| 679 | |||
| 680 | pub fn addLog(self: *Build, comptime format: []const u8, args: anytype) *LogStep { | ||
| 681 | const data = self.fmt(format, args); | ||
| 682 | const log_step = self.allocator.create(LogStep) catch @panic("OOM"); | ||
| 683 | log_step.* = LogStep.init(self, data); | ||
| 684 | return log_step; | ||
| 685 | } | 699 | } |
| 686 | 700 | ||
| 687 | pub fn addRemoveDirTree(self: *Build, dir_path: []const u8) *RemoveDirStep { | 701 | pub fn addRemoveDirTree(self: *Build, dir_path: []const u8) *RemoveDirStep { |
| ... | @@ -690,32 +704,14 @@ pub fn addRemoveDirTree(self: *Build, dir_path: []const u8) *RemoveDirStep { | ... | @@ -690,32 +704,14 @@ pub fn addRemoveDirTree(self: *Build, dir_path: []const u8) *RemoveDirStep { |
| 690 | return remove_dir_step; | 704 | return remove_dir_step; |
| 691 | } | 705 | } |
| 692 | 706 | ||
| 693 | pub fn addFmt(self: *Build, paths: []const []const u8) *FmtStep { | 707 | pub fn addFmt(b: *Build, options: FmtStep.Options) *FmtStep { |
| 694 | return FmtStep.create(self, paths); | 708 | return FmtStep.create(b, options); |
| 695 | } | 709 | } |
| 696 | 710 | ||
| 697 | pub fn addTranslateC(self: *Build, options: TranslateCStep.Options) *TranslateCStep { | 711 | pub fn addTranslateC(self: *Build, options: TranslateCStep.Options) *TranslateCStep { |
| 698 | return TranslateCStep.create(self, options); | 712 | return TranslateCStep.create(self, options); |
| 699 | } | 713 | } |
| 700 | 714 | ||
| 701 | pub fn make(self: *Build, step_names: []const []const u8) !void { | ||
| 702 | var wanted_steps = ArrayList(*Step).init(self.allocator); | ||
| 703 | defer wanted_steps.deinit(); | ||
| 704 | |||
| 705 | if (step_names.len == 0) { | ||
| 706 | try wanted_steps.append(self.default_step); | ||
| 707 | } else { | ||
| 708 | for (step_names) |step_name| { | ||
| 709 | const s = try self.getTopLevelStepByName(step_name); | ||
| 710 | try wanted_steps.append(s); | ||
| 711 | } | ||
| 712 | } | ||
| 713 | |||
| 714 | for (wanted_steps.items) |s| { | ||
| 715 | try self.makeOneStep(s); | ||
| 716 | } | ||
| 717 | } | ||
| 718 | |||
| 719 | pub fn getInstallStep(self: *Build) *Step { | 715 | pub fn getInstallStep(self: *Build) *Step { |
| 720 | return &self.install_tls.step; | 716 | return &self.install_tls.step; |
| 721 | } | 717 | } |
| ... | @@ -724,7 +720,8 @@ pub fn getUninstallStep(self: *Build) *Step { | ... | @@ -724,7 +720,8 @@ pub fn getUninstallStep(self: *Build) *Step { |
| 724 | return &self.uninstall_tls.step; | 720 | return &self.uninstall_tls.step; |
| 725 | } | 721 | } |
| 726 | 722 | ||
| 727 | fn makeUninstall(uninstall_step: *Step) anyerror!void { | 723 | fn makeUninstall(uninstall_step: *Step, prog_node: *std.Progress.Node) anyerror!void { |
| 724 | _ = prog_node; | ||
| 728 | const uninstall_tls = @fieldParentPtr(TopLevelStep, "step", uninstall_step); | 725 | const uninstall_tls = @fieldParentPtr(TopLevelStep, "step", uninstall_step); |
| 729 | const self = @fieldParentPtr(Build, "uninstall_tls", uninstall_tls); | 726 | const self = @fieldParentPtr(Build, "uninstall_tls", uninstall_tls); |
| 730 | 727 | ||
| ... | @@ -739,37 +736,6 @@ fn makeUninstall(uninstall_step: *Step) anyerror!void { | ... | @@ -739,37 +736,6 @@ fn makeUninstall(uninstall_step: *Step) anyerror!void { |
| 739 | // TODO remove empty directories | 736 | // TODO remove empty directories |
| 740 | } | 737 | } |
| 741 | 738 | ||
| 742 | fn makeOneStep(self: *Build, s: *Step) anyerror!void { | ||
| 743 | if (s.loop_flag) { | ||
| 744 | log.err("Dependency loop detected:\n {s}", .{s.name}); | ||
| 745 | return error.DependencyLoopDetected; | ||
| 746 | } | ||
| 747 | s.loop_flag = true; | ||
| 748 | |||
| 749 | for (s.dependencies.items) |dep| { | ||
| 750 | self.makeOneStep(dep) catch |err| { | ||
| 751 | if (err == error.DependencyLoopDetected) { | ||
| 752 | log.err(" {s}", .{s.name}); | ||
| 753 | } | ||
| 754 | return err; | ||
| 755 | }; | ||
| 756 | } | ||
| 757 | |||
| 758 | s.loop_flag = false; | ||
| 759 | |||
| 760 | try s.make(); | ||
| 761 | } | ||
| 762 | |||
| 763 | fn getTopLevelStepByName(self: *Build, name: []const u8) !*Step { | ||
| 764 | for (self.top_level_steps.items) |top_level_step| { | ||
| 765 | if (mem.eql(u8, top_level_step.step.name, name)) { | ||
| 766 | return &top_level_step.step; | ||
| 767 | } | ||
| 768 | } | ||
| 769 | log.err("Cannot run step '{s}' because it does not exist", .{name}); | ||
| 770 | return error.InvalidStepName; | ||
| 771 | } | ||
| 772 | |||
| 773 | pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_raw: []const u8) ?T { | 739 | pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_raw: []const u8) ?T { |
| 774 | const name = self.dupe(name_raw); | 740 | const name = self.dupe(name_raw); |
| 775 | const description = self.dupe(description_raw); | 741 | const description = self.dupe(description_raw); |
| ... | @@ -906,11 +872,15 @@ pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_ | ... | @@ -906,11 +872,15 @@ pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_ |
| 906 | 872 | ||
| 907 | pub fn step(self: *Build, name: []const u8, description: []const u8) *Step { | 873 | pub fn step(self: *Build, name: []const u8, description: []const u8) *Step { |
| 908 | const step_info = self.allocator.create(TopLevelStep) catch @panic("OOM"); | 874 | const step_info = self.allocator.create(TopLevelStep) catch @panic("OOM"); |
| 909 | step_info.* = TopLevelStep{ | 875 | step_info.* = .{ |
| 910 | .step = Step.initNoOp(.top_level, name, self.allocator), | 876 | .step = Step.init(.{ |
| 877 | .id = .top_level, | ||
| 878 | .name = name, | ||
| 879 | .owner = self, | ||
| 880 | }), | ||
| 911 | .description = self.dupe(description), | 881 | .description = self.dupe(description), |
| 912 | }; | 882 | }; |
| 913 | self.top_level_steps.append(step_info) catch @panic("OOM"); | 883 | self.top_level_steps.put(self.allocator, step_info.step.name, step_info) catch @panic("OOM"); |
| 914 | return &step_info.step; | 884 | return &step_info.step; |
| 915 | } | 885 | } |
| 916 | 886 | ||
| ... | @@ -1178,50 +1148,18 @@ pub fn validateUserInputDidItFail(self: *Build) bool { | ... | @@ -1178,50 +1148,18 @@ pub fn validateUserInputDidItFail(self: *Build) bool { |
| 1178 | return self.invalid_user_input; | 1148 | return self.invalid_user_input; |
| 1179 | } | 1149 | } |
| 1180 | 1150 | ||
| 1181 | pub fn spawnChild(self: *Build, argv: []const []const u8) !void { | 1151 | fn allocPrintCmd(ally: Allocator, opt_cwd: ?[]const u8, argv: []const []const u8) ![]u8 { |
| 1182 | return self.spawnChildEnvMap(null, self.env_map, argv); | 1152 | var buf = ArrayList(u8).init(ally); |
| 1183 | } | 1153 | if (opt_cwd) |cwd| try buf.writer().print("cd {s} && ", .{cwd}); |
| 1184 | |||
| 1185 | fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void { | ||
| 1186 | if (cwd) |yes_cwd| std.debug.print("cd {s} && ", .{yes_cwd}); | ||
| 1187 | for (argv) |arg| { | 1154 | for (argv) |arg| { |
| 1188 | std.debug.print("{s} ", .{arg}); | 1155 | try buf.writer().print("{s} ", .{arg}); |
| 1189 | } | 1156 | } |
| 1190 | std.debug.print("\n", .{}); | 1157 | return buf.toOwnedSlice(); |
| 1191 | } | 1158 | } |
| 1192 | 1159 | ||
| 1193 | pub fn spawnChildEnvMap(self: *Build, cwd: ?[]const u8, env_map: *const EnvMap, argv: []const []const u8) !void { | 1160 | fn printCmd(ally: Allocator, cwd: ?[]const u8, argv: []const []const u8) void { |
| 1194 | if (self.verbose) { | 1161 | const text = allocPrintCmd(ally, cwd, argv) catch @panic("OOM"); |
| 1195 | printCmd(cwd, argv); | 1162 | std.debug.print("{s}\n", .{text}); |
| 1196 | } | ||
| 1197 | |||
| 1198 | if (!std.process.can_spawn) | ||
| 1199 | return error.ExecNotSupported; | ||
| 1200 | |||
| 1201 | var child = std.ChildProcess.init(argv, self.allocator); | ||
| 1202 | child.cwd = cwd; | ||
| 1203 | child.env_map = env_map; | ||
| 1204 | |||
| 1205 | const term = child.spawnAndWait() catch |err| { | ||
| 1206 | log.err("Unable to spawn {s}: {s}", .{ argv[0], @errorName(err) }); | ||
| 1207 | return err; | ||
| 1208 | }; | ||
| 1209 | |||
| 1210 | switch (term) { | ||
| 1211 | .Exited => |code| { | ||
| 1212 | if (code != 0) { | ||
| 1213 | log.err("The following command exited with error code {}:", .{code}); | ||
| 1214 | printCmd(cwd, argv); | ||
| 1215 | return error.UncleanExit; | ||
| 1216 | } | ||
| 1217 | }, | ||
| 1218 | else => { | ||
| 1219 | log.err("The following command terminated unexpectedly:", .{}); | ||
| 1220 | printCmd(cwd, argv); | ||
| 1221 | |||
| 1222 | return error.UncleanExit; | ||
| 1223 | }, | ||
| 1224 | } | ||
| 1225 | } | 1163 | } |
| 1226 | 1164 | ||
| 1227 | pub fn installArtifact(self: *Build, artifact: *CompileStep) void { | 1165 | pub fn installArtifact(self: *Build, artifact: *CompileStep) void { |
| ... | @@ -1280,12 +1218,7 @@ pub fn addInstallFileWithDir( | ... | @@ -1280,12 +1218,7 @@ pub fn addInstallFileWithDir( |
| 1280 | install_dir: InstallDir, | 1218 | install_dir: InstallDir, |
| 1281 | dest_rel_path: []const u8, | 1219 | dest_rel_path: []const u8, |
| 1282 | ) *InstallFileStep { | 1220 | ) *InstallFileStep { |
| 1283 | if (dest_rel_path.len == 0) { | 1221 | return InstallFileStep.create(self, source.dupe(self), install_dir, dest_rel_path); |
| 1284 | panic("dest_rel_path must be non-empty", .{}); | ||
| 1285 | } | ||
| 1286 | const install_step = self.allocator.create(InstallFileStep) catch @panic("OOM"); | ||
| 1287 | install_step.* = InstallFileStep.init(self, source.dupe(self), install_dir, dest_rel_path); | ||
| 1288 | return install_step; | ||
| 1289 | } | 1222 | } |
| 1290 | 1223 | ||
| 1291 | pub fn addInstallDirectory(self: *Build, options: InstallDirectoryOptions) *InstallDirStep { | 1224 | pub fn addInstallDirectory(self: *Build, options: InstallDirectoryOptions) *InstallDirStep { |
| ... | @@ -1294,6 +1227,14 @@ pub fn addInstallDirectory(self: *Build, options: InstallDirectoryOptions) *Inst | ... | @@ -1294,6 +1227,14 @@ pub fn addInstallDirectory(self: *Build, options: InstallDirectoryOptions) *Inst |
| 1294 | return install_step; | 1227 | return install_step; |
| 1295 | } | 1228 | } |
| 1296 | 1229 | ||
| 1230 | pub fn addCheckFile( | ||
| 1231 | b: *Build, | ||
| 1232 | file_source: FileSource, | ||
| 1233 | options: CheckFileStep.Options, | ||
| 1234 | ) *CheckFileStep { | ||
| 1235 | return CheckFileStep.create(b, file_source, options); | ||
| 1236 | } | ||
| 1237 | |||
| 1297 | pub fn pushInstalledFile(self: *Build, dir: InstallDir, dest_rel_path: []const u8) void { | 1238 | pub fn pushInstalledFile(self: *Build, dir: InstallDir, dest_rel_path: []const u8) void { |
| 1298 | const file = InstalledFile{ | 1239 | const file = InstalledFile{ |
| 1299 | .dir = dir, | 1240 | .dir = dir, |
| ... | @@ -1302,18 +1243,6 @@ pub fn pushInstalledFile(self: *Build, dir: InstallDir, dest_rel_path: []const u | ... | @@ -1302,18 +1243,6 @@ pub fn pushInstalledFile(self: *Build, dir: InstallDir, dest_rel_path: []const u |
| 1302 | self.installed_files.append(file.dupe(self)) catch @panic("OOM"); | 1243 | self.installed_files.append(file.dupe(self)) catch @panic("OOM"); |
| 1303 | } | 1244 | } |
| 1304 | 1245 | ||
| 1305 | pub fn updateFile(self: *Build, source_path: []const u8, dest_path: []const u8) !void { | ||
| 1306 | if (self.verbose) { | ||
| 1307 | log.info("cp {s} {s} ", .{ source_path, dest_path }); | ||
| 1308 | } | ||
| 1309 | const cwd = fs.cwd(); | ||
| 1310 | const prev_status = try fs.Dir.updateFile(cwd, source_path, cwd, dest_path, .{}); | ||
| 1311 | if (self.verbose) switch (prev_status) { | ||
| 1312 | .stale => log.info("# installed", .{}), | ||
| 1313 | .fresh => log.info("# up-to-date", .{}), | ||
| 1314 | }; | ||
| 1315 | } | ||
| 1316 | |||
| 1317 | pub fn truncateFile(self: *Build, dest_path: []const u8) !void { | 1246 | pub fn truncateFile(self: *Build, dest_path: []const u8) !void { |
| 1318 | if (self.verbose) { | 1247 | if (self.verbose) { |
| 1319 | log.info("truncate {s}", .{dest_path}); | 1248 | log.info("truncate {s}", .{dest_path}); |
| ... | @@ -1397,7 +1326,7 @@ pub fn execAllowFail( | ... | @@ -1397,7 +1326,7 @@ pub fn execAllowFail( |
| 1397 | ) ExecError![]u8 { | 1326 | ) ExecError![]u8 { |
| 1398 | assert(argv.len != 0); | 1327 | assert(argv.len != 0); |
| 1399 | 1328 | ||
| 1400 | if (!std.process.can_spawn) | 1329 | if (!process.can_spawn) |
| 1401 | return error.ExecNotSupported; | 1330 | return error.ExecNotSupported; |
| 1402 | 1331 | ||
| 1403 | const max_output_size = 400 * 1024; | 1332 | const max_output_size = 400 * 1024; |
| ... | @@ -1430,59 +1359,27 @@ pub fn execAllowFail( | ... | @@ -1430,59 +1359,27 @@ pub fn execAllowFail( |
| 1430 | } | 1359 | } |
| 1431 | } | 1360 | } |
| 1432 | 1361 | ||
| 1433 | pub fn execFromStep(self: *Build, argv: []const []const u8, src_step: ?*Step) ![]u8 { | 1362 | /// This is a helper function to be called from build.zig scripts, *not* from |
| 1434 | assert(argv.len != 0); | 1363 | /// inside step make() functions. If any errors occur, it fails the build with |
| 1435 | 1364 | /// a helpful message. | |
| 1436 | if (self.verbose) { | 1365 | pub fn exec(b: *Build, argv: []const []const u8) []u8 { |
| 1437 | printCmd(null, argv); | 1366 | if (!process.can_spawn) { |
| 1438 | } | 1367 | std.debug.print("unable to spawn the following command: cannot spawn child process\n{s}\n", .{ |
| 1439 | 1368 | try allocPrintCmd(b.allocator, null, argv), | |
| 1440 | if (!std.process.can_spawn) { | 1369 | }); |
| 1441 | if (src_step) |s| log.err("{s}...", .{s.name}); | 1370 | process.exit(1); |
| 1442 | log.err("Unable to spawn the following command: cannot spawn child process", .{}); | ||
| 1443 | printCmd(null, argv); | ||
| 1444 | std.os.abort(); | ||
| 1445 | } | 1371 | } |
| 1446 | 1372 | ||
| 1447 | var code: u8 = undefined; | 1373 | var code: u8 = undefined; |
| 1448 | return self.execAllowFail(argv, &code, .Inherit) catch |err| switch (err) { | 1374 | return b.execAllowFail(argv, &code, .Inherit) catch |err| { |
| 1449 | error.ExecNotSupported => { | 1375 | const printed_cmd = allocPrintCmd(b.allocator, null, argv) catch @panic("OOM"); |
| 1450 | if (src_step) |s| log.err("{s}...", .{s.name}); | 1376 | std.debug.print("unable to spawn the following command: {s}\n{s}\n", .{ |
| 1451 | log.err("Unable to spawn the following command: cannot spawn child process", .{}); | 1377 | @errorName(err), printed_cmd, |
| 1452 | printCmd(null, argv); | 1378 | }); |
| 1453 | std.os.abort(); | 1379 | process.exit(1); |
| 1454 | }, | ||
| 1455 | error.FileNotFound => { | ||
| 1456 | if (src_step) |s| log.err("{s}...", .{s.name}); | ||
| 1457 | log.err("Unable to spawn the following command: file not found", .{}); | ||
| 1458 | printCmd(null, argv); | ||
| 1459 | std.os.exit(@truncate(u8, code)); | ||
| 1460 | }, | ||
| 1461 | error.ExitCodeFailure => { | ||
| 1462 | if (src_step) |s| log.err("{s}...", .{s.name}); | ||
| 1463 | if (self.prominent_compile_errors) { | ||
| 1464 | log.err("The step exited with error code {d}", .{code}); | ||
| 1465 | } else { | ||
| 1466 | log.err("The following command exited with error code {d}:", .{code}); | ||
| 1467 | printCmd(null, argv); | ||
| 1468 | } | ||
| 1469 | |||
| 1470 | std.os.exit(@truncate(u8, code)); | ||
| 1471 | }, | ||
| 1472 | error.ProcessTerminated => { | ||
| 1473 | if (src_step) |s| log.err("{s}...", .{s.name}); | ||
| 1474 | log.err("The following command terminated unexpectedly:", .{}); | ||
| 1475 | printCmd(null, argv); | ||
| 1476 | std.os.exit(@truncate(u8, code)); | ||
| 1477 | }, | ||
| 1478 | else => |e| return e, | ||
| 1479 | }; | 1380 | }; |
| 1480 | } | 1381 | } |
| 1481 | 1382 | ||
| 1482 | pub fn exec(self: *Build, argv: []const []const u8) ![]u8 { | ||
| 1483 | return self.execFromStep(argv, null); | ||
| 1484 | } | ||
| 1485 | |||
| 1486 | pub fn addSearchPrefix(self: *Build, search_prefix: []const u8) void { | 1383 | pub fn addSearchPrefix(self: *Build, search_prefix: []const u8) void { |
| 1487 | self.search_prefixes.append(self.dupePath(search_prefix)) catch @panic("OOM"); | 1384 | self.search_prefixes.append(self.dupePath(search_prefix)) catch @panic("OOM"); |
| 1488 | } | 1385 | } |
| ... | @@ -1547,10 +1444,29 @@ pub fn dependency(b: *Build, name: []const u8, args: anytype) *Dependency { | ... | @@ -1547,10 +1444,29 @@ pub fn dependency(b: *Build, name: []const u8, args: anytype) *Dependency { |
| 1547 | 1444 | ||
| 1548 | const full_path = b.pathFromRoot("build.zig.zon"); | 1445 | const full_path = b.pathFromRoot("build.zig.zon"); |
| 1549 | std.debug.print("no dependency named '{s}' in '{s}'. All packages used in build.zig must be declared in this file.\n", .{ name, full_path }); | 1446 | std.debug.print("no dependency named '{s}' in '{s}'. All packages used in build.zig must be declared in this file.\n", .{ name, full_path }); |
| 1550 | std.process.exit(1); | 1447 | process.exit(1); |
| 1448 | } | ||
| 1449 | |||
| 1450 | pub fn anonymousDependency( | ||
| 1451 | b: *Build, | ||
| 1452 | /// The path to the directory containing the dependency's build.zig file, | ||
| 1453 | /// relative to the current package's build.zig. | ||
| 1454 | relative_build_root: []const u8, | ||
| 1455 | /// A direct `@import` of the build.zig of the dependency. | ||
| 1456 | comptime build_zig: type, | ||
| 1457 | args: anytype, | ||
| 1458 | ) *Dependency { | ||
| 1459 | const arena = b.allocator; | ||
| 1460 | const build_root = b.build_root.join(arena, &.{relative_build_root}) catch @panic("OOM"); | ||
| 1461 | const name = arena.dupe(u8, relative_build_root) catch @panic("OOM"); | ||
| 1462 | for (name) |*byte| switch (byte.*) { | ||
| 1463 | '/', '\\' => byte.* = '.', | ||
| 1464 | else => continue, | ||
| 1465 | }; | ||
| 1466 | return dependencyInner(b, name, build_root, build_zig, args); | ||
| 1551 | } | 1467 | } |
| 1552 | 1468 | ||
| 1553 | fn dependencyInner( | 1469 | pub fn dependencyInner( |
| 1554 | b: *Build, | 1470 | b: *Build, |
| 1555 | name: []const u8, | 1471 | name: []const u8, |
| 1556 | build_root_string: []const u8, | 1472 | build_root_string: []const u8, |
| ... | @@ -1563,7 +1479,7 @@ fn dependencyInner( | ... | @@ -1563,7 +1479,7 @@ fn dependencyInner( |
| 1563 | std.debug.print("unable to open '{s}': {s}\n", .{ | 1479 | std.debug.print("unable to open '{s}': {s}\n", .{ |
| 1564 | build_root_string, @errorName(err), | 1480 | build_root_string, @errorName(err), |
| 1565 | }); | 1481 | }); |
| 1566 | std.process.exit(1); | 1482 | process.exit(1); |
| 1567 | }, | 1483 | }, |
| 1568 | }; | 1484 | }; |
| 1569 | const sub_builder = b.createChild(name, build_root, args) catch @panic("unhandled error"); | 1485 | const sub_builder = b.createChild(name, build_root, args) catch @panic("unhandled error"); |
| ... | @@ -1607,7 +1523,7 @@ pub const GeneratedFile = struct { | ... | @@ -1607,7 +1523,7 @@ pub const GeneratedFile = struct { |
| 1607 | 1523 | ||
| 1608 | pub fn getPath(self: GeneratedFile) []const u8 { | 1524 | pub fn getPath(self: GeneratedFile) []const u8 { |
| 1609 | return self.path orelse std.debug.panic( | 1525 | return self.path orelse std.debug.panic( |
| 1610 | "getPath() was called on a GeneratedFile that wasn't build yet. Is there a missing Step dependency on step '{s}'?", | 1526 | "getPath() was called on a GeneratedFile that wasn't built yet. Is there a missing Step dependency on step '{s}'?", |
| 1611 | .{self.step.name}, | 1527 | .{self.step.name}, |
| 1612 | ); | 1528 | ); |
| 1613 | } | 1529 | } |
| ... | @@ -1647,12 +1563,23 @@ pub const FileSource = union(enum) { | ... | @@ -1647,12 +1563,23 @@ pub const FileSource = union(enum) { |
| 1647 | } | 1563 | } |
| 1648 | 1564 | ||
| 1649 | /// Should only be called during make(), returns a path relative to the build root or absolute. | 1565 | /// Should only be called during make(), returns a path relative to the build root or absolute. |
| 1650 | pub fn getPath(self: FileSource, builder: *Build) []const u8 { | 1566 | pub fn getPath(self: FileSource, src_builder: *Build) []const u8 { |
| 1651 | const path = switch (self) { | 1567 | return getPath2(self, src_builder, null); |
| 1652 | .path => |p| builder.pathFromRoot(p), | 1568 | } |
| 1653 | .generated => |gen| gen.getPath(), | 1569 | |
| 1654 | }; | 1570 | /// Should only be called during make(), returns a path relative to the build root or absolute. |
| 1655 | return path; | 1571 | /// asking_step is only used for debugging purposes; it's the step being run that is asking for |
| 1572 | /// the path. | ||
| 1573 | pub fn getPath2(self: FileSource, src_builder: *Build, asking_step: ?*Step) []const u8 { | ||
| 1574 | switch (self) { | ||
| 1575 | .path => |p| return src_builder.pathFromRoot(p), | ||
| 1576 | .generated => |gen| return gen.path orelse { | ||
| 1577 | std.debug.getStderrMutex().lock(); | ||
| 1578 | const stderr = std.io.getStdErr(); | ||
| 1579 | dumpBadGetPathHelp(gen.step, stderr, src_builder, asking_step) catch {}; | ||
| 1580 | @panic("misconfigured build script"); | ||
| 1581 | }, | ||
| 1582 | } | ||
| 1656 | } | 1583 | } |
| 1657 | 1584 | ||
| 1658 | /// Duplicates the file source for a given builder. | 1585 | /// Duplicates the file source for a given builder. |
| ... | @@ -1664,6 +1591,54 @@ pub const FileSource = union(enum) { | ... | @@ -1664,6 +1591,54 @@ pub const FileSource = union(enum) { |
| 1664 | } | 1591 | } |
| 1665 | }; | 1592 | }; |
| 1666 | 1593 | ||
| 1594 | /// In this function the stderr mutex has already been locked. | ||
| 1595 | fn dumpBadGetPathHelp( | ||
| 1596 | s: *Step, | ||
| 1597 | stderr: fs.File, | ||
| 1598 | src_builder: *Build, | ||
| 1599 | asking_step: ?*Step, | ||
| 1600 | ) anyerror!void { | ||
| 1601 | const w = stderr.writer(); | ||
| 1602 | try w.print( | ||
| 1603 | \\getPath() was called on a GeneratedFile that wasn't built yet. | ||
| 1604 | \\ source package path: {s} | ||
| 1605 | \\ Is there a missing Step dependency on step '{s}'? | ||
| 1606 | \\ | ||
| 1607 | , .{ | ||
| 1608 | src_builder.build_root.path orelse ".", | ||
| 1609 | s.name, | ||
| 1610 | }); | ||
| 1611 | |||
| 1612 | const tty_config = std.debug.detectTTYConfig(stderr); | ||
| 1613 | tty_config.setColor(w, .Red) catch {}; | ||
| 1614 | try stderr.writeAll(" The step was created by this stack trace:\n"); | ||
| 1615 | tty_config.setColor(w, .Reset) catch {}; | ||
| 1616 | |||
| 1617 | const debug_info = std.debug.getSelfDebugInfo() catch |err| { | ||
| 1618 | try w.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{@errorName(err)}); | ||
| 1619 | return; | ||
| 1620 | }; | ||
| 1621 | const ally = debug_info.allocator; | ||
| 1622 | std.debug.writeStackTrace(s.getStackTrace(), w, ally, debug_info, tty_config) catch |err| { | ||
| 1623 | try stderr.writer().print("Unable to dump stack trace: {s}\n", .{@errorName(err)}); | ||
| 1624 | return; | ||
| 1625 | }; | ||
| 1626 | if (asking_step) |as| { | ||
| 1627 | tty_config.setColor(w, .Red) catch {}; | ||
| 1628 | try stderr.writeAll(" The step that is missing a dependency on the above step was created by this stack trace:\n"); | ||
| 1629 | tty_config.setColor(w, .Reset) catch {}; | ||
| 1630 | |||
| 1631 | std.debug.writeStackTrace(as.getStackTrace(), w, ally, debug_info, tty_config) catch |err| { | ||
| 1632 | try stderr.writer().print("Unable to dump stack trace: {s}\n", .{@errorName(err)}); | ||
| 1633 | return; | ||
| 1634 | }; | ||
| 1635 | } | ||
| 1636 | |||
| 1637 | tty_config.setColor(w, .Red) catch {}; | ||
| 1638 | try stderr.writeAll(" Hope that helps. Proceeding to panic.\n"); | ||
| 1639 | tty_config.setColor(w, .Reset) catch {}; | ||
| 1640 | } | ||
| 1641 | |||
| 1667 | /// Allocates a new string for assigning a value to a named macro. | 1642 | /// Allocates a new string for assigning a value to a named macro. |
| 1668 | /// If the value is omitted, it is set to 1. | 1643 | /// If the value is omitted, it is set to 1. |
| 1669 | /// `name` and `value` need not live longer than the function call. | 1644 | /// `name` and `value` need not live longer than the function call. |
| ... | @@ -1703,9 +1678,7 @@ pub const InstallDir = union(enum) { | ... | @@ -1703,9 +1678,7 @@ pub const InstallDir = union(enum) { |
| 1703 | /// Duplicates the install directory including the path if set to custom. | 1678 | /// Duplicates the install directory including the path if set to custom. |
| 1704 | pub fn dupe(self: InstallDir, builder: *Build) InstallDir { | 1679 | pub fn dupe(self: InstallDir, builder: *Build) InstallDir { |
| 1705 | if (self == .custom) { | 1680 | if (self == .custom) { |
| 1706 | // Written with this temporary to avoid RLS problems | 1681 | return .{ .custom = builder.dupe(self.custom) }; |
| 1707 | const duped_path = builder.dupe(self.custom); | ||
| 1708 | return .{ .custom = duped_path }; | ||
| 1709 | } else { | 1682 | } else { |
| 1710 | return self; | 1683 | return self; |
| 1711 | } | 1684 | } |
| ... | @@ -1753,17 +1726,45 @@ pub fn serializeCpu(allocator: Allocator, cpu: std.Target.Cpu) ![]const u8 { | ... | @@ -1753,17 +1726,45 @@ pub fn serializeCpu(allocator: Allocator, cpu: std.Target.Cpu) ![]const u8 { |
| 1753 | } | 1726 | } |
| 1754 | } | 1727 | } |
| 1755 | 1728 | ||
| 1729 | /// This function is intended to be called in the `configure` phase only. | ||
| 1730 | /// It returns an absolute directory path, which is potentially going to be a | ||
| 1731 | /// source of API breakage in the future, so keep that in mind when using this | ||
| 1732 | /// function. | ||
| 1733 | pub fn makeTempPath(b: *Build) []const u8 { | ||
| 1734 | const rand_int = std.crypto.random.int(u64); | ||
| 1735 | const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ hex64(rand_int); | ||
| 1736 | const result_path = b.cache_root.join(b.allocator, &.{tmp_dir_sub_path}) catch @panic("OOM"); | ||
| 1737 | fs.cwd().makePath(result_path) catch |err| { | ||
| 1738 | std.debug.print("unable to make tmp path '{s}': {s}\n", .{ | ||
| 1739 | result_path, @errorName(err), | ||
| 1740 | }); | ||
| 1741 | }; | ||
| 1742 | return result_path; | ||
| 1743 | } | ||
| 1744 | |||
| 1745 | /// There are a few copies of this function in miscellaneous places. Would be nice to find | ||
| 1746 | /// a home for them. | ||
| 1747 | fn hex64(x: u64) [16]u8 { | ||
| 1748 | const hex_charset = "0123456789abcdef"; | ||
| 1749 | var result: [16]u8 = undefined; | ||
| 1750 | var i: usize = 0; | ||
| 1751 | while (i < 8) : (i += 1) { | ||
| 1752 | const byte = @truncate(u8, x >> @intCast(u6, 8 * i)); | ||
| 1753 | result[i * 2 + 0] = hex_charset[byte >> 4]; | ||
| 1754 | result[i * 2 + 1] = hex_charset[byte & 15]; | ||
| 1755 | } | ||
| 1756 | return result; | ||
| 1757 | } | ||
| 1758 | |||
| 1756 | test { | 1759 | test { |
| 1757 | _ = CheckFileStep; | 1760 | _ = CheckFileStep; |
| 1758 | _ = CheckObjectStep; | 1761 | _ = CheckObjectStep; |
| 1759 | _ = EmulatableRunStep; | ||
| 1760 | _ = FmtStep; | 1762 | _ = FmtStep; |
| 1761 | _ = InstallArtifactStep; | 1763 | _ = InstallArtifactStep; |
| 1762 | _ = InstallDirStep; | 1764 | _ = InstallDirStep; |
| 1763 | _ = InstallFileStep; | 1765 | _ = InstallFileStep; |
| 1764 | _ = ObjCopyStep; | 1766 | _ = ObjCopyStep; |
| 1765 | _ = CompileStep; | 1767 | _ = CompileStep; |
| 1766 | _ = LogStep; | ||
| 1767 | _ = OptionsStep; | 1768 | _ = OptionsStep; |
| 1768 | _ = RemoveDirStep; | 1769 | _ = RemoveDirStep; |
| 1769 | _ = RunStep; | 1770 | _ = RunStep; |
lib/std/Build/Cache.zig+74-61| ... | @@ -7,27 +7,27 @@ pub const Directory = struct { | ... | @@ -7,27 +7,27 @@ pub const Directory = struct { |
| 7 | /// directly, but it is needed when passing the directory to a child process. | 7 | /// directly, but it is needed when passing the directory to a child process. |
| 8 | /// `null` means cwd. | 8 | /// `null` means cwd. |
| 9 | path: ?[]const u8, | 9 | path: ?[]const u8, |
| 10 | handle: std.fs.Dir, | 10 | handle: fs.Dir, |
| 11 | 11 | ||
| 12 | pub fn join(self: Directory, allocator: Allocator, paths: []const []const u8) ![]u8 { | 12 | pub fn join(self: Directory, allocator: Allocator, paths: []const []const u8) ![]u8 { |
| 13 | if (self.path) |p| { | 13 | if (self.path) |p| { |
| 14 | // TODO clean way to do this with only 1 allocation | 14 | // TODO clean way to do this with only 1 allocation |
| 15 | const part2 = try std.fs.path.join(allocator, paths); | 15 | const part2 = try fs.path.join(allocator, paths); |
| 16 | defer allocator.free(part2); | 16 | defer allocator.free(part2); |
| 17 | return std.fs.path.join(allocator, &[_][]const u8{ p, part2 }); | 17 | return fs.path.join(allocator, &[_][]const u8{ p, part2 }); |
| 18 | } else { | 18 | } else { |
| 19 | return std.fs.path.join(allocator, paths); | 19 | return fs.path.join(allocator, paths); |
| 20 | } | 20 | } |
| 21 | } | 21 | } |
| 22 | 22 | ||
| 23 | pub fn joinZ(self: Directory, allocator: Allocator, paths: []const []const u8) ![:0]u8 { | 23 | pub fn joinZ(self: Directory, allocator: Allocator, paths: []const []const u8) ![:0]u8 { |
| 24 | if (self.path) |p| { | 24 | if (self.path) |p| { |
| 25 | // TODO clean way to do this with only 1 allocation | 25 | // TODO clean way to do this with only 1 allocation |
| 26 | const part2 = try std.fs.path.join(allocator, paths); | 26 | const part2 = try fs.path.join(allocator, paths); |
| 27 | defer allocator.free(part2); | 27 | defer allocator.free(part2); |
| 28 | return std.fs.path.joinZ(allocator, &[_][]const u8{ p, part2 }); | 28 | return fs.path.joinZ(allocator, &[_][]const u8{ p, part2 }); |
| 29 | } else { | 29 | } else { |
| 30 | return std.fs.path.joinZ(allocator, paths); | 30 | return fs.path.joinZ(allocator, paths); |
| 31 | } | 31 | } |
| 32 | } | 32 | } |
| 33 | 33 | ||
| ... | @@ -39,6 +39,20 @@ pub const Directory = struct { | ... | @@ -39,6 +39,20 @@ pub const Directory = struct { |
| 39 | if (self.path) |p| gpa.free(p); | 39 | if (self.path) |p| gpa.free(p); |
| 40 | self.* = undefined; | 40 | self.* = undefined; |
| 41 | } | 41 | } |
| 42 | |||
| 43 | pub fn format( | ||
| 44 | self: Directory, | ||
| 45 | comptime fmt_string: []const u8, | ||
| 46 | options: fmt.FormatOptions, | ||
| 47 | writer: anytype, | ||
| 48 | ) !void { | ||
| 49 | _ = options; | ||
| 50 | if (fmt_string.len != 0) fmt.invalidFmtError(fmt, self); | ||
| 51 | if (self.path) |p| { | ||
| 52 | try writer.writeAll(p); | ||
| 53 | try writer.writeAll(fs.path.sep_str); | ||
| 54 | } | ||
| 55 | } | ||
| 42 | }; | 56 | }; |
| 43 | 57 | ||
| 44 | gpa: Allocator, | 58 | gpa: Allocator, |
| ... | @@ -243,10 +257,10 @@ pub const HashHelper = struct { | ... | @@ -243,10 +257,10 @@ pub const HashHelper = struct { |
| 243 | hh.hasher.final(&bin_digest); | 257 | hh.hasher.final(&bin_digest); |
| 244 | 258 | ||
| 245 | var out_digest: [hex_digest_len]u8 = undefined; | 259 | var out_digest: [hex_digest_len]u8 = undefined; |
| 246 | _ = std.fmt.bufPrint( | 260 | _ = fmt.bufPrint( |
| 247 | &out_digest, | 261 | &out_digest, |
| 248 | "{s}", | 262 | "{s}", |
| 249 | .{std.fmt.fmtSliceHexLower(&bin_digest)}, | 263 | .{fmt.fmtSliceHexLower(&bin_digest)}, |
| 250 | ) catch unreachable; | 264 | ) catch unreachable; |
| 251 | return out_digest; | 265 | return out_digest; |
| 252 | } | 266 | } |
| ... | @@ -365,10 +379,10 @@ pub const Manifest = struct { | ... | @@ -365,10 +379,10 @@ pub const Manifest = struct { |
| 365 | var bin_digest: BinDigest = undefined; | 379 | var bin_digest: BinDigest = undefined; |
| 366 | self.hash.hasher.final(&bin_digest); | 380 | self.hash.hasher.final(&bin_digest); |
| 367 | 381 | ||
| 368 | _ = std.fmt.bufPrint( | 382 | _ = fmt.bufPrint( |
| 369 | &self.hex_digest, | 383 | &self.hex_digest, |
| 370 | "{s}", | 384 | "{s}", |
| 371 | .{std.fmt.fmtSliceHexLower(&bin_digest)}, | 385 | .{fmt.fmtSliceHexLower(&bin_digest)}, |
| 372 | ) catch unreachable; | 386 | ) catch unreachable; |
| 373 | 387 | ||
| 374 | self.hash.hasher = hasher_init; | 388 | self.hash.hasher = hasher_init; |
| ... | @@ -408,7 +422,11 @@ pub const Manifest = struct { | ... | @@ -408,7 +422,11 @@ pub const Manifest = struct { |
| 408 | self.have_exclusive_lock = true; | 422 | self.have_exclusive_lock = true; |
| 409 | return false; // cache miss; exclusive lock already held | 423 | return false; // cache miss; exclusive lock already held |
| 410 | } else |err| switch (err) { | 424 | } else |err| switch (err) { |
| 411 | error.WouldBlock => continue, | 425 | // There are no dir components, so you would think |
| 426 | // that this was unreachable, however we have | ||
| 427 | // observed on macOS two processes racing to do | ||
| 428 | // openat() with O_CREAT manifest in ENOENT. | ||
| 429 | error.WouldBlock, error.FileNotFound => continue, | ||
| 412 | else => |e| return e, | 430 | else => |e| return e, |
| 413 | } | 431 | } |
| 414 | }, | 432 | }, |
| ... | @@ -425,7 +443,10 @@ pub const Manifest = struct { | ... | @@ -425,7 +443,10 @@ pub const Manifest = struct { |
| 425 | self.manifest_file = manifest_file; | 443 | self.manifest_file = manifest_file; |
| 426 | self.have_exclusive_lock = true; | 444 | self.have_exclusive_lock = true; |
| 427 | } else |err| switch (err) { | 445 | } else |err| switch (err) { |
| 428 | error.WouldBlock => { | 446 | // There are no dir components, so you would think that this was |
| 447 | // unreachable, however we have observed on macOS two processes racing | ||
| 448 | // to do openat() with O_CREAT manifest in ENOENT. | ||
| 449 | error.WouldBlock, error.FileNotFound => { | ||
| 429 | self.manifest_file = try self.cache.manifest_dir.openFile(&manifest_file_path, .{ | 450 | self.manifest_file = try self.cache.manifest_dir.openFile(&manifest_file_path, .{ |
| 430 | .lock = .Shared, | 451 | .lock = .Shared, |
| 431 | }); | 452 | }); |
| ... | @@ -469,7 +490,7 @@ pub const Manifest = struct { | ... | @@ -469,7 +490,7 @@ pub const Manifest = struct { |
| 469 | cache_hash_file.stat.size = fmt.parseInt(u64, size, 10) catch return error.InvalidFormat; | 490 | cache_hash_file.stat.size = fmt.parseInt(u64, size, 10) catch return error.InvalidFormat; |
| 470 | cache_hash_file.stat.inode = fmt.parseInt(fs.File.INode, inode, 10) catch return error.InvalidFormat; | 491 | cache_hash_file.stat.inode = fmt.parseInt(fs.File.INode, inode, 10) catch return error.InvalidFormat; |
| 471 | cache_hash_file.stat.mtime = fmt.parseInt(i64, mtime_nsec_str, 10) catch return error.InvalidFormat; | 492 | cache_hash_file.stat.mtime = fmt.parseInt(i64, mtime_nsec_str, 10) catch return error.InvalidFormat; |
| 472 | _ = std.fmt.hexToBytes(&cache_hash_file.bin_digest, digest_str) catch return error.InvalidFormat; | 493 | _ = fmt.hexToBytes(&cache_hash_file.bin_digest, digest_str) catch return error.InvalidFormat; |
| 473 | const prefix = fmt.parseInt(u8, prefix_str, 10) catch return error.InvalidFormat; | 494 | const prefix = fmt.parseInt(u8, prefix_str, 10) catch return error.InvalidFormat; |
| 474 | if (prefix >= self.cache.prefixes_len) return error.InvalidFormat; | 495 | if (prefix >= self.cache.prefixes_len) return error.InvalidFormat; |
| 475 | 496 | ||
| ... | @@ -806,10 +827,10 @@ pub const Manifest = struct { | ... | @@ -806,10 +827,10 @@ pub const Manifest = struct { |
| 806 | self.hash.hasher.final(&bin_digest); | 827 | self.hash.hasher.final(&bin_digest); |
| 807 | 828 | ||
| 808 | var out_digest: [hex_digest_len]u8 = undefined; | 829 | var out_digest: [hex_digest_len]u8 = undefined; |
| 809 | _ = std.fmt.bufPrint( | 830 | _ = fmt.bufPrint( |
| 810 | &out_digest, | 831 | &out_digest, |
| 811 | "{s}", | 832 | "{s}", |
| 812 | .{std.fmt.fmtSliceHexLower(&bin_digest)}, | 833 | .{fmt.fmtSliceHexLower(&bin_digest)}, |
| 813 | ) catch unreachable; | 834 | ) catch unreachable; |
| 814 | 835 | ||
| 815 | return out_digest; | 836 | return out_digest; |
| ... | @@ -831,10 +852,10 @@ pub const Manifest = struct { | ... | @@ -831,10 +852,10 @@ pub const Manifest = struct { |
| 831 | var encoded_digest: [hex_digest_len]u8 = undefined; | 852 | var encoded_digest: [hex_digest_len]u8 = undefined; |
| 832 | 853 | ||
| 833 | for (self.files.items) |file| { | 854 | for (self.files.items) |file| { |
| 834 | _ = std.fmt.bufPrint( | 855 | _ = fmt.bufPrint( |
| 835 | &encoded_digest, | 856 | &encoded_digest, |
| 836 | "{s}", | 857 | "{s}", |
| 837 | .{std.fmt.fmtSliceHexLower(&file.bin_digest)}, | 858 | .{fmt.fmtSliceHexLower(&file.bin_digest)}, |
| 838 | ) catch unreachable; | 859 | ) catch unreachable; |
| 839 | try writer.print("{d} {d} {d} {s} {d} {s}\n", .{ | 860 | try writer.print("{d} {d} {d} {s} {d} {s}\n", .{ |
| 840 | file.stat.size, | 861 | file.stat.size, |
| ... | @@ -955,16 +976,16 @@ fn hashFile(file: fs.File, bin_digest: *[Hasher.mac_length]u8) !void { | ... | @@ -955,16 +976,16 @@ fn hashFile(file: fs.File, bin_digest: *[Hasher.mac_length]u8) !void { |
| 955 | } | 976 | } |
| 956 | 977 | ||
| 957 | // Create/Write a file, close it, then grab its stat.mtime timestamp. | 978 | // Create/Write a file, close it, then grab its stat.mtime timestamp. |
| 958 | fn testGetCurrentFileTimestamp() !i128 { | 979 | fn testGetCurrentFileTimestamp(dir: fs.Dir) !i128 { |
| 959 | const test_out_file = "test-filetimestamp.tmp"; | 980 | const test_out_file = "test-filetimestamp.tmp"; |
| 960 | 981 | ||
| 961 | var file = try fs.cwd().createFile(test_out_file, .{ | 982 | var file = try dir.createFile(test_out_file, .{ |
| 962 | .read = true, | 983 | .read = true, |
| 963 | .truncate = true, | 984 | .truncate = true, |
| 964 | }); | 985 | }); |
| 965 | defer { | 986 | defer { |
| 966 | file.close(); | 987 | file.close(); |
| 967 | fs.cwd().deleteFile(test_out_file) catch {}; | 988 | dir.deleteFile(test_out_file) catch {}; |
| 968 | } | 989 | } |
| 969 | 990 | ||
| 970 | return (try file.stat()).mtime; | 991 | return (try file.stat()).mtime; |
| ... | @@ -976,16 +997,17 @@ test "cache file and then recall it" { | ... | @@ -976,16 +997,17 @@ test "cache file and then recall it" { |
| 976 | return error.SkipZigTest; | 997 | return error.SkipZigTest; |
| 977 | } | 998 | } |
| 978 | 999 | ||
| 979 | const cwd = fs.cwd(); | 1000 | var tmp = testing.tmpDir(.{}); |
| 1001 | defer tmp.cleanup(); | ||
| 980 | 1002 | ||
| 981 | const temp_file = "test.txt"; | 1003 | const temp_file = "test.txt"; |
| 982 | const temp_manifest_dir = "temp_manifest_dir"; | 1004 | const temp_manifest_dir = "temp_manifest_dir"; |
| 983 | 1005 | ||
| 984 | try cwd.writeFile(temp_file, "Hello, world!\n"); | 1006 | try tmp.dir.writeFile(temp_file, "Hello, world!\n"); |
| 985 | 1007 | ||
| 986 | // Wait for file timestamps to tick | 1008 | // Wait for file timestamps to tick |
| 987 | const initial_time = try testGetCurrentFileTimestamp(); | 1009 | const initial_time = try testGetCurrentFileTimestamp(tmp.dir); |
| 988 | while ((try testGetCurrentFileTimestamp()) == initial_time) { | 1010 | while ((try testGetCurrentFileTimestamp(tmp.dir)) == initial_time) { |
| 989 | std.time.sleep(1); | 1011 | std.time.sleep(1); |
| 990 | } | 1012 | } |
| 991 | 1013 | ||
| ... | @@ -995,9 +1017,9 @@ test "cache file and then recall it" { | ... | @@ -995,9 +1017,9 @@ test "cache file and then recall it" { |
| 995 | { | 1017 | { |
| 996 | var cache = Cache{ | 1018 | var cache = Cache{ |
| 997 | .gpa = testing.allocator, | 1019 | .gpa = testing.allocator, |
| 998 | .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}), | 1020 | .manifest_dir = try tmp.dir.makeOpenPath(temp_manifest_dir, .{}), |
| 999 | }; | 1021 | }; |
| 1000 | cache.addPrefix(.{ .path = null, .handle = fs.cwd() }); | 1022 | cache.addPrefix(.{ .path = null, .handle = tmp.dir }); |
| 1001 | defer cache.manifest_dir.close(); | 1023 | defer cache.manifest_dir.close(); |
| 1002 | 1024 | ||
| 1003 | { | 1025 | { |
| ... | @@ -1033,9 +1055,6 @@ test "cache file and then recall it" { | ... | @@ -1033,9 +1055,6 @@ test "cache file and then recall it" { |
| 1033 | 1055 | ||
| 1034 | try testing.expectEqual(digest1, digest2); | 1056 | try testing.expectEqual(digest1, digest2); |
| 1035 | } | 1057 | } |
| 1036 | |||
| 1037 | try cwd.deleteTree(temp_manifest_dir); | ||
| 1038 | try cwd.deleteFile(temp_file); | ||
| 1039 | } | 1058 | } |
| 1040 | 1059 | ||
| 1041 | test "check that changing a file makes cache fail" { | 1060 | test "check that changing a file makes cache fail" { |
| ... | @@ -1043,21 +1062,19 @@ test "check that changing a file makes cache fail" { | ... | @@ -1043,21 +1062,19 @@ test "check that changing a file makes cache fail" { |
| 1043 | // https://github.com/ziglang/zig/issues/5437 | 1062 | // https://github.com/ziglang/zig/issues/5437 |
| 1044 | return error.SkipZigTest; | 1063 | return error.SkipZigTest; |
| 1045 | } | 1064 | } |
| 1046 | const cwd = fs.cwd(); | 1065 | var tmp = testing.tmpDir(.{}); |
| 1066 | defer tmp.cleanup(); | ||
| 1047 | 1067 | ||
| 1048 | const temp_file = "cache_hash_change_file_test.txt"; | 1068 | const temp_file = "cache_hash_change_file_test.txt"; |
| 1049 | const temp_manifest_dir = "cache_hash_change_file_manifest_dir"; | 1069 | const temp_manifest_dir = "cache_hash_change_file_manifest_dir"; |
| 1050 | const original_temp_file_contents = "Hello, world!\n"; | 1070 | const original_temp_file_contents = "Hello, world!\n"; |
| 1051 | const updated_temp_file_contents = "Hello, world; but updated!\n"; | 1071 | const updated_temp_file_contents = "Hello, world; but updated!\n"; |
| 1052 | 1072 | ||
| 1053 | try cwd.deleteTree(temp_manifest_dir); | 1073 | try tmp.dir.writeFile(temp_file, original_temp_file_contents); |
| 1054 | try cwd.deleteTree(temp_file); | ||
| 1055 | |||
| 1056 | try cwd.writeFile(temp_file, original_temp_file_contents); | ||
| 1057 | 1074 | ||
| 1058 | // Wait for file timestamps to tick | 1075 | // Wait for file timestamps to tick |
| 1059 | const initial_time = try testGetCurrentFileTimestamp(); | 1076 | const initial_time = try testGetCurrentFileTimestamp(tmp.dir); |
| 1060 | while ((try testGetCurrentFileTimestamp()) == initial_time) { | 1077 | while ((try testGetCurrentFileTimestamp(tmp.dir)) == initial_time) { |
| 1061 | std.time.sleep(1); | 1078 | std.time.sleep(1); |
| 1062 | } | 1079 | } |
| 1063 | 1080 | ||
| ... | @@ -1067,9 +1084,9 @@ test "check that changing a file makes cache fail" { | ... | @@ -1067,9 +1084,9 @@ test "check that changing a file makes cache fail" { |
| 1067 | { | 1084 | { |
| 1068 | var cache = Cache{ | 1085 | var cache = Cache{ |
| 1069 | .gpa = testing.allocator, | 1086 | .gpa = testing.allocator, |
| 1070 | .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}), | 1087 | .manifest_dir = try tmp.dir.makeOpenPath(temp_manifest_dir, .{}), |
| 1071 | }; | 1088 | }; |
| 1072 | cache.addPrefix(.{ .path = null, .handle = fs.cwd() }); | 1089 | cache.addPrefix(.{ .path = null, .handle = tmp.dir }); |
| 1073 | defer cache.manifest_dir.close(); | 1090 | defer cache.manifest_dir.close(); |
| 1074 | 1091 | ||
| 1075 | { | 1092 | { |
| ... | @@ -1089,7 +1106,7 @@ test "check that changing a file makes cache fail" { | ... | @@ -1089,7 +1106,7 @@ test "check that changing a file makes cache fail" { |
| 1089 | try ch.writeManifest(); | 1106 | try ch.writeManifest(); |
| 1090 | } | 1107 | } |
| 1091 | 1108 | ||
| 1092 | try cwd.writeFile(temp_file, updated_temp_file_contents); | 1109 | try tmp.dir.writeFile(temp_file, updated_temp_file_contents); |
| 1093 | 1110 | ||
| 1094 | { | 1111 | { |
| 1095 | var ch = cache.obtain(); | 1112 | var ch = cache.obtain(); |
| ... | @@ -1111,9 +1128,6 @@ test "check that changing a file makes cache fail" { | ... | @@ -1111,9 +1128,6 @@ test "check that changing a file makes cache fail" { |
| 1111 | 1128 | ||
| 1112 | try testing.expect(!mem.eql(u8, digest1[0..], digest2[0..])); | 1129 | try testing.expect(!mem.eql(u8, digest1[0..], digest2[0..])); |
| 1113 | } | 1130 | } |
| 1114 | |||
| 1115 | try cwd.deleteTree(temp_manifest_dir); | ||
| 1116 | try cwd.deleteTree(temp_file); | ||
| 1117 | } | 1131 | } |
| 1118 | 1132 | ||
| 1119 | test "no file inputs" { | 1133 | test "no file inputs" { |
| ... | @@ -1121,18 +1135,20 @@ test "no file inputs" { | ... | @@ -1121,18 +1135,20 @@ test "no file inputs" { |
| 1121 | // https://github.com/ziglang/zig/issues/5437 | 1135 | // https://github.com/ziglang/zig/issues/5437 |
| 1122 | return error.SkipZigTest; | 1136 | return error.SkipZigTest; |
| 1123 | } | 1137 | } |
| 1124 | const cwd = fs.cwd(); | 1138 | |
| 1139 | var tmp = testing.tmpDir(.{}); | ||
| 1140 | defer tmp.cleanup(); | ||
| 1141 | |||
| 1125 | const temp_manifest_dir = "no_file_inputs_manifest_dir"; | 1142 | const temp_manifest_dir = "no_file_inputs_manifest_dir"; |
| 1126 | defer cwd.deleteTree(temp_manifest_dir) catch {}; | ||
| 1127 | 1143 | ||
| 1128 | var digest1: [hex_digest_len]u8 = undefined; | 1144 | var digest1: [hex_digest_len]u8 = undefined; |
| 1129 | var digest2: [hex_digest_len]u8 = undefined; | 1145 | var digest2: [hex_digest_len]u8 = undefined; |
| 1130 | 1146 | ||
| 1131 | var cache = Cache{ | 1147 | var cache = Cache{ |
| 1132 | .gpa = testing.allocator, | 1148 | .gpa = testing.allocator, |
| 1133 | .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}), | 1149 | .manifest_dir = try tmp.dir.makeOpenPath(temp_manifest_dir, .{}), |
| 1134 | }; | 1150 | }; |
| 1135 | cache.addPrefix(.{ .path = null, .handle = fs.cwd() }); | 1151 | cache.addPrefix(.{ .path = null, .handle = tmp.dir }); |
| 1136 | defer cache.manifest_dir.close(); | 1152 | defer cache.manifest_dir.close(); |
| 1137 | 1153 | ||
| 1138 | { | 1154 | { |
| ... | @@ -1167,18 +1183,19 @@ test "Manifest with files added after initial hash work" { | ... | @@ -1167,18 +1183,19 @@ test "Manifest with files added after initial hash work" { |
| 1167 | // https://github.com/ziglang/zig/issues/5437 | 1183 | // https://github.com/ziglang/zig/issues/5437 |
| 1168 | return error.SkipZigTest; | 1184 | return error.SkipZigTest; |
| 1169 | } | 1185 | } |
| 1170 | const cwd = fs.cwd(); | 1186 | var tmp = testing.tmpDir(.{}); |
| 1187 | defer tmp.cleanup(); | ||
| 1171 | 1188 | ||
| 1172 | const temp_file1 = "cache_hash_post_file_test1.txt"; | 1189 | const temp_file1 = "cache_hash_post_file_test1.txt"; |
| 1173 | const temp_file2 = "cache_hash_post_file_test2.txt"; | 1190 | const temp_file2 = "cache_hash_post_file_test2.txt"; |
| 1174 | const temp_manifest_dir = "cache_hash_post_file_manifest_dir"; | 1191 | const temp_manifest_dir = "cache_hash_post_file_manifest_dir"; |
| 1175 | 1192 | ||
| 1176 | try cwd.writeFile(temp_file1, "Hello, world!\n"); | 1193 | try tmp.dir.writeFile(temp_file1, "Hello, world!\n"); |
| 1177 | try cwd.writeFile(temp_file2, "Hello world the second!\n"); | 1194 | try tmp.dir.writeFile(temp_file2, "Hello world the second!\n"); |
| 1178 | 1195 | ||
| 1179 | // Wait for file timestamps to tick | 1196 | // Wait for file timestamps to tick |
| 1180 | const initial_time = try testGetCurrentFileTimestamp(); | 1197 | const initial_time = try testGetCurrentFileTimestamp(tmp.dir); |
| 1181 | while ((try testGetCurrentFileTimestamp()) == initial_time) { | 1198 | while ((try testGetCurrentFileTimestamp(tmp.dir)) == initial_time) { |
| 1182 | std.time.sleep(1); | 1199 | std.time.sleep(1); |
| 1183 | } | 1200 | } |
| 1184 | 1201 | ||
| ... | @@ -1189,9 +1206,9 @@ test "Manifest with files added after initial hash work" { | ... | @@ -1189,9 +1206,9 @@ test "Manifest with files added after initial hash work" { |
| 1189 | { | 1206 | { |
| 1190 | var cache = Cache{ | 1207 | var cache = Cache{ |
| 1191 | .gpa = testing.allocator, | 1208 | .gpa = testing.allocator, |
| 1192 | .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}), | 1209 | .manifest_dir = try tmp.dir.makeOpenPath(temp_manifest_dir, .{}), |
| 1193 | }; | 1210 | }; |
| 1194 | cache.addPrefix(.{ .path = null, .handle = fs.cwd() }); | 1211 | cache.addPrefix(.{ .path = null, .handle = tmp.dir }); |
| 1195 | defer cache.manifest_dir.close(); | 1212 | defer cache.manifest_dir.close(); |
| 1196 | 1213 | ||
| 1197 | { | 1214 | { |
| ... | @@ -1224,11 +1241,11 @@ test "Manifest with files added after initial hash work" { | ... | @@ -1224,11 +1241,11 @@ test "Manifest with files added after initial hash work" { |
| 1224 | try testing.expect(mem.eql(u8, &digest1, &digest2)); | 1241 | try testing.expect(mem.eql(u8, &digest1, &digest2)); |
| 1225 | 1242 | ||
| 1226 | // Modify the file added after initial hash | 1243 | // Modify the file added after initial hash |
| 1227 | try cwd.writeFile(temp_file2, "Hello world the second, updated\n"); | 1244 | try tmp.dir.writeFile(temp_file2, "Hello world the second, updated\n"); |
| 1228 | 1245 | ||
| 1229 | // Wait for file timestamps to tick | 1246 | // Wait for file timestamps to tick |
| 1230 | const initial_time2 = try testGetCurrentFileTimestamp(); | 1247 | const initial_time2 = try testGetCurrentFileTimestamp(tmp.dir); |
| 1231 | while ((try testGetCurrentFileTimestamp()) == initial_time2) { | 1248 | while ((try testGetCurrentFileTimestamp(tmp.dir)) == initial_time2) { |
| 1232 | std.time.sleep(1); | 1249 | std.time.sleep(1); |
| 1233 | } | 1250 | } |
| 1234 | 1251 | ||
| ... | @@ -1251,8 +1268,4 @@ test "Manifest with files added after initial hash work" { | ... | @@ -1251,8 +1268,4 @@ test "Manifest with files added after initial hash work" { |
| 1251 | 1268 | ||
| 1252 | try testing.expect(!mem.eql(u8, &digest1, &digest3)); | 1269 | try testing.expect(!mem.eql(u8, &digest1, &digest3)); |
| 1253 | } | 1270 | } |
| 1254 | |||
| 1255 | try cwd.deleteTree(temp_manifest_dir); | ||
| 1256 | try cwd.deleteFile(temp_file1); | ||
| 1257 | try cwd.deleteFile(temp_file2); | ||
| 1258 | } | 1271 | } |
lib/std/Build/CheckFileStep.zig+62-25| ... | @@ -1,51 +1,88 @@ | ... | @@ -1,51 +1,88 @@ |
| 1 | const std = @import("../std.zig"); | 1 | //! Fail the build step if a file does not match certain checks. |
| 2 | const Step = std.Build.Step; | 2 | //! TODO: make this more flexible, supporting more kinds of checks. |
| 3 | const fs = std.fs; | 3 | //! TODO: generalize the code in std.testing.expectEqualStrings and make this |
| 4 | const mem = std.mem; | 4 | //! CheckFileStep produce those helpful diagnostics when there is not a match. |
| 5 | |||
| 6 | const CheckFileStep = @This(); | ||
| 7 | |||
| 8 | pub const base_id = .check_file; | ||
| 9 | 5 | ||
| 10 | step: Step, | 6 | step: Step, |
| 11 | builder: *std.Build, | ||
| 12 | expected_matches: []const []const u8, | 7 | expected_matches: []const []const u8, |
| 8 | expected_exact: ?[]const u8, | ||
| 13 | source: std.Build.FileSource, | 9 | source: std.Build.FileSource, |
| 14 | max_bytes: usize = 20 * 1024 * 1024, | 10 | max_bytes: usize = 20 * 1024 * 1024, |
| 15 | 11 | ||
| 12 | pub const base_id = .check_file; | ||
| 13 | |||
| 14 | pub const Options = struct { | ||
| 15 | expected_matches: []const []const u8 = &.{}, | ||
| 16 | expected_exact: ?[]const u8 = null, | ||
| 17 | }; | ||
| 18 | |||
| 16 | pub fn create( | 19 | pub fn create( |
| 17 | builder: *std.Build, | 20 | owner: *std.Build, |
| 18 | source: std.Build.FileSource, | 21 | source: std.Build.FileSource, |
| 19 | expected_matches: []const []const u8, | 22 | options: Options, |
| 20 | ) *CheckFileStep { | 23 | ) *CheckFileStep { |
| 21 | const self = builder.allocator.create(CheckFileStep) catch @panic("OOM"); | 24 | const self = owner.allocator.create(CheckFileStep) catch @panic("OOM"); |
| 22 | self.* = CheckFileStep{ | 25 | self.* = .{ |
| 23 | .builder = builder, | 26 | .step = Step.init(.{ |
| 24 | .step = Step.init(.check_file, "CheckFile", builder.allocator, make), | 27 | .id = .check_file, |
| 25 | .source = source.dupe(builder), | 28 | .name = "CheckFile", |
| 26 | .expected_matches = builder.dupeStrings(expected_matches), | 29 | .owner = owner, |
| 30 | .makeFn = make, | ||
| 31 | }), | ||
| 32 | .source = source.dupe(owner), | ||
| 33 | .expected_matches = owner.dupeStrings(options.expected_matches), | ||
| 34 | .expected_exact = options.expected_exact, | ||
| 27 | }; | 35 | }; |
| 28 | self.source.addStepDependencies(&self.step); | 36 | self.source.addStepDependencies(&self.step); |
| 29 | return self; | 37 | return self; |
| 30 | } | 38 | } |
| 31 | 39 | ||
| 32 | fn make(step: *Step) !void { | 40 | pub fn setName(self: *CheckFileStep, name: []const u8) void { |
| 41 | self.step.name = name; | ||
| 42 | } | ||
| 43 | |||
| 44 | fn make(step: *Step, prog_node: *std.Progress.Node) !void { | ||
| 45 | _ = prog_node; | ||
| 46 | const b = step.owner; | ||
| 33 | const self = @fieldParentPtr(CheckFileStep, "step", step); | 47 | const self = @fieldParentPtr(CheckFileStep, "step", step); |
| 34 | 48 | ||
| 35 | const src_path = self.source.getPath(self.builder); | 49 | const src_path = self.source.getPath(b); |
| 36 | const contents = try fs.cwd().readFileAlloc(self.builder.allocator, src_path, self.max_bytes); | 50 | const contents = fs.cwd().readFileAlloc(b.allocator, src_path, self.max_bytes) catch |err| { |
| 51 | return step.fail("unable to read '{s}': {s}", .{ | ||
| 52 | src_path, @errorName(err), | ||
| 53 | }); | ||
| 54 | }; | ||
| 37 | 55 | ||
| 38 | for (self.expected_matches) |expected_match| { | 56 | for (self.expected_matches) |expected_match| { |
| 39 | if (mem.indexOf(u8, contents, expected_match) == null) { | 57 | if (mem.indexOf(u8, contents, expected_match) == null) { |
| 40 | std.debug.print( | 58 | return step.fail( |
| 41 | \\ | 59 | \\ |
| 42 | \\========= Expected to find: =================== | 60 | \\========= expected to find: =================== |
| 43 | \\{s} | 61 | \\{s} |
| 44 | \\========= But file does not contain it: ======= | 62 | \\========= but file does not contain it: ======= |
| 45 | \\{s} | 63 | \\{s} |
| 46 | \\ | 64 | \\=============================================== |
| 47 | , .{ expected_match, contents }); | 65 | , .{ expected_match, contents }); |
| 48 | return error.TestFailed; | 66 | } |
| 67 | } | ||
| 68 | |||
| 69 | if (self.expected_exact) |expected_exact| { | ||
| 70 | if (!mem.eql(u8, expected_exact, contents)) { | ||
| 71 | return step.fail( | ||
| 72 | \\ | ||
| 73 | \\========= expected: ===================== | ||
| 74 | \\{s} | ||
| 75 | \\========= but found: ==================== | ||
| 76 | \\{s} | ||
| 77 | \\========= from the following file: ====== | ||
| 78 | \\{s} | ||
| 79 | , .{ expected_exact, contents, src_path }); | ||
| 49 | } | 80 | } |
| 50 | } | 81 | } |
| 51 | } | 82 | } |
| 83 | |||
| 84 | const CheckFileStep = @This(); | ||
| 85 | const std = @import("../std.zig"); | ||
| 86 | const Step = std.Build.Step; | ||
| 87 | const fs = std.fs; | ||
| 88 | const mem = std.mem; |
lib/std/Build/CheckObjectStep.zig+95-91| ... | @@ -10,25 +10,31 @@ const CheckObjectStep = @This(); | ... | @@ -10,25 +10,31 @@ const CheckObjectStep = @This(); |
| 10 | 10 | ||
| 11 | const Allocator = mem.Allocator; | 11 | const Allocator = mem.Allocator; |
| 12 | const Step = std.Build.Step; | 12 | const Step = std.Build.Step; |
| 13 | const EmulatableRunStep = std.Build.EmulatableRunStep; | ||
| 14 | 13 | ||
| 15 | pub const base_id = .check_object; | 14 | pub const base_id = .check_object; |
| 16 | 15 | ||
| 17 | step: Step, | 16 | step: Step, |
| 18 | builder: *std.Build, | ||
| 19 | source: std.Build.FileSource, | 17 | source: std.Build.FileSource, |
| 20 | max_bytes: usize = 20 * 1024 * 1024, | 18 | max_bytes: usize = 20 * 1024 * 1024, |
| 21 | checks: std.ArrayList(Check), | 19 | checks: std.ArrayList(Check), |
| 22 | dump_symtab: bool = false, | 20 | dump_symtab: bool = false, |
| 23 | obj_format: std.Target.ObjectFormat, | 21 | obj_format: std.Target.ObjectFormat, |
| 24 | 22 | ||
| 25 | pub fn create(builder: *std.Build, source: std.Build.FileSource, obj_format: std.Target.ObjectFormat) *CheckObjectStep { | 23 | pub fn create( |
| 26 | const gpa = builder.allocator; | 24 | owner: *std.Build, |
| 25 | source: std.Build.FileSource, | ||
| 26 | obj_format: std.Target.ObjectFormat, | ||
| 27 | ) *CheckObjectStep { | ||
| 28 | const gpa = owner.allocator; | ||
| 27 | const self = gpa.create(CheckObjectStep) catch @panic("OOM"); | 29 | const self = gpa.create(CheckObjectStep) catch @panic("OOM"); |
| 28 | self.* = .{ | 30 | self.* = .{ |
| 29 | .builder = builder, | 31 | .step = Step.init(.{ |
| 30 | .step = Step.init(.check_file, "CheckObject", gpa, make), | 32 | .id = .check_file, |
| 31 | .source = source.dupe(builder), | 33 | .name = "CheckObject", |
| 34 | .owner = owner, | ||
| 35 | .makeFn = make, | ||
| 36 | }), | ||
| 37 | .source = source.dupe(owner), | ||
| 32 | .checks = std.ArrayList(Check).init(gpa), | 38 | .checks = std.ArrayList(Check).init(gpa), |
| 33 | .obj_format = obj_format, | 39 | .obj_format = obj_format, |
| 34 | }; | 40 | }; |
| ... | @@ -38,14 +44,18 @@ pub fn create(builder: *std.Build, source: std.Build.FileSource, obj_format: std | ... | @@ -38,14 +44,18 @@ pub fn create(builder: *std.Build, source: std.Build.FileSource, obj_format: std |
| 38 | 44 | ||
| 39 | /// Runs and (optionally) compares the output of a binary. | 45 | /// Runs and (optionally) compares the output of a binary. |
| 40 | /// Asserts `self` was generated from an executable step. | 46 | /// Asserts `self` was generated from an executable step. |
| 41 | pub fn runAndCompare(self: *CheckObjectStep) *EmulatableRunStep { | 47 | /// TODO this doesn't actually compare, and there's no apparent reason for it |
| 48 | /// to depend on the check object step. I don't see why this function should exist, | ||
| 49 | /// the caller could just add the run step directly. | ||
| 50 | pub fn runAndCompare(self: *CheckObjectStep) *std.Build.RunStep { | ||
| 42 | const dependencies_len = self.step.dependencies.items.len; | 51 | const dependencies_len = self.step.dependencies.items.len; |
| 43 | assert(dependencies_len > 0); | 52 | assert(dependencies_len > 0); |
| 44 | const exe_step = self.step.dependencies.items[dependencies_len - 1]; | 53 | const exe_step = self.step.dependencies.items[dependencies_len - 1]; |
| 45 | const exe = exe_step.cast(std.Build.CompileStep).?; | 54 | const exe = exe_step.cast(std.Build.CompileStep).?; |
| 46 | const emulatable_step = EmulatableRunStep.create(self.builder, "EmulatableRun", exe); | 55 | const run = self.step.owner.addRunArtifact(exe); |
| 47 | emulatable_step.step.dependOn(&self.step); | 56 | run.skip_foreign_checks = true; |
| 48 | return emulatable_step; | 57 | run.step.dependOn(&self.step); |
| 58 | return run; | ||
| 49 | } | 59 | } |
| 50 | 60 | ||
| 51 | /// There two types of actions currently suported: | 61 | /// There two types of actions currently suported: |
| ... | @@ -123,7 +133,8 @@ const Action = struct { | ... | @@ -123,7 +133,8 @@ const Action = struct { |
| 123 | /// Will return true if the `phrase` is correctly parsed into an RPN program and | 133 | /// Will return true if the `phrase` is correctly parsed into an RPN program and |
| 124 | /// its reduced, computed value compares using `op` with the expected value, either | 134 | /// its reduced, computed value compares using `op` with the expected value, either |
| 125 | /// a literal or another extracted variable. | 135 | /// a literal or another extracted variable. |
| 126 | fn computeCmp(act: Action, gpa: Allocator, global_vars: anytype) !bool { | 136 | fn computeCmp(act: Action, step: *Step, global_vars: anytype) !bool { |
| 137 | const gpa = step.owner.allocator; | ||
| 127 | var op_stack = std.ArrayList(enum { add, sub, mod, mul }).init(gpa); | 138 | var op_stack = std.ArrayList(enum { add, sub, mod, mul }).init(gpa); |
| 128 | var values = std.ArrayList(u64).init(gpa); | 139 | var values = std.ArrayList(u64).init(gpa); |
| 129 | 140 | ||
| ... | @@ -140,11 +151,11 @@ const Action = struct { | ... | @@ -140,11 +151,11 @@ const Action = struct { |
| 140 | } else { | 151 | } else { |
| 141 | const val = std.fmt.parseInt(u64, next, 0) catch blk: { | 152 | const val = std.fmt.parseInt(u64, next, 0) catch blk: { |
| 142 | break :blk global_vars.get(next) orelse { | 153 | break :blk global_vars.get(next) orelse { |
| 143 | std.debug.print( | 154 | try step.addError( |
| 144 | \\ | 155 | \\ |
| 145 | \\========= Variable was not extracted: =========== | 156 | \\========= variable was not extracted: =========== |
| 146 | \\{s} | 157 | \\{s} |
| 147 | \\ | 158 | \\================================================= |
| 148 | , .{next}); | 159 | , .{next}); |
| 149 | return error.UnknownVariable; | 160 | return error.UnknownVariable; |
| 150 | }; | 161 | }; |
| ... | @@ -176,11 +187,11 @@ const Action = struct { | ... | @@ -176,11 +187,11 @@ const Action = struct { |
| 176 | 187 | ||
| 177 | const exp_value = switch (act.expected.?.value) { | 188 | const exp_value = switch (act.expected.?.value) { |
| 178 | .variable => |name| global_vars.get(name) orelse { | 189 | .variable => |name| global_vars.get(name) orelse { |
| 179 | std.debug.print( | 190 | try step.addError( |
| 180 | \\ | 191 | \\ |
| 181 | \\========= Variable was not extracted: =========== | 192 | \\========= variable was not extracted: =========== |
| 182 | \\{s} | 193 | \\{s} |
| 183 | \\ | 194 | \\================================================= |
| 184 | , .{name}); | 195 | , .{name}); |
| 185 | return error.UnknownVariable; | 196 | return error.UnknownVariable; |
| 186 | }, | 197 | }, |
| ... | @@ -249,7 +260,7 @@ const Check = struct { | ... | @@ -249,7 +260,7 @@ const Check = struct { |
| 249 | 260 | ||
| 250 | /// Creates a new sequence of actions with `phrase` as the first anchor searched phrase. | 261 | /// Creates a new sequence of actions with `phrase` as the first anchor searched phrase. |
| 251 | pub fn checkStart(self: *CheckObjectStep, phrase: []const u8) void { | 262 | pub fn checkStart(self: *CheckObjectStep, phrase: []const u8) void { |
| 252 | var new_check = Check.create(self.builder); | 263 | var new_check = Check.create(self.step.owner); |
| 253 | new_check.match(phrase); | 264 | new_check.match(phrase); |
| 254 | self.checks.append(new_check) catch @panic("OOM"); | 265 | self.checks.append(new_check) catch @panic("OOM"); |
| 255 | } | 266 | } |
| ... | @@ -291,34 +302,34 @@ pub fn checkComputeCompare( | ... | @@ -291,34 +302,34 @@ pub fn checkComputeCompare( |
| 291 | program: []const u8, | 302 | program: []const u8, |
| 292 | expected: ComputeCompareExpected, | 303 | expected: ComputeCompareExpected, |
| 293 | ) void { | 304 | ) void { |
| 294 | var new_check = Check.create(self.builder); | 305 | var new_check = Check.create(self.step.owner); |
| 295 | new_check.computeCmp(program, expected); | 306 | new_check.computeCmp(program, expected); |
| 296 | self.checks.append(new_check) catch @panic("OOM"); | 307 | self.checks.append(new_check) catch @panic("OOM"); |
| 297 | } | 308 | } |
| 298 | 309 | ||
| 299 | fn make(step: *Step) !void { | 310 | fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 311 | _ = prog_node; | ||
| 312 | const b = step.owner; | ||
| 313 | const gpa = b.allocator; | ||
| 300 | const self = @fieldParentPtr(CheckObjectStep, "step", step); | 314 | const self = @fieldParentPtr(CheckObjectStep, "step", step); |
| 301 | 315 | ||
| 302 | const gpa = self.builder.allocator; | 316 | const src_path = self.source.getPath(b); |
| 303 | const src_path = self.source.getPath(self.builder); | 317 | const contents = fs.cwd().readFileAllocOptions( |
| 304 | const contents = try fs.cwd().readFileAllocOptions( | ||
| 305 | gpa, | 318 | gpa, |
| 306 | src_path, | 319 | src_path, |
| 307 | self.max_bytes, | 320 | self.max_bytes, |
| 308 | null, | 321 | null, |
| 309 | @alignOf(u64), | 322 | @alignOf(u64), |
| 310 | null, | 323 | null, |
| 311 | ); | 324 | ) catch |err| return step.fail("unable to read '{s}': {s}", .{ src_path, @errorName(err) }); |
| 312 | 325 | ||
| 313 | const output = switch (self.obj_format) { | 326 | const output = switch (self.obj_format) { |
| 314 | .macho => try MachODumper.parseAndDump(contents, .{ | 327 | .macho => try MachODumper.parseAndDump(step, contents, .{ |
| 315 | .gpa = gpa, | ||
| 316 | .dump_symtab = self.dump_symtab, | 328 | .dump_symtab = self.dump_symtab, |
| 317 | }), | 329 | }), |
| 318 | .elf => @panic("TODO elf parser"), | 330 | .elf => @panic("TODO elf parser"), |
| 319 | .coff => @panic("TODO coff parser"), | 331 | .coff => @panic("TODO coff parser"), |
| 320 | .wasm => try WasmDumper.parseAndDump(contents, .{ | 332 | .wasm => try WasmDumper.parseAndDump(step, contents, .{ |
| 321 | .gpa = gpa, | ||
| 322 | .dump_symtab = self.dump_symtab, | 333 | .dump_symtab = self.dump_symtab, |
| 323 | }), | 334 | }), |
| 324 | else => unreachable, | 335 | else => unreachable, |
| ... | @@ -334,54 +345,50 @@ fn make(step: *Step) !void { | ... | @@ -334,54 +345,50 @@ fn make(step: *Step) !void { |
| 334 | while (it.next()) |line| { | 345 | while (it.next()) |line| { |
| 335 | if (try act.match(line, &vars)) break; | 346 | if (try act.match(line, &vars)) break; |
| 336 | } else { | 347 | } else { |
| 337 | std.debug.print( | 348 | return step.fail( |
| 338 | \\ | 349 | \\ |
| 339 | \\========= Expected to find: ========================== | 350 | \\========= expected to find: ========================== |
| 340 | \\{s} | 351 | \\{s} |
| 341 | \\========= But parsed file does not contain it: ======= | 352 | \\========= but parsed file does not contain it: ======= |
| 342 | \\{s} | 353 | \\{s} |
| 343 | \\ | 354 | \\====================================================== |
| 344 | , .{ act.phrase, output }); | 355 | , .{ act.phrase, output }); |
| 345 | return error.TestFailed; | ||
| 346 | } | 356 | } |
| 347 | }, | 357 | }, |
| 348 | .not_present => { | 358 | .not_present => { |
| 349 | while (it.next()) |line| { | 359 | while (it.next()) |line| { |
| 350 | if (try act.match(line, &vars)) { | 360 | if (try act.match(line, &vars)) { |
| 351 | std.debug.print( | 361 | return step.fail( |
| 352 | \\ | 362 | \\ |
| 353 | \\========= Expected not to find: =================== | 363 | \\========= expected not to find: =================== |
| 354 | \\{s} | 364 | \\{s} |
| 355 | \\========= But parsed file does contain it: ======== | 365 | \\========= but parsed file does contain it: ======== |
| 356 | \\{s} | 366 | \\{s} |
| 357 | \\ | 367 | \\=================================================== |
| 358 | , .{ act.phrase, output }); | 368 | , .{ act.phrase, output }); |
| 359 | return error.TestFailed; | ||
| 360 | } | 369 | } |
| 361 | } | 370 | } |
| 362 | }, | 371 | }, |
| 363 | .compute_cmp => { | 372 | .compute_cmp => { |
| 364 | const res = act.computeCmp(gpa, vars) catch |err| switch (err) { | 373 | const res = act.computeCmp(step, vars) catch |err| switch (err) { |
| 365 | error.UnknownVariable => { | 374 | error.UnknownVariable => { |
| 366 | std.debug.print( | 375 | return step.fail( |
| 367 | \\========= From parsed file: ===================== | 376 | \\========= from parsed file: ===================== |
| 368 | \\{s} | 377 | \\{s} |
| 369 | \\ | 378 | \\================================================= |
| 370 | , .{output}); | 379 | , .{output}); |
| 371 | return error.TestFailed; | ||
| 372 | }, | 380 | }, |
| 373 | else => |e| return e, | 381 | else => |e| return e, |
| 374 | }; | 382 | }; |
| 375 | if (!res) { | 383 | if (!res) { |
| 376 | std.debug.print( | 384 | return step.fail( |
| 377 | \\ | 385 | \\ |
| 378 | \\========= Comparison failed for action: =========== | 386 | \\========= comparison failed for action: =========== |
| 379 | \\{s} {} | 387 | \\{s} {} |
| 380 | \\========= From parsed file: ======================= | 388 | \\========= from parsed file: ======================= |
| 381 | \\{s} | 389 | \\{s} |
| 382 | \\ | 390 | \\=================================================== |
| 383 | , .{ act.phrase, act.expected.?, output }); | 391 | , .{ act.phrase, act.expected.?, output }); |
| 384 | return error.TestFailed; | ||
| 385 | } | 392 | } |
| 386 | }, | 393 | }, |
| 387 | } | 394 | } |
| ... | @@ -390,7 +397,6 @@ fn make(step: *Step) !void { | ... | @@ -390,7 +397,6 @@ fn make(step: *Step) !void { |
| 390 | } | 397 | } |
| 391 | 398 | ||
| 392 | const Opts = struct { | 399 | const Opts = struct { |
| 393 | gpa: ?Allocator = null, | ||
| 394 | dump_symtab: bool = false, | 400 | dump_symtab: bool = false, |
| 395 | }; | 401 | }; |
| 396 | 402 | ||
| ... | @@ -398,8 +404,8 @@ const MachODumper = struct { | ... | @@ -398,8 +404,8 @@ const MachODumper = struct { |
| 398 | const LoadCommandIterator = macho.LoadCommandIterator; | 404 | const LoadCommandIterator = macho.LoadCommandIterator; |
| 399 | const symtab_label = "symtab"; | 405 | const symtab_label = "symtab"; |
| 400 | 406 | ||
| 401 | fn parseAndDump(bytes: []align(@alignOf(u64)) const u8, opts: Opts) ![]const u8 { | 407 | fn parseAndDump(step: *Step, bytes: []align(@alignOf(u64)) const u8, opts: Opts) ![]const u8 { |
| 402 | const gpa = opts.gpa orelse unreachable; // MachO dumper requires an allocator | 408 | const gpa = step.owner.allocator; |
| 403 | var stream = std.io.fixedBufferStream(bytes); | 409 | var stream = std.io.fixedBufferStream(bytes); |
| 404 | const reader = stream.reader(); | 410 | const reader = stream.reader(); |
| 405 | 411 | ||
| ... | @@ -681,8 +687,8 @@ const MachODumper = struct { | ... | @@ -681,8 +687,8 @@ const MachODumper = struct { |
| 681 | const WasmDumper = struct { | 687 | const WasmDumper = struct { |
| 682 | const symtab_label = "symbols"; | 688 | const symtab_label = "symbols"; |
| 683 | 689 | ||
| 684 | fn parseAndDump(bytes: []const u8, opts: Opts) ![]const u8 { | 690 | fn parseAndDump(step: *Step, bytes: []const u8, opts: Opts) ![]const u8 { |
| 685 | const gpa = opts.gpa orelse unreachable; // Wasm dumper requires an allocator | 691 | const gpa = step.owner.allocator; |
| 686 | if (opts.dump_symtab) { | 692 | if (opts.dump_symtab) { |
| 687 | @panic("TODO: Implement symbol table parsing and dumping"); | 693 | @panic("TODO: Implement symbol table parsing and dumping"); |
| 688 | } | 694 | } |
| ... | @@ -703,20 +709,24 @@ const WasmDumper = struct { | ... | @@ -703,20 +709,24 @@ const WasmDumper = struct { |
| 703 | const writer = output.writer(); | 709 | const writer = output.writer(); |
| 704 | 710 | ||
| 705 | while (reader.readByte()) |current_byte| { | 711 | while (reader.readByte()) |current_byte| { |
| 706 | const section = std.meta.intToEnum(std.wasm.Section, current_byte) catch |err| { | 712 | const section = std.meta.intToEnum(std.wasm.Section, current_byte) catch { |
| 707 | std.debug.print("Found invalid section id '{d}'\n", .{current_byte}); | 713 | return step.fail("Found invalid section id '{d}'", .{current_byte}); |
| 708 | return err; | ||
| 709 | }; | 714 | }; |
| 710 | 715 | ||
| 711 | const section_length = try std.leb.readULEB128(u32, reader); | 716 | const section_length = try std.leb.readULEB128(u32, reader); |
| 712 | try parseAndDumpSection(section, bytes[fbs.pos..][0..section_length], writer); | 717 | try parseAndDumpSection(step, section, bytes[fbs.pos..][0..section_length], writer); |
| 713 | fbs.pos += section_length; | 718 | fbs.pos += section_length; |
| 714 | } else |_| {} // reached end of stream | 719 | } else |_| {} // reached end of stream |
| 715 | 720 | ||
| 716 | return output.toOwnedSlice(); | 721 | return output.toOwnedSlice(); |
| 717 | } | 722 | } |
| 718 | 723 | ||
| 719 | fn parseAndDumpSection(section: std.wasm.Section, data: []const u8, writer: anytype) !void { | 724 | fn parseAndDumpSection( |
| 725 | step: *Step, | ||
| 726 | section: std.wasm.Section, | ||
| 727 | data: []const u8, | ||
| 728 | writer: anytype, | ||
| 729 | ) !void { | ||
| 720 | var fbs = std.io.fixedBufferStream(data); | 730 | var fbs = std.io.fixedBufferStream(data); |
| 721 | const reader = fbs.reader(); | 731 | const reader = fbs.reader(); |
| 722 | 732 | ||
| ... | @@ -739,7 +749,7 @@ const WasmDumper = struct { | ... | @@ -739,7 +749,7 @@ const WasmDumper = struct { |
| 739 | => { | 749 | => { |
| 740 | const entries = try std.leb.readULEB128(u32, reader); | 750 | const entries = try std.leb.readULEB128(u32, reader); |
| 741 | try writer.print("\nentries {d}\n", .{entries}); | 751 | try writer.print("\nentries {d}\n", .{entries}); |
| 742 | try dumpSection(section, data[fbs.pos..], entries, writer); | 752 | try dumpSection(step, section, data[fbs.pos..], entries, writer); |
| 743 | }, | 753 | }, |
| 744 | .custom => { | 754 | .custom => { |
| 745 | const name_length = try std.leb.readULEB128(u32, reader); | 755 | const name_length = try std.leb.readULEB128(u32, reader); |
| ... | @@ -748,7 +758,7 @@ const WasmDumper = struct { | ... | @@ -748,7 +758,7 @@ const WasmDumper = struct { |
| 748 | try writer.print("\nname {s}\n", .{name}); | 758 | try writer.print("\nname {s}\n", .{name}); |
| 749 | 759 | ||
| 750 | if (mem.eql(u8, name, "name")) { | 760 | if (mem.eql(u8, name, "name")) { |
| 751 | try parseDumpNames(reader, writer, data); | 761 | try parseDumpNames(step, reader, writer, data); |
| 752 | } else if (mem.eql(u8, name, "producers")) { | 762 | } else if (mem.eql(u8, name, "producers")) { |
| 753 | try parseDumpProducers(reader, writer, data); | 763 | try parseDumpProducers(reader, writer, data); |
| 754 | } else if (mem.eql(u8, name, "target_features")) { | 764 | } else if (mem.eql(u8, name, "target_features")) { |
| ... | @@ -764,7 +774,7 @@ const WasmDumper = struct { | ... | @@ -764,7 +774,7 @@ const WasmDumper = struct { |
| 764 | } | 774 | } |
| 765 | } | 775 | } |
| 766 | 776 | ||
| 767 | fn dumpSection(section: std.wasm.Section, data: []const u8, entries: u32, writer: anytype) !void { | 777 | fn dumpSection(step: *Step, section: std.wasm.Section, data: []const u8, entries: u32, writer: anytype) !void { |
| 768 | var fbs = std.io.fixedBufferStream(data); | 778 | var fbs = std.io.fixedBufferStream(data); |
| 769 | const reader = fbs.reader(); | 779 | const reader = fbs.reader(); |
| 770 | 780 | ||
| ... | @@ -774,19 +784,18 @@ const WasmDumper = struct { | ... | @@ -774,19 +784,18 @@ const WasmDumper = struct { |
| 774 | while (i < entries) : (i += 1) { | 784 | while (i < entries) : (i += 1) { |
| 775 | const func_type = try reader.readByte(); | 785 | const func_type = try reader.readByte(); |
| 776 | if (func_type != std.wasm.function_type) { | 786 | if (func_type != std.wasm.function_type) { |
| 777 | std.debug.print("Expected function type, found byte '{d}'\n", .{func_type}); | 787 | return step.fail("expected function type, found byte '{d}'", .{func_type}); |
| 778 | return error.UnexpectedByte; | ||
| 779 | } | 788 | } |
| 780 | const params = try std.leb.readULEB128(u32, reader); | 789 | const params = try std.leb.readULEB128(u32, reader); |
| 781 | try writer.print("params {d}\n", .{params}); | 790 | try writer.print("params {d}\n", .{params}); |
| 782 | var index: u32 = 0; | 791 | var index: u32 = 0; |
| 783 | while (index < params) : (index += 1) { | 792 | while (index < params) : (index += 1) { |
| 784 | try parseDumpType(std.wasm.Valtype, reader, writer); | 793 | try parseDumpType(step, std.wasm.Valtype, reader, writer); |
| 785 | } else index = 0; | 794 | } else index = 0; |
| 786 | const returns = try std.leb.readULEB128(u32, reader); | 795 | const returns = try std.leb.readULEB128(u32, reader); |
| 787 | try writer.print("returns {d}\n", .{returns}); | 796 | try writer.print("returns {d}\n", .{returns}); |
| 788 | while (index < returns) : (index += 1) { | 797 | while (index < returns) : (index += 1) { |
| 789 | try parseDumpType(std.wasm.Valtype, reader, writer); | 798 | try parseDumpType(step, std.wasm.Valtype, reader, writer); |
| 790 | } | 799 | } |
| 791 | } | 800 | } |
| 792 | }, | 801 | }, |
| ... | @@ -800,9 +809,8 @@ const WasmDumper = struct { | ... | @@ -800,9 +809,8 @@ const WasmDumper = struct { |
| 800 | const name = data[fbs.pos..][0..name_len]; | 809 | const name = data[fbs.pos..][0..name_len]; |
| 801 | fbs.pos += name_len; | 810 | fbs.pos += name_len; |
| 802 | 811 | ||
| 803 | const kind = std.meta.intToEnum(std.wasm.ExternalKind, try reader.readByte()) catch |err| { | 812 | const kind = std.meta.intToEnum(std.wasm.ExternalKind, try reader.readByte()) catch { |
| 804 | std.debug.print("Invalid import kind\n", .{}); | 813 | return step.fail("invalid import kind", .{}); |
| 805 | return err; | ||
| 806 | }; | 814 | }; |
| 807 | 815 | ||
| 808 | try writer.print( | 816 | try writer.print( |
| ... | @@ -819,11 +827,11 @@ const WasmDumper = struct { | ... | @@ -819,11 +827,11 @@ const WasmDumper = struct { |
| 819 | try parseDumpLimits(reader, writer); | 827 | try parseDumpLimits(reader, writer); |
| 820 | }, | 828 | }, |
| 821 | .global => { | 829 | .global => { |
| 822 | try parseDumpType(std.wasm.Valtype, reader, writer); | 830 | try parseDumpType(step, std.wasm.Valtype, reader, writer); |
| 823 | try writer.print("mutable {}\n", .{0x01 == try std.leb.readULEB128(u32, reader)}); | 831 | try writer.print("mutable {}\n", .{0x01 == try std.leb.readULEB128(u32, reader)}); |
| 824 | }, | 832 | }, |
| 825 | .table => { | 833 | .table => { |
| 826 | try parseDumpType(std.wasm.RefType, reader, writer); | 834 | try parseDumpType(step, std.wasm.RefType, reader, writer); |
| 827 | try parseDumpLimits(reader, writer); | 835 | try parseDumpLimits(reader, writer); |
| 828 | }, | 836 | }, |
| 829 | } | 837 | } |
| ... | @@ -838,7 +846,7 @@ const WasmDumper = struct { | ... | @@ -838,7 +846,7 @@ const WasmDumper = struct { |
| 838 | .table => { | 846 | .table => { |
| 839 | var i: u32 = 0; | 847 | var i: u32 = 0; |
| 840 | while (i < entries) : (i += 1) { | 848 | while (i < entries) : (i += 1) { |
| 841 | try parseDumpType(std.wasm.RefType, reader, writer); | 849 | try parseDumpType(step, std.wasm.RefType, reader, writer); |
| 842 | try parseDumpLimits(reader, writer); | 850 | try parseDumpLimits(reader, writer); |
| 843 | } | 851 | } |
| 844 | }, | 852 | }, |
| ... | @@ -851,9 +859,9 @@ const WasmDumper = struct { | ... | @@ -851,9 +859,9 @@ const WasmDumper = struct { |
| 851 | .global => { | 859 | .global => { |
| 852 | var i: u32 = 0; | 860 | var i: u32 = 0; |
| 853 | while (i < entries) : (i += 1) { | 861 | while (i < entries) : (i += 1) { |
| 854 | try parseDumpType(std.wasm.Valtype, reader, writer); | 862 | try parseDumpType(step, std.wasm.Valtype, reader, writer); |
| 855 | try writer.print("mutable {}\n", .{0x01 == try std.leb.readULEB128(u1, reader)}); | 863 | try writer.print("mutable {}\n", .{0x01 == try std.leb.readULEB128(u1, reader)}); |
| 856 | try parseDumpInit(reader, writer); | 864 | try parseDumpInit(step, reader, writer); |
| 857 | } | 865 | } |
| 858 | }, | 866 | }, |
| 859 | .@"export" => { | 867 | .@"export" => { |
| ... | @@ -863,9 +871,8 @@ const WasmDumper = struct { | ... | @@ -863,9 +871,8 @@ const WasmDumper = struct { |
| 863 | const name = data[fbs.pos..][0..name_len]; | 871 | const name = data[fbs.pos..][0..name_len]; |
| 864 | fbs.pos += name_len; | 872 | fbs.pos += name_len; |
| 865 | const kind_byte = try std.leb.readULEB128(u8, reader); | 873 | const kind_byte = try std.leb.readULEB128(u8, reader); |
| 866 | const kind = std.meta.intToEnum(std.wasm.ExternalKind, kind_byte) catch |err| { | 874 | const kind = std.meta.intToEnum(std.wasm.ExternalKind, kind_byte) catch { |
| 867 | std.debug.print("invalid export kind value '{d}'\n", .{kind_byte}); | 875 | return step.fail("invalid export kind value '{d}'", .{kind_byte}); |
| 868 | return err; | ||
| 869 | }; | 876 | }; |
| 870 | const index = try std.leb.readULEB128(u32, reader); | 877 | const index = try std.leb.readULEB128(u32, reader); |
| 871 | try writer.print( | 878 | try writer.print( |
| ... | @@ -880,7 +887,7 @@ const WasmDumper = struct { | ... | @@ -880,7 +887,7 @@ const WasmDumper = struct { |
| 880 | var i: u32 = 0; | 887 | var i: u32 = 0; |
| 881 | while (i < entries) : (i += 1) { | 888 | while (i < entries) : (i += 1) { |
| 882 | try writer.print("table index {d}\n", .{try std.leb.readULEB128(u32, reader)}); | 889 | try writer.print("table index {d}\n", .{try std.leb.readULEB128(u32, reader)}); |
| 883 | try parseDumpInit(reader, writer); | 890 | try parseDumpInit(step, reader, writer); |
| 884 | 891 | ||
| 885 | const function_indexes = try std.leb.readULEB128(u32, reader); | 892 | const function_indexes = try std.leb.readULEB128(u32, reader); |
| 886 | var function_index: u32 = 0; | 893 | var function_index: u32 = 0; |
| ... | @@ -896,7 +903,7 @@ const WasmDumper = struct { | ... | @@ -896,7 +903,7 @@ const WasmDumper = struct { |
| 896 | while (i < entries) : (i += 1) { | 903 | while (i < entries) : (i += 1) { |
| 897 | const index = try std.leb.readULEB128(u32, reader); | 904 | const index = try std.leb.readULEB128(u32, reader); |
| 898 | try writer.print("memory index 0x{x}\n", .{index}); | 905 | try writer.print("memory index 0x{x}\n", .{index}); |
| 899 | try parseDumpInit(reader, writer); | 906 | try parseDumpInit(step, reader, writer); |
| 900 | const size = try std.leb.readULEB128(u32, reader); | 907 | const size = try std.leb.readULEB128(u32, reader); |
| 901 | try writer.print("size {d}\n", .{size}); | 908 | try writer.print("size {d}\n", .{size}); |
| 902 | try reader.skipBytes(size, .{}); // we do not care about the content of the segments | 909 | try reader.skipBytes(size, .{}); // we do not care about the content of the segments |
| ... | @@ -906,11 +913,10 @@ const WasmDumper = struct { | ... | @@ -906,11 +913,10 @@ const WasmDumper = struct { |
| 906 | } | 913 | } |
| 907 | } | 914 | } |
| 908 | 915 | ||
| 909 | fn parseDumpType(comptime WasmType: type, reader: anytype, writer: anytype) !void { | 916 | fn parseDumpType(step: *Step, comptime WasmType: type, reader: anytype, writer: anytype) !void { |
| 910 | const type_byte = try reader.readByte(); | 917 | const type_byte = try reader.readByte(); |
| 911 | const valtype = std.meta.intToEnum(WasmType, type_byte) catch |err| { | 918 | const valtype = std.meta.intToEnum(WasmType, type_byte) catch { |
| 912 | std.debug.print("Invalid wasm type value '{d}'\n", .{type_byte}); | 919 | return step.fail("Invalid wasm type value '{d}'", .{type_byte}); |
| 913 | return err; | ||
| 914 | }; | 920 | }; |
| 915 | try writer.print("type {s}\n", .{@tagName(valtype)}); | 921 | try writer.print("type {s}\n", .{@tagName(valtype)}); |
| 916 | } | 922 | } |
| ... | @@ -925,11 +931,10 @@ const WasmDumper = struct { | ... | @@ -925,11 +931,10 @@ const WasmDumper = struct { |
| 925 | } | 931 | } |
| 926 | } | 932 | } |
| 927 | 933 | ||
| 928 | fn parseDumpInit(reader: anytype, writer: anytype) !void { | 934 | fn parseDumpInit(step: *Step, reader: anytype, writer: anytype) !void { |
| 929 | const byte = try std.leb.readULEB128(u8, reader); | 935 | const byte = try std.leb.readULEB128(u8, reader); |
| 930 | const opcode = std.meta.intToEnum(std.wasm.Opcode, byte) catch |err| { | 936 | const opcode = std.meta.intToEnum(std.wasm.Opcode, byte) catch { |
| 931 | std.debug.print("invalid wasm opcode '{d}'\n", .{byte}); | 937 | return step.fail("invalid wasm opcode '{d}'", .{byte}); |
| 932 | return err; | ||
| 933 | }; | 938 | }; |
| 934 | switch (opcode) { | 939 | switch (opcode) { |
| 935 | .i32_const => try writer.print("i32.const {x}\n", .{try std.leb.readILEB128(i32, reader)}), | 940 | .i32_const => try writer.print("i32.const {x}\n", .{try std.leb.readILEB128(i32, reader)}), |
| ... | @@ -941,14 +946,13 @@ const WasmDumper = struct { | ... | @@ -941,14 +946,13 @@ const WasmDumper = struct { |
| 941 | } | 946 | } |
| 942 | const end_opcode = try std.leb.readULEB128(u8, reader); | 947 | const end_opcode = try std.leb.readULEB128(u8, reader); |
| 943 | if (end_opcode != std.wasm.opcode(.end)) { | 948 | if (end_opcode != std.wasm.opcode(.end)) { |
| 944 | std.debug.print("expected 'end' opcode in init expression\n", .{}); | 949 | return step.fail("expected 'end' opcode in init expression", .{}); |
| 945 | return error.MissingEndOpcode; | ||
| 946 | } | 950 | } |
| 947 | } | 951 | } |
| 948 | 952 | ||
| 949 | fn parseDumpNames(reader: anytype, writer: anytype, data: []const u8) !void { | 953 | fn parseDumpNames(step: *Step, reader: anytype, writer: anytype, data: []const u8) !void { |
| 950 | while (reader.context.pos < data.len) { | 954 | while (reader.context.pos < data.len) { |
| 951 | try parseDumpType(std.wasm.NameSubsection, reader, writer); | 955 | try parseDumpType(step, std.wasm.NameSubsection, reader, writer); |
| 952 | const size = try std.leb.readULEB128(u32, reader); | 956 | const size = try std.leb.readULEB128(u32, reader); |
| 953 | const entries = try std.leb.readULEB128(u32, reader); | 957 | const entries = try std.leb.readULEB128(u32, reader); |
| 954 | try writer.print( | 958 | try writer.print( |
lib/std/Build/CompileStep.zig+443-376| ... | @@ -1,7 +1,6 @@ | ... | @@ -1,7 +1,6 @@ |
| 1 | const builtin = @import("builtin"); | 1 | const builtin = @import("builtin"); |
| 2 | const std = @import("../std.zig"); | 2 | const std = @import("../std.zig"); |
| 3 | const mem = std.mem; | 3 | const mem = std.mem; |
| 4 | const log = std.log; | ||
| 5 | const fs = std.fs; | 4 | const fs = std.fs; |
| 6 | const assert = std.debug.assert; | 5 | const assert = std.debug.assert; |
| 7 | const panic = std.debug.panic; | 6 | const panic = std.debug.panic; |
| ... | @@ -22,7 +21,6 @@ const InstallDir = std.Build.InstallDir; | ... | @@ -22,7 +21,6 @@ const InstallDir = std.Build.InstallDir; |
| 22 | const InstallArtifactStep = std.Build.InstallArtifactStep; | 21 | const InstallArtifactStep = std.Build.InstallArtifactStep; |
| 23 | const GeneratedFile = std.Build.GeneratedFile; | 22 | const GeneratedFile = std.Build.GeneratedFile; |
| 24 | const ObjCopyStep = std.Build.ObjCopyStep; | 23 | const ObjCopyStep = std.Build.ObjCopyStep; |
| 25 | const EmulatableRunStep = std.Build.EmulatableRunStep; | ||
| 26 | const CheckObjectStep = std.Build.CheckObjectStep; | 24 | const CheckObjectStep = std.Build.CheckObjectStep; |
| 27 | const RunStep = std.Build.RunStep; | 25 | const RunStep = std.Build.RunStep; |
| 28 | const OptionsStep = std.Build.OptionsStep; | 26 | const OptionsStep = std.Build.OptionsStep; |
| ... | @@ -32,7 +30,6 @@ const CompileStep = @This(); | ... | @@ -32,7 +30,6 @@ const CompileStep = @This(); |
| 32 | pub const base_id: Step.Id = .compile; | 30 | pub const base_id: Step.Id = .compile; |
| 33 | 31 | ||
| 34 | step: Step, | 32 | step: Step, |
| 35 | builder: *std.Build, | ||
| 36 | name: []const u8, | 33 | name: []const u8, |
| 37 | target: CrossTarget, | 34 | target: CrossTarget, |
| 38 | target_info: NativeTargetInfo, | 35 | target_info: NativeTargetInfo, |
| ... | @@ -49,9 +46,9 @@ strip: ?bool, | ... | @@ -49,9 +46,9 @@ strip: ?bool, |
| 49 | unwind_tables: ?bool, | 46 | unwind_tables: ?bool, |
| 50 | // keep in sync with src/link.zig:CompressDebugSections | 47 | // keep in sync with src/link.zig:CompressDebugSections |
| 51 | compress_debug_sections: enum { none, zlib } = .none, | 48 | compress_debug_sections: enum { none, zlib } = .none, |
| 52 | lib_paths: ArrayList([]const u8), | 49 | lib_paths: ArrayList(FileSource), |
| 53 | rpaths: ArrayList([]const u8), | 50 | rpaths: ArrayList(FileSource), |
| 54 | framework_dirs: ArrayList([]const u8), | 51 | framework_dirs: ArrayList(FileSource), |
| 55 | frameworks: StringHashMap(FrameworkLinkInfo), | 52 | frameworks: StringHashMap(FrameworkLinkInfo), |
| 56 | verbose_link: bool, | 53 | verbose_link: bool, |
| 57 | verbose_cc: bool, | 54 | verbose_cc: bool, |
| ... | @@ -86,7 +83,6 @@ c_std: std.Build.CStd, | ... | @@ -86,7 +83,6 @@ c_std: std.Build.CStd, |
| 86 | zig_lib_dir: ?[]const u8, | 83 | zig_lib_dir: ?[]const u8, |
| 87 | main_pkg_path: ?[]const u8, | 84 | main_pkg_path: ?[]const u8, |
| 88 | exec_cmd_args: ?[]const ?[]const u8, | 85 | exec_cmd_args: ?[]const ?[]const u8, |
| 89 | name_prefix: []const u8, | ||
| 90 | filter: ?[]const u8, | 86 | filter: ?[]const u8, |
| 91 | test_evented_io: bool = false, | 87 | test_evented_io: bool = false, |
| 92 | test_runner: ?[]const u8, | 88 | test_runner: ?[]const u8, |
| ... | @@ -210,10 +206,17 @@ want_lto: ?bool = null, | ... | @@ -210,10 +206,17 @@ want_lto: ?bool = null, |
| 210 | use_llvm: ?bool = null, | 206 | use_llvm: ?bool = null, |
| 211 | use_lld: ?bool = null, | 207 | use_lld: ?bool = null, |
| 212 | 208 | ||
| 209 | /// This is an advanced setting that can change the intent of this CompileStep. | ||
| 210 | /// If this slice has nonzero length, it means that this CompileStep exists to | ||
| 211 | /// check for compile errors and return *success* if they match, and failure | ||
| 212 | /// otherwise. | ||
| 213 | expect_errors: []const []const u8 = &.{}, | ||
| 214 | |||
| 213 | output_path_source: GeneratedFile, | 215 | output_path_source: GeneratedFile, |
| 214 | output_lib_path_source: GeneratedFile, | 216 | output_lib_path_source: GeneratedFile, |
| 215 | output_h_path_source: GeneratedFile, | 217 | output_h_path_source: GeneratedFile, |
| 216 | output_pdb_path_source: GeneratedFile, | 218 | output_pdb_path_source: GeneratedFile, |
| 219 | output_dirname_source: GeneratedFile, | ||
| 217 | 220 | ||
| 218 | pub const CSourceFiles = struct { | 221 | pub const CSourceFiles = struct { |
| 219 | files: []const []const u8, | 222 | files: []const []const u8, |
| ... | @@ -277,6 +280,7 @@ pub const Options = struct { | ... | @@ -277,6 +280,7 @@ pub const Options = struct { |
| 277 | kind: Kind, | 280 | kind: Kind, |
| 278 | linkage: ?Linkage = null, | 281 | linkage: ?Linkage = null, |
| 279 | version: ?std.builtin.Version = null, | 282 | version: ?std.builtin.Version = null, |
| 283 | max_rss: usize = 0, | ||
| 280 | }; | 284 | }; |
| 281 | 285 | ||
| 282 | pub const Kind = enum { | 286 | pub const Kind = enum { |
| ... | @@ -284,7 +288,6 @@ pub const Kind = enum { | ... | @@ -284,7 +288,6 @@ pub const Kind = enum { |
| 284 | lib, | 288 | lib, |
| 285 | obj, | 289 | obj, |
| 286 | @"test", | 290 | @"test", |
| 287 | test_exe, | ||
| 288 | }; | 291 | }; |
| 289 | 292 | ||
| 290 | pub const Linkage = enum { dynamic, static }; | 293 | pub const Linkage = enum { dynamic, static }; |
| ... | @@ -305,18 +308,35 @@ pub const EmitOption = union(enum) { | ... | @@ -305,18 +308,35 @@ pub const EmitOption = union(enum) { |
| 305 | } | 308 | } |
| 306 | }; | 309 | }; |
| 307 | 310 | ||
| 308 | pub fn create(builder: *std.Build, options: Options) *CompileStep { | 311 | pub fn create(owner: *std.Build, options: Options) *CompileStep { |
| 309 | const name = builder.dupe(options.name); | 312 | const name = owner.dupe(options.name); |
| 310 | const root_src: ?FileSource = if (options.root_source_file) |rsrc| rsrc.dupe(builder) else null; | 313 | const root_src: ?FileSource = if (options.root_source_file) |rsrc| rsrc.dupe(owner) else null; |
| 311 | if (mem.indexOf(u8, name, "/") != null or mem.indexOf(u8, name, "\\") != null) { | 314 | if (mem.indexOf(u8, name, "/") != null or mem.indexOf(u8, name, "\\") != null) { |
| 312 | panic("invalid name: '{s}'. It looks like a file path, but it is supposed to be the library or application name.", .{name}); | 315 | panic("invalid name: '{s}'. It looks like a file path, but it is supposed to be the library or application name.", .{name}); |
| 313 | } | 316 | } |
| 314 | 317 | ||
| 315 | const self = builder.allocator.create(CompileStep) catch @panic("OOM"); | 318 | // Avoid the common case of the step name looking like "zig test test". |
| 319 | const name_adjusted = if (options.kind == .@"test" and mem.eql(u8, name, "test")) | ||
| 320 | "" | ||
| 321 | else | ||
| 322 | owner.fmt("{s} ", .{name}); | ||
| 323 | |||
| 324 | const step_name = owner.fmt("{s} {s}{s} {s}", .{ | ||
| 325 | switch (options.kind) { | ||
| 326 | .exe => "zig build-exe", | ||
| 327 | .lib => "zig build-lib", | ||
| 328 | .obj => "zig build-obj", | ||
| 329 | .@"test" => "zig test", | ||
| 330 | }, | ||
| 331 | name_adjusted, | ||
| 332 | @tagName(options.optimize), | ||
| 333 | options.target.zigTriple(owner.allocator) catch @panic("OOM"), | ||
| 334 | }); | ||
| 335 | |||
| 336 | const self = owner.allocator.create(CompileStep) catch @panic("OOM"); | ||
| 316 | self.* = CompileStep{ | 337 | self.* = CompileStep{ |
| 317 | .strip = null, | 338 | .strip = null, |
| 318 | .unwind_tables = null, | 339 | .unwind_tables = null, |
| 319 | .builder = builder, | ||
| 320 | .verbose_link = false, | 340 | .verbose_link = false, |
| 321 | .verbose_cc = false, | 341 | .verbose_cc = false, |
| 322 | .optimize = options.optimize, | 342 | .optimize = options.optimize, |
| ... | @@ -325,29 +345,34 @@ pub fn create(builder: *std.Build, options: Options) *CompileStep { | ... | @@ -325,29 +345,34 @@ pub fn create(builder: *std.Build, options: Options) *CompileStep { |
| 325 | .kind = options.kind, | 345 | .kind = options.kind, |
| 326 | .root_src = root_src, | 346 | .root_src = root_src, |
| 327 | .name = name, | 347 | .name = name, |
| 328 | .frameworks = StringHashMap(FrameworkLinkInfo).init(builder.allocator), | 348 | .frameworks = StringHashMap(FrameworkLinkInfo).init(owner.allocator), |
| 329 | .step = Step.init(base_id, name, builder.allocator, make), | 349 | .step = Step.init(.{ |
| 350 | .id = base_id, | ||
| 351 | .name = step_name, | ||
| 352 | .owner = owner, | ||
| 353 | .makeFn = make, | ||
| 354 | .max_rss = options.max_rss, | ||
| 355 | }), | ||
| 330 | .version = options.version, | 356 | .version = options.version, |
| 331 | .out_filename = undefined, | 357 | .out_filename = undefined, |
| 332 | .out_h_filename = builder.fmt("{s}.h", .{name}), | 358 | .out_h_filename = owner.fmt("{s}.h", .{name}), |
| 333 | .out_lib_filename = undefined, | 359 | .out_lib_filename = undefined, |
| 334 | .out_pdb_filename = builder.fmt("{s}.pdb", .{name}), | 360 | .out_pdb_filename = owner.fmt("{s}.pdb", .{name}), |
| 335 | .major_only_filename = null, | 361 | .major_only_filename = null, |
| 336 | .name_only_filename = null, | 362 | .name_only_filename = null, |
| 337 | .modules = std.StringArrayHashMap(*Module).init(builder.allocator), | 363 | .modules = std.StringArrayHashMap(*Module).init(owner.allocator), |
| 338 | .include_dirs = ArrayList(IncludeDir).init(builder.allocator), | 364 | .include_dirs = ArrayList(IncludeDir).init(owner.allocator), |
| 339 | .link_objects = ArrayList(LinkObject).init(builder.allocator), | 365 | .link_objects = ArrayList(LinkObject).init(owner.allocator), |
| 340 | .c_macros = ArrayList([]const u8).init(builder.allocator), | 366 | .c_macros = ArrayList([]const u8).init(owner.allocator), |
| 341 | .lib_paths = ArrayList([]const u8).init(builder.allocator), | 367 | .lib_paths = ArrayList(FileSource).init(owner.allocator), |
| 342 | .rpaths = ArrayList([]const u8).init(builder.allocator), | 368 | .rpaths = ArrayList(FileSource).init(owner.allocator), |
| 343 | .framework_dirs = ArrayList([]const u8).init(builder.allocator), | 369 | .framework_dirs = ArrayList(FileSource).init(owner.allocator), |
| 344 | .installed_headers = ArrayList(*Step).init(builder.allocator), | 370 | .installed_headers = ArrayList(*Step).init(owner.allocator), |
| 345 | .object_src = undefined, | 371 | .object_src = undefined, |
| 346 | .c_std = std.Build.CStd.C99, | 372 | .c_std = std.Build.CStd.C99, |
| 347 | .zig_lib_dir = null, | 373 | .zig_lib_dir = null, |
| 348 | .main_pkg_path = null, | 374 | .main_pkg_path = null, |
| 349 | .exec_cmd_args = null, | 375 | .exec_cmd_args = null, |
| 350 | .name_prefix = "", | ||
| 351 | .filter = null, | 376 | .filter = null, |
| 352 | .test_runner = null, | 377 | .test_runner = null, |
| 353 | .disable_stack_probing = false, | 378 | .disable_stack_probing = false, |
| ... | @@ -363,6 +388,7 @@ pub fn create(builder: *std.Build, options: Options) *CompileStep { | ... | @@ -363,6 +388,7 @@ pub fn create(builder: *std.Build, options: Options) *CompileStep { |
| 363 | .output_lib_path_source = GeneratedFile{ .step = &self.step }, | 388 | .output_lib_path_source = GeneratedFile{ .step = &self.step }, |
| 364 | .output_h_path_source = GeneratedFile{ .step = &self.step }, | 389 | .output_h_path_source = GeneratedFile{ .step = &self.step }, |
| 365 | .output_pdb_path_source = GeneratedFile{ .step = &self.step }, | 390 | .output_pdb_path_source = GeneratedFile{ .step = &self.step }, |
| 391 | .output_dirname_source = GeneratedFile{ .step = &self.step }, | ||
| 366 | 392 | ||
| 367 | .target_info = NativeTargetInfo.detect(self.target) catch @panic("unhandled error"), | 393 | .target_info = NativeTargetInfo.detect(self.target) catch @panic("unhandled error"), |
| 368 | }; | 394 | }; |
| ... | @@ -372,15 +398,16 @@ pub fn create(builder: *std.Build, options: Options) *CompileStep { | ... | @@ -372,15 +398,16 @@ pub fn create(builder: *std.Build, options: Options) *CompileStep { |
| 372 | } | 398 | } |
| 373 | 399 | ||
| 374 | fn computeOutFileNames(self: *CompileStep) void { | 400 | fn computeOutFileNames(self: *CompileStep) void { |
| 401 | const b = self.step.owner; | ||
| 375 | const target = self.target_info.target; | 402 | const target = self.target_info.target; |
| 376 | 403 | ||
| 377 | self.out_filename = std.zig.binNameAlloc(self.builder.allocator, .{ | 404 | self.out_filename = std.zig.binNameAlloc(b.allocator, .{ |
| 378 | .root_name = self.name, | 405 | .root_name = self.name, |
| 379 | .target = target, | 406 | .target = target, |
| 380 | .output_mode = switch (self.kind) { | 407 | .output_mode = switch (self.kind) { |
| 381 | .lib => .Lib, | 408 | .lib => .Lib, |
| 382 | .obj => .Obj, | 409 | .obj => .Obj, |
| 383 | .exe, .@"test", .test_exe => .Exe, | 410 | .exe, .@"test" => .Exe, |
| 384 | }, | 411 | }, |
| 385 | .link_mode = if (self.linkage) |some| @as(std.builtin.LinkMode, switch (some) { | 412 | .link_mode = if (self.linkage) |some| @as(std.builtin.LinkMode, switch (some) { |
| 386 | .dynamic => .Dynamic, | 413 | .dynamic => .Dynamic, |
| ... | @@ -394,30 +421,30 @@ fn computeOutFileNames(self: *CompileStep) void { | ... | @@ -394,30 +421,30 @@ fn computeOutFileNames(self: *CompileStep) void { |
| 394 | self.out_lib_filename = self.out_filename; | 421 | self.out_lib_filename = self.out_filename; |
| 395 | } else if (self.version) |version| { | 422 | } else if (self.version) |version| { |
| 396 | if (target.isDarwin()) { | 423 | if (target.isDarwin()) { |
| 397 | self.major_only_filename = self.builder.fmt("lib{s}.{d}.dylib", .{ | 424 | self.major_only_filename = b.fmt("lib{s}.{d}.dylib", .{ |
| 398 | self.name, | 425 | self.name, |
| 399 | version.major, | 426 | version.major, |
| 400 | }); | 427 | }); |
| 401 | self.name_only_filename = self.builder.fmt("lib{s}.dylib", .{self.name}); | 428 | self.name_only_filename = b.fmt("lib{s}.dylib", .{self.name}); |
| 402 | self.out_lib_filename = self.out_filename; | 429 | self.out_lib_filename = self.out_filename; |
| 403 | } else if (target.os.tag == .windows) { | 430 | } else if (target.os.tag == .windows) { |
| 404 | self.out_lib_filename = self.builder.fmt("{s}.lib", .{self.name}); | 431 | self.out_lib_filename = b.fmt("{s}.lib", .{self.name}); |
| 405 | } else { | 432 | } else { |
| 406 | self.major_only_filename = self.builder.fmt("lib{s}.so.{d}", .{ self.name, version.major }); | 433 | self.major_only_filename = b.fmt("lib{s}.so.{d}", .{ self.name, version.major }); |
| 407 | self.name_only_filename = self.builder.fmt("lib{s}.so", .{self.name}); | 434 | self.name_only_filename = b.fmt("lib{s}.so", .{self.name}); |
| 408 | self.out_lib_filename = self.out_filename; | 435 | self.out_lib_filename = self.out_filename; |
| 409 | } | 436 | } |
| 410 | } else { | 437 | } else { |
| 411 | if (target.isDarwin()) { | 438 | if (target.isDarwin()) { |
| 412 | self.out_lib_filename = self.out_filename; | 439 | self.out_lib_filename = self.out_filename; |
| 413 | } else if (target.os.tag == .windows) { | 440 | } else if (target.os.tag == .windows) { |
| 414 | self.out_lib_filename = self.builder.fmt("{s}.lib", .{self.name}); | 441 | self.out_lib_filename = b.fmt("{s}.lib", .{self.name}); |
| 415 | } else { | 442 | } else { |
| 416 | self.out_lib_filename = self.out_filename; | 443 | self.out_lib_filename = self.out_filename; |
| 417 | } | 444 | } |
| 418 | } | 445 | } |
| 419 | if (self.output_dir != null) { | 446 | if (self.output_dir != null) { |
| 420 | self.output_lib_path_source.path = self.builder.pathJoin( | 447 | self.output_lib_path_source.path = b.pathJoin( |
| 421 | &.{ self.output_dir.?, self.out_lib_filename }, | 448 | &.{ self.output_dir.?, self.out_lib_filename }, |
| 422 | ); | 449 | ); |
| 423 | } | 450 | } |
| ... | @@ -425,17 +452,20 @@ fn computeOutFileNames(self: *CompileStep) void { | ... | @@ -425,17 +452,20 @@ fn computeOutFileNames(self: *CompileStep) void { |
| 425 | } | 452 | } |
| 426 | 453 | ||
| 427 | pub fn setOutputDir(self: *CompileStep, dir: []const u8) void { | 454 | pub fn setOutputDir(self: *CompileStep, dir: []const u8) void { |
| 428 | self.output_dir = self.builder.dupePath(dir); | 455 | const b = self.step.owner; |
| 456 | self.output_dir = b.dupePath(dir); | ||
| 429 | } | 457 | } |
| 430 | 458 | ||
| 431 | pub fn install(self: *CompileStep) void { | 459 | pub fn install(self: *CompileStep) void { |
| 432 | self.builder.installArtifact(self); | 460 | const b = self.step.owner; |
| 461 | b.installArtifact(self); | ||
| 433 | } | 462 | } |
| 434 | 463 | ||
| 435 | pub fn installHeader(a: *CompileStep, src_path: []const u8, dest_rel_path: []const u8) void { | 464 | pub fn installHeader(cs: *CompileStep, src_path: []const u8, dest_rel_path: []const u8) void { |
| 436 | const install_file = a.builder.addInstallHeaderFile(src_path, dest_rel_path); | 465 | const b = cs.step.owner; |
| 437 | a.builder.getInstallStep().dependOn(&install_file.step); | 466 | const install_file = b.addInstallHeaderFile(src_path, dest_rel_path); |
| 438 | a.installed_headers.append(&install_file.step) catch @panic("OOM"); | 467 | b.getInstallStep().dependOn(&install_file.step); |
| 468 | cs.installed_headers.append(&install_file.step) catch @panic("OOM"); | ||
| 439 | } | 469 | } |
| 440 | 470 | ||
| 441 | pub const InstallConfigHeaderOptions = struct { | 471 | pub const InstallConfigHeaderOptions = struct { |
| ... | @@ -449,13 +479,14 @@ pub fn installConfigHeader( | ... | @@ -449,13 +479,14 @@ pub fn installConfigHeader( |
| 449 | options: InstallConfigHeaderOptions, | 479 | options: InstallConfigHeaderOptions, |
| 450 | ) void { | 480 | ) void { |
| 451 | const dest_rel_path = options.dest_rel_path orelse config_header.include_path; | 481 | const dest_rel_path = options.dest_rel_path orelse config_header.include_path; |
| 452 | const install_file = cs.builder.addInstallFileWithDir( | 482 | const b = cs.step.owner; |
| 483 | const install_file = b.addInstallFileWithDir( | ||
| 453 | .{ .generated = &config_header.output_file }, | 484 | .{ .generated = &config_header.output_file }, |
| 454 | options.install_dir, | 485 | options.install_dir, |
| 455 | dest_rel_path, | 486 | dest_rel_path, |
| 456 | ); | 487 | ); |
| 457 | install_file.step.dependOn(&config_header.step); | 488 | install_file.step.dependOn(&config_header.step); |
| 458 | cs.builder.getInstallStep().dependOn(&install_file.step); | 489 | b.getInstallStep().dependOn(&install_file.step); |
| 459 | cs.installed_headers.append(&install_file.step) catch @panic("OOM"); | 490 | cs.installed_headers.append(&install_file.step) catch @panic("OOM"); |
| 460 | } | 491 | } |
| 461 | 492 | ||
| ... | @@ -472,91 +503,83 @@ pub fn installHeadersDirectory( | ... | @@ -472,91 +503,83 @@ pub fn installHeadersDirectory( |
| 472 | } | 503 | } |
| 473 | 504 | ||
| 474 | pub fn installHeadersDirectoryOptions( | 505 | pub fn installHeadersDirectoryOptions( |
| 475 | a: *CompileStep, | 506 | cs: *CompileStep, |
| 476 | options: std.Build.InstallDirStep.Options, | 507 | options: std.Build.InstallDirStep.Options, |
| 477 | ) void { | 508 | ) void { |
| 478 | const install_dir = a.builder.addInstallDirectory(options); | 509 | const b = cs.step.owner; |
| 479 | a.builder.getInstallStep().dependOn(&install_dir.step); | 510 | const install_dir = b.addInstallDirectory(options); |
| 480 | a.installed_headers.append(&install_dir.step) catch @panic("OOM"); | 511 | b.getInstallStep().dependOn(&install_dir.step); |
| 512 | cs.installed_headers.append(&install_dir.step) catch @panic("OOM"); | ||
| 481 | } | 513 | } |
| 482 | 514 | ||
| 483 | pub fn installLibraryHeaders(a: *CompileStep, l: *CompileStep) void { | 515 | pub fn installLibraryHeaders(cs: *CompileStep, l: *CompileStep) void { |
| 484 | assert(l.kind == .lib); | 516 | assert(l.kind == .lib); |
| 485 | const install_step = a.builder.getInstallStep(); | 517 | const b = cs.step.owner; |
| 518 | const install_step = b.getInstallStep(); | ||
| 486 | // Copy each element from installed_headers, modifying the builder | 519 | // Copy each element from installed_headers, modifying the builder |
| 487 | // to be the new parent's builder. | 520 | // to be the new parent's builder. |
| 488 | for (l.installed_headers.items) |step| { | 521 | for (l.installed_headers.items) |step| { |
| 489 | const step_copy = switch (step.id) { | 522 | const step_copy = switch (step.id) { |
| 490 | inline .install_file, .install_dir => |id| blk: { | 523 | inline .install_file, .install_dir => |id| blk: { |
| 491 | const T = id.Type(); | 524 | const T = id.Type(); |
| 492 | const ptr = a.builder.allocator.create(T) catch @panic("OOM"); | 525 | const ptr = b.allocator.create(T) catch @panic("OOM"); |
| 493 | ptr.* = step.cast(T).?.*; | 526 | ptr.* = step.cast(T).?.*; |
| 494 | ptr.override_source_builder = ptr.builder; | 527 | ptr.dest_builder = b; |
| 495 | ptr.builder = a.builder; | ||
| 496 | break :blk &ptr.step; | 528 | break :blk &ptr.step; |
| 497 | }, | 529 | }, |
| 498 | else => unreachable, | 530 | else => unreachable, |
| 499 | }; | 531 | }; |
| 500 | a.installed_headers.append(step_copy) catch @panic("OOM"); | 532 | cs.installed_headers.append(step_copy) catch @panic("OOM"); |
| 501 | install_step.dependOn(step_copy); | 533 | install_step.dependOn(step_copy); |
| 502 | } | 534 | } |
| 503 | a.installed_headers.appendSlice(l.installed_headers.items) catch @panic("OOM"); | 535 | cs.installed_headers.appendSlice(l.installed_headers.items) catch @panic("OOM"); |
| 504 | } | 536 | } |
| 505 | 537 | ||
| 506 | pub fn addObjCopy(cs: *CompileStep, options: ObjCopyStep.Options) *ObjCopyStep { | 538 | pub fn addObjCopy(cs: *CompileStep, options: ObjCopyStep.Options) *ObjCopyStep { |
| 539 | const b = cs.step.owner; | ||
| 507 | var copy = options; | 540 | var copy = options; |
| 508 | if (copy.basename == null) { | 541 | if (copy.basename == null) { |
| 509 | if (options.format) |f| { | 542 | if (options.format) |f| { |
| 510 | copy.basename = cs.builder.fmt("{s}.{s}", .{ cs.name, @tagName(f) }); | 543 | copy.basename = b.fmt("{s}.{s}", .{ cs.name, @tagName(f) }); |
| 511 | } else { | 544 | } else { |
| 512 | copy.basename = cs.name; | 545 | copy.basename = cs.name; |
| 513 | } | 546 | } |
| 514 | } | 547 | } |
| 515 | return cs.builder.addObjCopy(cs.getOutputSource(), copy); | 548 | return b.addObjCopy(cs.getOutputSource(), copy); |
| 516 | } | 549 | } |
| 517 | 550 | ||
| 518 | /// Deprecated: use `std.Build.addRunArtifact` | 551 | /// Deprecated: use `std.Build.addRunArtifact` |
| 519 | /// This function will run in the context of the package that created the executable, | 552 | /// This function will run in the context of the package that created the executable, |
| 520 | /// which is undesirable when running an executable provided by a dependency package. | 553 | /// which is undesirable when running an executable provided by a dependency package. |
| 521 | pub fn run(exe: *CompileStep) *RunStep { | 554 | pub fn run(cs: *CompileStep) *RunStep { |
| 522 | return exe.builder.addRunArtifact(exe); | 555 | return cs.step.owner.addRunArtifact(cs); |
| 523 | } | ||
| 524 | |||
| 525 | /// Creates an `EmulatableRunStep` with an executable built with `addExecutable`. | ||
| 526 | /// Allows running foreign binaries through emulation platforms such as Qemu or Rosetta. | ||
| 527 | /// When a binary cannot be ran through emulation or the option is disabled, a warning | ||
| 528 | /// will be printed and the binary will *NOT* be ran. | ||
| 529 | pub fn runEmulatable(exe: *CompileStep) *EmulatableRunStep { | ||
| 530 | assert(exe.kind == .exe or exe.kind == .test_exe); | ||
| 531 | |||
| 532 | const run_step = EmulatableRunStep.create(exe.builder, exe.builder.fmt("run {s}", .{exe.step.name}), exe); | ||
| 533 | if (exe.vcpkg_bin_path) |path| { | ||
| 534 | RunStep.addPathDirInternal(&run_step.step, exe.builder, path); | ||
| 535 | } | ||
| 536 | return run_step; | ||
| 537 | } | 556 | } |
| 538 | 557 | ||
| 539 | pub fn checkObject(self: *CompileStep, obj_format: std.Target.ObjectFormat) *CheckObjectStep { | 558 | pub fn checkObject(self: *CompileStep) *CheckObjectStep { |
| 540 | return CheckObjectStep.create(self.builder, self.getOutputSource(), obj_format); | 559 | return CheckObjectStep.create(self.step.owner, self.getOutputSource(), self.target_info.target.ofmt); |
| 541 | } | 560 | } |
| 542 | 561 | ||
| 543 | pub fn setLinkerScriptPath(self: *CompileStep, source: FileSource) void { | 562 | pub fn setLinkerScriptPath(self: *CompileStep, source: FileSource) void { |
| 544 | self.linker_script = source.dupe(self.builder); | 563 | const b = self.step.owner; |
| 564 | self.linker_script = source.dupe(b); | ||
| 545 | source.addStepDependencies(&self.step); | 565 | source.addStepDependencies(&self.step); |
| 546 | } | 566 | } |
| 547 | 567 | ||
| 548 | pub fn linkFramework(self: *CompileStep, framework_name: []const u8) void { | 568 | pub fn linkFramework(self: *CompileStep, framework_name: []const u8) void { |
| 549 | self.frameworks.put(self.builder.dupe(framework_name), .{}) catch @panic("OOM"); | 569 | const b = self.step.owner; |
| 570 | self.frameworks.put(b.dupe(framework_name), .{}) catch @panic("OOM"); | ||
| 550 | } | 571 | } |
| 551 | 572 | ||
| 552 | pub fn linkFrameworkNeeded(self: *CompileStep, framework_name: []const u8) void { | 573 | pub fn linkFrameworkNeeded(self: *CompileStep, framework_name: []const u8) void { |
| 553 | self.frameworks.put(self.builder.dupe(framework_name), .{ | 574 | const b = self.step.owner; |
| 575 | self.frameworks.put(b.dupe(framework_name), .{ | ||
| 554 | .needed = true, | 576 | .needed = true, |
| 555 | }) catch @panic("OOM"); | 577 | }) catch @panic("OOM"); |
| 556 | } | 578 | } |
| 557 | 579 | ||
| 558 | pub fn linkFrameworkWeak(self: *CompileStep, framework_name: []const u8) void { | 580 | pub fn linkFrameworkWeak(self: *CompileStep, framework_name: []const u8) void { |
| 559 | self.frameworks.put(self.builder.dupe(framework_name), .{ | 581 | const b = self.step.owner; |
| 582 | self.frameworks.put(b.dupe(framework_name), .{ | ||
| 560 | .weak = true, | 583 | .weak = true, |
| 561 | }) catch @panic("OOM"); | 584 | }) catch @panic("OOM"); |
| 562 | } | 585 | } |
| ... | @@ -595,7 +618,7 @@ pub fn producesPdbFile(self: *CompileStep) bool { | ... | @@ -595,7 +618,7 @@ pub fn producesPdbFile(self: *CompileStep) bool { |
| 595 | if (!self.target.isWindows() and !self.target.isUefi()) return false; | 618 | if (!self.target.isWindows() and !self.target.isUefi()) return false; |
| 596 | if (self.target.getObjectFormat() == .c) return false; | 619 | if (self.target.getObjectFormat() == .c) return false; |
| 597 | if (self.strip == true) return false; | 620 | if (self.strip == true) return false; |
| 598 | return self.isDynamicLibrary() or self.kind == .exe or self.kind == .test_exe; | 621 | return self.isDynamicLibrary() or self.kind == .exe or self.kind == .@"test"; |
| 599 | } | 622 | } |
| 600 | 623 | ||
| 601 | pub fn linkLibC(self: *CompileStep) void { | 624 | pub fn linkLibC(self: *CompileStep) void { |
| ... | @@ -609,21 +632,24 @@ pub fn linkLibCpp(self: *CompileStep) void { | ... | @@ -609,21 +632,24 @@ pub fn linkLibCpp(self: *CompileStep) void { |
| 609 | /// If the value is omitted, it is set to 1. | 632 | /// If the value is omitted, it is set to 1. |
| 610 | /// `name` and `value` need not live longer than the function call. | 633 | /// `name` and `value` need not live longer than the function call. |
| 611 | pub fn defineCMacro(self: *CompileStep, name: []const u8, value: ?[]const u8) void { | 634 | pub fn defineCMacro(self: *CompileStep, name: []const u8, value: ?[]const u8) void { |
| 612 | const macro = std.Build.constructCMacro(self.builder.allocator, name, value); | 635 | const b = self.step.owner; |
| 636 | const macro = std.Build.constructCMacro(b.allocator, name, value); | ||
| 613 | self.c_macros.append(macro) catch @panic("OOM"); | 637 | self.c_macros.append(macro) catch @panic("OOM"); |
| 614 | } | 638 | } |
| 615 | 639 | ||
| 616 | /// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1. | 640 | /// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1. |
| 617 | pub fn defineCMacroRaw(self: *CompileStep, name_and_value: []const u8) void { | 641 | pub fn defineCMacroRaw(self: *CompileStep, name_and_value: []const u8) void { |
| 618 | self.c_macros.append(self.builder.dupe(name_and_value)) catch @panic("OOM"); | 642 | const b = self.step.owner; |
| 643 | self.c_macros.append(b.dupe(name_and_value)) catch @panic("OOM"); | ||
| 619 | } | 644 | } |
| 620 | 645 | ||
| 621 | /// This one has no integration with anything, it just puts -lname on the command line. | 646 | /// This one has no integration with anything, it just puts -lname on the command line. |
| 622 | /// Prefer to use `linkSystemLibrary` instead. | 647 | /// Prefer to use `linkSystemLibrary` instead. |
| 623 | pub fn linkSystemLibraryName(self: *CompileStep, name: []const u8) void { | 648 | pub fn linkSystemLibraryName(self: *CompileStep, name: []const u8) void { |
| 649 | const b = self.step.owner; | ||
| 624 | self.link_objects.append(.{ | 650 | self.link_objects.append(.{ |
| 625 | .system_lib = .{ | 651 | .system_lib = .{ |
| 626 | .name = self.builder.dupe(name), | 652 | .name = b.dupe(name), |
| 627 | .needed = false, | 653 | .needed = false, |
| 628 | .weak = false, | 654 | .weak = false, |
| 629 | .use_pkg_config = .no, | 655 | .use_pkg_config = .no, |
| ... | @@ -634,9 +660,10 @@ pub fn linkSystemLibraryName(self: *CompileStep, name: []const u8) void { | ... | @@ -634,9 +660,10 @@ pub fn linkSystemLibraryName(self: *CompileStep, name: []const u8) void { |
| 634 | /// This one has no integration with anything, it just puts -needed-lname on the command line. | 660 | /// This one has no integration with anything, it just puts -needed-lname on the command line. |
| 635 | /// Prefer to use `linkSystemLibraryNeeded` instead. | 661 | /// Prefer to use `linkSystemLibraryNeeded` instead. |
| 636 | pub fn linkSystemLibraryNeededName(self: *CompileStep, name: []const u8) void { | 662 | pub fn linkSystemLibraryNeededName(self: *CompileStep, name: []const u8) void { |
| 663 | const b = self.step.owner; | ||
| 637 | self.link_objects.append(.{ | 664 | self.link_objects.append(.{ |
| 638 | .system_lib = .{ | 665 | .system_lib = .{ |
| 639 | .name = self.builder.dupe(name), | 666 | .name = b.dupe(name), |
| 640 | .needed = true, | 667 | .needed = true, |
| 641 | .weak = false, | 668 | .weak = false, |
| 642 | .use_pkg_config = .no, | 669 | .use_pkg_config = .no, |
| ... | @@ -647,9 +674,10 @@ pub fn linkSystemLibraryNeededName(self: *CompileStep, name: []const u8) void { | ... | @@ -647,9 +674,10 @@ pub fn linkSystemLibraryNeededName(self: *CompileStep, name: []const u8) void { |
| 647 | /// Darwin-only. This one has no integration with anything, it just puts -weak-lname on the | 674 | /// Darwin-only. This one has no integration with anything, it just puts -weak-lname on the |
| 648 | /// command line. Prefer to use `linkSystemLibraryWeak` instead. | 675 | /// command line. Prefer to use `linkSystemLibraryWeak` instead. |
| 649 | pub fn linkSystemLibraryWeakName(self: *CompileStep, name: []const u8) void { | 676 | pub fn linkSystemLibraryWeakName(self: *CompileStep, name: []const u8) void { |
| 677 | const b = self.step.owner; | ||
| 650 | self.link_objects.append(.{ | 678 | self.link_objects.append(.{ |
| 651 | .system_lib = .{ | 679 | .system_lib = .{ |
| 652 | .name = self.builder.dupe(name), | 680 | .name = b.dupe(name), |
| 653 | .needed = false, | 681 | .needed = false, |
| 654 | .weak = true, | 682 | .weak = true, |
| 655 | .use_pkg_config = .no, | 683 | .use_pkg_config = .no, |
| ... | @@ -660,9 +688,10 @@ pub fn linkSystemLibraryWeakName(self: *CompileStep, name: []const u8) void { | ... | @@ -660,9 +688,10 @@ pub fn linkSystemLibraryWeakName(self: *CompileStep, name: []const u8) void { |
| 660 | /// This links against a system library, exclusively using pkg-config to find the library. | 688 | /// This links against a system library, exclusively using pkg-config to find the library. |
| 661 | /// Prefer to use `linkSystemLibrary` instead. | 689 | /// Prefer to use `linkSystemLibrary` instead. |
| 662 | pub fn linkSystemLibraryPkgConfigOnly(self: *CompileStep, lib_name: []const u8) void { | 690 | pub fn linkSystemLibraryPkgConfigOnly(self: *CompileStep, lib_name: []const u8) void { |
| 691 | const b = self.step.owner; | ||
| 663 | self.link_objects.append(.{ | 692 | self.link_objects.append(.{ |
| 664 | .system_lib = .{ | 693 | .system_lib = .{ |
| 665 | .name = self.builder.dupe(lib_name), | 694 | .name = b.dupe(lib_name), |
| 666 | .needed = false, | 695 | .needed = false, |
| 667 | .weak = false, | 696 | .weak = false, |
| 668 | .use_pkg_config = .force, | 697 | .use_pkg_config = .force, |
| ... | @@ -673,9 +702,10 @@ pub fn linkSystemLibraryPkgConfigOnly(self: *CompileStep, lib_name: []const u8) | ... | @@ -673,9 +702,10 @@ pub fn linkSystemLibraryPkgConfigOnly(self: *CompileStep, lib_name: []const u8) |
| 673 | /// This links against a system library, exclusively using pkg-config to find the library. | 702 | /// This links against a system library, exclusively using pkg-config to find the library. |
| 674 | /// Prefer to use `linkSystemLibraryNeeded` instead. | 703 | /// Prefer to use `linkSystemLibraryNeeded` instead. |
| 675 | pub fn linkSystemLibraryNeededPkgConfigOnly(self: *CompileStep, lib_name: []const u8) void { | 704 | pub fn linkSystemLibraryNeededPkgConfigOnly(self: *CompileStep, lib_name: []const u8) void { |
| 705 | const b = self.step.owner; | ||
| 676 | self.link_objects.append(.{ | 706 | self.link_objects.append(.{ |
| 677 | .system_lib = .{ | 707 | .system_lib = .{ |
| 678 | .name = self.builder.dupe(lib_name), | 708 | .name = b.dupe(lib_name), |
| 679 | .needed = true, | 709 | .needed = true, |
| 680 | .weak = false, | 710 | .weak = false, |
| 681 | .use_pkg_config = .force, | 711 | .use_pkg_config = .force, |
| ... | @@ -685,14 +715,15 @@ pub fn linkSystemLibraryNeededPkgConfigOnly(self: *CompileStep, lib_name: []cons | ... | @@ -685,14 +715,15 @@ pub fn linkSystemLibraryNeededPkgConfigOnly(self: *CompileStep, lib_name: []cons |
| 685 | 715 | ||
| 686 | /// Run pkg-config for the given library name and parse the output, returning the arguments | 716 | /// Run pkg-config for the given library name and parse the output, returning the arguments |
| 687 | /// that should be passed to zig to link the given library. | 717 | /// that should be passed to zig to link the given library. |
| 688 | pub fn runPkgConfig(self: *CompileStep, lib_name: []const u8) ![]const []const u8 { | 718 | fn runPkgConfig(self: *CompileStep, lib_name: []const u8) ![]const []const u8 { |
| 719 | const b = self.step.owner; | ||
| 689 | const pkg_name = match: { | 720 | const pkg_name = match: { |
| 690 | // First we have to map the library name to pkg config name. Unfortunately, | 721 | // First we have to map the library name to pkg config name. Unfortunately, |
| 691 | // there are several examples where this is not straightforward: | 722 | // there are several examples where this is not straightforward: |
| 692 | // -lSDL2 -> pkg-config sdl2 | 723 | // -lSDL2 -> pkg-config sdl2 |
| 693 | // -lgdk-3 -> pkg-config gdk-3.0 | 724 | // -lgdk-3 -> pkg-config gdk-3.0 |
| 694 | // -latk-1.0 -> pkg-config atk | 725 | // -latk-1.0 -> pkg-config atk |
| 695 | const pkgs = try getPkgConfigList(self.builder); | 726 | const pkgs = try getPkgConfigList(b); |
| 696 | 727 | ||
| 697 | // Exact match means instant winner. | 728 | // Exact match means instant winner. |
| 698 | for (pkgs) |pkg| { | 729 | for (pkgs) |pkg| { |
| ... | @@ -732,7 +763,7 @@ pub fn runPkgConfig(self: *CompileStep, lib_name: []const u8) ![]const []const u | ... | @@ -732,7 +763,7 @@ pub fn runPkgConfig(self: *CompileStep, lib_name: []const u8) ![]const []const u |
| 732 | }; | 763 | }; |
| 733 | 764 | ||
| 734 | var code: u8 = undefined; | 765 | var code: u8 = undefined; |
| 735 | const stdout = if (self.builder.execAllowFail(&[_][]const u8{ | 766 | const stdout = if (b.execAllowFail(&[_][]const u8{ |
| 736 | "pkg-config", | 767 | "pkg-config", |
| 737 | pkg_name, | 768 | pkg_name, |
| 738 | "--cflags", | 769 | "--cflags", |
| ... | @@ -745,7 +776,7 @@ pub fn runPkgConfig(self: *CompileStep, lib_name: []const u8) ![]const []const u | ... | @@ -745,7 +776,7 @@ pub fn runPkgConfig(self: *CompileStep, lib_name: []const u8) ![]const []const u |
| 745 | else => return err, | 776 | else => return err, |
| 746 | }; | 777 | }; |
| 747 | 778 | ||
| 748 | var zig_args = ArrayList([]const u8).init(self.builder.allocator); | 779 | var zig_args = ArrayList([]const u8).init(b.allocator); |
| 749 | defer zig_args.deinit(); | 780 | defer zig_args.deinit(); |
| 750 | 781 | ||
| 751 | var it = mem.tokenize(u8, stdout, " \r\n\t"); | 782 | var it = mem.tokenize(u8, stdout, " \r\n\t"); |
| ... | @@ -770,8 +801,8 @@ pub fn runPkgConfig(self: *CompileStep, lib_name: []const u8) ![]const []const u | ... | @@ -770,8 +801,8 @@ pub fn runPkgConfig(self: *CompileStep, lib_name: []const u8) ![]const []const u |
| 770 | try zig_args.appendSlice(&[_][]const u8{ "-D", macro }); | 801 | try zig_args.appendSlice(&[_][]const u8{ "-D", macro }); |
| 771 | } else if (mem.startsWith(u8, tok, "-D")) { | 802 | } else if (mem.startsWith(u8, tok, "-D")) { |
| 772 | try zig_args.append(tok); | 803 | try zig_args.append(tok); |
| 773 | } else if (self.builder.verbose) { | 804 | } else if (b.debug_pkg_config) { |
| 774 | log.warn("Ignoring pkg-config flag '{s}'", .{tok}); | 805 | return self.step.fail("unknown pkg-config flag '{s}'", .{tok}); |
| 775 | } | 806 | } |
| 776 | } | 807 | } |
| 777 | 808 | ||
| ... | @@ -794,6 +825,7 @@ fn linkSystemLibraryInner(self: *CompileStep, name: []const u8, opts: struct { | ... | @@ -794,6 +825,7 @@ fn linkSystemLibraryInner(self: *CompileStep, name: []const u8, opts: struct { |
| 794 | needed: bool = false, | 825 | needed: bool = false, |
| 795 | weak: bool = false, | 826 | weak: bool = false, |
| 796 | }) void { | 827 | }) void { |
| 828 | const b = self.step.owner; | ||
| 797 | if (isLibCLibrary(name)) { | 829 | if (isLibCLibrary(name)) { |
| 798 | self.linkLibC(); | 830 | self.linkLibC(); |
| 799 | return; | 831 | return; |
| ... | @@ -805,7 +837,7 @@ fn linkSystemLibraryInner(self: *CompileStep, name: []const u8, opts: struct { | ... | @@ -805,7 +837,7 @@ fn linkSystemLibraryInner(self: *CompileStep, name: []const u8, opts: struct { |
| 805 | 837 | ||
| 806 | self.link_objects.append(.{ | 838 | self.link_objects.append(.{ |
| 807 | .system_lib = .{ | 839 | .system_lib = .{ |
| 808 | .name = self.builder.dupe(name), | 840 | .name = b.dupe(name), |
| 809 | .needed = opts.needed, | 841 | .needed = opts.needed, |
| 810 | .weak = opts.weak, | 842 | .weak = opts.weak, |
| 811 | .use_pkg_config = .yes, | 843 | .use_pkg_config = .yes, |
| ... | @@ -813,27 +845,31 @@ fn linkSystemLibraryInner(self: *CompileStep, name: []const u8, opts: struct { | ... | @@ -813,27 +845,31 @@ fn linkSystemLibraryInner(self: *CompileStep, name: []const u8, opts: struct { |
| 813 | }) catch @panic("OOM"); | 845 | }) catch @panic("OOM"); |
| 814 | } | 846 | } |
| 815 | 847 | ||
| 816 | pub fn setNamePrefix(self: *CompileStep, text: []const u8) void { | 848 | pub fn setName(self: *CompileStep, text: []const u8) void { |
| 817 | assert(self.kind == .@"test" or self.kind == .test_exe); | 849 | const b = self.step.owner; |
| 818 | self.name_prefix = self.builder.dupe(text); | 850 | assert(self.kind == .@"test"); |
| 851 | self.name = b.dupe(text); | ||
| 819 | } | 852 | } |
| 820 | 853 | ||
| 821 | pub fn setFilter(self: *CompileStep, text: ?[]const u8) void { | 854 | pub fn setFilter(self: *CompileStep, text: ?[]const u8) void { |
| 822 | assert(self.kind == .@"test" or self.kind == .test_exe); | 855 | const b = self.step.owner; |
| 823 | self.filter = if (text) |t| self.builder.dupe(t) else null; | 856 | assert(self.kind == .@"test"); |
| 857 | self.filter = if (text) |t| b.dupe(t) else null; | ||
| 824 | } | 858 | } |
| 825 | 859 | ||
| 826 | pub fn setTestRunner(self: *CompileStep, path: ?[]const u8) void { | 860 | pub fn setTestRunner(self: *CompileStep, path: ?[]const u8) void { |
| 827 | assert(self.kind == .@"test" or self.kind == .test_exe); | 861 | const b = self.step.owner; |
| 828 | self.test_runner = if (path) |p| self.builder.dupePath(p) else null; | 862 | assert(self.kind == .@"test"); |
| 863 | self.test_runner = if (path) |p| b.dupePath(p) else null; | ||
| 829 | } | 864 | } |
| 830 | 865 | ||
| 831 | /// Handy when you have many C/C++ source files and want them all to have the same flags. | 866 | /// Handy when you have many C/C++ source files and want them all to have the same flags. |
| 832 | pub fn addCSourceFiles(self: *CompileStep, files: []const []const u8, flags: []const []const u8) void { | 867 | pub fn addCSourceFiles(self: *CompileStep, files: []const []const u8, flags: []const []const u8) void { |
| 833 | const c_source_files = self.builder.allocator.create(CSourceFiles) catch @panic("OOM"); | 868 | const b = self.step.owner; |
| 869 | const c_source_files = b.allocator.create(CSourceFiles) catch @panic("OOM"); | ||
| 834 | 870 | ||
| 835 | const files_copy = self.builder.dupeStrings(files); | 871 | const files_copy = b.dupeStrings(files); |
| 836 | const flags_copy = self.builder.dupeStrings(flags); | 872 | const flags_copy = b.dupeStrings(flags); |
| 837 | 873 | ||
| 838 | c_source_files.* = .{ | 874 | c_source_files.* = .{ |
| 839 | .files = files_copy, | 875 | .files = files_copy, |
| ... | @@ -850,8 +886,9 @@ pub fn addCSourceFile(self: *CompileStep, file: []const u8, flags: []const []con | ... | @@ -850,8 +886,9 @@ pub fn addCSourceFile(self: *CompileStep, file: []const u8, flags: []const []con |
| 850 | } | 886 | } |
| 851 | 887 | ||
| 852 | pub fn addCSourceFileSource(self: *CompileStep, source: CSourceFile) void { | 888 | pub fn addCSourceFileSource(self: *CompileStep, source: CSourceFile) void { |
| 853 | const c_source_file = self.builder.allocator.create(CSourceFile) catch @panic("OOM"); | 889 | const b = self.step.owner; |
| 854 | c_source_file.* = source.dupe(self.builder); | 890 | const c_source_file = b.allocator.create(CSourceFile) catch @panic("OOM"); |
| 891 | c_source_file.* = source.dupe(b); | ||
| 855 | self.link_objects.append(.{ .c_source_file = c_source_file }) catch @panic("OOM"); | 892 | self.link_objects.append(.{ .c_source_file = c_source_file }) catch @panic("OOM"); |
| 856 | source.source.addStepDependencies(&self.step); | 893 | source.source.addStepDependencies(&self.step); |
| 857 | } | 894 | } |
| ... | @@ -865,52 +902,61 @@ pub fn setVerboseCC(self: *CompileStep, value: bool) void { | ... | @@ -865,52 +902,61 @@ pub fn setVerboseCC(self: *CompileStep, value: bool) void { |
| 865 | } | 902 | } |
| 866 | 903 | ||
| 867 | pub fn overrideZigLibDir(self: *CompileStep, dir_path: []const u8) void { | 904 | pub fn overrideZigLibDir(self: *CompileStep, dir_path: []const u8) void { |
| 868 | self.zig_lib_dir = self.builder.dupePath(dir_path); | 905 | const b = self.step.owner; |
| 906 | self.zig_lib_dir = b.dupePath(dir_path); | ||
| 869 | } | 907 | } |
| 870 | 908 | ||
| 871 | pub fn setMainPkgPath(self: *CompileStep, dir_path: []const u8) void { | 909 | pub fn setMainPkgPath(self: *CompileStep, dir_path: []const u8) void { |
| 872 | self.main_pkg_path = self.builder.dupePath(dir_path); | 910 | const b = self.step.owner; |
| 911 | self.main_pkg_path = b.dupePath(dir_path); | ||
| 873 | } | 912 | } |
| 874 | 913 | ||
| 875 | pub fn setLibCFile(self: *CompileStep, libc_file: ?FileSource) void { | 914 | pub fn setLibCFile(self: *CompileStep, libc_file: ?FileSource) void { |
| 876 | self.libc_file = if (libc_file) |f| f.dupe(self.builder) else null; | 915 | const b = self.step.owner; |
| 916 | self.libc_file = if (libc_file) |f| f.dupe(b) else null; | ||
| 877 | } | 917 | } |
| 878 | 918 | ||
| 879 | /// Returns the generated executable, library or object file. | 919 | /// Returns the generated executable, library or object file. |
| 880 | /// To run an executable built with zig build, use `run`, or create an install step and invoke it. | 920 | /// To run an executable built with zig build, use `run`, or create an install step and invoke it. |
| 881 | pub fn getOutputSource(self: *CompileStep) FileSource { | 921 | pub fn getOutputSource(self: *CompileStep) FileSource { |
| 882 | return FileSource{ .generated = &self.output_path_source }; | 922 | return .{ .generated = &self.output_path_source }; |
| 923 | } | ||
| 924 | |||
| 925 | pub fn getOutputDirectorySource(self: *CompileStep) FileSource { | ||
| 926 | return .{ .generated = &self.output_dirname_source }; | ||
| 883 | } | 927 | } |
| 884 | 928 | ||
| 885 | /// Returns the generated import library. This function can only be called for libraries. | 929 | /// Returns the generated import library. This function can only be called for libraries. |
| 886 | pub fn getOutputLibSource(self: *CompileStep) FileSource { | 930 | pub fn getOutputLibSource(self: *CompileStep) FileSource { |
| 887 | assert(self.kind == .lib); | 931 | assert(self.kind == .lib); |
| 888 | return FileSource{ .generated = &self.output_lib_path_source }; | 932 | return .{ .generated = &self.output_lib_path_source }; |
| 889 | } | 933 | } |
| 890 | 934 | ||
| 891 | /// Returns the generated header file. | 935 | /// Returns the generated header file. |
| 892 | /// This function can only be called for libraries or object files which have `emit_h` set. | 936 | /// This function can only be called for libraries or object files which have `emit_h` set. |
| 893 | pub fn getOutputHSource(self: *CompileStep) FileSource { | 937 | pub fn getOutputHSource(self: *CompileStep) FileSource { |
| 894 | assert(self.kind != .exe and self.kind != .test_exe and self.kind != .@"test"); | 938 | assert(self.kind != .exe and self.kind != .@"test"); |
| 895 | assert(self.emit_h); | 939 | assert(self.emit_h); |
| 896 | return FileSource{ .generated = &self.output_h_path_source }; | 940 | return .{ .generated = &self.output_h_path_source }; |
| 897 | } | 941 | } |
| 898 | 942 | ||
| 899 | /// Returns the generated PDB file. This function can only be called for Windows and UEFI. | 943 | /// Returns the generated PDB file. This function can only be called for Windows and UEFI. |
| 900 | pub fn getOutputPdbSource(self: *CompileStep) FileSource { | 944 | pub fn getOutputPdbSource(self: *CompileStep) FileSource { |
| 901 | // TODO: Is this right? Isn't PDB for *any* PE/COFF file? | 945 | // TODO: Is this right? Isn't PDB for *any* PE/COFF file? |
| 902 | assert(self.target.isWindows() or self.target.isUefi()); | 946 | assert(self.target.isWindows() or self.target.isUefi()); |
| 903 | return FileSource{ .generated = &self.output_pdb_path_source }; | 947 | return .{ .generated = &self.output_pdb_path_source }; |
| 904 | } | 948 | } |
| 905 | 949 | ||
| 906 | pub fn addAssemblyFile(self: *CompileStep, path: []const u8) void { | 950 | pub fn addAssemblyFile(self: *CompileStep, path: []const u8) void { |
| 951 | const b = self.step.owner; | ||
| 907 | self.link_objects.append(.{ | 952 | self.link_objects.append(.{ |
| 908 | .assembly_file = .{ .path = self.builder.dupe(path) }, | 953 | .assembly_file = .{ .path = b.dupe(path) }, |
| 909 | }) catch @panic("OOM"); | 954 | }) catch @panic("OOM"); |
| 910 | } | 955 | } |
| 911 | 956 | ||
| 912 | pub fn addAssemblyFileSource(self: *CompileStep, source: FileSource) void { | 957 | pub fn addAssemblyFileSource(self: *CompileStep, source: FileSource) void { |
| 913 | const source_duped = source.dupe(self.builder); | 958 | const b = self.step.owner; |
| 959 | const source_duped = source.dupe(b); | ||
| 914 | self.link_objects.append(.{ .assembly_file = source_duped }) catch @panic("OOM"); | 960 | self.link_objects.append(.{ .assembly_file = source_duped }) catch @panic("OOM"); |
| 915 | source_duped.addStepDependencies(&self.step); | 961 | source_duped.addStepDependencies(&self.step); |
| 916 | } | 962 | } |
| ... | @@ -920,7 +966,8 @@ pub fn addObjectFile(self: *CompileStep, source_file: []const u8) void { | ... | @@ -920,7 +966,8 @@ pub fn addObjectFile(self: *CompileStep, source_file: []const u8) void { |
| 920 | } | 966 | } |
| 921 | 967 | ||
| 922 | pub fn addObjectFileSource(self: *CompileStep, source: FileSource) void { | 968 | pub fn addObjectFileSource(self: *CompileStep, source: FileSource) void { |
| 923 | self.link_objects.append(.{ .static_path = source.dupe(self.builder) }) catch @panic("OOM"); | 969 | const b = self.step.owner; |
| 970 | self.link_objects.append(.{ .static_path = source.dupe(b) }) catch @panic("OOM"); | ||
| 924 | source.addStepDependencies(&self.step); | 971 | source.addStepDependencies(&self.step); |
| 925 | } | 972 | } |
| 926 | 973 | ||
| ... | @@ -935,11 +982,13 @@ pub const addLibPath = @compileError("deprecated, use addLibraryPath"); | ... | @@ -935,11 +982,13 @@ pub const addLibPath = @compileError("deprecated, use addLibraryPath"); |
| 935 | pub const addFrameworkDir = @compileError("deprecated, use addFrameworkPath"); | 982 | pub const addFrameworkDir = @compileError("deprecated, use addFrameworkPath"); |
| 936 | 983 | ||
| 937 | pub fn addSystemIncludePath(self: *CompileStep, path: []const u8) void { | 984 | pub fn addSystemIncludePath(self: *CompileStep, path: []const u8) void { |
| 938 | self.include_dirs.append(IncludeDir{ .raw_path_system = self.builder.dupe(path) }) catch @panic("OOM"); | 985 | const b = self.step.owner; |
| 986 | self.include_dirs.append(IncludeDir{ .raw_path_system = b.dupe(path) }) catch @panic("OOM"); | ||
| 939 | } | 987 | } |
| 940 | 988 | ||
| 941 | pub fn addIncludePath(self: *CompileStep, path: []const u8) void { | 989 | pub fn addIncludePath(self: *CompileStep, path: []const u8) void { |
| 942 | self.include_dirs.append(IncludeDir{ .raw_path = self.builder.dupe(path) }) catch @panic("OOM"); | 990 | const b = self.step.owner; |
| 991 | self.include_dirs.append(IncludeDir{ .raw_path = b.dupe(path) }) catch @panic("OOM"); | ||
| 943 | } | 992 | } |
| 944 | 993 | ||
| 945 | pub fn addConfigHeader(self: *CompileStep, config_header: *ConfigHeaderStep) void { | 994 | pub fn addConfigHeader(self: *CompileStep, config_header: *ConfigHeaderStep) void { |
| ... | @@ -948,23 +997,42 @@ pub fn addConfigHeader(self: *CompileStep, config_header: *ConfigHeaderStep) voi | ... | @@ -948,23 +997,42 @@ pub fn addConfigHeader(self: *CompileStep, config_header: *ConfigHeaderStep) voi |
| 948 | } | 997 | } |
| 949 | 998 | ||
| 950 | pub fn addLibraryPath(self: *CompileStep, path: []const u8) void { | 999 | pub fn addLibraryPath(self: *CompileStep, path: []const u8) void { |
| 951 | self.lib_paths.append(self.builder.dupe(path)) catch @panic("OOM"); | 1000 | const b = self.step.owner; |
| 1001 | self.lib_paths.append(.{ .path = b.dupe(path) }) catch @panic("OOM"); | ||
| 1002 | } | ||
| 1003 | |||
| 1004 | pub fn addLibraryPathDirectorySource(self: *CompileStep, directory_source: FileSource) void { | ||
| 1005 | self.lib_paths.append(directory_source) catch @panic("OOM"); | ||
| 1006 | directory_source.addStepDependencies(&self.step); | ||
| 952 | } | 1007 | } |
| 953 | 1008 | ||
| 954 | pub fn addRPath(self: *CompileStep, path: []const u8) void { | 1009 | pub fn addRPath(self: *CompileStep, path: []const u8) void { |
| 955 | self.rpaths.append(self.builder.dupe(path)) catch @panic("OOM"); | 1010 | const b = self.step.owner; |
| 1011 | self.rpaths.append(.{ .path = b.dupe(path) }) catch @panic("OOM"); | ||
| 1012 | } | ||
| 1013 | |||
| 1014 | pub fn addRPathDirectorySource(self: *CompileStep, directory_source: FileSource) void { | ||
| 1015 | self.rpaths.append(directory_source) catch @panic("OOM"); | ||
| 1016 | directory_source.addStepDependencies(&self.step); | ||
| 956 | } | 1017 | } |
| 957 | 1018 | ||
| 958 | pub fn addFrameworkPath(self: *CompileStep, dir_path: []const u8) void { | 1019 | pub fn addFrameworkPath(self: *CompileStep, dir_path: []const u8) void { |
| 959 | self.framework_dirs.append(self.builder.dupe(dir_path)) catch @panic("OOM"); | 1020 | const b = self.step.owner; |
| 1021 | self.framework_dirs.append(.{ .path = b.dupe(dir_path) }) catch @panic("OOM"); | ||
| 1022 | } | ||
| 1023 | |||
| 1024 | pub fn addFrameworkPathDirectorySource(self: *CompileStep, directory_source: FileSource) void { | ||
| 1025 | self.framework_dirs.append(directory_source) catch @panic("OOM"); | ||
| 1026 | directory_source.addStepDependencies(&self.step); | ||
| 960 | } | 1027 | } |
| 961 | 1028 | ||
| 962 | /// Adds a module to be used with `@import` and exposing it in the current | 1029 | /// Adds a module to be used with `@import` and exposing it in the current |
| 963 | /// package's module table using `name`. | 1030 | /// package's module table using `name`. |
| 964 | pub fn addModule(cs: *CompileStep, name: []const u8, module: *Module) void { | 1031 | pub fn addModule(cs: *CompileStep, name: []const u8, module: *Module) void { |
| 965 | cs.modules.put(cs.builder.dupe(name), module) catch @panic("OOM"); | 1032 | const b = cs.step.owner; |
| 1033 | cs.modules.put(b.dupe(name), module) catch @panic("OOM"); | ||
| 966 | 1034 | ||
| 967 | var done = std.AutoHashMap(*Module, void).init(cs.builder.allocator); | 1035 | var done = std.AutoHashMap(*Module, void).init(b.allocator); |
| 968 | defer done.deinit(); | 1036 | defer done.deinit(); |
| 969 | cs.addRecursiveBuildDeps(module, &done) catch @panic("OOM"); | 1037 | cs.addRecursiveBuildDeps(module, &done) catch @panic("OOM"); |
| 970 | } | 1038 | } |
| ... | @@ -972,7 +1040,8 @@ pub fn addModule(cs: *CompileStep, name: []const u8, module: *Module) void { | ... | @@ -972,7 +1040,8 @@ pub fn addModule(cs: *CompileStep, name: []const u8, module: *Module) void { |
| 972 | /// Adds a module to be used with `@import` without exposing it in the current | 1040 | /// Adds a module to be used with `@import` without exposing it in the current |
| 973 | /// package's module table. | 1041 | /// package's module table. |
| 974 | pub fn addAnonymousModule(cs: *CompileStep, name: []const u8, options: std.Build.CreateModuleOptions) void { | 1042 | pub fn addAnonymousModule(cs: *CompileStep, name: []const u8, options: std.Build.CreateModuleOptions) void { |
| 975 | const module = cs.builder.createModule(options); | 1043 | const b = cs.step.owner; |
| 1044 | const module = b.createModule(options); | ||
| 976 | return addModule(cs, name, module); | 1045 | return addModule(cs, name, module); |
| 977 | } | 1046 | } |
| 978 | 1047 | ||
| ... | @@ -992,12 +1061,13 @@ fn addRecursiveBuildDeps(cs: *CompileStep, module: *Module, done: *std.AutoHashM | ... | @@ -992,12 +1061,13 @@ fn addRecursiveBuildDeps(cs: *CompileStep, module: *Module, done: *std.AutoHashM |
| 992 | /// If Vcpkg was found on the system, it will be added to include and lib | 1061 | /// If Vcpkg was found on the system, it will be added to include and lib |
| 993 | /// paths for the specified target. | 1062 | /// paths for the specified target. |
| 994 | pub fn addVcpkgPaths(self: *CompileStep, linkage: CompileStep.Linkage) !void { | 1063 | pub fn addVcpkgPaths(self: *CompileStep, linkage: CompileStep.Linkage) !void { |
| 1064 | const b = self.step.owner; | ||
| 995 | // Ideally in the Unattempted case we would call the function recursively | 1065 | // Ideally in the Unattempted case we would call the function recursively |
| 996 | // after findVcpkgRoot and have only one switch statement, but the compiler | 1066 | // after findVcpkgRoot and have only one switch statement, but the compiler |
| 997 | // cannot resolve the error set. | 1067 | // cannot resolve the error set. |
| 998 | switch (self.builder.vcpkg_root) { | 1068 | switch (b.vcpkg_root) { |
| 999 | .unattempted => { | 1069 | .unattempted => { |
| 1000 | self.builder.vcpkg_root = if (try findVcpkgRoot(self.builder.allocator)) |root| | 1070 | b.vcpkg_root = if (try findVcpkgRoot(b.allocator)) |root| |
| 1001 | VcpkgRoot{ .found = root } | 1071 | VcpkgRoot{ .found = root } |
| 1002 | else | 1072 | else |
| 1003 | .not_found; | 1073 | .not_found; |
| ... | @@ -1006,31 +1076,32 @@ pub fn addVcpkgPaths(self: *CompileStep, linkage: CompileStep.Linkage) !void { | ... | @@ -1006,31 +1076,32 @@ pub fn addVcpkgPaths(self: *CompileStep, linkage: CompileStep.Linkage) !void { |
| 1006 | .found => {}, | 1076 | .found => {}, |
| 1007 | } | 1077 | } |
| 1008 | 1078 | ||
| 1009 | switch (self.builder.vcpkg_root) { | 1079 | switch (b.vcpkg_root) { |
| 1010 | .unattempted => unreachable, | 1080 | .unattempted => unreachable, |
| 1011 | .not_found => return error.VcpkgNotFound, | 1081 | .not_found => return error.VcpkgNotFound, |
| 1012 | .found => |root| { | 1082 | .found => |root| { |
| 1013 | const allocator = self.builder.allocator; | 1083 | const allocator = b.allocator; |
| 1014 | const triplet = try self.target.vcpkgTriplet(allocator, if (linkage == .static) .Static else .Dynamic); | 1084 | const triplet = try self.target.vcpkgTriplet(allocator, if (linkage == .static) .Static else .Dynamic); |
| 1015 | defer self.builder.allocator.free(triplet); | 1085 | defer b.allocator.free(triplet); |
| 1016 | 1086 | ||
| 1017 | const include_path = self.builder.pathJoin(&.{ root, "installed", triplet, "include" }); | 1087 | const include_path = b.pathJoin(&.{ root, "installed", triplet, "include" }); |
| 1018 | errdefer allocator.free(include_path); | 1088 | errdefer allocator.free(include_path); |
| 1019 | try self.include_dirs.append(IncludeDir{ .raw_path = include_path }); | 1089 | try self.include_dirs.append(IncludeDir{ .raw_path = include_path }); |
| 1020 | 1090 | ||
| 1021 | const lib_path = self.builder.pathJoin(&.{ root, "installed", triplet, "lib" }); | 1091 | const lib_path = b.pathJoin(&.{ root, "installed", triplet, "lib" }); |
| 1022 | try self.lib_paths.append(lib_path); | 1092 | try self.lib_paths.append(.{ .path = lib_path }); |
| 1023 | 1093 | ||
| 1024 | self.vcpkg_bin_path = self.builder.pathJoin(&.{ root, "installed", triplet, "bin" }); | 1094 | self.vcpkg_bin_path = b.pathJoin(&.{ root, "installed", triplet, "bin" }); |
| 1025 | }, | 1095 | }, |
| 1026 | } | 1096 | } |
| 1027 | } | 1097 | } |
| 1028 | 1098 | ||
| 1029 | pub fn setExecCmd(self: *CompileStep, args: []const ?[]const u8) void { | 1099 | pub fn setExecCmd(self: *CompileStep, args: []const ?[]const u8) void { |
| 1100 | const b = self.step.owner; | ||
| 1030 | assert(self.kind == .@"test"); | 1101 | assert(self.kind == .@"test"); |
| 1031 | const duped_args = self.builder.allocator.alloc(?[]u8, args.len) catch @panic("OOM"); | 1102 | const duped_args = b.allocator.alloc(?[]u8, args.len) catch @panic("OOM"); |
| 1032 | for (args, 0..) |arg, i| { | 1103 | for (args, 0..) |arg, i| { |
| 1033 | duped_args[i] = if (arg) |a| self.builder.dupe(a) else null; | 1104 | duped_args[i] = if (arg) |a| b.dupe(a) else null; |
| 1034 | } | 1105 | } |
| 1035 | self.exec_cmd_args = duped_args; | 1106 | self.exec_cmd_args = duped_args; |
| 1036 | } | 1107 | } |
| ... | @@ -1039,22 +1110,27 @@ fn linkLibraryOrObject(self: *CompileStep, other: *CompileStep) void { | ... | @@ -1039,22 +1110,27 @@ fn linkLibraryOrObject(self: *CompileStep, other: *CompileStep) void { |
| 1039 | self.step.dependOn(&other.step); | 1110 | self.step.dependOn(&other.step); |
| 1040 | self.link_objects.append(.{ .other_step = other }) catch @panic("OOM"); | 1111 | self.link_objects.append(.{ .other_step = other }) catch @panic("OOM"); |
| 1041 | self.include_dirs.append(.{ .other_step = other }) catch @panic("OOM"); | 1112 | self.include_dirs.append(.{ .other_step = other }) catch @panic("OOM"); |
| 1113 | |||
| 1114 | for (other.installed_headers.items) |install_step| { | ||
| 1115 | self.step.dependOn(install_step); | ||
| 1116 | } | ||
| 1042 | } | 1117 | } |
| 1043 | 1118 | ||
| 1044 | fn appendModuleArgs( | 1119 | fn appendModuleArgs( |
| 1045 | cs: *CompileStep, | 1120 | cs: *CompileStep, |
| 1046 | zig_args: *ArrayList([]const u8), | 1121 | zig_args: *ArrayList([]const u8), |
| 1047 | ) error{OutOfMemory}!void { | 1122 | ) error{OutOfMemory}!void { |
| 1123 | const b = cs.step.owner; | ||
| 1048 | // First, traverse the whole dependency graph and give every module a unique name, ideally one | 1124 | // First, traverse the whole dependency graph and give every module a unique name, ideally one |
| 1049 | // named after what it's called somewhere in the graph. It will help here to have both a mapping | 1125 | // named after what it's called somewhere in the graph. It will help here to have both a mapping |
| 1050 | // from module to name and a set of all the currently-used names. | 1126 | // from module to name and a set of all the currently-used names. |
| 1051 | var mod_names = std.AutoHashMap(*Module, []const u8).init(cs.builder.allocator); | 1127 | var mod_names = std.AutoHashMap(*Module, []const u8).init(b.allocator); |
| 1052 | var names = std.StringHashMap(void).init(cs.builder.allocator); | 1128 | var names = std.StringHashMap(void).init(b.allocator); |
| 1053 | 1129 | ||
| 1054 | var to_name = std.ArrayList(struct { | 1130 | var to_name = std.ArrayList(struct { |
| 1055 | name: []const u8, | 1131 | name: []const u8, |
| 1056 | mod: *Module, | 1132 | mod: *Module, |
| 1057 | }).init(cs.builder.allocator); | 1133 | }).init(b.allocator); |
| 1058 | { | 1134 | { |
| 1059 | var it = cs.modules.iterator(); | 1135 | var it = cs.modules.iterator(); |
| 1060 | while (it.next()) |kv| { | 1136 | while (it.next()) |kv| { |
| ... | @@ -1075,7 +1151,7 @@ fn appendModuleArgs( | ... | @@ -1075,7 +1151,7 @@ fn appendModuleArgs( |
| 1075 | if (mod_names.contains(dep.mod)) continue; | 1151 | if (mod_names.contains(dep.mod)) continue; |
| 1076 | 1152 | ||
| 1077 | // We'll use this buffer to store the name we decide on | 1153 | // We'll use this buffer to store the name we decide on |
| 1078 | var buf = try cs.builder.allocator.alloc(u8, dep.name.len + 32); | 1154 | var buf = try b.allocator.alloc(u8, dep.name.len + 32); |
| 1079 | // First, try just the exposed dependency name | 1155 | // First, try just the exposed dependency name |
| 1080 | std.mem.copy(u8, buf, dep.name); | 1156 | std.mem.copy(u8, buf, dep.name); |
| 1081 | var name = buf[0..dep.name.len]; | 1157 | var name = buf[0..dep.name.len]; |
| ... | @@ -1112,15 +1188,15 @@ fn appendModuleArgs( | ... | @@ -1112,15 +1188,15 @@ fn appendModuleArgs( |
| 1112 | const mod = kv.key_ptr.*; | 1188 | const mod = kv.key_ptr.*; |
| 1113 | const name = kv.value_ptr.*; | 1189 | const name = kv.value_ptr.*; |
| 1114 | 1190 | ||
| 1115 | const deps_str = try constructDepString(cs.builder.allocator, mod_names, mod.dependencies); | 1191 | const deps_str = try constructDepString(b.allocator, mod_names, mod.dependencies); |
| 1116 | const src = mod.builder.pathFromRoot(mod.source_file.getPath(mod.builder)); | 1192 | const src = mod.builder.pathFromRoot(mod.source_file.getPath(mod.builder)); |
| 1117 | try zig_args.append("--mod"); | 1193 | try zig_args.append("--mod"); |
| 1118 | try zig_args.append(try std.fmt.allocPrint(cs.builder.allocator, "{s}:{s}:{s}", .{ name, deps_str, src })); | 1194 | try zig_args.append(try std.fmt.allocPrint(b.allocator, "{s}:{s}:{s}", .{ name, deps_str, src })); |
| 1119 | } | 1195 | } |
| 1120 | } | 1196 | } |
| 1121 | 1197 | ||
| 1122 | // Lastly, output the root dependencies | 1198 | // Lastly, output the root dependencies |
| 1123 | const deps_str = try constructDepString(cs.builder.allocator, mod_names, cs.modules); | 1199 | const deps_str = try constructDepString(b.allocator, mod_names, cs.modules); |
| 1124 | if (deps_str.len > 0) { | 1200 | if (deps_str.len > 0) { |
| 1125 | try zig_args.append("--deps"); | 1201 | try zig_args.append("--deps"); |
| 1126 | try zig_args.append(deps_str); | 1202 | try zig_args.append(deps_str); |
| ... | @@ -1150,43 +1226,36 @@ fn constructDepString( | ... | @@ -1150,43 +1226,36 @@ fn constructDepString( |
| 1150 | } | 1226 | } |
| 1151 | } | 1227 | } |
| 1152 | 1228 | ||
| 1153 | fn make(step: *Step) !void { | 1229 | fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 1230 | const b = step.owner; | ||
| 1154 | const self = @fieldParentPtr(CompileStep, "step", step); | 1231 | const self = @fieldParentPtr(CompileStep, "step", step); |
| 1155 | const builder = self.builder; | ||
| 1156 | 1232 | ||
| 1157 | if (self.root_src == null and self.link_objects.items.len == 0) { | 1233 | if (self.root_src == null and self.link_objects.items.len == 0) { |
| 1158 | log.err("{s}: linker needs 1 or more objects to link", .{self.step.name}); | 1234 | return step.fail("the linker needs one or more objects to link", .{}); |
| 1159 | return error.NeedAnObject; | ||
| 1160 | } | 1235 | } |
| 1161 | 1236 | ||
| 1162 | var zig_args = ArrayList([]const u8).init(builder.allocator); | 1237 | var zig_args = ArrayList([]const u8).init(b.allocator); |
| 1163 | defer zig_args.deinit(); | 1238 | defer zig_args.deinit(); |
| 1164 | 1239 | ||
| 1165 | try zig_args.append(builder.zig_exe); | 1240 | try zig_args.append(b.zig_exe); |
| 1166 | 1241 | ||
| 1167 | const cmd = switch (self.kind) { | 1242 | const cmd = switch (self.kind) { |
| 1168 | .lib => "build-lib", | 1243 | .lib => "build-lib", |
| 1169 | .exe => "build-exe", | 1244 | .exe => "build-exe", |
| 1170 | .obj => "build-obj", | 1245 | .obj => "build-obj", |
| 1171 | .@"test" => "test", | 1246 | .@"test" => "test", |
| 1172 | .test_exe => "test", | ||
| 1173 | }; | 1247 | }; |
| 1174 | try zig_args.append(cmd); | 1248 | try zig_args.append(cmd); |
| 1175 | 1249 | ||
| 1176 | if (builder.color != .auto) { | 1250 | if (b.reference_trace) |some| { |
| 1177 | try zig_args.append("--color"); | 1251 | try zig_args.append(try std.fmt.allocPrint(b.allocator, "-freference-trace={d}", .{some})); |
| 1178 | try zig_args.append(@tagName(builder.color)); | ||
| 1179 | } | ||
| 1180 | |||
| 1181 | if (builder.reference_trace) |some| { | ||
| 1182 | try zig_args.append(try std.fmt.allocPrint(builder.allocator, "-freference-trace={d}", .{some})); | ||
| 1183 | } | 1252 | } |
| 1184 | 1253 | ||
| 1185 | try addFlag(&zig_args, "LLVM", self.use_llvm); | 1254 | try addFlag(&zig_args, "LLVM", self.use_llvm); |
| 1186 | try addFlag(&zig_args, "LLD", self.use_lld); | 1255 | try addFlag(&zig_args, "LLD", self.use_lld); |
| 1187 | 1256 | ||
| 1188 | if (self.target.ofmt) |ofmt| { | 1257 | if (self.target.ofmt) |ofmt| { |
| 1189 | try zig_args.append(try std.fmt.allocPrint(builder.allocator, "-ofmt={s}", .{@tagName(ofmt)})); | 1258 | try zig_args.append(try std.fmt.allocPrint(b.allocator, "-ofmt={s}", .{@tagName(ofmt)})); |
| 1190 | } | 1259 | } |
| 1191 | 1260 | ||
| 1192 | if (self.entry_symbol_name) |entry| { | 1261 | if (self.entry_symbol_name) |entry| { |
| ... | @@ -1196,18 +1265,18 @@ fn make(step: *Step) !void { | ... | @@ -1196,18 +1265,18 @@ fn make(step: *Step) !void { |
| 1196 | 1265 | ||
| 1197 | if (self.stack_size) |stack_size| { | 1266 | if (self.stack_size) |stack_size| { |
| 1198 | try zig_args.append("--stack"); | 1267 | try zig_args.append("--stack"); |
| 1199 | try zig_args.append(try std.fmt.allocPrint(builder.allocator, "{}", .{stack_size})); | 1268 | try zig_args.append(try std.fmt.allocPrint(b.allocator, "{}", .{stack_size})); |
| 1200 | } | 1269 | } |
| 1201 | 1270 | ||
| 1202 | if (self.root_src) |root_src| try zig_args.append(root_src.getPath(builder)); | 1271 | if (self.root_src) |root_src| try zig_args.append(root_src.getPath(b)); |
| 1203 | 1272 | ||
| 1204 | // We will add link objects from transitive dependencies, but we want to keep | 1273 | // We will add link objects from transitive dependencies, but we want to keep |
| 1205 | // all link objects in the same order provided. | 1274 | // all link objects in the same order provided. |
| 1206 | // This array is used to keep self.link_objects immutable. | 1275 | // This array is used to keep self.link_objects immutable. |
| 1207 | var transitive_deps: TransitiveDeps = .{ | 1276 | var transitive_deps: TransitiveDeps = .{ |
| 1208 | .link_objects = ArrayList(LinkObject).init(builder.allocator), | 1277 | .link_objects = ArrayList(LinkObject).init(b.allocator), |
| 1209 | .seen_system_libs = StringHashMap(void).init(builder.allocator), | 1278 | .seen_system_libs = StringHashMap(void).init(b.allocator), |
| 1210 | .seen_steps = std.AutoHashMap(*const Step, void).init(builder.allocator), | 1279 | .seen_steps = std.AutoHashMap(*const Step, void).init(b.allocator), |
| 1211 | .is_linking_libcpp = self.is_linking_libcpp, | 1280 | .is_linking_libcpp = self.is_linking_libcpp, |
| 1212 | .is_linking_libc = self.is_linking_libc, | 1281 | .is_linking_libc = self.is_linking_libc, |
| 1213 | .frameworks = &self.frameworks, | 1282 | .frameworks = &self.frameworks, |
| ... | @@ -1220,14 +1289,13 @@ fn make(step: *Step) !void { | ... | @@ -1220,14 +1289,13 @@ fn make(step: *Step) !void { |
| 1220 | 1289 | ||
| 1221 | for (transitive_deps.link_objects.items) |link_object| { | 1290 | for (transitive_deps.link_objects.items) |link_object| { |
| 1222 | switch (link_object) { | 1291 | switch (link_object) { |
| 1223 | .static_path => |static_path| try zig_args.append(static_path.getPath(builder)), | 1292 | .static_path => |static_path| try zig_args.append(static_path.getPath(b)), |
| 1224 | 1293 | ||
| 1225 | .other_step => |other| switch (other.kind) { | 1294 | .other_step => |other| switch (other.kind) { |
| 1226 | .exe => @panic("Cannot link with an executable build artifact"), | 1295 | .exe => @panic("Cannot link with an executable build artifact"), |
| 1227 | .test_exe => @panic("Cannot link with an executable build artifact"), | ||
| 1228 | .@"test" => @panic("Cannot link with a test"), | 1296 | .@"test" => @panic("Cannot link with a test"), |
| 1229 | .obj => { | 1297 | .obj => { |
| 1230 | try zig_args.append(other.getOutputSource().getPath(builder)); | 1298 | try zig_args.append(other.getOutputSource().getPath(b)); |
| 1231 | }, | 1299 | }, |
| 1232 | .lib => l: { | 1300 | .lib => l: { |
| 1233 | if (self.isStaticLibrary() and other.isStaticLibrary()) { | 1301 | if (self.isStaticLibrary() and other.isStaticLibrary()) { |
| ... | @@ -1235,7 +1303,7 @@ fn make(step: *Step) !void { | ... | @@ -1235,7 +1303,7 @@ fn make(step: *Step) !void { |
| 1235 | break :l; | 1303 | break :l; |
| 1236 | } | 1304 | } |
| 1237 | 1305 | ||
| 1238 | const full_path_lib = other.getOutputLibSource().getPath(builder); | 1306 | const full_path_lib = other.getOutputLibSource().getPath(b); |
| 1239 | try zig_args.append(full_path_lib); | 1307 | try zig_args.append(full_path_lib); |
| 1240 | 1308 | ||
| 1241 | if (other.linkage == Linkage.dynamic and !self.target.isWindows()) { | 1309 | if (other.linkage == Linkage.dynamic and !self.target.isWindows()) { |
| ... | @@ -1250,14 +1318,11 @@ fn make(step: *Step) !void { | ... | @@ -1250,14 +1318,11 @@ fn make(step: *Step) !void { |
| 1250 | .system_lib => |system_lib| { | 1318 | .system_lib => |system_lib| { |
| 1251 | const prefix: []const u8 = prefix: { | 1319 | const prefix: []const u8 = prefix: { |
| 1252 | if (system_lib.needed) break :prefix "-needed-l"; | 1320 | if (system_lib.needed) break :prefix "-needed-l"; |
| 1253 | if (system_lib.weak) { | 1321 | if (system_lib.weak) break :prefix "-weak-l"; |
| 1254 | if (self.target.isDarwin()) break :prefix "-weak-l"; | ||
| 1255 | log.warn("Weak library import used for a non-darwin target, this will be converted to normally library import `-lname`", .{}); | ||
| 1256 | } | ||
| 1257 | break :prefix "-l"; | 1322 | break :prefix "-l"; |
| 1258 | }; | 1323 | }; |
| 1259 | switch (system_lib.use_pkg_config) { | 1324 | switch (system_lib.use_pkg_config) { |
| 1260 | .no => try zig_args.append(builder.fmt("{s}{s}", .{ prefix, system_lib.name })), | 1325 | .no => try zig_args.append(b.fmt("{s}{s}", .{ prefix, system_lib.name })), |
| 1261 | .yes, .force => { | 1326 | .yes, .force => { |
| 1262 | if (self.runPkgConfig(system_lib.name)) |args| { | 1327 | if (self.runPkgConfig(system_lib.name)) |args| { |
| 1263 | try zig_args.appendSlice(args); | 1328 | try zig_args.appendSlice(args); |
| ... | @@ -1271,7 +1336,7 @@ fn make(step: *Step) !void { | ... | @@ -1271,7 +1336,7 @@ fn make(step: *Step) !void { |
| 1271 | .yes => { | 1336 | .yes => { |
| 1272 | // pkg-config failed, so fall back to linking the library | 1337 | // pkg-config failed, so fall back to linking the library |
| 1273 | // by name directly. | 1338 | // by name directly. |
| 1274 | try zig_args.append(builder.fmt("{s}{s}", .{ | 1339 | try zig_args.append(b.fmt("{s}{s}", .{ |
| 1275 | prefix, | 1340 | prefix, |
| 1276 | system_lib.name, | 1341 | system_lib.name, |
| 1277 | })); | 1342 | })); |
| ... | @@ -1294,7 +1359,7 @@ fn make(step: *Step) !void { | ... | @@ -1294,7 +1359,7 @@ fn make(step: *Step) !void { |
| 1294 | try zig_args.append("--"); | 1359 | try zig_args.append("--"); |
| 1295 | prev_has_extra_flags = false; | 1360 | prev_has_extra_flags = false; |
| 1296 | } | 1361 | } |
| 1297 | try zig_args.append(asm_file.getPath(builder)); | 1362 | try zig_args.append(asm_file.getPath(b)); |
| 1298 | }, | 1363 | }, |
| 1299 | 1364 | ||
| 1300 | .c_source_file => |c_source_file| { | 1365 | .c_source_file => |c_source_file| { |
| ... | @@ -1311,7 +1376,7 @@ fn make(step: *Step) !void { | ... | @@ -1311,7 +1376,7 @@ fn make(step: *Step) !void { |
| 1311 | } | 1376 | } |
| 1312 | try zig_args.append("--"); | 1377 | try zig_args.append("--"); |
| 1313 | } | 1378 | } |
| 1314 | try zig_args.append(c_source_file.source.getPath(builder)); | 1379 | try zig_args.append(c_source_file.source.getPath(b)); |
| 1315 | }, | 1380 | }, |
| 1316 | 1381 | ||
| 1317 | .c_source_files => |c_source_files| { | 1382 | .c_source_files => |c_source_files| { |
| ... | @@ -1329,7 +1394,7 @@ fn make(step: *Step) !void { | ... | @@ -1329,7 +1394,7 @@ fn make(step: *Step) !void { |
| 1329 | try zig_args.append("--"); | 1394 | try zig_args.append("--"); |
| 1330 | } | 1395 | } |
| 1331 | for (c_source_files.files) |file| { | 1396 | for (c_source_files.files) |file| { |
| 1332 | try zig_args.append(builder.pathFromRoot(file)); | 1397 | try zig_args.append(b.pathFromRoot(file)); |
| 1333 | } | 1398 | } |
| 1334 | }, | 1399 | }, |
| 1335 | } | 1400 | } |
| ... | @@ -1345,7 +1410,7 @@ fn make(step: *Step) !void { | ... | @@ -1345,7 +1410,7 @@ fn make(step: *Step) !void { |
| 1345 | 1410 | ||
| 1346 | if (self.image_base) |image_base| { | 1411 | if (self.image_base) |image_base| { |
| 1347 | try zig_args.append("--image-base"); | 1412 | try zig_args.append("--image-base"); |
| 1348 | try zig_args.append(builder.fmt("0x{x}", .{image_base})); | 1413 | try zig_args.append(b.fmt("0x{x}", .{image_base})); |
| 1349 | } | 1414 | } |
| 1350 | 1415 | ||
| 1351 | if (self.filter) |filter| { | 1416 | if (self.filter) |filter| { |
| ... | @@ -1357,39 +1422,34 @@ fn make(step: *Step) !void { | ... | @@ -1357,39 +1422,34 @@ fn make(step: *Step) !void { |
| 1357 | try zig_args.append("--test-evented-io"); | 1422 | try zig_args.append("--test-evented-io"); |
| 1358 | } | 1423 | } |
| 1359 | 1424 | ||
| 1360 | if (self.name_prefix.len != 0) { | ||
| 1361 | try zig_args.append("--test-name-prefix"); | ||
| 1362 | try zig_args.append(self.name_prefix); | ||
| 1363 | } | ||
| 1364 | |||
| 1365 | if (self.test_runner) |test_runner| { | 1425 | if (self.test_runner) |test_runner| { |
| 1366 | try zig_args.append("--test-runner"); | 1426 | try zig_args.append("--test-runner"); |
| 1367 | try zig_args.append(builder.pathFromRoot(test_runner)); | 1427 | try zig_args.append(b.pathFromRoot(test_runner)); |
| 1368 | } | 1428 | } |
| 1369 | 1429 | ||
| 1370 | for (builder.debug_log_scopes) |log_scope| { | 1430 | for (b.debug_log_scopes) |log_scope| { |
| 1371 | try zig_args.append("--debug-log"); | 1431 | try zig_args.append("--debug-log"); |
| 1372 | try zig_args.append(log_scope); | 1432 | try zig_args.append(log_scope); |
| 1373 | } | 1433 | } |
| 1374 | 1434 | ||
| 1375 | if (builder.debug_compile_errors) { | 1435 | if (b.debug_compile_errors) { |
| 1376 | try zig_args.append("--debug-compile-errors"); | 1436 | try zig_args.append("--debug-compile-errors"); |
| 1377 | } | 1437 | } |
| 1378 | 1438 | ||
| 1379 | if (builder.verbose_cimport) try zig_args.append("--verbose-cimport"); | 1439 | if (b.verbose_cimport) try zig_args.append("--verbose-cimport"); |
| 1380 | if (builder.verbose_air) try zig_args.append("--verbose-air"); | 1440 | if (b.verbose_air) try zig_args.append("--verbose-air"); |
| 1381 | if (builder.verbose_llvm_ir) try zig_args.append("--verbose-llvm-ir"); | 1441 | if (b.verbose_llvm_ir) try zig_args.append("--verbose-llvm-ir"); |
| 1382 | if (builder.verbose_link or self.verbose_link) try zig_args.append("--verbose-link"); | 1442 | if (b.verbose_link or self.verbose_link) try zig_args.append("--verbose-link"); |
| 1383 | if (builder.verbose_cc or self.verbose_cc) try zig_args.append("--verbose-cc"); | 1443 | if (b.verbose_cc or self.verbose_cc) try zig_args.append("--verbose-cc"); |
| 1384 | if (builder.verbose_llvm_cpu_features) try zig_args.append("--verbose-llvm-cpu-features"); | 1444 | if (b.verbose_llvm_cpu_features) try zig_args.append("--verbose-llvm-cpu-features"); |
| 1385 | 1445 | ||
| 1386 | if (self.emit_analysis.getArg(builder, "emit-analysis")) |arg| try zig_args.append(arg); | 1446 | if (self.emit_analysis.getArg(b, "emit-analysis")) |arg| try zig_args.append(arg); |
| 1387 | if (self.emit_asm.getArg(builder, "emit-asm")) |arg| try zig_args.append(arg); | 1447 | if (self.emit_asm.getArg(b, "emit-asm")) |arg| try zig_args.append(arg); |
| 1388 | if (self.emit_bin.getArg(builder, "emit-bin")) |arg| try zig_args.append(arg); | 1448 | if (self.emit_bin.getArg(b, "emit-bin")) |arg| try zig_args.append(arg); |
| 1389 | if (self.emit_docs.getArg(builder, "emit-docs")) |arg| try zig_args.append(arg); | 1449 | if (self.emit_docs.getArg(b, "emit-docs")) |arg| try zig_args.append(arg); |
| 1390 | if (self.emit_implib.getArg(builder, "emit-implib")) |arg| try zig_args.append(arg); | 1450 | if (self.emit_implib.getArg(b, "emit-implib")) |arg| try zig_args.append(arg); |
| 1391 | if (self.emit_llvm_bc.getArg(builder, "emit-llvm-bc")) |arg| try zig_args.append(arg); | 1451 | if (self.emit_llvm_bc.getArg(b, "emit-llvm-bc")) |arg| try zig_args.append(arg); |
| 1392 | if (self.emit_llvm_ir.getArg(builder, "emit-llvm-ir")) |arg| try zig_args.append(arg); | 1452 | if (self.emit_llvm_ir.getArg(b, "emit-llvm-ir")) |arg| try zig_args.append(arg); |
| 1393 | 1453 | ||
| 1394 | if (self.emit_h) try zig_args.append("-femit-h"); | 1454 | if (self.emit_h) try zig_args.append("-femit-h"); |
| 1395 | 1455 | ||
| ... | @@ -1430,31 +1490,31 @@ fn make(step: *Step) !void { | ... | @@ -1430,31 +1490,31 @@ fn make(step: *Step) !void { |
| 1430 | } | 1490 | } |
| 1431 | if (self.link_z_common_page_size) |size| { | 1491 | if (self.link_z_common_page_size) |size| { |
| 1432 | try zig_args.append("-z"); | 1492 | try zig_args.append("-z"); |
| 1433 | try zig_args.append(builder.fmt("common-page-size={d}", .{size})); | 1493 | try zig_args.append(b.fmt("common-page-size={d}", .{size})); |
| 1434 | } | 1494 | } |
| 1435 | if (self.link_z_max_page_size) |size| { | 1495 | if (self.link_z_max_page_size) |size| { |
| 1436 | try zig_args.append("-z"); | 1496 | try zig_args.append("-z"); |
| 1437 | try zig_args.append(builder.fmt("max-page-size={d}", .{size})); | 1497 | try zig_args.append(b.fmt("max-page-size={d}", .{size})); |
| 1438 | } | 1498 | } |
| 1439 | 1499 | ||
| 1440 | if (self.libc_file) |libc_file| { | 1500 | if (self.libc_file) |libc_file| { |
| 1441 | try zig_args.append("--libc"); | 1501 | try zig_args.append("--libc"); |
| 1442 | try zig_args.append(libc_file.getPath(builder)); | 1502 | try zig_args.append(libc_file.getPath(b)); |
| 1443 | } else if (builder.libc_file) |libc_file| { | 1503 | } else if (b.libc_file) |libc_file| { |
| 1444 | try zig_args.append("--libc"); | 1504 | try zig_args.append("--libc"); |
| 1445 | try zig_args.append(libc_file); | 1505 | try zig_args.append(libc_file); |
| 1446 | } | 1506 | } |
| 1447 | 1507 | ||
| 1448 | switch (self.optimize) { | 1508 | switch (self.optimize) { |
| 1449 | .Debug => {}, // Skip since it's the default. | 1509 | .Debug => {}, // Skip since it's the default. |
| 1450 | else => try zig_args.append(builder.fmt("-O{s}", .{@tagName(self.optimize)})), | 1510 | else => try zig_args.append(b.fmt("-O{s}", .{@tagName(self.optimize)})), |
| 1451 | } | 1511 | } |
| 1452 | 1512 | ||
| 1453 | try zig_args.append("--cache-dir"); | 1513 | try zig_args.append("--cache-dir"); |
| 1454 | try zig_args.append(builder.cache_root.path orelse "."); | 1514 | try zig_args.append(b.cache_root.path orelse "."); |
| 1455 | 1515 | ||
| 1456 | try zig_args.append("--global-cache-dir"); | 1516 | try zig_args.append("--global-cache-dir"); |
| 1457 | try zig_args.append(builder.global_cache_root.path orelse "."); | 1517 | try zig_args.append(b.global_cache_root.path orelse "."); |
| 1458 | 1518 | ||
| 1459 | try zig_args.append("--name"); | 1519 | try zig_args.append("--name"); |
| 1460 | try zig_args.append(self.name); | 1520 | try zig_args.append(self.name); |
| ... | @@ -1466,11 +1526,11 @@ fn make(step: *Step) !void { | ... | @@ -1466,11 +1526,11 @@ fn make(step: *Step) !void { |
| 1466 | if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic) { | 1526 | if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic) { |
| 1467 | if (self.version) |version| { | 1527 | if (self.version) |version| { |
| 1468 | try zig_args.append("--version"); | 1528 | try zig_args.append("--version"); |
| 1469 | try zig_args.append(builder.fmt("{}", .{version})); | 1529 | try zig_args.append(b.fmt("{}", .{version})); |
| 1470 | } | 1530 | } |
| 1471 | 1531 | ||
| 1472 | if (self.target.isDarwin()) { | 1532 | if (self.target.isDarwin()) { |
| 1473 | const install_name = self.install_name orelse builder.fmt("@rpath/{s}{s}{s}", .{ | 1533 | const install_name = self.install_name orelse b.fmt("@rpath/{s}{s}{s}", .{ |
| 1474 | self.target.libPrefix(), | 1534 | self.target.libPrefix(), |
| 1475 | self.name, | 1535 | self.name, |
| 1476 | self.target.dynamicLibSuffix(), | 1536 | self.target.dynamicLibSuffix(), |
| ... | @@ -1484,7 +1544,7 @@ fn make(step: *Step) !void { | ... | @@ -1484,7 +1544,7 @@ fn make(step: *Step) !void { |
| 1484 | try zig_args.appendSlice(&[_][]const u8{ "--entitlements", entitlements }); | 1544 | try zig_args.appendSlice(&[_][]const u8{ "--entitlements", entitlements }); |
| 1485 | } | 1545 | } |
| 1486 | if (self.pagezero_size) |pagezero_size| { | 1546 | if (self.pagezero_size) |pagezero_size| { |
| 1487 | const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{pagezero_size}); | 1547 | const size = try std.fmt.allocPrint(b.allocator, "{x}", .{pagezero_size}); |
| 1488 | try zig_args.appendSlice(&[_][]const u8{ "-pagezero_size", size }); | 1548 | try zig_args.appendSlice(&[_][]const u8{ "-pagezero_size", size }); |
| 1489 | } | 1549 | } |
| 1490 | if (self.search_strategy) |strat| switch (strat) { | 1550 | if (self.search_strategy) |strat| switch (strat) { |
| ... | @@ -1492,7 +1552,7 @@ fn make(step: *Step) !void { | ... | @@ -1492,7 +1552,7 @@ fn make(step: *Step) !void { |
| 1492 | .dylibs_first => try zig_args.append("-search_dylibs_first"), | 1552 | .dylibs_first => try zig_args.append("-search_dylibs_first"), |
| 1493 | }; | 1553 | }; |
| 1494 | if (self.headerpad_size) |headerpad_size| { | 1554 | if (self.headerpad_size) |headerpad_size| { |
| 1495 | const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{headerpad_size}); | 1555 | const size = try std.fmt.allocPrint(b.allocator, "{x}", .{headerpad_size}); |
| 1496 | try zig_args.appendSlice(&[_][]const u8{ "-headerpad", size }); | 1556 | try zig_args.appendSlice(&[_][]const u8{ "-headerpad", size }); |
| 1497 | } | 1557 | } |
| 1498 | if (self.headerpad_max_install_names) { | 1558 | if (self.headerpad_max_install_names) { |
| ... | @@ -1540,16 +1600,16 @@ fn make(step: *Step) !void { | ... | @@ -1540,16 +1600,16 @@ fn make(step: *Step) !void { |
| 1540 | try zig_args.append("--export-table"); | 1600 | try zig_args.append("--export-table"); |
| 1541 | } | 1601 | } |
| 1542 | if (self.initial_memory) |initial_memory| { | 1602 | if (self.initial_memory) |initial_memory| { |
| 1543 | try zig_args.append(builder.fmt("--initial-memory={d}", .{initial_memory})); | 1603 | try zig_args.append(b.fmt("--initial-memory={d}", .{initial_memory})); |
| 1544 | } | 1604 | } |
| 1545 | if (self.max_memory) |max_memory| { | 1605 | if (self.max_memory) |max_memory| { |
| 1546 | try zig_args.append(builder.fmt("--max-memory={d}", .{max_memory})); | 1606 | try zig_args.append(b.fmt("--max-memory={d}", .{max_memory})); |
| 1547 | } | 1607 | } |
| 1548 | if (self.shared_memory) { | 1608 | if (self.shared_memory) { |
| 1549 | try zig_args.append("--shared-memory"); | 1609 | try zig_args.append("--shared-memory"); |
| 1550 | } | 1610 | } |
| 1551 | if (self.global_base) |global_base| { | 1611 | if (self.global_base) |global_base| { |
| 1552 | try zig_args.append(builder.fmt("--global-base={d}", .{global_base})); | 1612 | try zig_args.append(b.fmt("--global-base={d}", .{global_base})); |
| 1553 | } | 1613 | } |
| 1554 | 1614 | ||
| 1555 | if (self.code_model != .default) { | 1615 | if (self.code_model != .default) { |
| ... | @@ -1557,16 +1617,16 @@ fn make(step: *Step) !void { | ... | @@ -1557,16 +1617,16 @@ fn make(step: *Step) !void { |
| 1557 | try zig_args.append(@tagName(self.code_model)); | 1617 | try zig_args.append(@tagName(self.code_model)); |
| 1558 | } | 1618 | } |
| 1559 | if (self.wasi_exec_model) |model| { | 1619 | if (self.wasi_exec_model) |model| { |
| 1560 | try zig_args.append(builder.fmt("-mexec-model={s}", .{@tagName(model)})); | 1620 | try zig_args.append(b.fmt("-mexec-model={s}", .{@tagName(model)})); |
| 1561 | } | 1621 | } |
| 1562 | for (self.export_symbol_names) |symbol_name| { | 1622 | for (self.export_symbol_names) |symbol_name| { |
| 1563 | try zig_args.append(builder.fmt("--export={s}", .{symbol_name})); | 1623 | try zig_args.append(b.fmt("--export={s}", .{symbol_name})); |
| 1564 | } | 1624 | } |
| 1565 | 1625 | ||
| 1566 | if (!self.target.isNative()) { | 1626 | if (!self.target.isNative()) { |
| 1567 | try zig_args.appendSlice(&.{ | 1627 | try zig_args.appendSlice(&.{ |
| 1568 | "-target", try self.target.zigTriple(builder.allocator), | 1628 | "-target", try self.target.zigTriple(b.allocator), |
| 1569 | "-mcpu", try std.Build.serializeCpu(builder.allocator, self.target.getCpu()), | 1629 | "-mcpu", try std.Build.serializeCpu(b.allocator, self.target.getCpu()), |
| 1570 | }); | 1630 | }); |
| 1571 | 1631 | ||
| 1572 | if (self.target.dynamic_linker.get()) |dynamic_linker| { | 1632 | if (self.target.dynamic_linker.get()) |dynamic_linker| { |
| ... | @@ -1577,12 +1637,12 @@ fn make(step: *Step) !void { | ... | @@ -1577,12 +1637,12 @@ fn make(step: *Step) !void { |
| 1577 | 1637 | ||
| 1578 | if (self.linker_script) |linker_script| { | 1638 | if (self.linker_script) |linker_script| { |
| 1579 | try zig_args.append("--script"); | 1639 | try zig_args.append("--script"); |
| 1580 | try zig_args.append(linker_script.getPath(builder)); | 1640 | try zig_args.append(linker_script.getPath(b)); |
| 1581 | } | 1641 | } |
| 1582 | 1642 | ||
| 1583 | if (self.version_script) |version_script| { | 1643 | if (self.version_script) |version_script| { |
| 1584 | try zig_args.append("--version-script"); | 1644 | try zig_args.append("--version-script"); |
| 1585 | try zig_args.append(builder.pathFromRoot(version_script)); | 1645 | try zig_args.append(b.pathFromRoot(version_script)); |
| 1586 | } | 1646 | } |
| 1587 | 1647 | ||
| 1588 | if (self.kind == .@"test") { | 1648 | if (self.kind == .@"test") { |
| ... | @@ -1595,83 +1655,7 @@ fn make(step: *Step) !void { | ... | @@ -1595,83 +1655,7 @@ fn make(step: *Step) !void { |
| 1595 | try zig_args.append("--test-cmd-bin"); | 1655 | try zig_args.append("--test-cmd-bin"); |
| 1596 | } | 1656 | } |
| 1597 | } | 1657 | } |
| 1598 | } else { | ||
| 1599 | const need_cross_glibc = self.target.isGnuLibC() and transitive_deps.is_linking_libc; | ||
| 1600 | |||
| 1601 | switch (builder.host.getExternalExecutor(self.target_info, .{ | ||
| 1602 | .qemu_fixes_dl = need_cross_glibc and builder.glibc_runtimes_dir != null, | ||
| 1603 | .link_libc = transitive_deps.is_linking_libc, | ||
| 1604 | })) { | ||
| 1605 | .native => {}, | ||
| 1606 | .bad_dl, .bad_os_or_cpu => { | ||
| 1607 | try zig_args.append("--test-no-exec"); | ||
| 1608 | }, | ||
| 1609 | .rosetta => if (builder.enable_rosetta) { | ||
| 1610 | try zig_args.append("--test-cmd-bin"); | ||
| 1611 | } else { | ||
| 1612 | try zig_args.append("--test-no-exec"); | ||
| 1613 | }, | ||
| 1614 | .qemu => |bin_name| ok: { | ||
| 1615 | if (builder.enable_qemu) qemu: { | ||
| 1616 | const glibc_dir_arg = if (need_cross_glibc) | ||
| 1617 | builder.glibc_runtimes_dir orelse break :qemu | ||
| 1618 | else | ||
| 1619 | null; | ||
| 1620 | try zig_args.append("--test-cmd"); | ||
| 1621 | try zig_args.append(bin_name); | ||
| 1622 | if (glibc_dir_arg) |dir| { | ||
| 1623 | // TODO look into making this a call to `linuxTriple`. This | ||
| 1624 | // needs the directory to be called "i686" rather than | ||
| 1625 | // "x86" which is why we do it manually here. | ||
| 1626 | const fmt_str = "{s}" ++ fs.path.sep_str ++ "{s}-{s}-{s}"; | ||
| 1627 | const cpu_arch = self.target.getCpuArch(); | ||
| 1628 | const os_tag = self.target.getOsTag(); | ||
| 1629 | const abi = self.target.getAbi(); | ||
| 1630 | const cpu_arch_name: []const u8 = if (cpu_arch == .x86) | ||
| 1631 | "i686" | ||
| 1632 | else | ||
| 1633 | @tagName(cpu_arch); | ||
| 1634 | const full_dir = try std.fmt.allocPrint(builder.allocator, fmt_str, .{ | ||
| 1635 | dir, cpu_arch_name, @tagName(os_tag), @tagName(abi), | ||
| 1636 | }); | ||
| 1637 | |||
| 1638 | try zig_args.append("--test-cmd"); | ||
| 1639 | try zig_args.append("-L"); | ||
| 1640 | try zig_args.append("--test-cmd"); | ||
| 1641 | try zig_args.append(full_dir); | ||
| 1642 | } | ||
| 1643 | try zig_args.append("--test-cmd-bin"); | ||
| 1644 | break :ok; | ||
| 1645 | } | ||
| 1646 | try zig_args.append("--test-no-exec"); | ||
| 1647 | }, | ||
| 1648 | .wine => |bin_name| if (builder.enable_wine) { | ||
| 1649 | try zig_args.append("--test-cmd"); | ||
| 1650 | try zig_args.append(bin_name); | ||
| 1651 | try zig_args.append("--test-cmd-bin"); | ||
| 1652 | } else { | ||
| 1653 | try zig_args.append("--test-no-exec"); | ||
| 1654 | }, | ||
| 1655 | .wasmtime => |bin_name| if (builder.enable_wasmtime) { | ||
| 1656 | try zig_args.append("--test-cmd"); | ||
| 1657 | try zig_args.append(bin_name); | ||
| 1658 | try zig_args.append("--test-cmd"); | ||
| 1659 | try zig_args.append("--dir=."); | ||
| 1660 | try zig_args.append("--test-cmd-bin"); | ||
| 1661 | } else { | ||
| 1662 | try zig_args.append("--test-no-exec"); | ||
| 1663 | }, | ||
| 1664 | .darling => |bin_name| if (builder.enable_darling) { | ||
| 1665 | try zig_args.append("--test-cmd"); | ||
| 1666 | try zig_args.append(bin_name); | ||
| 1667 | try zig_args.append("--test-cmd-bin"); | ||
| 1668 | } else { | ||
| 1669 | try zig_args.append("--test-no-exec"); | ||
| 1670 | }, | ||
| 1671 | } | ||
| 1672 | } | 1658 | } |
| 1673 | } else if (self.kind == .test_exe) { | ||
| 1674 | try zig_args.append("--test-no-exec"); | ||
| 1675 | } | 1659 | } |
| 1676 | 1660 | ||
| 1677 | try self.appendModuleArgs(&zig_args); | 1661 | try self.appendModuleArgs(&zig_args); |
| ... | @@ -1680,18 +1664,18 @@ fn make(step: *Step) !void { | ... | @@ -1680,18 +1664,18 @@ fn make(step: *Step) !void { |
| 1680 | switch (include_dir) { | 1664 | switch (include_dir) { |
| 1681 | .raw_path => |include_path| { | 1665 | .raw_path => |include_path| { |
| 1682 | try zig_args.append("-I"); | 1666 | try zig_args.append("-I"); |
| 1683 | try zig_args.append(builder.pathFromRoot(include_path)); | 1667 | try zig_args.append(b.pathFromRoot(include_path)); |
| 1684 | }, | 1668 | }, |
| 1685 | .raw_path_system => |include_path| { | 1669 | .raw_path_system => |include_path| { |
| 1686 | if (builder.sysroot != null) { | 1670 | if (b.sysroot != null) { |
| 1687 | try zig_args.append("-iwithsysroot"); | 1671 | try zig_args.append("-iwithsysroot"); |
| 1688 | } else { | 1672 | } else { |
| 1689 | try zig_args.append("-isystem"); | 1673 | try zig_args.append("-isystem"); |
| 1690 | } | 1674 | } |
| 1691 | 1675 | ||
| 1692 | const resolved_include_path = builder.pathFromRoot(include_path); | 1676 | const resolved_include_path = b.pathFromRoot(include_path); |
| 1693 | 1677 | ||
| 1694 | const common_include_path = if (builtin.os.tag == .windows and builder.sysroot != null and fs.path.isAbsolute(resolved_include_path)) blk: { | 1678 | const common_include_path = if (builtin.os.tag == .windows and b.sysroot != null and fs.path.isAbsolute(resolved_include_path)) blk: { |
| 1695 | // We need to check for disk designator and strip it out from dir path so | 1679 | // We need to check for disk designator and strip it out from dir path so |
| 1696 | // that zig/clang can concat resolved_include_path with sysroot. | 1680 | // that zig/clang can concat resolved_include_path with sysroot. |
| 1697 | const disk_designator = fs.path.diskDesignatorWindows(resolved_include_path); | 1681 | const disk_designator = fs.path.diskDesignatorWindows(resolved_include_path); |
| ... | @@ -1707,17 +1691,14 @@ fn make(step: *Step) !void { | ... | @@ -1707,17 +1691,14 @@ fn make(step: *Step) !void { |
| 1707 | }, | 1691 | }, |
| 1708 | .other_step => |other| { | 1692 | .other_step => |other| { |
| 1709 | if (other.emit_h) { | 1693 | if (other.emit_h) { |
| 1710 | const h_path = other.getOutputHSource().getPath(builder); | 1694 | const h_path = other.getOutputHSource().getPath(b); |
| 1711 | try zig_args.append("-isystem"); | 1695 | try zig_args.append("-isystem"); |
| 1712 | try zig_args.append(fs.path.dirname(h_path).?); | 1696 | try zig_args.append(fs.path.dirname(h_path).?); |
| 1713 | } | 1697 | } |
| 1714 | if (other.installed_headers.items.len > 0) { | 1698 | if (other.installed_headers.items.len > 0) { |
| 1715 | for (other.installed_headers.items) |install_step| { | ||
| 1716 | try install_step.make(); | ||
| 1717 | } | ||
| 1718 | try zig_args.append("-I"); | 1699 | try zig_args.append("-I"); |
| 1719 | try zig_args.append(builder.pathJoin(&.{ | 1700 | try zig_args.append(b.pathJoin(&.{ |
| 1720 | other.builder.install_prefix, "include", | 1701 | other.step.owner.install_prefix, "include", |
| 1721 | })); | 1702 | })); |
| 1722 | } | 1703 | } |
| 1723 | }, | 1704 | }, |
| ... | @@ -1729,33 +1710,35 @@ fn make(step: *Step) !void { | ... | @@ -1729,33 +1710,35 @@ fn make(step: *Step) !void { |
| 1729 | } | 1710 | } |
| 1730 | } | 1711 | } |
| 1731 | 1712 | ||
| 1732 | for (self.lib_paths.items) |lib_path| { | 1713 | for (self.c_macros.items) |c_macro| { |
| 1733 | try zig_args.append("-L"); | 1714 | try zig_args.append("-D"); |
| 1734 | try zig_args.append(lib_path); | 1715 | try zig_args.append(c_macro); |
| 1735 | } | 1716 | } |
| 1736 | 1717 | ||
| 1737 | for (self.rpaths.items) |rpath| { | 1718 | try zig_args.ensureUnusedCapacity(2 * self.lib_paths.items.len); |
| 1738 | try zig_args.append("-rpath"); | 1719 | for (self.lib_paths.items) |lib_path| { |
| 1739 | try zig_args.append(rpath); | 1720 | zig_args.appendAssumeCapacity("-L"); |
| 1721 | zig_args.appendAssumeCapacity(lib_path.getPath2(b, step)); | ||
| 1740 | } | 1722 | } |
| 1741 | 1723 | ||
| 1742 | for (self.c_macros.items) |c_macro| { | 1724 | try zig_args.ensureUnusedCapacity(2 * self.rpaths.items.len); |
| 1743 | try zig_args.append("-D"); | 1725 | for (self.rpaths.items) |rpath| { |
| 1744 | try zig_args.append(c_macro); | 1726 | zig_args.appendAssumeCapacity("-rpath"); |
| 1727 | zig_args.appendAssumeCapacity(rpath.getPath2(b, step)); | ||
| 1745 | } | 1728 | } |
| 1746 | 1729 | ||
| 1747 | if (self.target.isDarwin()) { | 1730 | for (self.framework_dirs.items) |directory_source| { |
| 1748 | for (self.framework_dirs.items) |dir| { | 1731 | if (b.sysroot != null) { |
| 1749 | if (builder.sysroot != null) { | 1732 | try zig_args.append("-iframeworkwithsysroot"); |
| 1750 | try zig_args.append("-iframeworkwithsysroot"); | 1733 | } else { |
| 1751 | } else { | 1734 | try zig_args.append("-iframework"); |
| 1752 | try zig_args.append("-iframework"); | ||
| 1753 | } | ||
| 1754 | try zig_args.append(dir); | ||
| 1755 | try zig_args.append("-F"); | ||
| 1756 | try zig_args.append(dir); | ||
| 1757 | } | 1735 | } |
| 1736 | try zig_args.append(directory_source.getPath2(b, step)); | ||
| 1737 | try zig_args.append("-F"); | ||
| 1738 | try zig_args.append(directory_source.getPath2(b, step)); | ||
| 1739 | } | ||
| 1758 | 1740 | ||
| 1741 | { | ||
| 1759 | var it = self.frameworks.iterator(); | 1742 | var it = self.frameworks.iterator(); |
| 1760 | while (it.next()) |entry| { | 1743 | while (it.next()) |entry| { |
| 1761 | const name = entry.key_ptr.*; | 1744 | const name = entry.key_ptr.*; |
| ... | @@ -1769,29 +1752,45 @@ fn make(step: *Step) !void { | ... | @@ -1769,29 +1752,45 @@ fn make(step: *Step) !void { |
| 1769 | } | 1752 | } |
| 1770 | try zig_args.append(name); | 1753 | try zig_args.append(name); |
| 1771 | } | 1754 | } |
| 1772 | } else { | ||
| 1773 | if (self.framework_dirs.items.len > 0) { | ||
| 1774 | log.info("Framework directories have been added for a non-darwin target, this will have no affect on the build", .{}); | ||
| 1775 | } | ||
| 1776 | |||
| 1777 | if (self.frameworks.count() > 0) { | ||
| 1778 | log.info("Frameworks have been added for a non-darwin target, this will have no affect on the build", .{}); | ||
| 1779 | } | ||
| 1780 | } | 1755 | } |
| 1781 | 1756 | ||
| 1782 | if (builder.sysroot) |sysroot| { | 1757 | if (b.sysroot) |sysroot| { |
| 1783 | try zig_args.appendSlice(&[_][]const u8{ "--sysroot", sysroot }); | 1758 | try zig_args.appendSlice(&[_][]const u8{ "--sysroot", sysroot }); |
| 1784 | } | 1759 | } |
| 1785 | 1760 | ||
| 1786 | for (builder.search_prefixes.items) |search_prefix| { | 1761 | for (b.search_prefixes.items) |search_prefix| { |
| 1787 | try zig_args.append("-L"); | 1762 | var prefix_dir = fs.cwd().openDir(search_prefix, .{}) catch |err| { |
| 1788 | try zig_args.append(builder.pathJoin(&.{ | 1763 | return step.fail("unable to open prefix directory '{s}': {s}", .{ |
| 1789 | search_prefix, "lib", | 1764 | search_prefix, @errorName(err), |
| 1790 | })); | 1765 | }); |
| 1791 | try zig_args.append("-I"); | 1766 | }; |
| 1792 | try zig_args.append(builder.pathJoin(&.{ | 1767 | defer prefix_dir.close(); |
| 1793 | search_prefix, "include", | 1768 | |
| 1794 | })); | 1769 | // Avoid passing -L and -I flags for nonexistent directories. |
| 1770 | // This prevents a warning, that should probably be upgraded to an error in Zig's | ||
| 1771 | // CLI parsing code, when the linker sees an -L directory that does not exist. | ||
| 1772 | |||
| 1773 | if (prefix_dir.accessZ("lib", .{})) |_| { | ||
| 1774 | try zig_args.appendSlice(&.{ | ||
| 1775 | "-L", try fs.path.join(b.allocator, &.{ search_prefix, "lib" }), | ||
| 1776 | }); | ||
| 1777 | } else |err| switch (err) { | ||
| 1778 | error.FileNotFound => {}, | ||
| 1779 | else => |e| return step.fail("unable to access '{s}/lib' directory: {s}", .{ | ||
| 1780 | search_prefix, @errorName(e), | ||
| 1781 | }), | ||
| 1782 | } | ||
| 1783 | |||
| 1784 | if (prefix_dir.accessZ("include", .{})) |_| { | ||
| 1785 | try zig_args.appendSlice(&.{ | ||
| 1786 | "-I", try fs.path.join(b.allocator, &.{ search_prefix, "include" }), | ||
| 1787 | }); | ||
| 1788 | } else |err| switch (err) { | ||
| 1789 | error.FileNotFound => {}, | ||
| 1790 | else => |e| return step.fail("unable to access '{s}/include' directory: {s}", .{ | ||
| 1791 | search_prefix, @errorName(e), | ||
| 1792 | }), | ||
| 1793 | } | ||
| 1795 | } | 1794 | } |
| 1796 | 1795 | ||
| 1797 | try addFlag(&zig_args, "valgrind", self.valgrind_support); | 1796 | try addFlag(&zig_args, "valgrind", self.valgrind_support); |
| ... | @@ -1800,15 +1799,15 @@ fn make(step: *Step) !void { | ... | @@ -1800,15 +1799,15 @@ fn make(step: *Step) !void { |
| 1800 | 1799 | ||
| 1801 | if (self.zig_lib_dir) |dir| { | 1800 | if (self.zig_lib_dir) |dir| { |
| 1802 | try zig_args.append("--zig-lib-dir"); | 1801 | try zig_args.append("--zig-lib-dir"); |
| 1803 | try zig_args.append(builder.pathFromRoot(dir)); | 1802 | try zig_args.append(b.pathFromRoot(dir)); |
| 1804 | } else if (builder.zig_lib_dir) |dir| { | 1803 | } else if (b.zig_lib_dir) |dir| { |
| 1805 | try zig_args.append("--zig-lib-dir"); | 1804 | try zig_args.append("--zig-lib-dir"); |
| 1806 | try zig_args.append(dir); | 1805 | try zig_args.append(dir); |
| 1807 | } | 1806 | } |
| 1808 | 1807 | ||
| 1809 | if (self.main_pkg_path) |dir| { | 1808 | if (self.main_pkg_path) |dir| { |
| 1810 | try zig_args.append("--main-pkg-path"); | 1809 | try zig_args.append("--main-pkg-path"); |
| 1811 | try zig_args.append(builder.pathFromRoot(dir)); | 1810 | try zig_args.append(b.pathFromRoot(dir)); |
| 1812 | } | 1811 | } |
| 1813 | 1812 | ||
| 1814 | try addFlag(&zig_args, "PIC", self.force_pic); | 1813 | try addFlag(&zig_args, "PIC", self.force_pic); |
| ... | @@ -1830,6 +1829,7 @@ fn make(step: *Step) !void { | ... | @@ -1830,6 +1829,7 @@ fn make(step: *Step) !void { |
| 1830 | } | 1829 | } |
| 1831 | 1830 | ||
| 1832 | try zig_args.append("--enable-cache"); | 1831 | try zig_args.append("--enable-cache"); |
| 1832 | try zig_args.append("--listen=-"); | ||
| 1833 | 1833 | ||
| 1834 | // Windows has an argument length limit of 32,766 characters, macOS 262,144 and Linux | 1834 | // Windows has an argument length limit of 32,766 characters, macOS 262,144 and Linux |
| 1835 | // 2,097,152. If our args exceed 30 KiB, we instead write them to a "response file" and | 1835 | // 2,097,152. If our args exceed 30 KiB, we instead write them to a "response file" and |
| ... | @@ -1840,15 +1840,15 @@ fn make(step: *Step) !void { | ... | @@ -1840,15 +1840,15 @@ fn make(step: *Step) !void { |
| 1840 | args_length += arg.len + 1; // +1 to account for null terminator | 1840 | args_length += arg.len + 1; // +1 to account for null terminator |
| 1841 | } | 1841 | } |
| 1842 | if (args_length >= 30 * 1024) { | 1842 | if (args_length >= 30 * 1024) { |
| 1843 | try builder.cache_root.handle.makePath("args"); | 1843 | try b.cache_root.handle.makePath("args"); |
| 1844 | 1844 | ||
| 1845 | const args_to_escape = zig_args.items[2..]; | 1845 | const args_to_escape = zig_args.items[2..]; |
| 1846 | var escaped_args = try ArrayList([]const u8).initCapacity(builder.allocator, args_to_escape.len); | 1846 | var escaped_args = try ArrayList([]const u8).initCapacity(b.allocator, args_to_escape.len); |
| 1847 | arg_blk: for (args_to_escape) |arg| { | 1847 | arg_blk: for (args_to_escape) |arg| { |
| 1848 | for (arg, 0..) |c, arg_idx| { | 1848 | for (arg, 0..) |c, arg_idx| { |
| 1849 | if (c == '\\' or c == '"') { | 1849 | if (c == '\\' or c == '"') { |
| 1850 | // Slow path for arguments that need to be escaped. We'll need to allocate and copy | 1850 | // Slow path for arguments that need to be escaped. We'll need to allocate and copy |
| 1851 | var escaped = try ArrayList(u8).initCapacity(builder.allocator, arg.len + 1); | 1851 | var escaped = try ArrayList(u8).initCapacity(b.allocator, arg.len + 1); |
| 1852 | const writer = escaped.writer(); | 1852 | const writer = escaped.writer(); |
| 1853 | try writer.writeAll(arg[0..arg_idx]); | 1853 | try writer.writeAll(arg[0..arg_idx]); |
| 1854 | for (arg[arg_idx..]) |to_escape| { | 1854 | for (arg[arg_idx..]) |to_escape| { |
| ... | @@ -1864,8 +1864,8 @@ fn make(step: *Step) !void { | ... | @@ -1864,8 +1864,8 @@ fn make(step: *Step) !void { |
| 1864 | 1864 | ||
| 1865 | // Write the args to zig-cache/args/<SHA256 hash of args> to avoid conflicts with | 1865 | // Write the args to zig-cache/args/<SHA256 hash of args> to avoid conflicts with |
| 1866 | // other zig build commands running in parallel. | 1866 | // other zig build commands running in parallel. |
| 1867 | const partially_quoted = try std.mem.join(builder.allocator, "\" \"", escaped_args.items); | 1867 | const partially_quoted = try std.mem.join(b.allocator, "\" \"", escaped_args.items); |
| 1868 | const args = try std.mem.concat(builder.allocator, u8, &[_][]const u8{ "\"", partially_quoted, "\"" }); | 1868 | const args = try std.mem.concat(b.allocator, u8, &[_][]const u8{ "\"", partially_quoted, "\"" }); |
| 1869 | 1869 | ||
| 1870 | var args_hash: [Sha256.digest_length]u8 = undefined; | 1870 | var args_hash: [Sha256.digest_length]u8 = undefined; |
| 1871 | Sha256.hash(args, &args_hash, .{}); | 1871 | Sha256.hash(args, &args_hash, .{}); |
| ... | @@ -1877,28 +1877,35 @@ fn make(step: *Step) !void { | ... | @@ -1877,28 +1877,35 @@ fn make(step: *Step) !void { |
| 1877 | ); | 1877 | ); |
| 1878 | 1878 | ||
| 1879 | const args_file = "args" ++ fs.path.sep_str ++ args_hex_hash; | 1879 | const args_file = "args" ++ fs.path.sep_str ++ args_hex_hash; |
| 1880 | try builder.cache_root.handle.writeFile(args_file, args); | 1880 | try b.cache_root.handle.writeFile(args_file, args); |
| 1881 | 1881 | ||
| 1882 | const resolved_args_file = try mem.concat(builder.allocator, u8, &.{ | 1882 | const resolved_args_file = try mem.concat(b.allocator, u8, &.{ |
| 1883 | "@", | 1883 | "@", |
| 1884 | try builder.cache_root.join(builder.allocator, &.{args_file}), | 1884 | try b.cache_root.join(b.allocator, &.{args_file}), |
| 1885 | }); | 1885 | }); |
| 1886 | 1886 | ||
| 1887 | zig_args.shrinkRetainingCapacity(2); | 1887 | zig_args.shrinkRetainingCapacity(2); |
| 1888 | try zig_args.append(resolved_args_file); | 1888 | try zig_args.append(resolved_args_file); |
| 1889 | } | 1889 | } |
| 1890 | 1890 | ||
| 1891 | const output_dir_nl = try builder.execFromStep(zig_args.items, &self.step); | 1891 | const output_bin_path = step.evalZigProcess(zig_args.items, prog_node) catch |err| switch (err) { |
| 1892 | const build_output_dir = mem.trimRight(u8, output_dir_nl, "\r\n"); | 1892 | error.NeedCompileErrorCheck => { |
| 1893 | assert(self.expect_errors.len != 0); | ||
| 1894 | try checkCompileErrors(self); | ||
| 1895 | return; | ||
| 1896 | }, | ||
| 1897 | else => |e| return e, | ||
| 1898 | }; | ||
| 1899 | const build_output_dir = fs.path.dirname(output_bin_path).?; | ||
| 1893 | 1900 | ||
| 1894 | if (self.output_dir) |output_dir| { | 1901 | if (self.output_dir) |output_dir| { |
| 1895 | var src_dir = try std.fs.cwd().openIterableDir(build_output_dir, .{}); | 1902 | var src_dir = try fs.cwd().openIterableDir(build_output_dir, .{}); |
| 1896 | defer src_dir.close(); | 1903 | defer src_dir.close(); |
| 1897 | 1904 | ||
| 1898 | // Create the output directory if it doesn't exist. | 1905 | // Create the output directory if it doesn't exist. |
| 1899 | try std.fs.cwd().makePath(output_dir); | 1906 | try fs.cwd().makePath(output_dir); |
| 1900 | 1907 | ||
| 1901 | var dest_dir = try std.fs.cwd().openDir(output_dir, .{}); | 1908 | var dest_dir = try fs.cwd().openDir(output_dir, .{}); |
| 1902 | defer dest_dir.close(); | 1909 | defer dest_dir.close(); |
| 1903 | 1910 | ||
| 1904 | var it = src_dir.iterate(); | 1911 | var it = src_dir.iterate(); |
| ... | @@ -1922,25 +1929,34 @@ fn make(step: *Step) !void { | ... | @@ -1922,25 +1929,34 @@ fn make(step: *Step) !void { |
| 1922 | 1929 | ||
| 1923 | // Update generated files | 1930 | // Update generated files |
| 1924 | if (self.output_dir != null) { | 1931 | if (self.output_dir != null) { |
| 1925 | self.output_path_source.path = builder.pathJoin( | 1932 | self.output_dirname_source.path = self.output_dir.?; |
| 1933 | |||
| 1934 | self.output_path_source.path = b.pathJoin( | ||
| 1926 | &.{ self.output_dir.?, self.out_filename }, | 1935 | &.{ self.output_dir.?, self.out_filename }, |
| 1927 | ); | 1936 | ); |
| 1928 | 1937 | ||
| 1929 | if (self.emit_h) { | 1938 | if (self.emit_h) { |
| 1930 | self.output_h_path_source.path = builder.pathJoin( | 1939 | self.output_h_path_source.path = b.pathJoin( |
| 1931 | &.{ self.output_dir.?, self.out_h_filename }, | 1940 | &.{ self.output_dir.?, self.out_h_filename }, |
| 1932 | ); | 1941 | ); |
| 1933 | } | 1942 | } |
| 1934 | 1943 | ||
| 1935 | if (self.target.isWindows() or self.target.isUefi()) { | 1944 | if (self.target.isWindows() or self.target.isUefi()) { |
| 1936 | self.output_pdb_path_source.path = builder.pathJoin( | 1945 | self.output_pdb_path_source.path = b.pathJoin( |
| 1937 | &.{ self.output_dir.?, self.out_pdb_filename }, | 1946 | &.{ self.output_dir.?, self.out_pdb_filename }, |
| 1938 | ); | 1947 | ); |
| 1939 | } | 1948 | } |
| 1940 | } | 1949 | } |
| 1941 | 1950 | ||
| 1942 | if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic and self.version != null and self.target.wantSharedLibSymLinks()) { | 1951 | if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic and |
| 1943 | try doAtomicSymLinks(builder.allocator, self.getOutputSource().getPath(builder), self.major_only_filename.?, self.name_only_filename.?); | 1952 | self.version != null and self.target.wantSharedLibSymLinks()) |
| 1953 | { | ||
| 1954 | try doAtomicSymLinks( | ||
| 1955 | step, | ||
| 1956 | self.getOutputSource().getPath(b), | ||
| 1957 | self.major_only_filename.?, | ||
| 1958 | self.name_only_filename.?, | ||
| 1959 | ); | ||
| 1944 | } | 1960 | } |
| 1945 | } | 1961 | } |
| 1946 | 1962 | ||
| ... | @@ -1982,30 +1998,27 @@ fn findVcpkgRoot(allocator: Allocator) !?[]const u8 { | ... | @@ -1982,30 +1998,27 @@ fn findVcpkgRoot(allocator: Allocator) !?[]const u8 { |
| 1982 | } | 1998 | } |
| 1983 | 1999 | ||
| 1984 | pub fn doAtomicSymLinks( | 2000 | pub fn doAtomicSymLinks( |
| 1985 | allocator: Allocator, | 2001 | step: *Step, |
| 1986 | output_path: []const u8, | 2002 | output_path: []const u8, |
| 1987 | filename_major_only: []const u8, | 2003 | filename_major_only: []const u8, |
| 1988 | filename_name_only: []const u8, | 2004 | filename_name_only: []const u8, |
| 1989 | ) !void { | 2005 | ) !void { |
| 2006 | const arena = step.owner.allocator; | ||
| 1990 | const out_dir = fs.path.dirname(output_path) orelse "."; | 2007 | const out_dir = fs.path.dirname(output_path) orelse "."; |
| 1991 | const out_basename = fs.path.basename(output_path); | 2008 | const out_basename = fs.path.basename(output_path); |
| 1992 | // sym link for libfoo.so.1 to libfoo.so.1.2.3 | 2009 | // sym link for libfoo.so.1 to libfoo.so.1.2.3 |
| 1993 | const major_only_path = try fs.path.join( | 2010 | const major_only_path = try fs.path.join(arena, &.{ out_dir, filename_major_only }); |
| 1994 | allocator, | 2011 | fs.atomicSymLink(arena, out_basename, major_only_path) catch |err| { |
| 1995 | &[_][]const u8{ out_dir, filename_major_only }, | 2012 | return step.fail("unable to symlink {s} -> {s}: {s}", .{ |
| 1996 | ); | 2013 | major_only_path, out_basename, @errorName(err), |
| 1997 | fs.atomicSymLink(allocator, out_basename, major_only_path) catch |err| { | 2014 | }); |
| 1998 | log.err("Unable to symlink {s} -> {s}", .{ major_only_path, out_basename }); | ||
| 1999 | return err; | ||
| 2000 | }; | 2015 | }; |
| 2001 | // sym link for libfoo.so to libfoo.so.1 | 2016 | // sym link for libfoo.so to libfoo.so.1 |
| 2002 | const name_only_path = try fs.path.join( | 2017 | const name_only_path = try fs.path.join(arena, &.{ out_dir, filename_name_only }); |
| 2003 | allocator, | 2018 | fs.atomicSymLink(arena, filename_major_only, name_only_path) catch |err| { |
| 2004 | &[_][]const u8{ out_dir, filename_name_only }, | 2019 | return step.fail("Unable to symlink {s} -> {s}: {s}", .{ |
| 2005 | ); | 2020 | name_only_path, filename_major_only, @errorName(err), |
| 2006 | fs.atomicSymLink(allocator, filename_major_only, name_only_path) catch |err| { | 2021 | }); |
| 2007 | log.err("Unable to symlink {s} -> {s}", .{ name_only_path, filename_major_only }); | ||
| 2008 | return err; | ||
| 2009 | }; | 2022 | }; |
| 2010 | } | 2023 | } |
| 2011 | 2024 | ||
| ... | @@ -2117,3 +2130,57 @@ const TransitiveDeps = struct { | ... | @@ -2117,3 +2130,57 @@ const TransitiveDeps = struct { |
| 2117 | } | 2130 | } |
| 2118 | } | 2131 | } |
| 2119 | }; | 2132 | }; |
| 2133 | |||
| 2134 | fn checkCompileErrors(self: *CompileStep) !void { | ||
| 2135 | // Clear this field so that it does not get printed by the build runner. | ||
| 2136 | const actual_eb = self.step.result_error_bundle; | ||
| 2137 | self.step.result_error_bundle = std.zig.ErrorBundle.empty; | ||
| 2138 | |||
| 2139 | const arena = self.step.owner.allocator; | ||
| 2140 | |||
| 2141 | var actual_stderr_list = std.ArrayList(u8).init(arena); | ||
| 2142 | try actual_eb.renderToWriter(.{ | ||
| 2143 | .ttyconf = .no_color, | ||
| 2144 | .include_reference_trace = false, | ||
| 2145 | .include_source_line = false, | ||
| 2146 | }, actual_stderr_list.writer()); | ||
| 2147 | const actual_stderr = try actual_stderr_list.toOwnedSlice(); | ||
| 2148 | |||
| 2149 | // Render the expected lines into a string that we can compare verbatim. | ||
| 2150 | var expected_generated = std.ArrayList(u8).init(arena); | ||
| 2151 | |||
| 2152 | var actual_line_it = mem.split(u8, actual_stderr, "\n"); | ||
| 2153 | for (self.expect_errors) |expect_line| { | ||
| 2154 | const actual_line = actual_line_it.next() orelse { | ||
| 2155 | try expected_generated.appendSlice(expect_line); | ||
| 2156 | try expected_generated.append('\n'); | ||
| 2157 | continue; | ||
| 2158 | }; | ||
| 2159 | if (mem.endsWith(u8, actual_line, expect_line)) { | ||
| 2160 | try expected_generated.appendSlice(actual_line); | ||
| 2161 | try expected_generated.append('\n'); | ||
| 2162 | continue; | ||
| 2163 | } | ||
| 2164 | if (mem.startsWith(u8, expect_line, ":?:?: ")) { | ||
| 2165 | if (mem.endsWith(u8, actual_line, expect_line[":?:?: ".len..])) { | ||
| 2166 | try expected_generated.appendSlice(actual_line); | ||
| 2167 | try expected_generated.append('\n'); | ||
| 2168 | continue; | ||
| 2169 | } | ||
| 2170 | } | ||
| 2171 | try expected_generated.appendSlice(expect_line); | ||
| 2172 | try expected_generated.append('\n'); | ||
| 2173 | } | ||
| 2174 | |||
| 2175 | if (mem.eql(u8, expected_generated.items, actual_stderr)) return; | ||
| 2176 | |||
| 2177 | // TODO merge this with the testing.expectEqualStrings logic, and also CheckFile | ||
| 2178 | return self.step.fail( | ||
| 2179 | \\ | ||
| 2180 | \\========= expected: ===================== | ||
| 2181 | \\{s} | ||
| 2182 | \\========= but found: ==================== | ||
| 2183 | \\{s} | ||
| 2184 | \\========================================= | ||
| 2185 | , .{ expected_generated.items, actual_stderr }); | ||
| 2186 | } |
lib/std/Build/ConfigHeaderStep.zig+82-73| ... | @@ -1,9 +1,3 @@ | ... | @@ -1,9 +1,3 @@ |
| 1 | const std = @import("../std.zig"); | ||
| 2 | const ConfigHeaderStep = @This(); | ||
| 3 | const Step = std.Build.Step; | ||
| 4 | |||
| 5 | pub const base_id: Step.Id = .config_header; | ||
| 6 | |||
| 7 | pub const Style = union(enum) { | 1 | pub const Style = union(enum) { |
| 8 | /// The configure format supported by autotools. It uses `#undef foo` to | 2 | /// The configure format supported by autotools. It uses `#undef foo` to |
| 9 | /// mark lines that can be substituted with different values. | 3 | /// mark lines that can be substituted with different values. |
| ... | @@ -34,7 +28,6 @@ pub const Value = union(enum) { | ... | @@ -34,7 +28,6 @@ pub const Value = union(enum) { |
| 34 | }; | 28 | }; |
| 35 | 29 | ||
| 36 | step: Step, | 30 | step: Step, |
| 37 | builder: *std.Build, | ||
| 38 | values: std.StringArrayHashMap(Value), | 31 | values: std.StringArrayHashMap(Value), |
| 39 | output_file: std.Build.GeneratedFile, | 32 | output_file: std.Build.GeneratedFile, |
| 40 | 33 | ||
| ... | @@ -42,43 +35,57 @@ style: Style, | ... | @@ -42,43 +35,57 @@ style: Style, |
| 42 | max_bytes: usize, | 35 | max_bytes: usize, |
| 43 | include_path: []const u8, | 36 | include_path: []const u8, |
| 44 | 37 | ||
| 38 | pub const base_id: Step.Id = .config_header; | ||
| 39 | |||
| 45 | pub const Options = struct { | 40 | pub const Options = struct { |
| 46 | style: Style = .blank, | 41 | style: Style = .blank, |
| 47 | max_bytes: usize = 2 * 1024 * 1024, | 42 | max_bytes: usize = 2 * 1024 * 1024, |
| 48 | include_path: ?[]const u8 = null, | 43 | include_path: ?[]const u8 = null, |
| 44 | first_ret_addr: ?usize = null, | ||
| 49 | }; | 45 | }; |
| 50 | 46 | ||
| 51 | pub fn create(builder: *std.Build, options: Options) *ConfigHeaderStep { | 47 | pub fn create(owner: *std.Build, options: Options) *ConfigHeaderStep { |
| 52 | const self = builder.allocator.create(ConfigHeaderStep) catch @panic("OOM"); | 48 | const self = owner.allocator.create(ConfigHeaderStep) catch @panic("OOM"); |
| 53 | const name = if (options.style.getFileSource()) |s| | ||
| 54 | builder.fmt("configure {s} header {s}", .{ @tagName(options.style), s.getDisplayName() }) | ||
| 55 | else | ||
| 56 | builder.fmt("configure {s} header", .{@tagName(options.style)}); | ||
| 57 | self.* = .{ | ||
| 58 | .builder = builder, | ||
| 59 | .step = Step.init(base_id, name, builder.allocator, make), | ||
| 60 | .style = options.style, | ||
| 61 | .values = std.StringArrayHashMap(Value).init(builder.allocator), | ||
| 62 | 49 | ||
| 63 | .max_bytes = options.max_bytes, | 50 | var include_path: []const u8 = "config.h"; |
| 64 | .include_path = "config.h", | ||
| 65 | .output_file = .{ .step = &self.step }, | ||
| 66 | }; | ||
| 67 | 51 | ||
| 68 | if (options.style.getFileSource()) |s| switch (s) { | 52 | if (options.style.getFileSource()) |s| switch (s) { |
| 69 | .path => |p| { | 53 | .path => |p| { |
| 70 | const basename = std.fs.path.basename(p); | 54 | const basename = std.fs.path.basename(p); |
| 71 | if (std.mem.endsWith(u8, basename, ".h.in")) { | 55 | if (std.mem.endsWith(u8, basename, ".h.in")) { |
| 72 | self.include_path = basename[0 .. basename.len - 3]; | 56 | include_path = basename[0 .. basename.len - 3]; |
| 73 | } | 57 | } |
| 74 | }, | 58 | }, |
| 75 | else => {}, | 59 | else => {}, |
| 76 | }; | 60 | }; |
| 77 | 61 | ||
| 78 | if (options.include_path) |include_path| { | 62 | if (options.include_path) |p| { |
| 79 | self.include_path = include_path; | 63 | include_path = p; |
| 80 | } | 64 | } |
| 81 | 65 | ||
| 66 | const name = if (options.style.getFileSource()) |s| | ||
| 67 | owner.fmt("configure {s} header {s} to {s}", .{ | ||
| 68 | @tagName(options.style), s.getDisplayName(), include_path, | ||
| 69 | }) | ||
| 70 | else | ||
| 71 | owner.fmt("configure {s} header to {s}", .{ @tagName(options.style), include_path }); | ||
| 72 | |||
| 73 | self.* = .{ | ||
| 74 | .step = Step.init(.{ | ||
| 75 | .id = base_id, | ||
| 76 | .name = name, | ||
| 77 | .owner = owner, | ||
| 78 | .makeFn = make, | ||
| 79 | .first_ret_addr = options.first_ret_addr orelse @returnAddress(), | ||
| 80 | }), | ||
| 81 | .style = options.style, | ||
| 82 | .values = std.StringArrayHashMap(Value).init(owner.allocator), | ||
| 83 | |||
| 84 | .max_bytes = options.max_bytes, | ||
| 85 | .include_path = include_path, | ||
| 86 | .output_file = .{ .step = &self.step }, | ||
| 87 | }; | ||
| 88 | |||
| 82 | return self; | 89 | return self; |
| 83 | } | 90 | } |
| 84 | 91 | ||
| ... | @@ -146,26 +153,20 @@ fn putValue(self: *ConfigHeaderStep, field_name: []const u8, comptime T: type, v | ... | @@ -146,26 +153,20 @@ fn putValue(self: *ConfigHeaderStep, field_name: []const u8, comptime T: type, v |
| 146 | } | 153 | } |
| 147 | } | 154 | } |
| 148 | 155 | ||
| 149 | fn make(step: *Step) !void { | 156 | fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 157 | _ = prog_node; | ||
| 158 | const b = step.owner; | ||
| 150 | const self = @fieldParentPtr(ConfigHeaderStep, "step", step); | 159 | const self = @fieldParentPtr(ConfigHeaderStep, "step", step); |
| 151 | const gpa = self.builder.allocator; | 160 | const gpa = b.allocator; |
| 152 | 161 | const arena = b.allocator; | |
| 153 | // The cache is used here not really as a way to speed things up - because writing | ||
| 154 | // the data to a file would probably be very fast - but as a way to find a canonical | ||
| 155 | // location to put build artifacts. | ||
| 156 | 162 | ||
| 157 | // If, for example, a hard-coded path was used as the location to put ConfigHeaderStep | 163 | var man = b.cache.obtain(); |
| 158 | // files, then two ConfigHeaderStep executing in parallel might clobber each other. | 164 | defer man.deinit(); |
| 159 | 165 | ||
| 160 | // TODO port the cache system from the compiler to zig std lib. Until then | ||
| 161 | // we construct the path directly, and no "cache hit" detection happens; | ||
| 162 | // the files are always written. | ||
| 163 | // Note there is very similar code over in WriteFileStep | ||
| 164 | const Hasher = std.crypto.auth.siphash.SipHash128(1, 3); | ||
| 165 | // Random bytes to make ConfigHeaderStep unique. Refresh this with new | 166 | // Random bytes to make ConfigHeaderStep unique. Refresh this with new |
| 166 | // random bytes when ConfigHeaderStep implementation is modified in a | 167 | // random bytes when ConfigHeaderStep implementation is modified in a |
| 167 | // non-backwards-compatible way. | 168 | // non-backwards-compatible way. |
| 168 | var hash = Hasher.init("PGuDTpidxyMqnkGM"); | 169 | man.hash.add(@as(u32, 0xdef08d23)); |
| 169 | 170 | ||
| 170 | var output = std.ArrayList(u8).init(gpa); | 171 | var output = std.ArrayList(u8).init(gpa); |
| 171 | defer output.deinit(); | 172 | defer output.deinit(); |
| ... | @@ -177,15 +178,15 @@ fn make(step: *Step) !void { | ... | @@ -177,15 +178,15 @@ fn make(step: *Step) !void { |
| 177 | switch (self.style) { | 178 | switch (self.style) { |
| 178 | .autoconf => |file_source| { | 179 | .autoconf => |file_source| { |
| 179 | try output.appendSlice(c_generated_line); | 180 | try output.appendSlice(c_generated_line); |
| 180 | const src_path = file_source.getPath(self.builder); | 181 | const src_path = file_source.getPath(b); |
| 181 | const contents = try std.fs.cwd().readFileAlloc(gpa, src_path, self.max_bytes); | 182 | const contents = try std.fs.cwd().readFileAlloc(arena, src_path, self.max_bytes); |
| 182 | try render_autoconf(contents, &output, self.values, src_path); | 183 | try render_autoconf(step, contents, &output, self.values, src_path); |
| 183 | }, | 184 | }, |
| 184 | .cmake => |file_source| { | 185 | .cmake => |file_source| { |
| 185 | try output.appendSlice(c_generated_line); | 186 | try output.appendSlice(c_generated_line); |
| 186 | const src_path = file_source.getPath(self.builder); | 187 | const src_path = file_source.getPath(b); |
| 187 | const contents = try std.fs.cwd().readFileAlloc(gpa, src_path, self.max_bytes); | 188 | const contents = try std.fs.cwd().readFileAlloc(arena, src_path, self.max_bytes); |
| 188 | try render_cmake(contents, &output, self.values, src_path); | 189 | try render_cmake(step, contents, &output, self.values, src_path); |
| 189 | }, | 190 | }, |
| 190 | .blank => { | 191 | .blank => { |
| 191 | try output.appendSlice(c_generated_line); | 192 | try output.appendSlice(c_generated_line); |
| ... | @@ -197,43 +198,44 @@ fn make(step: *Step) !void { | ... | @@ -197,43 +198,44 @@ fn make(step: *Step) !void { |
| 197 | }, | 198 | }, |
| 198 | } | 199 | } |
| 199 | 200 | ||
| 200 | hash.update(output.items); | 201 | man.hash.addBytes(output.items); |
| 201 | 202 | ||
| 202 | var digest: [16]u8 = undefined; | 203 | if (try step.cacheHit(&man)) { |
| 203 | hash.final(&digest); | 204 | const digest = man.final(); |
| 204 | var hash_basename: [digest.len * 2]u8 = undefined; | 205 | self.output_file.path = try b.cache_root.join(arena, &.{ |
| 205 | _ = std.fmt.bufPrint( | 206 | "o", &digest, self.include_path, |
| 206 | &hash_basename, | 207 | }); |
| 207 | "{s}", | 208 | return; |
| 208 | .{std.fmt.fmtSliceHexLower(&digest)}, | 209 | } |
| 209 | ) catch unreachable; | ||
| 210 | 210 | ||
| 211 | const output_dir = try self.builder.cache_root.join(gpa, &.{ "o", &hash_basename }); | 211 | const digest = man.final(); |
| 212 | 212 | ||
| 213 | // If output_path has directory parts, deal with them. Example: | 213 | // If output_path has directory parts, deal with them. Example: |
| 214 | // output_dir is zig-cache/o/HASH | 214 | // output_dir is zig-cache/o/HASH |
| 215 | // output_path is libavutil/avconfig.h | 215 | // output_path is libavutil/avconfig.h |
| 216 | // We want to open directory zig-cache/o/HASH/libavutil/ | 216 | // We want to open directory zig-cache/o/HASH/libavutil/ |
| 217 | // but keep output_dir as zig-cache/o/HASH for -I include | 217 | // but keep output_dir as zig-cache/o/HASH for -I include |
| 218 | const sub_dir_path = if (std.fs.path.dirname(self.include_path)) |d| | 218 | const sub_path = try std.fs.path.join(arena, &.{ "o", &digest, self.include_path }); |
| 219 | try std.fs.path.join(gpa, &.{ output_dir, d }) | 219 | const sub_path_dirname = std.fs.path.dirname(sub_path).?; |
| 220 | else | ||
| 221 | output_dir; | ||
| 222 | 220 | ||
| 223 | var dir = std.fs.cwd().makeOpenPath(sub_dir_path, .{}) catch |err| { | 221 | b.cache_root.handle.makePath(sub_path_dirname) catch |err| { |
| 224 | std.debug.print("unable to make path {s}: {s}\n", .{ output_dir, @errorName(err) }); | 222 | return step.fail("unable to make path '{}{s}': {s}", .{ |
| 225 | return err; | 223 | b.cache_root, sub_path_dirname, @errorName(err), |
| 224 | }); | ||
| 226 | }; | 225 | }; |
| 227 | defer dir.close(); | ||
| 228 | 226 | ||
| 229 | try dir.writeFile(std.fs.path.basename(self.include_path), output.items); | 227 | b.cache_root.handle.writeFile(sub_path, output.items) catch |err| { |
| 228 | return step.fail("unable to write file '{}{s}': {s}", .{ | ||
| 229 | b.cache_root, sub_path, @errorName(err), | ||
| 230 | }); | ||
| 231 | }; | ||
| 230 | 232 | ||
| 231 | self.output_file.path = try std.fs.path.join(self.builder.allocator, &.{ | 233 | self.output_file.path = try b.cache_root.join(arena, &.{sub_path}); |
| 232 | output_dir, self.include_path, | 234 | try man.writeManifest(); |
| 233 | }); | ||
| 234 | } | 235 | } |
| 235 | 236 | ||
| 236 | fn render_autoconf( | 237 | fn render_autoconf( |
| 238 | step: *Step, | ||
| 237 | contents: []const u8, | 239 | contents: []const u8, |
| 238 | output: *std.ArrayList(u8), | 240 | output: *std.ArrayList(u8), |
| 239 | values: std.StringArrayHashMap(Value), | 241 | values: std.StringArrayHashMap(Value), |
| ... | @@ -260,7 +262,7 @@ fn render_autoconf( | ... | @@ -260,7 +262,7 @@ fn render_autoconf( |
| 260 | } | 262 | } |
| 261 | const name = it.rest(); | 263 | const name = it.rest(); |
| 262 | const kv = values_copy.fetchSwapRemove(name) orelse { | 264 | const kv = values_copy.fetchSwapRemove(name) orelse { |
| 263 | std.debug.print("{s}:{d}: error: unspecified config header value: '{s}'\n", .{ | 265 | try step.addError("{s}:{d}: error: unspecified config header value: '{s}'", .{ |
| 264 | src_path, line_index + 1, name, | 266 | src_path, line_index + 1, name, |
| 265 | }); | 267 | }); |
| 266 | any_errors = true; | 268 | any_errors = true; |
| ... | @@ -270,15 +272,17 @@ fn render_autoconf( | ... | @@ -270,15 +272,17 @@ fn render_autoconf( |
| 270 | } | 272 | } |
| 271 | 273 | ||
| 272 | for (values_copy.keys()) |name| { | 274 | for (values_copy.keys()) |name| { |
| 273 | std.debug.print("{s}: error: config header value unused: '{s}'\n", .{ src_path, name }); | 275 | try step.addError("{s}: error: config header value unused: '{s}'", .{ src_path, name }); |
| 276 | any_errors = true; | ||
| 274 | } | 277 | } |
| 275 | 278 | ||
| 276 | if (any_errors) { | 279 | if (any_errors) { |
| 277 | return error.HeaderConfigFailed; | 280 | return error.MakeFailed; |
| 278 | } | 281 | } |
| 279 | } | 282 | } |
| 280 | 283 | ||
| 281 | fn render_cmake( | 284 | fn render_cmake( |
| 285 | step: *Step, | ||
| 282 | contents: []const u8, | 286 | contents: []const u8, |
| 283 | output: *std.ArrayList(u8), | 287 | output: *std.ArrayList(u8), |
| 284 | values: std.StringArrayHashMap(Value), | 288 | values: std.StringArrayHashMap(Value), |
| ... | @@ -304,14 +308,14 @@ fn render_cmake( | ... | @@ -304,14 +308,14 @@ fn render_cmake( |
| 304 | continue; | 308 | continue; |
| 305 | } | 309 | } |
| 306 | const name = it.next() orelse { | 310 | const name = it.next() orelse { |
| 307 | std.debug.print("{s}:{d}: error: missing define name\n", .{ | 311 | try step.addError("{s}:{d}: error: missing define name", .{ |
| 308 | src_path, line_index + 1, | 312 | src_path, line_index + 1, |
| 309 | }); | 313 | }); |
| 310 | any_errors = true; | 314 | any_errors = true; |
| 311 | continue; | 315 | continue; |
| 312 | }; | 316 | }; |
| 313 | const kv = values_copy.fetchSwapRemove(name) orelse { | 317 | const kv = values_copy.fetchSwapRemove(name) orelse { |
| 314 | std.debug.print("{s}:{d}: error: unspecified config header value: '{s}'\n", .{ | 318 | try step.addError("{s}:{d}: error: unspecified config header value: '{s}'", .{ |
| 315 | src_path, line_index + 1, name, | 319 | src_path, line_index + 1, name, |
| 316 | }); | 320 | }); |
| 317 | any_errors = true; | 321 | any_errors = true; |
| ... | @@ -321,7 +325,8 @@ fn render_cmake( | ... | @@ -321,7 +325,8 @@ fn render_cmake( |
| 321 | } | 325 | } |
| 322 | 326 | ||
| 323 | for (values_copy.keys()) |name| { | 327 | for (values_copy.keys()) |name| { |
| 324 | std.debug.print("{s}: error: config header value unused: '{s}'\n", .{ src_path, name }); | 328 | try step.addError("{s}: error: config header value unused: '{s}'", .{ src_path, name }); |
| 329 | any_errors = true; | ||
| 325 | } | 330 | } |
| 326 | 331 | ||
| 327 | if (any_errors) { | 332 | if (any_errors) { |
| ... | @@ -426,3 +431,7 @@ fn renderValueNasm(output: *std.ArrayList(u8), name: []const u8, value: Value) ! | ... | @@ -426,3 +431,7 @@ fn renderValueNasm(output: *std.ArrayList(u8), name: []const u8, value: Value) ! |
| 426 | }, | 431 | }, |
| 427 | } | 432 | } |
| 428 | } | 433 | } |
| 434 | |||
| 435 | const std = @import("../std.zig"); | ||
| 436 | const ConfigHeaderStep = @This(); | ||
| 437 | const Step = std.Build.Step; |
lib/std/Build/EmulatableRunStep.zig deleted-213| ... | @@ -1,213 +0,0 @@ | ||
| 1 | //! Unlike `RunStep` this step will provide emulation, when enabled, to run foreign binaries. | ||
| 2 | //! When a binary is foreign, but emulation for the target is disabled, the specified binary | ||
| 3 | //! will not be run and therefore also not validated against its output. | ||
| 4 | //! This step can be useful when wishing to run a built binary on multiple platforms, | ||
| 5 | //! without having to verify if it's possible to be ran against. | ||
| 6 | |||
| 7 | const std = @import("../std.zig"); | ||
| 8 | const Step = std.Build.Step; | ||
| 9 | const CompileStep = std.Build.CompileStep; | ||
| 10 | const RunStep = std.Build.RunStep; | ||
| 11 | |||
| 12 | const fs = std.fs; | ||
| 13 | const process = std.process; | ||
| 14 | const EnvMap = process.EnvMap; | ||
| 15 | |||
| 16 | const EmulatableRunStep = @This(); | ||
| 17 | |||
| 18 | pub const base_id = .emulatable_run; | ||
| 19 | |||
| 20 | const max_stdout_size = 1 * 1024 * 1024; // 1 MiB | ||
| 21 | |||
| 22 | step: Step, | ||
| 23 | builder: *std.Build, | ||
| 24 | |||
| 25 | /// The artifact (executable) to be run by this step | ||
| 26 | exe: *CompileStep, | ||
| 27 | |||
| 28 | /// Set this to `null` to ignore the exit code for the purpose of determining a successful execution | ||
| 29 | expected_term: ?std.ChildProcess.Term = .{ .Exited = 0 }, | ||
| 30 | |||
| 31 | /// Override this field to modify the environment | ||
| 32 | env_map: ?*EnvMap, | ||
| 33 | |||
| 34 | /// Set this to modify the current working directory | ||
| 35 | cwd: ?[]const u8, | ||
| 36 | |||
| 37 | stdout_action: RunStep.StdIoAction = .inherit, | ||
| 38 | stderr_action: RunStep.StdIoAction = .inherit, | ||
| 39 | |||
| 40 | /// When set to true, hides the warning of skipping a foreign binary which cannot be run on the host | ||
| 41 | /// or through emulation. | ||
| 42 | hide_foreign_binaries_warning: bool, | ||
| 43 | |||
| 44 | /// Creates a step that will execute the given artifact. This step will allow running the | ||
| 45 | /// binary through emulation when any of the emulation options such as `enable_rosetta` are set to true. | ||
| 46 | /// When set to false, and the binary is foreign, running the executable is skipped. | ||
| 47 | /// Asserts given artifact is an executable. | ||
| 48 | pub fn create(builder: *std.Build, name: []const u8, artifact: *CompileStep) *EmulatableRunStep { | ||
| 49 | std.debug.assert(artifact.kind == .exe or artifact.kind == .test_exe); | ||
| 50 | const self = builder.allocator.create(EmulatableRunStep) catch @panic("OOM"); | ||
| 51 | |||
| 52 | const option_name = "hide-foreign-warnings"; | ||
| 53 | const hide_warnings = if (builder.available_options_map.get(option_name) == null) warn: { | ||
| 54 | break :warn builder.option(bool, option_name, "Hide the warning when a foreign binary which is incompatible is skipped") orelse false; | ||
| 55 | } else false; | ||
| 56 | |||
| 57 | self.* = .{ | ||
| 58 | .builder = builder, | ||
| 59 | .step = Step.init(.emulatable_run, name, builder.allocator, make), | ||
| 60 | .exe = artifact, | ||
| 61 | .env_map = null, | ||
| 62 | .cwd = null, | ||
| 63 | .hide_foreign_binaries_warning = hide_warnings, | ||
| 64 | }; | ||
| 65 | self.step.dependOn(&artifact.step); | ||
| 66 | |||
| 67 | return self; | ||
| 68 | } | ||
| 69 | |||
| 70 | fn make(step: *Step) !void { | ||
| 71 | const self = @fieldParentPtr(EmulatableRunStep, "step", step); | ||
| 72 | const host_info = self.builder.host; | ||
| 73 | |||
| 74 | var argv_list = std.ArrayList([]const u8).init(self.builder.allocator); | ||
| 75 | defer argv_list.deinit(); | ||
| 76 | |||
| 77 | const need_cross_glibc = self.exe.target.isGnuLibC() and self.exe.is_linking_libc; | ||
| 78 | switch (host_info.getExternalExecutor(self.exe.target_info, .{ | ||
| 79 | .qemu_fixes_dl = need_cross_glibc and self.builder.glibc_runtimes_dir != null, | ||
| 80 | .link_libc = self.exe.is_linking_libc, | ||
| 81 | })) { | ||
| 82 | .native => {}, | ||
| 83 | .rosetta => if (!self.builder.enable_rosetta) return warnAboutForeignBinaries(self), | ||
| 84 | .wine => |bin_name| if (self.builder.enable_wine) { | ||
| 85 | try argv_list.append(bin_name); | ||
| 86 | } else return, | ||
| 87 | .qemu => |bin_name| if (self.builder.enable_qemu) { | ||
| 88 | const glibc_dir_arg = if (need_cross_glibc) | ||
| 89 | self.builder.glibc_runtimes_dir orelse return | ||
| 90 | else | ||
| 91 | null; | ||
| 92 | try argv_list.append(bin_name); | ||
| 93 | if (glibc_dir_arg) |dir| { | ||
| 94 | // TODO look into making this a call to `linuxTriple`. This | ||
| 95 | // needs the directory to be called "i686" rather than | ||
| 96 | // "x86" which is why we do it manually here. | ||
| 97 | const fmt_str = "{s}" ++ fs.path.sep_str ++ "{s}-{s}-{s}"; | ||
| 98 | const cpu_arch = self.exe.target.getCpuArch(); | ||
| 99 | const os_tag = self.exe.target.getOsTag(); | ||
| 100 | const abi = self.exe.target.getAbi(); | ||
| 101 | const cpu_arch_name: []const u8 = if (cpu_arch == .x86) | ||
| 102 | "i686" | ||
| 103 | else | ||
| 104 | @tagName(cpu_arch); | ||
| 105 | const full_dir = try std.fmt.allocPrint(self.builder.allocator, fmt_str, .{ | ||
| 106 | dir, cpu_arch_name, @tagName(os_tag), @tagName(abi), | ||
| 107 | }); | ||
| 108 | |||
| 109 | try argv_list.append("-L"); | ||
| 110 | try argv_list.append(full_dir); | ||
| 111 | } | ||
| 112 | } else return warnAboutForeignBinaries(self), | ||
| 113 | .darling => |bin_name| if (self.builder.enable_darling) { | ||
| 114 | try argv_list.append(bin_name); | ||
| 115 | } else return warnAboutForeignBinaries(self), | ||
| 116 | .wasmtime => |bin_name| if (self.builder.enable_wasmtime) { | ||
| 117 | try argv_list.append(bin_name); | ||
| 118 | try argv_list.append("--dir=."); | ||
| 119 | } else return warnAboutForeignBinaries(self), | ||
| 120 | else => return warnAboutForeignBinaries(self), | ||
| 121 | } | ||
| 122 | |||
| 123 | if (self.exe.target.isWindows()) { | ||
| 124 | // On Windows we don't have rpaths so we have to add .dll search paths to PATH | ||
| 125 | RunStep.addPathForDynLibsInternal(&self.step, self.builder, self.exe); | ||
| 126 | } | ||
| 127 | |||
| 128 | const executable_path = self.exe.installed_path orelse self.exe.getOutputSource().getPath(self.builder); | ||
| 129 | try argv_list.append(executable_path); | ||
| 130 | |||
| 131 | try RunStep.runCommand( | ||
| 132 | argv_list.items, | ||
| 133 | self.builder, | ||
| 134 | self.expected_term, | ||
| 135 | self.stdout_action, | ||
| 136 | self.stderr_action, | ||
| 137 | .Inherit, | ||
| 138 | self.env_map, | ||
| 139 | self.cwd, | ||
| 140 | false, | ||
| 141 | ); | ||
| 142 | } | ||
| 143 | |||
| 144 | pub fn expectStdErrEqual(self: *EmulatableRunStep, bytes: []const u8) void { | ||
| 145 | self.stderr_action = .{ .expect_exact = self.builder.dupe(bytes) }; | ||
| 146 | } | ||
| 147 | |||
| 148 | pub fn expectStdOutEqual(self: *EmulatableRunStep, bytes: []const u8) void { | ||
| 149 | self.stdout_action = .{ .expect_exact = self.builder.dupe(bytes) }; | ||
| 150 | } | ||
| 151 | |||
| 152 | fn warnAboutForeignBinaries(step: *EmulatableRunStep) void { | ||
| 153 | if (step.hide_foreign_binaries_warning) return; | ||
| 154 | const builder = step.builder; | ||
| 155 | const artifact = step.exe; | ||
| 156 | |||
| 157 | const host_name = builder.host.target.zigTriple(builder.allocator) catch @panic("unhandled error"); | ||
| 158 | const foreign_name = artifact.target.zigTriple(builder.allocator) catch @panic("unhandled error"); | ||
| 159 | const target_info = std.zig.system.NativeTargetInfo.detect(artifact.target) catch @panic("unhandled error"); | ||
| 160 | const need_cross_glibc = artifact.target.isGnuLibC() and artifact.is_linking_libc; | ||
| 161 | switch (builder.host.getExternalExecutor(target_info, .{ | ||
| 162 | .qemu_fixes_dl = need_cross_glibc and builder.glibc_runtimes_dir != null, | ||
| 163 | .link_libc = artifact.is_linking_libc, | ||
| 164 | })) { | ||
| 165 | .native => unreachable, | ||
| 166 | .bad_dl => |foreign_dl| { | ||
| 167 | const host_dl = builder.host.dynamic_linker.get() orelse "(none)"; | ||
| 168 | std.debug.print("the host system does not appear to be capable of executing binaries from the target because the host dynamic linker is '{s}', while the target dynamic linker is '{s}'. Consider setting the dynamic linker as '{s}'.\n", .{ | ||
| 169 | host_dl, foreign_dl, host_dl, | ||
| 170 | }); | ||
| 171 | }, | ||
| 172 | .bad_os_or_cpu => { | ||
| 173 | std.debug.print("the host system ({s}) does not appear to be capable of executing binaries from the target ({s}).\n", .{ | ||
| 174 | host_name, foreign_name, | ||
| 175 | }); | ||
| 176 | }, | ||
| 177 | .darling => if (!builder.enable_darling) { | ||
| 178 | std.debug.print( | ||
| 179 | "the host system ({s}) does not appear to be capable of executing binaries " ++ | ||
| 180 | "from the target ({s}). Consider enabling darling.\n", | ||
| 181 | .{ host_name, foreign_name }, | ||
| 182 | ); | ||
| 183 | }, | ||
| 184 | .rosetta => if (!builder.enable_rosetta) { | ||
| 185 | std.debug.print( | ||
| 186 | "the host system ({s}) does not appear to be capable of executing binaries " ++ | ||
| 187 | "from the target ({s}). Consider enabling rosetta.\n", | ||
| 188 | .{ host_name, foreign_name }, | ||
| 189 | ); | ||
| 190 | }, | ||
| 191 | .wine => if (!builder.enable_wine) { | ||
| 192 | std.debug.print( | ||
| 193 | "the host system ({s}) does not appear to be capable of executing binaries " ++ | ||
| 194 | "from the target ({s}). Consider enabling wine.\n", | ||
| 195 | .{ host_name, foreign_name }, | ||
| 196 | ); | ||
| 197 | }, | ||
| 198 | .qemu => if (!builder.enable_qemu) { | ||
| 199 | std.debug.print( | ||
| 200 | "the host system ({s}) does not appear to be capable of executing binaries " ++ | ||
| 201 | "from the target ({s}). Consider enabling qemu.\n", | ||
| 202 | .{ host_name, foreign_name }, | ||
| 203 | ); | ||
| 204 | }, | ||
| 205 | .wasmtime => { | ||
| 206 | std.debug.print( | ||
| 207 | "the host system ({s}) does not appear to be capable of executing binaries " ++ | ||
| 208 | "from the target ({s}). Consider enabling wasmtime.\n", | ||
| 209 | .{ host_name, foreign_name }, | ||
| 210 | ); | ||
| 211 | }, | ||
| 212 | } | ||
| 213 | } | ||
lib/std/Build/FmtStep.zig+63-22| ... | @@ -1,32 +1,73 @@ | ... | @@ -1,32 +1,73 @@ |
| 1 | const std = @import("../std.zig"); | 1 | //! This step has two modes: |
| 2 | const Step = std.Build.Step; | 2 | //! * Modify mode: directly modify source files, formatting them in place. |
| 3 | const FmtStep = @This(); | 3 | //! * Check mode: fail the step if a non-conforming file is found. |
| 4 | |||
| 5 | step: Step, | ||
| 6 | paths: []const []const u8, | ||
| 7 | exclude_paths: []const []const u8, | ||
| 8 | check: bool, | ||
| 4 | 9 | ||
| 5 | pub const base_id = .fmt; | 10 | pub const base_id = .fmt; |
| 6 | 11 | ||
| 7 | step: Step, | 12 | pub const Options = struct { |
| 8 | builder: *std.Build, | 13 | paths: []const []const u8 = &.{}, |
| 9 | argv: [][]const u8, | 14 | exclude_paths: []const []const u8 = &.{}, |
| 10 | 15 | /// If true, fails the build step when any non-conforming files are encountered. | |
| 11 | pub fn create(builder: *std.Build, paths: []const []const u8) *FmtStep { | 16 | check: bool = false, |
| 12 | const self = builder.allocator.create(FmtStep) catch @panic("OOM"); | 17 | }; |
| 13 | const name = "zig fmt"; | ||
| 14 | self.* = FmtStep{ | ||
| 15 | .step = Step.init(.fmt, name, builder.allocator, make), | ||
| 16 | .builder = builder, | ||
| 17 | .argv = builder.allocator.alloc([]u8, paths.len + 2) catch @panic("OOM"), | ||
| 18 | }; | ||
| 19 | 18 | ||
| 20 | self.argv[0] = builder.zig_exe; | 19 | pub fn create(owner: *std.Build, options: Options) *FmtStep { |
| 21 | self.argv[1] = "fmt"; | 20 | const self = owner.allocator.create(FmtStep) catch @panic("OOM"); |
| 22 | for (paths, 0..) |path, i| { | 21 | const name = if (options.check) "zig fmt --check" else "zig fmt"; |
| 23 | self.argv[2 + i] = builder.pathFromRoot(path); | 22 | self.* = .{ |
| 24 | } | 23 | .step = Step.init(.{ |
| 24 | .id = base_id, | ||
| 25 | .name = name, | ||
| 26 | .owner = owner, | ||
| 27 | .makeFn = make, | ||
| 28 | }), | ||
| 29 | .paths = options.paths, | ||
| 30 | .exclude_paths = options.exclude_paths, | ||
| 31 | .check = options.check, | ||
| 32 | }; | ||
| 25 | return self; | 33 | return self; |
| 26 | } | 34 | } |
| 27 | 35 | ||
| 28 | fn make(step: *Step) !void { | 36 | fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 37 | // zig fmt is fast enough that no progress is needed. | ||
| 38 | _ = prog_node; | ||
| 39 | |||
| 40 | // TODO: if check=false, this means we are modifying source files in place, which | ||
| 41 | // is an operation that could race against other operations also modifying source files | ||
| 42 | // in place. In this case, this step should obtain a write lock while making those | ||
| 43 | // modifications. | ||
| 44 | |||
| 45 | const b = step.owner; | ||
| 46 | const arena = b.allocator; | ||
| 29 | const self = @fieldParentPtr(FmtStep, "step", step); | 47 | const self = @fieldParentPtr(FmtStep, "step", step); |
| 30 | 48 | ||
| 31 | return self.builder.spawnChild(self.argv); | 49 | var argv: std.ArrayListUnmanaged([]const u8) = .{}; |
| 50 | try argv.ensureUnusedCapacity(arena, 2 + 1 + self.paths.len + 2 * self.exclude_paths.len); | ||
| 51 | |||
| 52 | argv.appendAssumeCapacity(b.zig_exe); | ||
| 53 | argv.appendAssumeCapacity("fmt"); | ||
| 54 | |||
| 55 | if (self.check) { | ||
| 56 | argv.appendAssumeCapacity("--check"); | ||
| 57 | } | ||
| 58 | |||
| 59 | for (self.paths) |p| { | ||
| 60 | argv.appendAssumeCapacity(b.pathFromRoot(p)); | ||
| 61 | } | ||
| 62 | |||
| 63 | for (self.exclude_paths) |p| { | ||
| 64 | argv.appendAssumeCapacity("--exclude"); | ||
| 65 | argv.appendAssumeCapacity(b.pathFromRoot(p)); | ||
| 66 | } | ||
| 67 | |||
| 68 | return step.evalChildProcess(argv.items); | ||
| 32 | } | 69 | } |
| 70 | |||
| 71 | const std = @import("../std.zig"); | ||
| 72 | const Step = std.Build.Step; | ||
| 73 | const FmtStep = @This(); |
lib/std/Build/InstallArtifactStep.zig+77-27| ... | @@ -3,83 +3,133 @@ const Step = std.Build.Step; | ... | @@ -3,83 +3,133 @@ const Step = std.Build.Step; |
| 3 | const CompileStep = std.Build.CompileStep; | 3 | const CompileStep = std.Build.CompileStep; |
| 4 | const InstallDir = std.Build.InstallDir; | 4 | const InstallDir = std.Build.InstallDir; |
| 5 | const InstallArtifactStep = @This(); | 5 | const InstallArtifactStep = @This(); |
| 6 | const fs = std.fs; | ||
| 6 | 7 | ||
| 7 | pub const base_id = .install_artifact; | 8 | pub const base_id = .install_artifact; |
| 8 | 9 | ||
| 9 | step: Step, | 10 | step: Step, |
| 10 | builder: *std.Build, | 11 | dest_builder: *std.Build, |
| 11 | artifact: *CompileStep, | 12 | artifact: *CompileStep, |
| 12 | dest_dir: InstallDir, | 13 | dest_dir: InstallDir, |
| 13 | pdb_dir: ?InstallDir, | 14 | pdb_dir: ?InstallDir, |
| 14 | h_dir: ?InstallDir, | 15 | h_dir: ?InstallDir, |
| 16 | /// If non-null, adds additional path components relative to dest_dir, and | ||
| 17 | /// overrides the basename of the CompileStep. | ||
| 18 | dest_sub_path: ?[]const u8, | ||
| 15 | 19 | ||
| 16 | pub fn create(builder: *std.Build, artifact: *CompileStep) *InstallArtifactStep { | 20 | pub fn create(owner: *std.Build, artifact: *CompileStep) *InstallArtifactStep { |
| 17 | if (artifact.install_step) |s| return s; | 21 | if (artifact.install_step) |s| return s; |
| 18 | 22 | ||
| 19 | const self = builder.allocator.create(InstallArtifactStep) catch @panic("OOM"); | 23 | const self = owner.allocator.create(InstallArtifactStep) catch @panic("OOM"); |
| 20 | self.* = InstallArtifactStep{ | 24 | self.* = InstallArtifactStep{ |
| 21 | .builder = builder, | 25 | .step = Step.init(.{ |
| 22 | .step = Step.init(.install_artifact, builder.fmt("install {s}", .{artifact.step.name}), builder.allocator, make), | 26 | .id = base_id, |
| 27 | .name = owner.fmt("install {s}", .{artifact.name}), | ||
| 28 | .owner = owner, | ||
| 29 | .makeFn = make, | ||
| 30 | }), | ||
| 31 | .dest_builder = owner, | ||
| 23 | .artifact = artifact, | 32 | .artifact = artifact, |
| 24 | .dest_dir = artifact.override_dest_dir orelse switch (artifact.kind) { | 33 | .dest_dir = artifact.override_dest_dir orelse switch (artifact.kind) { |
| 25 | .obj => @panic("Cannot install a .obj build artifact."), | 34 | .obj => @panic("Cannot install a .obj build artifact."), |
| 26 | .@"test" => @panic("Cannot install a .test build artifact, use .test_exe instead."), | 35 | .exe, .@"test" => InstallDir{ .bin = {} }, |
| 27 | .exe, .test_exe => InstallDir{ .bin = {} }, | ||
| 28 | .lib => InstallDir{ .lib = {} }, | 36 | .lib => InstallDir{ .lib = {} }, |
| 29 | }, | 37 | }, |
| 30 | .pdb_dir = if (artifact.producesPdbFile()) blk: { | 38 | .pdb_dir = if (artifact.producesPdbFile()) blk: { |
| 31 | if (artifact.kind == .exe or artifact.kind == .test_exe) { | 39 | if (artifact.kind == .exe or artifact.kind == .@"test") { |
| 32 | break :blk InstallDir{ .bin = {} }; | 40 | break :blk InstallDir{ .bin = {} }; |
| 33 | } else { | 41 | } else { |
| 34 | break :blk InstallDir{ .lib = {} }; | 42 | break :blk InstallDir{ .lib = {} }; |
| 35 | } | 43 | } |
| 36 | } else null, | 44 | } else null, |
| 37 | .h_dir = if (artifact.kind == .lib and artifact.emit_h) .header else null, | 45 | .h_dir = if (artifact.kind == .lib and artifact.emit_h) .header else null, |
| 46 | .dest_sub_path = null, | ||
| 38 | }; | 47 | }; |
| 39 | self.step.dependOn(&artifact.step); | 48 | self.step.dependOn(&artifact.step); |
| 40 | artifact.install_step = self; | 49 | artifact.install_step = self; |
| 41 | 50 | ||
| 42 | builder.pushInstalledFile(self.dest_dir, artifact.out_filename); | 51 | owner.pushInstalledFile(self.dest_dir, artifact.out_filename); |
| 43 | if (self.artifact.isDynamicLibrary()) { | 52 | if (self.artifact.isDynamicLibrary()) { |
| 44 | if (artifact.major_only_filename) |name| { | 53 | if (artifact.major_only_filename) |name| { |
| 45 | builder.pushInstalledFile(.lib, name); | 54 | owner.pushInstalledFile(.lib, name); |
| 46 | } | 55 | } |
| 47 | if (artifact.name_only_filename) |name| { | 56 | if (artifact.name_only_filename) |name| { |
| 48 | builder.pushInstalledFile(.lib, name); | 57 | owner.pushInstalledFile(.lib, name); |
| 49 | } | 58 | } |
| 50 | if (self.artifact.target.isWindows()) { | 59 | if (self.artifact.target.isWindows()) { |
| 51 | builder.pushInstalledFile(.lib, artifact.out_lib_filename); | 60 | owner.pushInstalledFile(.lib, artifact.out_lib_filename); |
| 52 | } | 61 | } |
| 53 | } | 62 | } |
| 54 | if (self.pdb_dir) |pdb_dir| { | 63 | if (self.pdb_dir) |pdb_dir| { |
| 55 | builder.pushInstalledFile(pdb_dir, artifact.out_pdb_filename); | 64 | owner.pushInstalledFile(pdb_dir, artifact.out_pdb_filename); |
| 56 | } | 65 | } |
| 57 | if (self.h_dir) |h_dir| { | 66 | if (self.h_dir) |h_dir| { |
| 58 | builder.pushInstalledFile(h_dir, artifact.out_h_filename); | 67 | owner.pushInstalledFile(h_dir, artifact.out_h_filename); |
| 59 | } | 68 | } |
| 60 | return self; | 69 | return self; |
| 61 | } | 70 | } |
| 62 | 71 | ||
| 63 | fn make(step: *Step) !void { | 72 | fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 73 | _ = prog_node; | ||
| 74 | const src_builder = step.owner; | ||
| 64 | const self = @fieldParentPtr(InstallArtifactStep, "step", step); | 75 | const self = @fieldParentPtr(InstallArtifactStep, "step", step); |
| 65 | const builder = self.builder; | 76 | const dest_builder = self.dest_builder; |
| 66 | 77 | ||
| 67 | const full_dest_path = builder.getInstallPath(self.dest_dir, self.artifact.out_filename); | 78 | const dest_sub_path = if (self.dest_sub_path) |sub_path| sub_path else self.artifact.out_filename; |
| 68 | try builder.updateFile(self.artifact.getOutputSource().getPath(builder), full_dest_path); | 79 | const full_dest_path = dest_builder.getInstallPath(self.dest_dir, dest_sub_path); |
| 69 | if (self.artifact.isDynamicLibrary() and self.artifact.version != null and self.artifact.target.wantSharedLibSymLinks()) { | 80 | const cwd = fs.cwd(); |
| 70 | try CompileStep.doAtomicSymLinks(builder.allocator, full_dest_path, self.artifact.major_only_filename.?, self.artifact.name_only_filename.?); | 81 | |
| 82 | var all_cached = true; | ||
| 83 | |||
| 84 | { | ||
| 85 | const full_src_path = self.artifact.getOutputSource().getPath(src_builder); | ||
| 86 | const p = fs.Dir.updateFile(cwd, full_src_path, cwd, full_dest_path, .{}) catch |err| { | ||
| 87 | return step.fail("unable to update file from '{s}' to '{s}': {s}", .{ | ||
| 88 | full_src_path, full_dest_path, @errorName(err), | ||
| 89 | }); | ||
| 90 | }; | ||
| 91 | all_cached = all_cached and p == .fresh; | ||
| 92 | } | ||
| 93 | |||
| 94 | if (self.artifact.isDynamicLibrary() and | ||
| 95 | self.artifact.version != null and | ||
| 96 | self.artifact.target.wantSharedLibSymLinks()) | ||
| 97 | { | ||
| 98 | try CompileStep.doAtomicSymLinks(step, full_dest_path, self.artifact.major_only_filename.?, self.artifact.name_only_filename.?); | ||
| 71 | } | 99 | } |
| 72 | if (self.artifact.isDynamicLibrary() and self.artifact.target.isWindows() and self.artifact.emit_implib != .no_emit) { | 100 | if (self.artifact.isDynamicLibrary() and |
| 73 | const full_implib_path = builder.getInstallPath(self.dest_dir, self.artifact.out_lib_filename); | 101 | self.artifact.target.isWindows() and |
| 74 | try builder.updateFile(self.artifact.getOutputLibSource().getPath(builder), full_implib_path); | 102 | self.artifact.emit_implib != .no_emit) |
| 103 | { | ||
| 104 | const full_src_path = self.artifact.getOutputLibSource().getPath(src_builder); | ||
| 105 | const full_implib_path = dest_builder.getInstallPath(self.dest_dir, self.artifact.out_lib_filename); | ||
| 106 | const p = fs.Dir.updateFile(cwd, full_src_path, cwd, full_implib_path, .{}) catch |err| { | ||
| 107 | return step.fail("unable to update file from '{s}' to '{s}': {s}", .{ | ||
| 108 | full_src_path, full_implib_path, @errorName(err), | ||
| 109 | }); | ||
| 110 | }; | ||
| 111 | all_cached = all_cached and p == .fresh; | ||
| 75 | } | 112 | } |
| 76 | if (self.pdb_dir) |pdb_dir| { | 113 | if (self.pdb_dir) |pdb_dir| { |
| 77 | const full_pdb_path = builder.getInstallPath(pdb_dir, self.artifact.out_pdb_filename); | 114 | const full_src_path = self.artifact.getOutputPdbSource().getPath(src_builder); |
| 78 | try builder.updateFile(self.artifact.getOutputPdbSource().getPath(builder), full_pdb_path); | 115 | const full_pdb_path = dest_builder.getInstallPath(pdb_dir, self.artifact.out_pdb_filename); |
| 116 | const p = fs.Dir.updateFile(cwd, full_src_path, cwd, full_pdb_path, .{}) catch |err| { | ||
| 117 | return step.fail("unable to update file from '{s}' to '{s}': {s}", .{ | ||
| 118 | full_src_path, full_pdb_path, @errorName(err), | ||
| 119 | }); | ||
| 120 | }; | ||
| 121 | all_cached = all_cached and p == .fresh; | ||
| 79 | } | 122 | } |
| 80 | if (self.h_dir) |h_dir| { | 123 | if (self.h_dir) |h_dir| { |
| 81 | const full_h_path = builder.getInstallPath(h_dir, self.artifact.out_h_filename); | 124 | const full_src_path = self.artifact.getOutputHSource().getPath(src_builder); |
| 82 | try builder.updateFile(self.artifact.getOutputHSource().getPath(builder), full_h_path); | 125 | const full_h_path = dest_builder.getInstallPath(h_dir, self.artifact.out_h_filename); |
| 126 | const p = fs.Dir.updateFile(cwd, full_src_path, cwd, full_h_path, .{}) catch |err| { | ||
| 127 | return step.fail("unable to update file from '{s}' to '{s}': {s}", .{ | ||
| 128 | full_src_path, full_h_path, @errorName(err), | ||
| 129 | }); | ||
| 130 | }; | ||
| 131 | all_cached = all_cached and p == .fresh; | ||
| 83 | } | 132 | } |
| 84 | self.artifact.installed_path = full_dest_path; | 133 | self.artifact.installed_path = full_dest_path; |
| 134 | step.result_cached = all_cached; | ||
| 85 | } | 135 | } |
lib/std/Build/InstallDirStep.zig+43-26| ... | @@ -4,14 +4,12 @@ const fs = std.fs; | ... | @@ -4,14 +4,12 @@ const fs = std.fs; |
| 4 | const Step = std.Build.Step; | 4 | const Step = std.Build.Step; |
| 5 | const InstallDir = std.Build.InstallDir; | 5 | const InstallDir = std.Build.InstallDir; |
| 6 | const InstallDirStep = @This(); | 6 | const InstallDirStep = @This(); |
| 7 | const log = std.log; | ||
| 8 | 7 | ||
| 9 | step: Step, | 8 | step: Step, |
| 10 | builder: *std.Build, | ||
| 11 | options: Options, | 9 | options: Options, |
| 12 | /// This is used by the build system when a file being installed comes from one | 10 | /// This is used by the build system when a file being installed comes from one |
| 13 | /// package but is being installed by another. | 11 | /// package but is being installed by another. |
| 14 | override_source_builder: ?*std.Build = null, | 12 | dest_builder: *std.Build, |
| 15 | 13 | ||
| 16 | pub const base_id = .install_dir; | 14 | pub const base_id = .install_dir; |
| 17 | 15 | ||
| ... | @@ -40,31 +38,35 @@ pub const Options = struct { | ... | @@ -40,31 +38,35 @@ pub const Options = struct { |
| 40 | } | 38 | } |
| 41 | }; | 39 | }; |
| 42 | 40 | ||
| 43 | pub fn init( | 41 | pub fn init(owner: *std.Build, options: Options) InstallDirStep { |
| 44 | builder: *std.Build, | 42 | owner.pushInstalledFile(options.install_dir, options.install_subdir); |
| 45 | options: Options, | 43 | return .{ |
| 46 | ) InstallDirStep { | 44 | .step = Step.init(.{ |
| 47 | builder.pushInstalledFile(options.install_dir, options.install_subdir); | 45 | .id = .install_dir, |
| 48 | return InstallDirStep{ | 46 | .name = owner.fmt("install {s}/", .{options.source_dir}), |
| 49 | .builder = builder, | 47 | .owner = owner, |
| 50 | .step = Step.init(.install_dir, builder.fmt("install {s}/", .{options.source_dir}), builder.allocator, make), | 48 | .makeFn = make, |
| 51 | .options = options.dupe(builder), | 49 | }), |
| 50 | .options = options.dupe(owner), | ||
| 51 | .dest_builder = owner, | ||
| 52 | }; | 52 | }; |
| 53 | } | 53 | } |
| 54 | 54 | ||
| 55 | fn make(step: *Step) !void { | 55 | fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 56 | _ = prog_node; | ||
| 56 | const self = @fieldParentPtr(InstallDirStep, "step", step); | 57 | const self = @fieldParentPtr(InstallDirStep, "step", step); |
| 57 | const dest_prefix = self.builder.getInstallPath(self.options.install_dir, self.options.install_subdir); | 58 | const dest_builder = self.dest_builder; |
| 58 | const src_builder = self.override_source_builder orelse self.builder; | 59 | const arena = dest_builder.allocator; |
| 59 | const full_src_dir = src_builder.pathFromRoot(self.options.source_dir); | 60 | const dest_prefix = dest_builder.getInstallPath(self.options.install_dir, self.options.install_subdir); |
| 60 | var src_dir = std.fs.cwd().openIterableDir(full_src_dir, .{}) catch |err| { | 61 | const src_builder = self.step.owner; |
| 61 | log.err("InstallDirStep: unable to open source directory '{s}': {s}", .{ | 62 | var src_dir = src_builder.build_root.handle.openIterableDir(self.options.source_dir, .{}) catch |err| { |
| 62 | full_src_dir, @errorName(err), | 63 | return step.fail("unable to open source directory '{}{s}': {s}", .{ |
| 64 | src_builder.build_root, self.options.source_dir, @errorName(err), | ||
| 63 | }); | 65 | }); |
| 64 | return error.StepFailed; | ||
| 65 | }; | 66 | }; |
| 66 | defer src_dir.close(); | 67 | defer src_dir.close(); |
| 67 | var it = try src_dir.walk(self.builder.allocator); | 68 | var it = try src_dir.walk(arena); |
| 69 | var all_cached = true; | ||
| 68 | next_entry: while (try it.next()) |entry| { | 70 | next_entry: while (try it.next()) |entry| { |
| 69 | for (self.options.exclude_extensions) |ext| { | 71 | for (self.options.exclude_extensions) |ext| { |
| 70 | if (mem.endsWith(u8, entry.path, ext)) { | 72 | if (mem.endsWith(u8, entry.path, ext)) { |
| ... | @@ -72,22 +74,37 @@ fn make(step: *Step) !void { | ... | @@ -72,22 +74,37 @@ fn make(step: *Step) !void { |
| 72 | } | 74 | } |
| 73 | } | 75 | } |
| 74 | 76 | ||
| 75 | const full_path = self.builder.pathJoin(&.{ full_src_dir, entry.path }); | 77 | // relative to src build root |
| 76 | const dest_path = self.builder.pathJoin(&.{ dest_prefix, entry.path }); | 78 | const src_sub_path = try fs.path.join(arena, &.{ self.options.source_dir, entry.path }); |
| 79 | const dest_path = try fs.path.join(arena, &.{ dest_prefix, entry.path }); | ||
| 80 | const cwd = fs.cwd(); | ||
| 77 | 81 | ||
| 78 | switch (entry.kind) { | 82 | switch (entry.kind) { |
| 79 | .Directory => try fs.cwd().makePath(dest_path), | 83 | .Directory => try cwd.makePath(dest_path), |
| 80 | .File => { | 84 | .File => { |
| 81 | for (self.options.blank_extensions) |ext| { | 85 | for (self.options.blank_extensions) |ext| { |
| 82 | if (mem.endsWith(u8, entry.path, ext)) { | 86 | if (mem.endsWith(u8, entry.path, ext)) { |
| 83 | try self.builder.truncateFile(dest_path); | 87 | try dest_builder.truncateFile(dest_path); |
| 84 | continue :next_entry; | 88 | continue :next_entry; |
| 85 | } | 89 | } |
| 86 | } | 90 | } |
| 87 | 91 | ||
| 88 | try self.builder.updateFile(full_path, dest_path); | 92 | const prev_status = fs.Dir.updateFile( |
| 93 | src_builder.build_root.handle, | ||
| 94 | src_sub_path, | ||
| 95 | cwd, | ||
| 96 | dest_path, | ||
| 97 | .{}, | ||
| 98 | ) catch |err| { | ||
| 99 | return step.fail("unable to update file from '{}{s}' to '{s}': {s}", .{ | ||
| 100 | src_builder.build_root, src_sub_path, dest_path, @errorName(err), | ||
| 101 | }); | ||
| 102 | }; | ||
| 103 | all_cached = all_cached and prev_status == .fresh; | ||
| 89 | }, | 104 | }, |
| 90 | else => continue, | 105 | else => continue, |
| 91 | } | 106 | } |
| 92 | } | 107 | } |
| 108 | |||
| 109 | step.result_cached = all_cached; | ||
| 93 | } | 110 | } |
lib/std/Build/InstallFileStep.zig+34-17| ... | @@ -3,38 +3,55 @@ const Step = std.Build.Step; | ... | @@ -3,38 +3,55 @@ const Step = std.Build.Step; |
| 3 | const FileSource = std.Build.FileSource; | 3 | const FileSource = std.Build.FileSource; |
| 4 | const InstallDir = std.Build.InstallDir; | 4 | const InstallDir = std.Build.InstallDir; |
| 5 | const InstallFileStep = @This(); | 5 | const InstallFileStep = @This(); |
| 6 | const assert = std.debug.assert; | ||
| 6 | 7 | ||
| 7 | pub const base_id = .install_file; | 8 | pub const base_id = .install_file; |
| 8 | 9 | ||
| 9 | step: Step, | 10 | step: Step, |
| 10 | builder: *std.Build, | ||
| 11 | source: FileSource, | 11 | source: FileSource, |
| 12 | dir: InstallDir, | 12 | dir: InstallDir, |
| 13 | dest_rel_path: []const u8, | 13 | dest_rel_path: []const u8, |
| 14 | /// This is used by the build system when a file being installed comes from one | 14 | /// This is used by the build system when a file being installed comes from one |
| 15 | /// package but is being installed by another. | 15 | /// package but is being installed by another. |
| 16 | override_source_builder: ?*std.Build = null, | 16 | dest_builder: *std.Build, |
| 17 | 17 | ||
| 18 | pub fn init( | 18 | pub fn create( |
| 19 | builder: *std.Build, | 19 | owner: *std.Build, |
| 20 | source: FileSource, | 20 | source: FileSource, |
| 21 | dir: InstallDir, | 21 | dir: InstallDir, |
| 22 | dest_rel_path: []const u8, | 22 | dest_rel_path: []const u8, |
| 23 | ) InstallFileStep { | 23 | ) *InstallFileStep { |
| 24 | builder.pushInstalledFile(dir, dest_rel_path); | 24 | assert(dest_rel_path.len != 0); |
| 25 | return InstallFileStep{ | 25 | owner.pushInstalledFile(dir, dest_rel_path); |
| 26 | .builder = builder, | 26 | const self = owner.allocator.create(InstallFileStep) catch @panic("OOM"); |
| 27 | .step = Step.init(.install_file, builder.fmt("install {s} to {s}", .{ source.getDisplayName(), dest_rel_path }), builder.allocator, make), | 27 | self.* = .{ |
| 28 | .source = source.dupe(builder), | 28 | .step = Step.init(.{ |
| 29 | .dir = dir.dupe(builder), | 29 | .id = base_id, |
| 30 | .dest_rel_path = builder.dupePath(dest_rel_path), | 30 | .name = owner.fmt("install {s} to {s}", .{ source.getDisplayName(), dest_rel_path }), |
| 31 | .owner = owner, | ||
| 32 | .makeFn = make, | ||
| 33 | }), | ||
| 34 | .source = source.dupe(owner), | ||
| 35 | .dir = dir.dupe(owner), | ||
| 36 | .dest_rel_path = owner.dupePath(dest_rel_path), | ||
| 37 | .dest_builder = owner, | ||
| 31 | }; | 38 | }; |
| 39 | source.addStepDependencies(&self.step); | ||
| 40 | return self; | ||
| 32 | } | 41 | } |
| 33 | 42 | ||
| 34 | fn make(step: *Step) !void { | 43 | fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 44 | _ = prog_node; | ||
| 45 | const src_builder = step.owner; | ||
| 35 | const self = @fieldParentPtr(InstallFileStep, "step", step); | 46 | const self = @fieldParentPtr(InstallFileStep, "step", step); |
| 36 | const src_builder = self.override_source_builder orelse self.builder; | 47 | const dest_builder = self.dest_builder; |
| 37 | const full_src_path = self.source.getPath(src_builder); | 48 | const full_src_path = self.source.getPath2(src_builder, step); |
| 38 | const full_dest_path = self.builder.getInstallPath(self.dir, self.dest_rel_path); | 49 | const full_dest_path = dest_builder.getInstallPath(self.dir, self.dest_rel_path); |
| 39 | try self.builder.updateFile(full_src_path, full_dest_path); | 50 | const cwd = std.fs.cwd(); |
| 51 | const prev = std.fs.Dir.updateFile(cwd, full_src_path, cwd, full_dest_path, .{}) catch |err| { | ||
| 52 | return step.fail("unable to update file from '{s}' to '{s}': {s}", .{ | ||
| 53 | full_src_path, full_dest_path, @errorName(err), | ||
| 54 | }); | ||
| 55 | }; | ||
| 56 | step.result_cached = prev == .fresh; | ||
| 40 | } | 57 | } |
lib/std/Build/LogStep.zig deleted-23| ... | @@ -1,23 +0,0 @@ | ||
| 1 | const std = @import("../std.zig"); | ||
| 2 | const log = std.log; | ||
| 3 | const Step = std.Build.Step; | ||
| 4 | const LogStep = @This(); | ||
| 5 | |||
| 6 | pub const base_id = .log; | ||
| 7 | |||
| 8 | step: Step, | ||
| 9 | builder: *std.Build, | ||
| 10 | data: []const u8, | ||
| 11 | |||
| 12 | pub fn init(builder: *std.Build, data: []const u8) LogStep { | ||
| 13 | return LogStep{ | ||
| 14 | .builder = builder, | ||
| 15 | .step = Step.init(.log, builder.fmt("log {s}", .{data}), builder.allocator, make), | ||
| 16 | .data = builder.dupe(data), | ||
| 17 | }; | ||
| 18 | } | ||
| 19 | |||
| 20 | fn make(step: *Step) anyerror!void { | ||
| 21 | const self = @fieldParentPtr(LogStep, "step", step); | ||
| 22 | log.info("{s}", .{self.data}); | ||
| 23 | } | ||
lib/std/Build/ObjCopyStep.zig+15-31| ... | @@ -21,7 +21,6 @@ pub const RawFormat = enum { | ... | @@ -21,7 +21,6 @@ pub const RawFormat = enum { |
| 21 | }; | 21 | }; |
| 22 | 22 | ||
| 23 | step: Step, | 23 | step: Step, |
| 24 | builder: *std.Build, | ||
| 25 | file_source: std.Build.FileSource, | 24 | file_source: std.Build.FileSource, |
| 26 | basename: []const u8, | 25 | basename: []const u8, |
| 27 | output_file: std.Build.GeneratedFile, | 26 | output_file: std.Build.GeneratedFile, |
| ... | @@ -38,19 +37,18 @@ pub const Options = struct { | ... | @@ -38,19 +37,18 @@ pub const Options = struct { |
| 38 | }; | 37 | }; |
| 39 | 38 | ||
| 40 | pub fn create( | 39 | pub fn create( |
| 41 | builder: *std.Build, | 40 | owner: *std.Build, |
| 42 | file_source: std.Build.FileSource, | 41 | file_source: std.Build.FileSource, |
| 43 | options: Options, | 42 | options: Options, |
| 44 | ) *ObjCopyStep { | 43 | ) *ObjCopyStep { |
| 45 | const self = builder.allocator.create(ObjCopyStep) catch @panic("OOM"); | 44 | const self = owner.allocator.create(ObjCopyStep) catch @panic("OOM"); |
| 46 | self.* = ObjCopyStep{ | 45 | self.* = ObjCopyStep{ |
| 47 | .step = Step.init( | 46 | .step = Step.init(.{ |
| 48 | base_id, | 47 | .id = base_id, |
| 49 | builder.fmt("objcopy {s}", .{file_source.getDisplayName()}), | 48 | .name = owner.fmt("objcopy {s}", .{file_source.getDisplayName()}), |
| 50 | builder.allocator, | 49 | .owner = owner, |
| 51 | make, | 50 | .makeFn = make, |
| 52 | ), | 51 | }), |
| 53 | .builder = builder, | ||
| 54 | .file_source = file_source, | 52 | .file_source = file_source, |
| 55 | .basename = options.basename orelse file_source.getDisplayName(), | 53 | .basename = options.basename orelse file_source.getDisplayName(), |
| 56 | .output_file = std.Build.GeneratedFile{ .step = &self.step }, | 54 | .output_file = std.Build.GeneratedFile{ .step = &self.step }, |
| ... | @@ -67,9 +65,9 @@ pub fn getOutputSource(self: *const ObjCopyStep) std.Build.FileSource { | ... | @@ -67,9 +65,9 @@ pub fn getOutputSource(self: *const ObjCopyStep) std.Build.FileSource { |
| 67 | return .{ .generated = &self.output_file }; | 65 | return .{ .generated = &self.output_file }; |
| 68 | } | 66 | } |
| 69 | 67 | ||
| 70 | fn make(step: *Step) !void { | 68 | fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 69 | const b = step.owner; | ||
| 71 | const self = @fieldParentPtr(ObjCopyStep, "step", step); | 70 | const self = @fieldParentPtr(ObjCopyStep, "step", step); |
| 72 | const b = self.builder; | ||
| 73 | 71 | ||
| 74 | var man = b.cache.obtain(); | 72 | var man = b.cache.obtain(); |
| 75 | defer man.deinit(); | 73 | defer man.deinit(); |
| ... | @@ -84,7 +82,7 @@ fn make(step: *Step) !void { | ... | @@ -84,7 +82,7 @@ fn make(step: *Step) !void { |
| 84 | man.hash.addOptional(self.pad_to); | 82 | man.hash.addOptional(self.pad_to); |
| 85 | man.hash.addOptional(self.format); | 83 | man.hash.addOptional(self.format); |
| 86 | 84 | ||
| 87 | if (man.hit() catch |err| failWithCacheError(man, err)) { | 85 | if (try step.cacheHit(&man)) { |
| 88 | // Cache hit, skip subprocess execution. | 86 | // Cache hit, skip subprocess execution. |
| 89 | const digest = man.final(); | 87 | const digest = man.final(); |
| 90 | self.output_file.path = try b.cache_root.join(b.allocator, &.{ | 88 | self.output_file.path = try b.cache_root.join(b.allocator, &.{ |
| ... | @@ -97,8 +95,7 @@ fn make(step: *Step) !void { | ... | @@ -97,8 +95,7 @@ fn make(step: *Step) !void { |
| 97 | const full_dest_path = try b.cache_root.join(b.allocator, &.{ "o", &digest, self.basename }); | 95 | const full_dest_path = try b.cache_root.join(b.allocator, &.{ "o", &digest, self.basename }); |
| 98 | const cache_path = "o" ++ fs.path.sep_str ++ digest; | 96 | const cache_path = "o" ++ fs.path.sep_str ++ digest; |
| 99 | b.cache_root.handle.makePath(cache_path) catch |err| { | 97 | b.cache_root.handle.makePath(cache_path) catch |err| { |
| 100 | std.debug.print("unable to make path {s}: {s}\n", .{ cache_path, @errorName(err) }); | 98 | return step.fail("unable to make path {s}: {s}", .{ cache_path, @errorName(err) }); |
| 101 | return err; | ||
| 102 | }; | 99 | }; |
| 103 | 100 | ||
| 104 | var argv = std.ArrayList([]const u8).init(b.allocator); | 101 | var argv = std.ArrayList([]const u8).init(b.allocator); |
| ... | @@ -116,23 +113,10 @@ fn make(step: *Step) !void { | ... | @@ -116,23 +113,10 @@ fn make(step: *Step) !void { |
| 116 | }; | 113 | }; |
| 117 | 114 | ||
| 118 | try argv.appendSlice(&.{ full_src_path, full_dest_path }); | 115 | try argv.appendSlice(&.{ full_src_path, full_dest_path }); |
| 119 | _ = try self.builder.execFromStep(argv.items, &self.step); | 116 | |
| 117 | try argv.append("--listen=-"); | ||
| 118 | _ = try step.evalZigProcess(argv.items, prog_node); | ||
| 120 | 119 | ||
| 121 | self.output_file.path = full_dest_path; | 120 | self.output_file.path = full_dest_path; |
| 122 | try man.writeManifest(); | 121 | try man.writeManifest(); |
| 123 | } | 122 | } |
| 124 | |||
| 125 | /// TODO consolidate this with the same function in RunStep? | ||
| 126 | /// Also properly deal with concurrency (see open PR) | ||
| 127 | fn failWithCacheError(man: std.Build.Cache.Manifest, err: anyerror) noreturn { | ||
| 128 | const i = man.failed_file_index orelse failWithSimpleError(err); | ||
| 129 | const pp = man.files.items[i].prefixed_path orelse failWithSimpleError(err); | ||
| 130 | const prefix = man.cache.prefixes()[pp.prefix].path orelse ""; | ||
| 131 | std.debug.print("{s}: {s}/{s}\n", .{ @errorName(err), prefix, pp.sub_path }); | ||
| 132 | std.process.exit(1); | ||
| 133 | } | ||
| 134 | |||
| 135 | fn failWithSimpleError(err: anyerror) noreturn { | ||
| 136 | std.debug.print("{s}\n", .{@errorName(err)}); | ||
| 137 | std.process.exit(1); | ||
| 138 | } |
lib/std/Build/OptionsStep.zig+23-18| ... | @@ -12,21 +12,24 @@ pub const base_id = .options; | ... | @@ -12,21 +12,24 @@ pub const base_id = .options; |
| 12 | 12 | ||
| 13 | step: Step, | 13 | step: Step, |
| 14 | generated_file: GeneratedFile, | 14 | generated_file: GeneratedFile, |
| 15 | builder: *std.Build, | ||
| 16 | 15 | ||
| 17 | contents: std.ArrayList(u8), | 16 | contents: std.ArrayList(u8), |
| 18 | artifact_args: std.ArrayList(OptionArtifactArg), | 17 | artifact_args: std.ArrayList(OptionArtifactArg), |
| 19 | file_source_args: std.ArrayList(OptionFileSourceArg), | 18 | file_source_args: std.ArrayList(OptionFileSourceArg), |
| 20 | 19 | ||
| 21 | pub fn create(builder: *std.Build) *OptionsStep { | 20 | pub fn create(owner: *std.Build) *OptionsStep { |
| 22 | const self = builder.allocator.create(OptionsStep) catch @panic("OOM"); | 21 | const self = owner.allocator.create(OptionsStep) catch @panic("OOM"); |
| 23 | self.* = .{ | 22 | self.* = .{ |
| 24 | .builder = builder, | 23 | .step = Step.init(.{ |
| 25 | .step = Step.init(.options, "options", builder.allocator, make), | 24 | .id = base_id, |
| 25 | .name = "options", | ||
| 26 | .owner = owner, | ||
| 27 | .makeFn = make, | ||
| 28 | }), | ||
| 26 | .generated_file = undefined, | 29 | .generated_file = undefined, |
| 27 | .contents = std.ArrayList(u8).init(builder.allocator), | 30 | .contents = std.ArrayList(u8).init(owner.allocator), |
| 28 | .artifact_args = std.ArrayList(OptionArtifactArg).init(builder.allocator), | 31 | .artifact_args = std.ArrayList(OptionArtifactArg).init(owner.allocator), |
| 29 | .file_source_args = std.ArrayList(OptionFileSourceArg).init(builder.allocator), | 32 | .file_source_args = std.ArrayList(OptionFileSourceArg).init(owner.allocator), |
| 30 | }; | 33 | }; |
| 31 | self.generated_file = .{ .step = &self.step }; | 34 | self.generated_file = .{ .step = &self.step }; |
| 32 | 35 | ||
| ... | @@ -192,7 +195,7 @@ pub fn addOptionFileSource( | ... | @@ -192,7 +195,7 @@ pub fn addOptionFileSource( |
| 192 | ) void { | 195 | ) void { |
| 193 | self.file_source_args.append(.{ | 196 | self.file_source_args.append(.{ |
| 194 | .name = name, | 197 | .name = name, |
| 195 | .source = source.dupe(self.builder), | 198 | .source = source.dupe(self.step.owner), |
| 196 | }) catch @panic("OOM"); | 199 | }) catch @panic("OOM"); |
| 197 | source.addStepDependencies(&self.step); | 200 | source.addStepDependencies(&self.step); |
| 198 | } | 201 | } |
| ... | @@ -200,12 +203,12 @@ pub fn addOptionFileSource( | ... | @@ -200,12 +203,12 @@ pub fn addOptionFileSource( |
| 200 | /// The value is the path in the cache dir. | 203 | /// The value is the path in the cache dir. |
| 201 | /// Adds a dependency automatically. | 204 | /// Adds a dependency automatically. |
| 202 | pub fn addOptionArtifact(self: *OptionsStep, name: []const u8, artifact: *CompileStep) void { | 205 | pub fn addOptionArtifact(self: *OptionsStep, name: []const u8, artifact: *CompileStep) void { |
| 203 | self.artifact_args.append(.{ .name = self.builder.dupe(name), .artifact = artifact }) catch @panic("OOM"); | 206 | self.artifact_args.append(.{ .name = self.step.owner.dupe(name), .artifact = artifact }) catch @panic("OOM"); |
| 204 | self.step.dependOn(&artifact.step); | 207 | self.step.dependOn(&artifact.step); |
| 205 | } | 208 | } |
| 206 | 209 | ||
| 207 | pub fn createModule(self: *OptionsStep) *std.Build.Module { | 210 | pub fn createModule(self: *OptionsStep) *std.Build.Module { |
| 208 | return self.builder.createModule(.{ | 211 | return self.step.owner.createModule(.{ |
| 209 | .source_file = self.getSource(), | 212 | .source_file = self.getSource(), |
| 210 | .dependencies = &.{}, | 213 | .dependencies = &.{}, |
| 211 | }); | 214 | }); |
| ... | @@ -215,14 +218,18 @@ pub fn getSource(self: *OptionsStep) FileSource { | ... | @@ -215,14 +218,18 @@ pub fn getSource(self: *OptionsStep) FileSource { |
| 215 | return .{ .generated = &self.generated_file }; | 218 | return .{ .generated = &self.generated_file }; |
| 216 | } | 219 | } |
| 217 | 220 | ||
| 218 | fn make(step: *Step) !void { | 221 | fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 222 | // This step completes so quickly that no progress is necessary. | ||
| 223 | _ = prog_node; | ||
| 224 | |||
| 225 | const b = step.owner; | ||
| 219 | const self = @fieldParentPtr(OptionsStep, "step", step); | 226 | const self = @fieldParentPtr(OptionsStep, "step", step); |
| 220 | 227 | ||
| 221 | for (self.artifact_args.items) |item| { | 228 | for (self.artifact_args.items) |item| { |
| 222 | self.addOption( | 229 | self.addOption( |
| 223 | []const u8, | 230 | []const u8, |
| 224 | item.name, | 231 | item.name, |
| 225 | self.builder.pathFromRoot(item.artifact.getOutputSource().getPath(self.builder)), | 232 | b.pathFromRoot(item.artifact.getOutputSource().getPath(b)), |
| 226 | ); | 233 | ); |
| 227 | } | 234 | } |
| 228 | 235 | ||
| ... | @@ -230,20 +237,18 @@ fn make(step: *Step) !void { | ... | @@ -230,20 +237,18 @@ fn make(step: *Step) !void { |
| 230 | self.addOption( | 237 | self.addOption( |
| 231 | []const u8, | 238 | []const u8, |
| 232 | item.name, | 239 | item.name, |
| 233 | item.source.getPath(self.builder), | 240 | item.source.getPath(b), |
| 234 | ); | 241 | ); |
| 235 | } | 242 | } |
| 236 | 243 | ||
| 237 | var options_dir = try self.builder.cache_root.handle.makeOpenPath("options", .{}); | 244 | var options_dir = try b.cache_root.handle.makeOpenPath("options", .{}); |
| 238 | defer options_dir.close(); | 245 | defer options_dir.close(); |
| 239 | 246 | ||
| 240 | const basename = self.hashContentsToFileName(); | 247 | const basename = self.hashContentsToFileName(); |
| 241 | 248 | ||
| 242 | try options_dir.writeFile(&basename, self.contents.items); | 249 | try options_dir.writeFile(&basename, self.contents.items); |
| 243 | 250 | ||
| 244 | self.generated_file.path = try self.builder.cache_root.join(self.builder.allocator, &.{ | 251 | self.generated_file.path = try b.cache_root.join(b.allocator, &.{ "options", &basename }); |
| 245 | "options", &basename, | ||
| 246 | }); | ||
| 247 | } | 252 | } |
| 248 | 253 | ||
| 249 | fn hashContentsToFileName(self: *OptionsStep) [64]u8 { | 254 | fn hashContentsToFileName(self: *OptionsStep) [64]u8 { |
lib/std/Build/RemoveDirStep.zig+24-11| ... | @@ -1,5 +1,4 @@ | ... | @@ -1,5 +1,4 @@ |
| 1 | const std = @import("../std.zig"); | 1 | const std = @import("../std.zig"); |
| 2 | const log = std.log; | ||
| 3 | const fs = std.fs; | 2 | const fs = std.fs; |
| 4 | const Step = std.Build.Step; | 3 | const Step = std.Build.Step; |
| 5 | const RemoveDirStep = @This(); | 4 | const RemoveDirStep = @This(); |
| ... | @@ -7,23 +6,37 @@ const RemoveDirStep = @This(); | ... | @@ -7,23 +6,37 @@ const RemoveDirStep = @This(); |
| 7 | pub const base_id = .remove_dir; | 6 | pub const base_id = .remove_dir; |
| 8 | 7 | ||
| 9 | step: Step, | 8 | step: Step, |
| 10 | builder: *std.Build, | ||
| 11 | dir_path: []const u8, | 9 | dir_path: []const u8, |
| 12 | 10 | ||
| 13 | pub fn init(builder: *std.Build, dir_path: []const u8) RemoveDirStep { | 11 | pub fn init(owner: *std.Build, dir_path: []const u8) RemoveDirStep { |
| 14 | return RemoveDirStep{ | 12 | return RemoveDirStep{ |
| 15 | .builder = builder, | 13 | .step = Step.init(.{ |
| 16 | .step = Step.init(.remove_dir, builder.fmt("RemoveDir {s}", .{dir_path}), builder.allocator, make), | 14 | .id = .remove_dir, |
| 17 | .dir_path = builder.dupePath(dir_path), | 15 | .name = owner.fmt("RemoveDir {s}", .{dir_path}), |
| 16 | .owner = owner, | ||
| 17 | .makeFn = make, | ||
| 18 | }), | ||
| 19 | .dir_path = owner.dupePath(dir_path), | ||
| 18 | }; | 20 | }; |
| 19 | } | 21 | } |
| 20 | 22 | ||
| 21 | fn make(step: *Step) !void { | 23 | fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 24 | // TODO update progress node while walking file system. | ||
| 25 | // Should the standard library support this use case?? | ||
| 26 | _ = prog_node; | ||
| 27 | |||
| 28 | const b = step.owner; | ||
| 22 | const self = @fieldParentPtr(RemoveDirStep, "step", step); | 29 | const self = @fieldParentPtr(RemoveDirStep, "step", step); |
| 23 | 30 | ||
| 24 | const full_path = self.builder.pathFromRoot(self.dir_path); | 31 | b.build_root.handle.deleteTree(self.dir_path) catch |err| { |
| 25 | fs.cwd().deleteTree(full_path) catch |err| { | 32 | if (b.build_root.path) |base| { |
| 26 | log.err("Unable to remove {s}: {s}", .{ full_path, @errorName(err) }); | 33 | return step.fail("unable to recursively delete path '{s}/{s}': {s}", .{ |
| 27 | return err; | 34 | base, self.dir_path, @errorName(err), |
| 35 | }); | ||
| 36 | } else { | ||
| 37 | return step.fail("unable to recursively delete path '{s}': {s}", .{ | ||
| 38 | self.dir_path, @errorName(err), | ||
| 39 | }); | ||
| 40 | } | ||
| 28 | }; | 41 | }; |
| 29 | } | 42 | } |
lib/std/Build/RunStep.zig+975-271| ... | @@ -10,76 +10,136 @@ const ArrayList = std.ArrayList; | ... | @@ -10,76 +10,136 @@ const ArrayList = std.ArrayList; |
| 10 | const EnvMap = process.EnvMap; | 10 | const EnvMap = process.EnvMap; |
| 11 | const Allocator = mem.Allocator; | 11 | const Allocator = mem.Allocator; |
| 12 | const ExecError = std.Build.ExecError; | 12 | const ExecError = std.Build.ExecError; |
| 13 | 13 | const assert = std.debug.assert; | |
| 14 | const max_stdout_size = 1 * 1024 * 1024; // 1 MiB | ||
| 15 | 14 | ||
| 16 | const RunStep = @This(); | 15 | const RunStep = @This(); |
| 17 | 16 | ||
| 18 | pub const base_id: Step.Id = .run; | 17 | pub const base_id: Step.Id = .run; |
| 19 | 18 | ||
| 20 | step: Step, | 19 | step: Step, |
| 21 | builder: *std.Build, | ||
| 22 | 20 | ||
| 23 | /// See also addArg and addArgs to modifying this directly | 21 | /// See also addArg and addArgs to modifying this directly |
| 24 | argv: ArrayList(Arg), | 22 | argv: ArrayList(Arg), |
| 25 | 23 | ||
| 26 | /// Set this to modify the current working directory | 24 | /// Set this to modify the current working directory |
| 25 | /// TODO change this to a Build.Cache.Directory to better integrate with | ||
| 26 | /// future child process cwd API. | ||
| 27 | cwd: ?[]const u8, | 27 | cwd: ?[]const u8, |
| 28 | 28 | ||
| 29 | /// Override this field to modify the environment, or use setEnvironmentVariable | 29 | /// Override this field to modify the environment, or use setEnvironmentVariable |
| 30 | env_map: ?*EnvMap, | 30 | env_map: ?*EnvMap, |
| 31 | 31 | ||
| 32 | stdout_action: StdIoAction = .inherit, | 32 | /// Configures whether the RunStep is considered to have side-effects, and also |
| 33 | stderr_action: StdIoAction = .inherit, | 33 | /// whether the RunStep will inherit stdio streams, forwarding them to the |
| 34 | 34 | /// parent process, in which case will require a global lock to prevent other | |
| 35 | stdin_behavior: std.ChildProcess.StdIo = .Inherit, | 35 | /// steps from interfering with stdio while the subprocess associated with this |
| 36 | 36 | /// RunStep is running. | |
| 37 | /// Set this to `null` to ignore the exit code for the purpose of determining a successful execution | 37 | /// If the RunStep is determined to not have side-effects, then execution will |
| 38 | expected_term: ?std.ChildProcess.Term = .{ .Exited = 0 }, | 38 | /// be skipped if all output files are up-to-date and input files are |
| 39 | 39 | /// unchanged. | |
| 40 | /// Print the command before running it | 40 | stdio: StdIo = .infer_from_args, |
| 41 | print: bool, | 41 | /// This field must be `null` if stdio is `inherit`. |
| 42 | /// Controls whether execution is skipped if the output file is up-to-date. | 42 | stdin: ?[]const u8 = null, |
| 43 | /// The default is to always run if there is no output file, and to skip | ||
| 44 | /// running if all output files are up-to-date. | ||
| 45 | condition: enum { output_outdated, always } = .output_outdated, | ||
| 46 | 43 | ||
| 47 | /// Additional file paths relative to build.zig that, when modified, indicate | 44 | /// Additional file paths relative to build.zig that, when modified, indicate |
| 48 | /// that the RunStep should be re-executed. | 45 | /// that the RunStep should be re-executed. |
| 46 | /// If the RunStep is determined to have side-effects, this field is ignored | ||
| 47 | /// and the RunStep is always executed when it appears in the build graph. | ||
| 49 | extra_file_dependencies: []const []const u8 = &.{}, | 48 | extra_file_dependencies: []const []const u8 = &.{}, |
| 50 | 49 | ||
| 51 | pub const StdIoAction = union(enum) { | 50 | /// After adding an output argument, this step will by default rename itself |
| 51 | /// for a better display name in the build summary. | ||
| 52 | /// This can be disabled by setting this to false. | ||
| 53 | rename_step_with_output_arg: bool = true, | ||
| 54 | |||
| 55 | /// If this is true, a RunStep which is configured to check the output of the | ||
| 56 | /// executed binary will not fail the build if the binary cannot be executed | ||
| 57 | /// due to being for a foreign binary to the host system which is running the | ||
| 58 | /// build graph. | ||
| 59 | /// Command-line arguments such as -fqemu and -fwasmtime may affect whether a | ||
| 60 | /// binary is detected as foreign, as well as system configuration such as | ||
| 61 | /// Rosetta (macOS) and binfmt_misc (Linux). | ||
| 62 | /// If this RunStep is considered to have side-effects, then this flag does | ||
| 63 | /// nothing. | ||
| 64 | skip_foreign_checks: bool = false, | ||
| 65 | |||
| 66 | /// If stderr or stdout exceeds this amount, the child process is killed and | ||
| 67 | /// the step fails. | ||
| 68 | max_stdio_size: usize = 10 * 1024 * 1024, | ||
| 69 | |||
| 70 | captured_stdout: ?*Output = null, | ||
| 71 | captured_stderr: ?*Output = null, | ||
| 72 | |||
| 73 | has_side_effects: bool = false, | ||
| 74 | |||
| 75 | pub const StdIo = union(enum) { | ||
| 76 | /// Whether the RunStep has side-effects will be determined by whether or not one | ||
| 77 | /// of the args is an output file (added with `addOutputFileArg`). | ||
| 78 | /// If the RunStep is determined to have side-effects, this is the same as `inherit`. | ||
| 79 | /// The step will fail if the subprocess crashes or returns a non-zero exit code. | ||
| 80 | infer_from_args, | ||
| 81 | /// Causes the RunStep to be considered to have side-effects, and therefore | ||
| 82 | /// always execute when it appears in the build graph. | ||
| 83 | /// It also means that this step will obtain a global lock to prevent other | ||
| 84 | /// steps from running in the meantime. | ||
| 85 | /// The step will fail if the subprocess crashes or returns a non-zero exit code. | ||
| 52 | inherit, | 86 | inherit, |
| 53 | ignore, | 87 | /// Causes the RunStep to be considered to *not* have side-effects. The |
| 54 | expect_exact: []const u8, | 88 | /// process will be re-executed if any of the input dependencies are |
| 55 | expect_matches: []const []const u8, | 89 | /// modified. The exit code and standard I/O streams will be checked for |
| 90 | /// certain conditions, and the step will succeed or fail based on these | ||
| 91 | /// conditions. | ||
| 92 | /// Note that an explicit check for exit code 0 needs to be added to this | ||
| 93 | /// list if such a check is desireable. | ||
| 94 | check: std.ArrayList(Check), | ||
| 95 | /// This RunStep is running a zig unit test binary and will communicate | ||
| 96 | /// extra metadata over the IPC protocol. | ||
| 97 | zig_test, | ||
| 98 | |||
| 99 | pub const Check = union(enum) { | ||
| 100 | expect_stderr_exact: []const u8, | ||
| 101 | expect_stderr_match: []const u8, | ||
| 102 | expect_stdout_exact: []const u8, | ||
| 103 | expect_stdout_match: []const u8, | ||
| 104 | expect_term: std.process.Child.Term, | ||
| 105 | }; | ||
| 56 | }; | 106 | }; |
| 57 | 107 | ||
| 58 | pub const Arg = union(enum) { | 108 | pub const Arg = union(enum) { |
| 59 | artifact: *CompileStep, | 109 | artifact: *CompileStep, |
| 60 | file_source: std.Build.FileSource, | 110 | file_source: std.Build.FileSource, |
| 111 | directory_source: std.Build.FileSource, | ||
| 61 | bytes: []u8, | 112 | bytes: []u8, |
| 62 | output: Output, | 113 | output: *Output, |
| 114 | }; | ||
| 63 | 115 | ||
| 64 | pub const Output = struct { | 116 | pub const Output = struct { |
| 65 | generated_file: *std.Build.GeneratedFile, | 117 | generated_file: std.Build.GeneratedFile, |
| 66 | basename: []const u8, | 118 | prefix: []const u8, |
| 67 | }; | 119 | basename: []const u8, |
| 68 | }; | 120 | }; |
| 69 | 121 | ||
| 70 | pub fn create(builder: *std.Build, name: []const u8) *RunStep { | 122 | pub fn create(owner: *std.Build, name: []const u8) *RunStep { |
| 71 | const self = builder.allocator.create(RunStep) catch @panic("OOM"); | 123 | const self = owner.allocator.create(RunStep) catch @panic("OOM"); |
| 72 | self.* = RunStep{ | 124 | self.* = .{ |
| 73 | .builder = builder, | 125 | .step = Step.init(.{ |
| 74 | .step = Step.init(base_id, name, builder.allocator, make), | 126 | .id = base_id, |
| 75 | .argv = ArrayList(Arg).init(builder.allocator), | 127 | .name = name, |
| 128 | .owner = owner, | ||
| 129 | .makeFn = make, | ||
| 130 | }), | ||
| 131 | .argv = ArrayList(Arg).init(owner.allocator), | ||
| 76 | .cwd = null, | 132 | .cwd = null, |
| 77 | .env_map = null, | 133 | .env_map = null, |
| 78 | .print = builder.verbose, | ||
| 79 | }; | 134 | }; |
| 80 | return self; | 135 | return self; |
| 81 | } | 136 | } |
| 82 | 137 | ||
| 138 | pub fn setName(self: *RunStep, name: []const u8) void { | ||
| 139 | self.step.name = name; | ||
| 140 | self.rename_step_with_output_arg = false; | ||
| 141 | } | ||
| 142 | |||
| 83 | pub fn addArtifactArg(self: *RunStep, artifact: *CompileStep) void { | 143 | pub fn addArtifactArg(self: *RunStep, artifact: *CompileStep) void { |
| 84 | self.argv.append(Arg{ .artifact = artifact }) catch @panic("OOM"); | 144 | self.argv.append(Arg{ .artifact = artifact }) catch @panic("OOM"); |
| 85 | self.step.dependOn(&artifact.step); | 145 | self.step.dependOn(&artifact.step); |
| ... | @@ -89,25 +149,47 @@ pub fn addArtifactArg(self: *RunStep, artifact: *CompileStep) void { | ... | @@ -89,25 +149,47 @@ pub fn addArtifactArg(self: *RunStep, artifact: *CompileStep) void { |
| 89 | /// run, and returns a FileSource which can be used as inputs to other APIs | 149 | /// run, and returns a FileSource which can be used as inputs to other APIs |
| 90 | /// throughout the build system. | 150 | /// throughout the build system. |
| 91 | pub fn addOutputFileArg(rs: *RunStep, basename: []const u8) std.Build.FileSource { | 151 | pub fn addOutputFileArg(rs: *RunStep, basename: []const u8) std.Build.FileSource { |
| 92 | const generated_file = rs.builder.allocator.create(std.Build.GeneratedFile) catch @panic("OOM"); | 152 | return addPrefixedOutputFileArg(rs, "", basename); |
| 93 | generated_file.* = .{ .step = &rs.step }; | 153 | } |
| 94 | rs.argv.append(.{ .output = .{ | ||
| 95 | .generated_file = generated_file, | ||
| 96 | .basename = rs.builder.dupe(basename), | ||
| 97 | } }) catch @panic("OOM"); | ||
| 98 | 154 | ||
| 99 | return .{ .generated = generated_file }; | 155 | pub fn addPrefixedOutputFileArg( |
| 156 | rs: *RunStep, | ||
| 157 | prefix: []const u8, | ||
| 158 | basename: []const u8, | ||
| 159 | ) std.Build.FileSource { | ||
| 160 | const b = rs.step.owner; | ||
| 161 | |||
| 162 | const output = b.allocator.create(Output) catch @panic("OOM"); | ||
| 163 | output.* = .{ | ||
| 164 | .prefix = prefix, | ||
| 165 | .basename = basename, | ||
| 166 | .generated_file = .{ .step = &rs.step }, | ||
| 167 | }; | ||
| 168 | rs.argv.append(.{ .output = output }) catch @panic("OOM"); | ||
| 169 | |||
| 170 | if (rs.rename_step_with_output_arg) { | ||
| 171 | rs.setName(b.fmt("{s} ({s})", .{ rs.step.name, basename })); | ||
| 172 | } | ||
| 173 | |||
| 174 | return .{ .generated = &output.generated_file }; | ||
| 100 | } | 175 | } |
| 101 | 176 | ||
| 102 | pub fn addFileSourceArg(self: *RunStep, file_source: std.Build.FileSource) void { | 177 | pub fn addFileSourceArg(self: *RunStep, file_source: std.Build.FileSource) void { |
| 103 | self.argv.append(Arg{ | 178 | self.argv.append(.{ |
| 104 | .file_source = file_source.dupe(self.builder), | 179 | .file_source = file_source.dupe(self.step.owner), |
| 105 | }) catch @panic("OOM"); | 180 | }) catch @panic("OOM"); |
| 106 | file_source.addStepDependencies(&self.step); | 181 | file_source.addStepDependencies(&self.step); |
| 107 | } | 182 | } |
| 108 | 183 | ||
| 184 | pub fn addDirectorySourceArg(self: *RunStep, directory_source: std.Build.FileSource) void { | ||
| 185 | self.argv.append(.{ | ||
| 186 | .directory_source = directory_source.dupe(self.step.owner), | ||
| 187 | }) catch @panic("OOM"); | ||
| 188 | directory_source.addStepDependencies(&self.step); | ||
| 189 | } | ||
| 190 | |||
| 109 | pub fn addArg(self: *RunStep, arg: []const u8) void { | 191 | pub fn addArg(self: *RunStep, arg: []const u8) void { |
| 110 | self.argv.append(Arg{ .bytes = self.builder.dupe(arg) }) catch @panic("OOM"); | 192 | self.argv.append(.{ .bytes = self.step.owner.dupe(arg) }) catch @panic("OOM"); |
| 111 | } | 193 | } |
| 112 | 194 | ||
| 113 | pub fn addArgs(self: *RunStep, args: []const []const u8) void { | 195 | pub fn addArgs(self: *RunStep, args: []const []const u8) void { |
| ... | @@ -117,102 +199,183 @@ pub fn addArgs(self: *RunStep, args: []const []const u8) void { | ... | @@ -117,102 +199,183 @@ pub fn addArgs(self: *RunStep, args: []const []const u8) void { |
| 117 | } | 199 | } |
| 118 | 200 | ||
| 119 | pub fn clearEnvironment(self: *RunStep) void { | 201 | pub fn clearEnvironment(self: *RunStep) void { |
| 120 | const new_env_map = self.builder.allocator.create(EnvMap) catch @panic("OOM"); | 202 | const b = self.step.owner; |
| 121 | new_env_map.* = EnvMap.init(self.builder.allocator); | 203 | const new_env_map = b.allocator.create(EnvMap) catch @panic("OOM"); |
| 204 | new_env_map.* = EnvMap.init(b.allocator); | ||
| 122 | self.env_map = new_env_map; | 205 | self.env_map = new_env_map; |
| 123 | } | 206 | } |
| 124 | 207 | ||
| 125 | pub fn addPathDir(self: *RunStep, search_path: []const u8) void { | 208 | pub fn addPathDir(self: *RunStep, search_path: []const u8) void { |
| 126 | addPathDirInternal(&self.step, self.builder, search_path); | 209 | const b = self.step.owner; |
| 127 | } | 210 | const env_map = getEnvMapInternal(self); |
| 128 | |||
| 129 | /// For internal use only, users of `RunStep` should use `addPathDir` directly. | ||
| 130 | pub fn addPathDirInternal(step: *Step, builder: *std.Build, search_path: []const u8) void { | ||
| 131 | const env_map = getEnvMapInternal(step, builder.allocator); | ||
| 132 | 211 | ||
| 133 | const key = "PATH"; | 212 | const key = "PATH"; |
| 134 | var prev_path = env_map.get(key); | 213 | var prev_path = env_map.get(key); |
| 135 | 214 | ||
| 136 | if (prev_path) |pp| { | 215 | if (prev_path) |pp| { |
| 137 | const new_path = builder.fmt("{s}" ++ [1]u8{fs.path.delimiter} ++ "{s}", .{ pp, search_path }); | 216 | const new_path = b.fmt("{s}" ++ [1]u8{fs.path.delimiter} ++ "{s}", .{ pp, search_path }); |
| 138 | env_map.put(key, new_path) catch @panic("OOM"); | 217 | env_map.put(key, new_path) catch @panic("OOM"); |
| 139 | } else { | 218 | } else { |
| 140 | env_map.put(key, builder.dupePath(search_path)) catch @panic("OOM"); | 219 | env_map.put(key, b.dupePath(search_path)) catch @panic("OOM"); |
| 141 | } | 220 | } |
| 142 | } | 221 | } |
| 143 | 222 | ||
| 144 | pub fn getEnvMap(self: *RunStep) *EnvMap { | 223 | pub fn getEnvMap(self: *RunStep) *EnvMap { |
| 145 | return getEnvMapInternal(&self.step, self.builder.allocator); | 224 | return getEnvMapInternal(self); |
| 146 | } | 225 | } |
| 147 | 226 | ||
| 148 | fn getEnvMapInternal(step: *Step, allocator: Allocator) *EnvMap { | 227 | fn getEnvMapInternal(self: *RunStep) *EnvMap { |
| 149 | const maybe_env_map = switch (step.id) { | 228 | const arena = self.step.owner.allocator; |
| 150 | .run => step.cast(RunStep).?.env_map, | 229 | return self.env_map orelse { |
| 151 | .emulatable_run => step.cast(std.Build.EmulatableRunStep).?.env_map, | 230 | const env_map = arena.create(EnvMap) catch @panic("OOM"); |
| 152 | else => unreachable, | 231 | env_map.* = process.getEnvMap(arena) catch @panic("unhandled error"); |
| 153 | }; | 232 | self.env_map = env_map; |
| 154 | return maybe_env_map orelse { | ||
| 155 | const env_map = allocator.create(EnvMap) catch @panic("OOM"); | ||
| 156 | env_map.* = process.getEnvMap(allocator) catch @panic("unhandled error"); | ||
| 157 | switch (step.id) { | ||
| 158 | .run => step.cast(RunStep).?.env_map = env_map, | ||
| 159 | .emulatable_run => step.cast(RunStep).?.env_map = env_map, | ||
| 160 | else => unreachable, | ||
| 161 | } | ||
| 162 | return env_map; | 233 | return env_map; |
| 163 | }; | 234 | }; |
| 164 | } | 235 | } |
| 165 | 236 | ||
| 166 | pub fn setEnvironmentVariable(self: *RunStep, key: []const u8, value: []const u8) void { | 237 | pub fn setEnvironmentVariable(self: *RunStep, key: []const u8, value: []const u8) void { |
| 238 | const b = self.step.owner; | ||
| 167 | const env_map = self.getEnvMap(); | 239 | const env_map = self.getEnvMap(); |
| 168 | env_map.put( | 240 | env_map.put(b.dupe(key), b.dupe(value)) catch @panic("unhandled error"); |
| 169 | self.builder.dupe(key), | 241 | } |
| 170 | self.builder.dupe(value), | 242 | |
| 171 | ) catch @panic("unhandled error"); | 243 | pub fn removeEnvironmentVariable(self: *RunStep, key: []const u8) void { |
| 244 | self.getEnvMap().remove(key); | ||
| 172 | } | 245 | } |
| 173 | 246 | ||
| 247 | /// Adds a check for exact stderr match. Does not add any other checks. | ||
| 174 | pub fn expectStdErrEqual(self: *RunStep, bytes: []const u8) void { | 248 | pub fn expectStdErrEqual(self: *RunStep, bytes: []const u8) void { |
| 175 | self.stderr_action = .{ .expect_exact = self.builder.dupe(bytes) }; | 249 | const new_check: StdIo.Check = .{ .expect_stderr_exact = self.step.owner.dupe(bytes) }; |
| 250 | self.addCheck(new_check); | ||
| 176 | } | 251 | } |
| 177 | 252 | ||
| 253 | /// Adds a check for exact stdout match as well as a check for exit code 0, if | ||
| 254 | /// there is not already an expected termination check. | ||
| 178 | pub fn expectStdOutEqual(self: *RunStep, bytes: []const u8) void { | 255 | pub fn expectStdOutEqual(self: *RunStep, bytes: []const u8) void { |
| 179 | self.stdout_action = .{ .expect_exact = self.builder.dupe(bytes) }; | 256 | const new_check: StdIo.Check = .{ .expect_stdout_exact = self.step.owner.dupe(bytes) }; |
| 257 | self.addCheck(new_check); | ||
| 258 | if (!self.hasTermCheck()) { | ||
| 259 | self.expectExitCode(0); | ||
| 260 | } | ||
| 180 | } | 261 | } |
| 181 | 262 | ||
| 182 | fn stdIoActionToBehavior(action: StdIoAction) std.ChildProcess.StdIo { | 263 | pub fn expectExitCode(self: *RunStep, code: u8) void { |
| 183 | return switch (action) { | 264 | const new_check: StdIo.Check = .{ .expect_term = .{ .Exited = code } }; |
| 184 | .ignore => .Ignore, | 265 | self.addCheck(new_check); |
| 185 | .inherit => .Inherit, | 266 | } |
| 186 | .expect_exact, .expect_matches => .Pipe, | 267 | |
| 268 | pub fn hasTermCheck(self: RunStep) bool { | ||
| 269 | for (self.stdio.check.items) |check| switch (check) { | ||
| 270 | .expect_term => return true, | ||
| 271 | else => continue, | ||
| 187 | }; | 272 | }; |
| 273 | return false; | ||
| 188 | } | 274 | } |
| 189 | 275 | ||
| 190 | fn needOutputCheck(self: RunStep) bool { | 276 | pub fn addCheck(self: *RunStep, new_check: StdIo.Check) void { |
| 191 | switch (self.condition) { | 277 | switch (self.stdio) { |
| 192 | .always => return false, | 278 | .infer_from_args => { |
| 193 | .output_outdated => {}, | 279 | self.stdio = .{ .check = std.ArrayList(StdIo.Check).init(self.step.owner.allocator) }; |
| 280 | self.stdio.check.append(new_check) catch @panic("OOM"); | ||
| 281 | }, | ||
| 282 | .check => |*checks| checks.append(new_check) catch @panic("OOM"), | ||
| 283 | else => @panic("illegal call to addCheck: conflicting helper method calls. Suggest to directly set stdio field of RunStep instead"), | ||
| 194 | } | 284 | } |
| 195 | if (self.extra_file_dependencies.len > 0) return true; | 285 | } |
| 286 | |||
| 287 | pub fn captureStdErr(self: *RunStep) std.Build.FileSource { | ||
| 288 | assert(self.stdio != .inherit); | ||
| 289 | |||
| 290 | if (self.captured_stderr) |output| return .{ .generated = &output.generated_file }; | ||
| 291 | |||
| 292 | const output = self.step.owner.allocator.create(Output) catch @panic("OOM"); | ||
| 293 | output.* = .{ | ||
| 294 | .prefix = "", | ||
| 295 | .basename = "stderr", | ||
| 296 | .generated_file = .{ .step = &self.step }, | ||
| 297 | }; | ||
| 298 | self.captured_stderr = output; | ||
| 299 | return .{ .generated = &output.generated_file }; | ||
| 300 | } | ||
| 301 | |||
| 302 | pub fn captureStdOut(self: *RunStep) *std.Build.GeneratedFile { | ||
| 303 | assert(self.stdio != .inherit); | ||
| 196 | 304 | ||
| 305 | if (self.captured_stdout) |output| return .{ .generated = &output.generated_file }; | ||
| 306 | |||
| 307 | const output = self.step.owner.allocator.create(Output) catch @panic("OOM"); | ||
| 308 | output.* = .{ | ||
| 309 | .prefix = "", | ||
| 310 | .basename = "stdout", | ||
| 311 | .generated_file = .{ .step = &self.step }, | ||
| 312 | }; | ||
| 313 | self.captured_stdout = output; | ||
| 314 | return .{ .generated = &output.generated_file }; | ||
| 315 | } | ||
| 316 | |||
| 317 | /// Returns whether the RunStep has side effects *other than* updating the output arguments. | ||
| 318 | fn hasSideEffects(self: RunStep) bool { | ||
| 319 | if (self.has_side_effects) return true; | ||
| 320 | return switch (self.stdio) { | ||
| 321 | .infer_from_args => !self.hasAnyOutputArgs(), | ||
| 322 | .inherit => true, | ||
| 323 | .check => false, | ||
| 324 | .zig_test => false, | ||
| 325 | }; | ||
| 326 | } | ||
| 327 | |||
| 328 | fn hasAnyOutputArgs(self: RunStep) bool { | ||
| 329 | if (self.captured_stdout != null) return true; | ||
| 330 | if (self.captured_stderr != null) return true; | ||
| 197 | for (self.argv.items) |arg| switch (arg) { | 331 | for (self.argv.items) |arg| switch (arg) { |
| 198 | .output => return true, | 332 | .output => return true, |
| 199 | else => continue, | 333 | else => continue, |
| 200 | }; | 334 | }; |
| 335 | return false; | ||
| 336 | } | ||
| 337 | |||
| 338 | fn checksContainStdout(checks: []const StdIo.Check) bool { | ||
| 339 | for (checks) |check| switch (check) { | ||
| 340 | .expect_stderr_exact, | ||
| 341 | .expect_stderr_match, | ||
| 342 | .expect_term, | ||
| 343 | => continue, | ||
| 344 | |||
| 345 | .expect_stdout_exact, | ||
| 346 | .expect_stdout_match, | ||
| 347 | => return true, | ||
| 348 | }; | ||
| 349 | return false; | ||
| 350 | } | ||
| 201 | 351 | ||
| 352 | fn checksContainStderr(checks: []const StdIo.Check) bool { | ||
| 353 | for (checks) |check| switch (check) { | ||
| 354 | .expect_stdout_exact, | ||
| 355 | .expect_stdout_match, | ||
| 356 | .expect_term, | ||
| 357 | => continue, | ||
| 358 | |||
| 359 | .expect_stderr_exact, | ||
| 360 | .expect_stderr_match, | ||
| 361 | => return true, | ||
| 362 | }; | ||
| 202 | return false; | 363 | return false; |
| 203 | } | 364 | } |
| 204 | 365 | ||
| 205 | fn make(step: *Step) !void { | 366 | fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 367 | const b = step.owner; | ||
| 368 | const arena = b.allocator; | ||
| 206 | const self = @fieldParentPtr(RunStep, "step", step); | 369 | const self = @fieldParentPtr(RunStep, "step", step); |
| 207 | const need_output_check = self.needOutputCheck(); | 370 | const has_side_effects = self.hasSideEffects(); |
| 208 | 371 | ||
| 209 | var argv_list = ArrayList([]const u8).init(self.builder.allocator); | 372 | var argv_list = ArrayList([]const u8).init(arena); |
| 210 | var output_placeholders = ArrayList(struct { | 373 | var output_placeholders = ArrayList(struct { |
| 211 | index: usize, | 374 | index: usize, |
| 212 | output: Arg.Output, | 375 | output: *Output, |
| 213 | }).init(self.builder.allocator); | 376 | }).init(arena); |
| 214 | 377 | ||
| 215 | var man = self.builder.cache.obtain(); | 378 | var man = b.cache.obtain(); |
| 216 | defer man.deinit(); | 379 | defer man.deinit(); |
| 217 | 380 | ||
| 218 | for (self.argv.items) |arg| { | 381 | for (self.argv.items) |arg| { |
| ... | @@ -222,23 +385,29 @@ fn make(step: *Step) !void { | ... | @@ -222,23 +385,29 @@ fn make(step: *Step) !void { |
| 222 | man.hash.addBytes(bytes); | 385 | man.hash.addBytes(bytes); |
| 223 | }, | 386 | }, |
| 224 | .file_source => |file| { | 387 | .file_source => |file| { |
| 225 | const file_path = file.getPath(self.builder); | 388 | const file_path = file.getPath(b); |
| 226 | try argv_list.append(file_path); | 389 | try argv_list.append(file_path); |
| 227 | _ = try man.addFile(file_path, null); | 390 | _ = try man.addFile(file_path, null); |
| 228 | }, | 391 | }, |
| 392 | .directory_source => |file| { | ||
| 393 | const file_path = file.getPath(b); | ||
| 394 | try argv_list.append(file_path); | ||
| 395 | man.hash.addBytes(file_path); | ||
| 396 | }, | ||
| 229 | .artifact => |artifact| { | 397 | .artifact => |artifact| { |
| 230 | if (artifact.target.isWindows()) { | 398 | if (artifact.target.isWindows()) { |
| 231 | // On Windows we don't have rpaths so we have to add .dll search paths to PATH | 399 | // On Windows we don't have rpaths so we have to add .dll search paths to PATH |
| 232 | self.addPathForDynLibs(artifact); | 400 | self.addPathForDynLibs(artifact); |
| 233 | } | 401 | } |
| 234 | const file_path = artifact.installed_path orelse | 402 | const file_path = artifact.installed_path orelse |
| 235 | artifact.getOutputSource().getPath(self.builder); | 403 | artifact.getOutputSource().getPath(b); |
| 236 | 404 | ||
| 237 | try argv_list.append(file_path); | 405 | try argv_list.append(file_path); |
| 238 | 406 | ||
| 239 | _ = try man.addFile(file_path, null); | 407 | _ = try man.addFile(file_path, null); |
| 240 | }, | 408 | }, |
| 241 | .output => |output| { | 409 | .output => |output| { |
| 410 | man.hash.addBytes(output.prefix); | ||
| 242 | man.hash.addBytes(output.basename); | 411 | man.hash.addBytes(output.basename); |
| 243 | // Add a placeholder into the argument list because we need the | 412 | // Add a placeholder into the argument list because we need the |
| 244 | // manifest hash to be updated with all arguments before the | 413 | // manifest hash to be updated with all arguments before the |
| ... | @@ -252,60 +421,77 @@ fn make(step: *Step) !void { | ... | @@ -252,60 +421,77 @@ fn make(step: *Step) !void { |
| 252 | } | 421 | } |
| 253 | } | 422 | } |
| 254 | 423 | ||
| 255 | if (need_output_check) { | 424 | if (self.captured_stdout) |output| { |
| 256 | for (self.extra_file_dependencies) |file_path| { | 425 | man.hash.addBytes(output.basename); |
| 257 | _ = try man.addFile(self.builder.pathFromRoot(file_path), null); | 426 | } |
| 258 | } | ||
| 259 | 427 | ||
| 260 | if (man.hit() catch |err| failWithCacheError(man, err)) { | 428 | if (self.captured_stderr) |output| { |
| 261 | // cache hit, skip running command | 429 | man.hash.addBytes(output.basename); |
| 262 | const digest = man.final(); | 430 | } |
| 263 | for (output_placeholders.items) |placeholder| { | ||
| 264 | placeholder.output.generated_file.path = try self.builder.cache_root.join( | ||
| 265 | self.builder.allocator, | ||
| 266 | &.{ "o", &digest, placeholder.output.basename }, | ||
| 267 | ); | ||
| 268 | } | ||
| 269 | return; | ||
| 270 | } | ||
| 271 | 431 | ||
| 272 | const digest = man.final(); | 432 | hashStdIo(&man.hash, self.stdio); |
| 273 | 433 | ||
| 434 | if (has_side_effects) { | ||
| 435 | try runCommand(self, argv_list.items, has_side_effects, null, prog_node); | ||
| 436 | return; | ||
| 437 | } | ||
| 438 | |||
| 439 | for (self.extra_file_dependencies) |file_path| { | ||
| 440 | _ = try man.addFile(b.pathFromRoot(file_path), null); | ||
| 441 | } | ||
| 442 | |||
| 443 | if (try step.cacheHit(&man)) { | ||
| 444 | // cache hit, skip running command | ||
| 445 | const digest = man.final(); | ||
| 274 | for (output_placeholders.items) |placeholder| { | 446 | for (output_placeholders.items) |placeholder| { |
| 275 | const output_path = try self.builder.cache_root.join( | 447 | placeholder.output.generated_file.path = try b.cache_root.join(arena, &.{ |
| 276 | self.builder.allocator, | 448 | "o", &digest, placeholder.output.basename, |
| 277 | &.{ "o", &digest, placeholder.output.basename }, | 449 | }); |
| 278 | ); | 450 | } |
| 279 | const output_dir = fs.path.dirname(output_path).?; | 451 | |
| 280 | fs.cwd().makePath(output_dir) catch |err| { | 452 | if (self.captured_stdout) |output| { |
| 281 | std.debug.print("unable to make path {s}: {s}\n", .{ output_dir, @errorName(err) }); | 453 | output.generated_file.path = try b.cache_root.join(arena, &.{ |
| 282 | return err; | 454 | "o", &digest, output.basename, |
| 283 | }; | 455 | }); |
| 456 | } | ||
| 284 | 457 | ||
| 285 | placeholder.output.generated_file.path = output_path; | 458 | if (self.captured_stderr) |output| { |
| 286 | argv_list.items[placeholder.index] = output_path; | 459 | output.generated_file.path = try b.cache_root.join(arena, &.{ |
| 460 | "o", &digest, output.basename, | ||
| 461 | }); | ||
| 287 | } | 462 | } |
| 463 | |||
| 464 | step.result_cached = true; | ||
| 465 | return; | ||
| 288 | } | 466 | } |
| 289 | 467 | ||
| 290 | try runCommand( | 468 | const digest = man.final(); |
| 291 | argv_list.items, | 469 | |
| 292 | self.builder, | 470 | for (output_placeholders.items) |placeholder| { |
| 293 | self.expected_term, | 471 | const output_components = .{ "o", &digest, placeholder.output.basename }; |
| 294 | self.stdout_action, | 472 | const output_sub_path = try fs.path.join(arena, &output_components); |
| 295 | self.stderr_action, | 473 | const output_sub_dir_path = fs.path.dirname(output_sub_path).?; |
| 296 | self.stdin_behavior, | 474 | b.cache_root.handle.makePath(output_sub_dir_path) catch |err| { |
| 297 | self.env_map, | 475 | return step.fail("unable to make path '{}{s}': {s}", .{ |
| 298 | self.cwd, | 476 | b.cache_root, output_sub_dir_path, @errorName(err), |
| 299 | self.print, | 477 | }); |
| 300 | ); | 478 | }; |
| 301 | 479 | const output_path = try b.cache_root.join(arena, &output_components); | |
| 302 | if (need_output_check) { | 480 | placeholder.output.generated_file.path = output_path; |
| 303 | try man.writeManifest(); | 481 | const cli_arg = if (placeholder.output.prefix.len == 0) |
| 482 | output_path | ||
| 483 | else | ||
| 484 | b.fmt("{s}{s}", .{ placeholder.output.prefix, output_path }); | ||
| 485 | argv_list.items[placeholder.index] = cli_arg; | ||
| 304 | } | 486 | } |
| 487 | |||
| 488 | try runCommand(self, argv_list.items, has_side_effects, &digest, prog_node); | ||
| 489 | |||
| 490 | try step.writeManifest(&man); | ||
| 305 | } | 491 | } |
| 306 | 492 | ||
| 307 | fn formatTerm( | 493 | fn formatTerm( |
| 308 | term: ?std.ChildProcess.Term, | 494 | term: ?std.process.Child.Term, |
| 309 | comptime fmt: []const u8, | 495 | comptime fmt: []const u8, |
| 310 | options: std.fmt.FormatOptions, | 496 | options: std.fmt.FormatOptions, |
| 311 | writer: anytype, | 497 | writer: anytype, |
| ... | @@ -321,11 +507,11 @@ fn formatTerm( | ... | @@ -321,11 +507,11 @@ fn formatTerm( |
| 321 | try writer.writeAll("exited with any code"); | 507 | try writer.writeAll("exited with any code"); |
| 322 | } | 508 | } |
| 323 | } | 509 | } |
| 324 | fn fmtTerm(term: ?std.ChildProcess.Term) std.fmt.Formatter(formatTerm) { | 510 | fn fmtTerm(term: ?std.process.Child.Term) std.fmt.Formatter(formatTerm) { |
| 325 | return .{ .data = term }; | 511 | return .{ .data = term }; |
| 326 | } | 512 | } |
| 327 | 513 | ||
| 328 | fn termMatches(expected: ?std.ChildProcess.Term, actual: std.ChildProcess.Term) bool { | 514 | fn termMatches(expected: ?std.process.Child.Term, actual: std.process.Child.Term) bool { |
| 329 | return if (expected) |e| switch (e) { | 515 | return if (expected) |e| switch (e) { |
| 330 | .Exited => |expected_code| switch (actual) { | 516 | .Exited => |expected_code| switch (actual) { |
| 331 | .Exited => |actual_code| expected_code == actual_code, | 517 | .Exited => |actual_code| expected_code == actual_code, |
| ... | @@ -349,183 +535,701 @@ fn termMatches(expected: ?std.ChildProcess.Term, actual: std.ChildProcess.Term) | ... | @@ -349,183 +535,701 @@ fn termMatches(expected: ?std.ChildProcess.Term, actual: std.ChildProcess.Term) |
| 349 | }; | 535 | }; |
| 350 | } | 536 | } |
| 351 | 537 | ||
| 352 | pub fn runCommand( | 538 | fn runCommand( |
| 539 | self: *RunStep, | ||
| 353 | argv: []const []const u8, | 540 | argv: []const []const u8, |
| 354 | builder: *std.Build, | 541 | has_side_effects: bool, |
| 355 | expected_term: ?std.ChildProcess.Term, | 542 | digest: ?*const [std.Build.Cache.hex_digest_len]u8, |
| 356 | stdout_action: StdIoAction, | 543 | prog_node: *std.Progress.Node, |
| 357 | stderr_action: StdIoAction, | ||
| 358 | stdin_behavior: std.ChildProcess.StdIo, | ||
| 359 | env_map: ?*EnvMap, | ||
| 360 | maybe_cwd: ?[]const u8, | ||
| 361 | print: bool, | ||
| 362 | ) !void { | 544 | ) !void { |
| 363 | const cwd = if (maybe_cwd) |cwd| builder.pathFromRoot(cwd) else builder.build_root.path; | 545 | const step = &self.step; |
| 364 | 546 | const b = step.owner; | |
| 365 | if (!std.process.can_spawn) { | 547 | const arena = b.allocator; |
| 366 | const cmd = try std.mem.join(builder.allocator, " ", argv); | 548 | |
| 367 | std.debug.print("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{ | 549 | try step.handleChildProcUnsupported(self.cwd, argv); |
| 368 | @tagName(builtin.os.tag), cmd, | 550 | try Step.handleVerbose2(step.owner, self.cwd, self.env_map, argv); |
| 369 | }); | 551 | |
| 370 | builder.allocator.free(cmd); | 552 | const allow_skip = switch (self.stdio) { |
| 371 | return ExecError.ExecNotSupported; | 553 | .check, .zig_test => self.skip_foreign_checks, |
| 372 | } | 554 | else => false, |
| 555 | }; | ||
| 556 | |||
| 557 | var interp_argv = std.ArrayList([]const u8).init(b.allocator); | ||
| 558 | defer interp_argv.deinit(); | ||
| 559 | |||
| 560 | const result = spawnChildAndCollect(self, argv, has_side_effects, prog_node) catch |err| term: { | ||
| 561 | // InvalidExe: cpu arch mismatch | ||
| 562 | // FileNotFound: can happen with a wrong dynamic linker path | ||
| 563 | if (err == error.InvalidExe or err == error.FileNotFound) interpret: { | ||
| 564 | // TODO: learn the target from the binary directly rather than from | ||
| 565 | // relying on it being a CompileStep. This will make this logic | ||
| 566 | // work even for the edge case that the binary was produced by a | ||
| 567 | // third party. | ||
| 568 | const exe = switch (self.argv.items[0]) { | ||
| 569 | .artifact => |exe| exe, | ||
| 570 | else => break :interpret, | ||
| 571 | }; | ||
| 572 | switch (exe.kind) { | ||
| 573 | .exe, .@"test" => {}, | ||
| 574 | else => break :interpret, | ||
| 575 | } | ||
| 576 | |||
| 577 | const need_cross_glibc = exe.target.isGnuLibC() and exe.is_linking_libc; | ||
| 578 | switch (b.host.getExternalExecutor(exe.target_info, .{ | ||
| 579 | .qemu_fixes_dl = need_cross_glibc and b.glibc_runtimes_dir != null, | ||
| 580 | .link_libc = exe.is_linking_libc, | ||
| 581 | })) { | ||
| 582 | .native, .rosetta => { | ||
| 583 | if (allow_skip) return error.MakeSkipped; | ||
| 584 | break :interpret; | ||
| 585 | }, | ||
| 586 | .wine => |bin_name| { | ||
| 587 | if (b.enable_wine) { | ||
| 588 | try interp_argv.append(bin_name); | ||
| 589 | try interp_argv.appendSlice(argv); | ||
| 590 | } else { | ||
| 591 | return failForeign(self, "-fwine", argv[0], exe); | ||
| 592 | } | ||
| 593 | }, | ||
| 594 | .qemu => |bin_name| { | ||
| 595 | if (b.enable_qemu) { | ||
| 596 | const glibc_dir_arg = if (need_cross_glibc) | ||
| 597 | b.glibc_runtimes_dir orelse return | ||
| 598 | else | ||
| 599 | null; | ||
| 600 | |||
| 601 | try interp_argv.append(bin_name); | ||
| 602 | |||
| 603 | if (glibc_dir_arg) |dir| { | ||
| 604 | // TODO look into making this a call to `linuxTriple`. This | ||
| 605 | // needs the directory to be called "i686" rather than | ||
| 606 | // "x86" which is why we do it manually here. | ||
| 607 | const fmt_str = "{s}" ++ fs.path.sep_str ++ "{s}-{s}-{s}"; | ||
| 608 | const cpu_arch = exe.target.getCpuArch(); | ||
| 609 | const os_tag = exe.target.getOsTag(); | ||
| 610 | const abi = exe.target.getAbi(); | ||
| 611 | const cpu_arch_name: []const u8 = if (cpu_arch == .x86) | ||
| 612 | "i686" | ||
| 613 | else | ||
| 614 | @tagName(cpu_arch); | ||
| 615 | const full_dir = try std.fmt.allocPrint(b.allocator, fmt_str, .{ | ||
| 616 | dir, cpu_arch_name, @tagName(os_tag), @tagName(abi), | ||
| 617 | }); | ||
| 618 | |||
| 619 | try interp_argv.append("-L"); | ||
| 620 | try interp_argv.append(full_dir); | ||
| 621 | } | ||
| 622 | |||
| 623 | try interp_argv.appendSlice(argv); | ||
| 624 | } else { | ||
| 625 | return failForeign(self, "-fqemu", argv[0], exe); | ||
| 626 | } | ||
| 627 | }, | ||
| 628 | .darling => |bin_name| { | ||
| 629 | if (b.enable_darling) { | ||
| 630 | try interp_argv.append(bin_name); | ||
| 631 | try interp_argv.appendSlice(argv); | ||
| 632 | } else { | ||
| 633 | return failForeign(self, "-fdarling", argv[0], exe); | ||
| 634 | } | ||
| 635 | }, | ||
| 636 | .wasmtime => |bin_name| { | ||
| 637 | if (b.enable_wasmtime) { | ||
| 638 | try interp_argv.append(bin_name); | ||
| 639 | try interp_argv.append("--dir=."); | ||
| 640 | try interp_argv.append(argv[0]); | ||
| 641 | try interp_argv.append("--"); | ||
| 642 | try interp_argv.appendSlice(argv[1..]); | ||
| 643 | } else { | ||
| 644 | return failForeign(self, "-fwasmtime", argv[0], exe); | ||
| 645 | } | ||
| 646 | }, | ||
| 647 | .bad_dl => |foreign_dl| { | ||
| 648 | if (allow_skip) return error.MakeSkipped; | ||
| 649 | |||
| 650 | const host_dl = b.host.dynamic_linker.get() orelse "(none)"; | ||
| 651 | |||
| 652 | return step.fail( | ||
| 653 | \\the host system is unable to execute binaries from the target | ||
| 654 | \\ because the host dynamic linker is '{s}', | ||
| 655 | \\ while the target dynamic linker is '{s}'. | ||
| 656 | \\ consider setting the dynamic linker or enabling skip_foreign_checks in the Run step | ||
| 657 | , .{ host_dl, foreign_dl }); | ||
| 658 | }, | ||
| 659 | .bad_os_or_cpu => { | ||
| 660 | if (allow_skip) return error.MakeSkipped; | ||
| 661 | |||
| 662 | const host_name = try b.host.target.zigTriple(b.allocator); | ||
| 663 | const foreign_name = try exe.target.zigTriple(b.allocator); | ||
| 664 | |||
| 665 | return step.fail("the host system ({s}) is unable to execute binaries from the target ({s})", .{ | ||
| 666 | host_name, foreign_name, | ||
| 667 | }); | ||
| 668 | }, | ||
| 669 | } | ||
| 373 | 670 | ||
| 374 | var child = std.ChildProcess.init(argv, builder.allocator); | 671 | if (exe.target.isWindows()) { |
| 375 | child.cwd = cwd; | 672 | // On Windows we don't have rpaths so we have to add .dll search paths to PATH |
| 376 | child.env_map = env_map orelse builder.env_map; | 673 | self.addPathForDynLibs(exe); |
| 674 | } | ||
| 377 | 675 | ||
| 378 | child.stdin_behavior = stdin_behavior; | 676 | try Step.handleVerbose2(step.owner, self.cwd, self.env_map, interp_argv.items); |
| 379 | child.stdout_behavior = stdIoActionToBehavior(stdout_action); | ||
| 380 | child.stderr_behavior = stdIoActionToBehavior(stderr_action); | ||
| 381 | 677 | ||
| 382 | if (print) | 678 | break :term spawnChildAndCollect(self, interp_argv.items, has_side_effects, prog_node) catch |e| { |
| 383 | printCmd(cwd, argv); | 679 | return step.fail("unable to spawn interpreter {s}: {s}", .{ |
| 680 | interp_argv.items[0], @errorName(e), | ||
| 681 | }); | ||
| 682 | }; | ||
| 683 | } | ||
| 384 | 684 | ||
| 385 | child.spawn() catch |err| { | 685 | return step.fail("unable to spawn {s}: {s}", .{ argv[0], @errorName(err) }); |
| 386 | std.debug.print("Unable to spawn {s}: {s}\n", .{ argv[0], @errorName(err) }); | ||
| 387 | return err; | ||
| 388 | }; | 686 | }; |
| 389 | 687 | ||
| 390 | // TODO need to poll to read these streams to prevent a deadlock (or rely on evented I/O). | 688 | step.result_duration_ns = result.elapsed_ns; |
| 689 | step.result_peak_rss = result.peak_rss; | ||
| 690 | step.test_results = result.stdio.test_results; | ||
| 391 | 691 | ||
| 392 | var stdout: ?[]const u8 = null; | 692 | // Capture stdout and stderr to GeneratedFile objects. |
| 393 | defer if (stdout) |s| builder.allocator.free(s); | 693 | const Stream = struct { |
| 694 | captured: ?*Output, | ||
| 695 | is_null: bool, | ||
| 696 | bytes: []const u8, | ||
| 697 | }; | ||
| 698 | for ([_]Stream{ | ||
| 699 | .{ | ||
| 700 | .captured = self.captured_stdout, | ||
| 701 | .is_null = result.stdio.stdout_null, | ||
| 702 | .bytes = result.stdio.stdout, | ||
| 703 | }, | ||
| 704 | .{ | ||
| 705 | .captured = self.captured_stderr, | ||
| 706 | .is_null = result.stdio.stderr_null, | ||
| 707 | .bytes = result.stdio.stderr, | ||
| 708 | }, | ||
| 709 | }) |stream| { | ||
| 710 | if (stream.captured) |output| { | ||
| 711 | assert(!stream.is_null); | ||
| 712 | |||
| 713 | const output_components = .{ "o", digest.?, output.basename }; | ||
| 714 | const output_path = try b.cache_root.join(arena, &output_components); | ||
| 715 | output.generated_file.path = output_path; | ||
| 716 | |||
| 717 | const sub_path = try fs.path.join(arena, &output_components); | ||
| 718 | const sub_path_dirname = fs.path.dirname(sub_path).?; | ||
| 719 | b.cache_root.handle.makePath(sub_path_dirname) catch |err| { | ||
| 720 | return step.fail("unable to make path '{}{s}': {s}", .{ | ||
| 721 | b.cache_root, sub_path_dirname, @errorName(err), | ||
| 722 | }); | ||
| 723 | }; | ||
| 724 | b.cache_root.handle.writeFile(sub_path, stream.bytes) catch |err| { | ||
| 725 | return step.fail("unable to write file '{}{s}': {s}", .{ | ||
| 726 | b.cache_root, sub_path, @errorName(err), | ||
| 727 | }); | ||
| 728 | }; | ||
| 729 | } | ||
| 730 | } | ||
| 394 | 731 | ||
| 395 | switch (stdout_action) { | 732 | const final_argv = if (interp_argv.items.len == 0) argv else interp_argv.items; |
| 396 | .expect_exact, .expect_matches => { | 733 | |
| 397 | stdout = try child.stdout.?.reader().readAllAlloc(builder.allocator, max_stdout_size); | 734 | switch (self.stdio) { |
| 735 | .check => |checks| for (checks.items) |check| switch (check) { | ||
| 736 | .expect_stderr_exact => |expected_bytes| { | ||
| 737 | assert(!result.stdio.stderr_null); | ||
| 738 | if (!mem.eql(u8, expected_bytes, result.stdio.stderr)) { | ||
| 739 | return step.fail( | ||
| 740 | \\ | ||
| 741 | \\========= expected this stderr: ========= | ||
| 742 | \\{s} | ||
| 743 | \\========= but found: ==================== | ||
| 744 | \\{s} | ||
| 745 | \\========= from the following command: === | ||
| 746 | \\{s} | ||
| 747 | , .{ | ||
| 748 | expected_bytes, | ||
| 749 | result.stdio.stderr, | ||
| 750 | try Step.allocPrintCmd(arena, self.cwd, final_argv), | ||
| 751 | }); | ||
| 752 | } | ||
| 753 | }, | ||
| 754 | .expect_stderr_match => |match| { | ||
| 755 | assert(!result.stdio.stderr_null); | ||
| 756 | if (mem.indexOf(u8, result.stdio.stderr, match) == null) { | ||
| 757 | return step.fail( | ||
| 758 | \\ | ||
| 759 | \\========= expected to find in stderr: ========= | ||
| 760 | \\{s} | ||
| 761 | \\========= but stderr does not contain it: ===== | ||
| 762 | \\{s} | ||
| 763 | \\========= from the following command: ========= | ||
| 764 | \\{s} | ||
| 765 | , .{ | ||
| 766 | match, | ||
| 767 | result.stdio.stderr, | ||
| 768 | try Step.allocPrintCmd(arena, self.cwd, final_argv), | ||
| 769 | }); | ||
| 770 | } | ||
| 771 | }, | ||
| 772 | .expect_stdout_exact => |expected_bytes| { | ||
| 773 | assert(!result.stdio.stdout_null); | ||
| 774 | if (!mem.eql(u8, expected_bytes, result.stdio.stdout)) { | ||
| 775 | return step.fail( | ||
| 776 | \\ | ||
| 777 | \\========= expected this stdout: ========= | ||
| 778 | \\{s} | ||
| 779 | \\========= but found: ==================== | ||
| 780 | \\{s} | ||
| 781 | \\========= from the following command: === | ||
| 782 | \\{s} | ||
| 783 | , .{ | ||
| 784 | expected_bytes, | ||
| 785 | result.stdio.stdout, | ||
| 786 | try Step.allocPrintCmd(arena, self.cwd, final_argv), | ||
| 787 | }); | ||
| 788 | } | ||
| 789 | }, | ||
| 790 | .expect_stdout_match => |match| { | ||
| 791 | assert(!result.stdio.stdout_null); | ||
| 792 | if (mem.indexOf(u8, result.stdio.stdout, match) == null) { | ||
| 793 | return step.fail( | ||
| 794 | \\ | ||
| 795 | \\========= expected to find in stdout: ========= | ||
| 796 | \\{s} | ||
| 797 | \\========= but stdout does not contain it: ===== | ||
| 798 | \\{s} | ||
| 799 | \\========= from the following command: ========= | ||
| 800 | \\{s} | ||
| 801 | , .{ | ||
| 802 | match, | ||
| 803 | result.stdio.stdout, | ||
| 804 | try Step.allocPrintCmd(arena, self.cwd, final_argv), | ||
| 805 | }); | ||
| 806 | } | ||
| 807 | }, | ||
| 808 | .expect_term => |expected_term| { | ||
| 809 | if (!termMatches(expected_term, result.term)) { | ||
| 810 | return step.fail("the following command {} (expected {}):\n{s}", .{ | ||
| 811 | fmtTerm(result.term), | ||
| 812 | fmtTerm(expected_term), | ||
| 813 | try Step.allocPrintCmd(arena, self.cwd, final_argv), | ||
| 814 | }); | ||
| 815 | } | ||
| 816 | }, | ||
| 817 | }, | ||
| 818 | .zig_test => { | ||
| 819 | const expected_term: std.process.Child.Term = .{ .Exited = 0 }; | ||
| 820 | if (!termMatches(expected_term, result.term)) { | ||
| 821 | return step.fail("the following command {} (expected {}):\n{s}", .{ | ||
| 822 | fmtTerm(result.term), | ||
| 823 | fmtTerm(expected_term), | ||
| 824 | try Step.allocPrintCmd(arena, self.cwd, final_argv), | ||
| 825 | }); | ||
| 826 | } | ||
| 827 | if (!result.stdio.test_results.isSuccess()) { | ||
| 828 | return step.fail( | ||
| 829 | "the following test command failed:\n{s}", | ||
| 830 | .{try Step.allocPrintCmd(arena, self.cwd, final_argv)}, | ||
| 831 | ); | ||
| 832 | } | ||
| 833 | }, | ||
| 834 | else => { | ||
| 835 | try step.handleChildProcessTerm(result.term, self.cwd, final_argv); | ||
| 398 | }, | 836 | }, |
| 399 | .inherit, .ignore => {}, | ||
| 400 | } | 837 | } |
| 838 | } | ||
| 401 | 839 | ||
| 402 | var stderr: ?[]const u8 = null; | 840 | const ChildProcResult = struct { |
| 403 | defer if (stderr) |s| builder.allocator.free(s); | 841 | term: std.process.Child.Term, |
| 842 | elapsed_ns: u64, | ||
| 843 | peak_rss: usize, | ||
| 404 | 844 | ||
| 405 | switch (stderr_action) { | 845 | stdio: StdIoResult, |
| 406 | .expect_exact, .expect_matches => { | 846 | }; |
| 407 | stderr = try child.stderr.?.reader().readAllAlloc(builder.allocator, max_stdout_size); | 847 | |
| 408 | }, | 848 | fn spawnChildAndCollect( |
| 409 | .inherit, .ignore => {}, | 849 | self: *RunStep, |
| 850 | argv: []const []const u8, | ||
| 851 | has_side_effects: bool, | ||
| 852 | prog_node: *std.Progress.Node, | ||
| 853 | ) !ChildProcResult { | ||
| 854 | const b = self.step.owner; | ||
| 855 | const arena = b.allocator; | ||
| 856 | |||
| 857 | var child = std.process.Child.init(argv, arena); | ||
| 858 | if (self.cwd) |cwd| { | ||
| 859 | child.cwd = b.pathFromRoot(cwd); | ||
| 860 | } else { | ||
| 861 | child.cwd = b.build_root.path; | ||
| 862 | child.cwd_dir = b.build_root.handle; | ||
| 410 | } | 863 | } |
| 864 | child.env_map = self.env_map orelse b.env_map; | ||
| 865 | child.request_resource_usage_statistics = true; | ||
| 411 | 866 | ||
| 412 | const term = child.wait() catch |err| { | 867 | child.stdin_behavior = switch (self.stdio) { |
| 413 | std.debug.print("Unable to spawn {s}: {s}\n", .{ argv[0], @errorName(err) }); | 868 | .infer_from_args => if (has_side_effects) .Inherit else .Close, |
| 414 | return err; | 869 | .inherit => .Inherit, |
| 870 | .check => .Close, | ||
| 871 | .zig_test => .Pipe, | ||
| 415 | }; | 872 | }; |
| 873 | child.stdout_behavior = switch (self.stdio) { | ||
| 874 | .infer_from_args => if (has_side_effects) .Inherit else .Ignore, | ||
| 875 | .inherit => .Inherit, | ||
| 876 | .check => |checks| if (checksContainStdout(checks.items)) .Pipe else .Ignore, | ||
| 877 | .zig_test => .Pipe, | ||
| 878 | }; | ||
| 879 | child.stderr_behavior = switch (self.stdio) { | ||
| 880 | .infer_from_args => if (has_side_effects) .Inherit else .Pipe, | ||
| 881 | .inherit => .Inherit, | ||
| 882 | .check => .Pipe, | ||
| 883 | .zig_test => .Pipe, | ||
| 884 | }; | ||
| 885 | if (self.captured_stdout != null) child.stdout_behavior = .Pipe; | ||
| 886 | if (self.captured_stderr != null) child.stderr_behavior = .Pipe; | ||
| 887 | if (self.stdin != null) { | ||
| 888 | assert(child.stdin_behavior != .Inherit); | ||
| 889 | child.stdin_behavior = .Pipe; | ||
| 890 | } | ||
| 416 | 891 | ||
| 417 | if (!termMatches(expected_term, term)) { | 892 | try child.spawn(); |
| 418 | if (builder.prominent_compile_errors) { | 893 | var timer = try std.time.Timer.start(); |
| 419 | std.debug.print("Run step {} (expected {})\n", .{ fmtTerm(term), fmtTerm(expected_term) }); | 894 | |
| 420 | } else { | 895 | const result = if (self.stdio == .zig_test) |
| 421 | std.debug.print("The following command {} (expected {}):\n", .{ fmtTerm(term), fmtTerm(expected_term) }); | 896 | evalZigTest(self, &child, prog_node) |
| 422 | printCmd(cwd, argv); | 897 | else |
| 898 | evalGeneric(self, &child); | ||
| 899 | |||
| 900 | const term = try child.wait(); | ||
| 901 | const elapsed_ns = timer.read(); | ||
| 902 | |||
| 903 | return .{ | ||
| 904 | .stdio = try result, | ||
| 905 | .term = term, | ||
| 906 | .elapsed_ns = elapsed_ns, | ||
| 907 | .peak_rss = child.resource_usage_statistics.getMaxRss() orelse 0, | ||
| 908 | }; | ||
| 909 | } | ||
| 910 | |||
| 911 | const StdIoResult = struct { | ||
| 912 | // These use boolean flags instead of optionals as a workaround for | ||
| 913 | // https://github.com/ziglang/zig/issues/14783 | ||
| 914 | stdout: []const u8, | ||
| 915 | stderr: []const u8, | ||
| 916 | stdout_null: bool, | ||
| 917 | stderr_null: bool, | ||
| 918 | test_results: Step.TestResults, | ||
| 919 | }; | ||
| 920 | |||
| 921 | fn evalZigTest( | ||
| 922 | self: *RunStep, | ||
| 923 | child: *std.process.Child, | ||
| 924 | prog_node: *std.Progress.Node, | ||
| 925 | ) !StdIoResult { | ||
| 926 | const gpa = self.step.owner.allocator; | ||
| 927 | const arena = self.step.owner.allocator; | ||
| 928 | |||
| 929 | var poller = std.io.poll(gpa, enum { stdout, stderr }, .{ | ||
| 930 | .stdout = child.stdout.?, | ||
| 931 | .stderr = child.stderr.?, | ||
| 932 | }); | ||
| 933 | defer poller.deinit(); | ||
| 934 | |||
| 935 | try sendMessage(child.stdin.?, .query_test_metadata); | ||
| 936 | |||
| 937 | const Header = std.zig.Server.Message.Header; | ||
| 938 | |||
| 939 | const stdout = poller.fifo(.stdout); | ||
| 940 | const stderr = poller.fifo(.stderr); | ||
| 941 | |||
| 942 | var fail_count: u32 = 0; | ||
| 943 | var skip_count: u32 = 0; | ||
| 944 | var leak_count: u32 = 0; | ||
| 945 | var test_count: u32 = 0; | ||
| 946 | |||
| 947 | var metadata: ?TestMetadata = null; | ||
| 948 | |||
| 949 | var sub_prog_node: ?std.Progress.Node = null; | ||
| 950 | defer if (sub_prog_node) |*n| n.end(); | ||
| 951 | |||
| 952 | poll: while (true) { | ||
| 953 | while (stdout.readableLength() < @sizeOf(Header)) { | ||
| 954 | if (!(try poller.poll())) break :poll; | ||
| 955 | } | ||
| 956 | const header = stdout.reader().readStruct(Header) catch unreachable; | ||
| 957 | while (stdout.readableLength() < header.bytes_len) { | ||
| 958 | if (!(try poller.poll())) break :poll; | ||
| 423 | } | 959 | } |
| 424 | return error.UnexpectedExit; | 960 | const body = stdout.readableSliceOfLen(header.bytes_len); |
| 961 | |||
| 962 | switch (header.tag) { | ||
| 963 | .zig_version => { | ||
| 964 | if (!std.mem.eql(u8, builtin.zig_version_string, body)) { | ||
| 965 | return self.step.fail( | ||
| 966 | "zig version mismatch build runner vs compiler: '{s}' vs '{s}'", | ||
| 967 | .{ builtin.zig_version_string, body }, | ||
| 968 | ); | ||
| 969 | } | ||
| 970 | }, | ||
| 971 | .test_metadata => { | ||
| 972 | const TmHdr = std.zig.Server.Message.TestMetadata; | ||
| 973 | const tm_hdr = @ptrCast(*align(1) const TmHdr, body); | ||
| 974 | test_count = tm_hdr.tests_len; | ||
| 975 | |||
| 976 | const names_bytes = body[@sizeOf(TmHdr)..][0 .. test_count * @sizeOf(u32)]; | ||
| 977 | const async_frame_lens_bytes = body[@sizeOf(TmHdr) + names_bytes.len ..][0 .. test_count * @sizeOf(u32)]; | ||
| 978 | const expected_panic_msgs_bytes = body[@sizeOf(TmHdr) + names_bytes.len + async_frame_lens_bytes.len ..][0 .. test_count * @sizeOf(u32)]; | ||
| 979 | const string_bytes = body[@sizeOf(TmHdr) + names_bytes.len + async_frame_lens_bytes.len + expected_panic_msgs_bytes.len ..][0..tm_hdr.string_bytes_len]; | ||
| 980 | |||
| 981 | const names = std.mem.bytesAsSlice(u32, names_bytes); | ||
| 982 | const async_frame_lens = std.mem.bytesAsSlice(u32, async_frame_lens_bytes); | ||
| 983 | const expected_panic_msgs = std.mem.bytesAsSlice(u32, expected_panic_msgs_bytes); | ||
| 984 | const names_aligned = try arena.alloc(u32, names.len); | ||
| 985 | for (names_aligned, names) |*dest, src| dest.* = src; | ||
| 986 | |||
| 987 | const async_frame_lens_aligned = try arena.alloc(u32, async_frame_lens.len); | ||
| 988 | for (async_frame_lens_aligned, async_frame_lens) |*dest, src| dest.* = src; | ||
| 989 | |||
| 990 | const expected_panic_msgs_aligned = try arena.alloc(u32, expected_panic_msgs.len); | ||
| 991 | for (expected_panic_msgs_aligned, expected_panic_msgs) |*dest, src| dest.* = src; | ||
| 992 | |||
| 993 | prog_node.setEstimatedTotalItems(names.len); | ||
| 994 | metadata = .{ | ||
| 995 | .string_bytes = try arena.dupe(u8, string_bytes), | ||
| 996 | .names = names_aligned, | ||
| 997 | .async_frame_lens = async_frame_lens_aligned, | ||
| 998 | .expected_panic_msgs = expected_panic_msgs_aligned, | ||
| 999 | .next_index = 0, | ||
| 1000 | .prog_node = prog_node, | ||
| 1001 | }; | ||
| 1002 | |||
| 1003 | try requestNextTest(child.stdin.?, &metadata.?, &sub_prog_node); | ||
| 1004 | }, | ||
| 1005 | .test_results => { | ||
| 1006 | const md = metadata.?; | ||
| 1007 | |||
| 1008 | const TrHdr = std.zig.Server.Message.TestResults; | ||
| 1009 | const tr_hdr = @ptrCast(*align(1) const TrHdr, body); | ||
| 1010 | fail_count += @boolToInt(tr_hdr.flags.fail); | ||
| 1011 | skip_count += @boolToInt(tr_hdr.flags.skip); | ||
| 1012 | leak_count += @boolToInt(tr_hdr.flags.leak); | ||
| 1013 | |||
| 1014 | if (tr_hdr.flags.fail or tr_hdr.flags.leak) { | ||
| 1015 | const name = std.mem.sliceTo(md.string_bytes[md.names[tr_hdr.index]..], 0); | ||
| 1016 | const msg = std.mem.trim(u8, stderr.readableSlice(0), "\n"); | ||
| 1017 | const label = if (tr_hdr.flags.fail) "failed" else "leaked"; | ||
| 1018 | if (msg.len > 0) { | ||
| 1019 | try self.step.addError("'{s}' {s}: {s}", .{ name, label, msg }); | ||
| 1020 | } else { | ||
| 1021 | try self.step.addError("'{s}' {s}", .{ name, label }); | ||
| 1022 | } | ||
| 1023 | stderr.discard(msg.len); | ||
| 1024 | } | ||
| 1025 | |||
| 1026 | try requestNextTest(child.stdin.?, &metadata.?, &sub_prog_node); | ||
| 1027 | }, | ||
| 1028 | else => {}, // ignore other messages | ||
| 1029 | } | ||
| 1030 | |||
| 1031 | stdout.discard(body.len); | ||
| 425 | } | 1032 | } |
| 426 | 1033 | ||
| 427 | switch (stderr_action) { | 1034 | if (stderr.readableLength() > 0) { |
| 428 | .inherit, .ignore => {}, | 1035 | const msg = std.mem.trim(u8, try stderr.toOwnedSlice(), "\n"); |
| 429 | .expect_exact => |expected_bytes| { | 1036 | if (msg.len > 0) try self.step.result_error_msgs.append(arena, msg); |
| 430 | if (!mem.eql(u8, expected_bytes, stderr.?)) { | ||
| 431 | std.debug.print( | ||
| 432 | \\ | ||
| 433 | \\========= Expected this stderr: ========= | ||
| 434 | \\{s} | ||
| 435 | \\========= But found: ==================== | ||
| 436 | \\{s} | ||
| 437 | \\ | ||
| 438 | , .{ expected_bytes, stderr.? }); | ||
| 439 | printCmd(cwd, argv); | ||
| 440 | return error.TestFailed; | ||
| 441 | } | ||
| 442 | }, | ||
| 443 | .expect_matches => |matches| for (matches) |match| { | ||
| 444 | if (mem.indexOf(u8, stderr.?, match) == null) { | ||
| 445 | std.debug.print( | ||
| 446 | \\ | ||
| 447 | \\========= Expected to find in stderr: ========= | ||
| 448 | \\{s} | ||
| 449 | \\========= But stderr does not contain it: ===== | ||
| 450 | \\{s} | ||
| 451 | \\ | ||
| 452 | , .{ match, stderr.? }); | ||
| 453 | printCmd(cwd, argv); | ||
| 454 | return error.TestFailed; | ||
| 455 | } | ||
| 456 | }, | ||
| 457 | } | 1037 | } |
| 458 | 1038 | ||
| 459 | switch (stdout_action) { | 1039 | // Send EOF to stdin. |
| 460 | .inherit, .ignore => {}, | 1040 | child.stdin.?.close(); |
| 461 | .expect_exact => |expected_bytes| { | 1041 | child.stdin = null; |
| 462 | if (!mem.eql(u8, expected_bytes, stdout.?)) { | 1042 | |
| 463 | std.debug.print( | 1043 | return .{ |
| 464 | \\ | 1044 | .stdout = &.{}, |
| 465 | \\========= Expected this stdout: ========= | 1045 | .stderr = &.{}, |
| 466 | \\{s} | 1046 | .stdout_null = true, |
| 467 | \\========= But found: ==================== | 1047 | .stderr_null = true, |
| 468 | \\{s} | 1048 | .test_results = .{ |
| 469 | \\ | 1049 | .test_count = test_count, |
| 470 | , .{ expected_bytes, stdout.? }); | 1050 | .fail_count = fail_count, |
| 471 | printCmd(cwd, argv); | 1051 | .skip_count = skip_count, |
| 472 | return error.TestFailed; | 1052 | .leak_count = leak_count, |
| 473 | } | ||
| 474 | }, | ||
| 475 | .expect_matches => |matches| for (matches) |match| { | ||
| 476 | if (mem.indexOf(u8, stdout.?, match) == null) { | ||
| 477 | std.debug.print( | ||
| 478 | \\ | ||
| 479 | \\========= Expected to find in stdout: ========= | ||
| 480 | \\{s} | ||
| 481 | \\========= But stdout does not contain it: ===== | ||
| 482 | \\{s} | ||
| 483 | \\ | ||
| 484 | , .{ match, stdout.? }); | ||
| 485 | printCmd(cwd, argv); | ||
| 486 | return error.TestFailed; | ||
| 487 | } | ||
| 488 | }, | 1053 | }, |
| 1054 | }; | ||
| 1055 | } | ||
| 1056 | |||
| 1057 | const TestMetadata = struct { | ||
| 1058 | names: []const u32, | ||
| 1059 | async_frame_lens: []const u32, | ||
| 1060 | expected_panic_msgs: []const u32, | ||
| 1061 | string_bytes: []const u8, | ||
| 1062 | next_index: u32, | ||
| 1063 | prog_node: *std.Progress.Node, | ||
| 1064 | |||
| 1065 | fn testName(tm: TestMetadata, index: u32) []const u8 { | ||
| 1066 | return std.mem.sliceTo(tm.string_bytes[tm.names[index]..], 0); | ||
| 1067 | } | ||
| 1068 | }; | ||
| 1069 | |||
| 1070 | fn requestNextTest(in: fs.File, metadata: *TestMetadata, sub_prog_node: *?std.Progress.Node) !void { | ||
| 1071 | while (metadata.next_index < metadata.names.len) { | ||
| 1072 | const i = metadata.next_index; | ||
| 1073 | metadata.next_index += 1; | ||
| 1074 | |||
| 1075 | if (metadata.async_frame_lens[i] != 0) continue; | ||
| 1076 | if (metadata.expected_panic_msgs[i] != 0) continue; | ||
| 1077 | |||
| 1078 | const name = metadata.testName(i); | ||
| 1079 | if (sub_prog_node.*) |*n| n.end(); | ||
| 1080 | sub_prog_node.* = metadata.prog_node.start(name, 0); | ||
| 1081 | |||
| 1082 | try sendRunTestMessage(in, i); | ||
| 1083 | return; | ||
| 1084 | } else { | ||
| 1085 | try sendMessage(in, .exit); | ||
| 489 | } | 1086 | } |
| 490 | } | 1087 | } |
| 491 | 1088 | ||
| 492 | fn failWithCacheError(man: std.Build.Cache.Manifest, err: anyerror) noreturn { | 1089 | fn sendMessage(file: std.fs.File, tag: std.zig.Client.Message.Tag) !void { |
| 493 | const i = man.failed_file_index orelse failWithSimpleError(err); | 1090 | const header: std.zig.Client.Message.Header = .{ |
| 494 | const pp = man.files.items[i].prefixed_path orelse failWithSimpleError(err); | 1091 | .tag = tag, |
| 495 | const prefix = man.cache.prefixes()[pp.prefix].path orelse ""; | 1092 | .bytes_len = 0, |
| 496 | std.debug.print("{s}: {s}/{s}\n", .{ @errorName(err), prefix, pp.sub_path }); | 1093 | }; |
| 497 | std.process.exit(1); | 1094 | try file.writeAll(std.mem.asBytes(&header)); |
| 498 | } | 1095 | } |
| 499 | 1096 | ||
| 500 | fn failWithSimpleError(err: anyerror) noreturn { | 1097 | fn sendRunTestMessage(file: std.fs.File, index: u32) !void { |
| 501 | std.debug.print("{s}\n", .{@errorName(err)}); | 1098 | const header: std.zig.Client.Message.Header = .{ |
| 502 | std.process.exit(1); | 1099 | .tag = .run_test, |
| 1100 | .bytes_len = 4, | ||
| 1101 | }; | ||
| 1102 | const full_msg = std.mem.asBytes(&header) ++ std.mem.asBytes(&index); | ||
| 1103 | try file.writeAll(full_msg); | ||
| 503 | } | 1104 | } |
| 504 | 1105 | ||
| 505 | fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void { | 1106 | fn evalGeneric(self: *RunStep, child: *std.process.Child) !StdIoResult { |
| 506 | if (cwd) |yes_cwd| std.debug.print("cd {s} && ", .{yes_cwd}); | 1107 | const arena = self.step.owner.allocator; |
| 507 | for (argv) |arg| { | 1108 | |
| 508 | std.debug.print("{s} ", .{arg}); | 1109 | if (self.stdin) |stdin| { |
| 1110 | child.stdin.?.writeAll(stdin) catch |err| { | ||
| 1111 | return self.step.fail("unable to write stdin: {s}", .{@errorName(err)}); | ||
| 1112 | }; | ||
| 1113 | child.stdin.?.close(); | ||
| 1114 | child.stdin = null; | ||
| 509 | } | 1115 | } |
| 510 | std.debug.print("\n", .{}); | ||
| 511 | } | ||
| 512 | 1116 | ||
| 513 | fn addPathForDynLibs(self: *RunStep, artifact: *CompileStep) void { | 1117 | // These are not optionals, as a workaround for |
| 514 | addPathForDynLibsInternal(&self.step, self.builder, artifact); | 1118 | // https://github.com/ziglang/zig/issues/14783 |
| 1119 | var stdout_bytes: []const u8 = undefined; | ||
| 1120 | var stderr_bytes: []const u8 = undefined; | ||
| 1121 | var stdout_null = true; | ||
| 1122 | var stderr_null = true; | ||
| 1123 | |||
| 1124 | if (child.stdout) |stdout| { | ||
| 1125 | if (child.stderr) |stderr| { | ||
| 1126 | var poller = std.io.poll(arena, enum { stdout, stderr }, .{ | ||
| 1127 | .stdout = stdout, | ||
| 1128 | .stderr = stderr, | ||
| 1129 | }); | ||
| 1130 | defer poller.deinit(); | ||
| 1131 | |||
| 1132 | while (try poller.poll()) { | ||
| 1133 | if (poller.fifo(.stdout).count > self.max_stdio_size) | ||
| 1134 | return error.StdoutStreamTooLong; | ||
| 1135 | if (poller.fifo(.stderr).count > self.max_stdio_size) | ||
| 1136 | return error.StderrStreamTooLong; | ||
| 1137 | } | ||
| 1138 | |||
| 1139 | stdout_bytes = try poller.fifo(.stdout).toOwnedSlice(); | ||
| 1140 | stderr_bytes = try poller.fifo(.stderr).toOwnedSlice(); | ||
| 1141 | stdout_null = false; | ||
| 1142 | stderr_null = false; | ||
| 1143 | } else { | ||
| 1144 | stdout_bytes = try stdout.reader().readAllAlloc(arena, self.max_stdio_size); | ||
| 1145 | stdout_null = false; | ||
| 1146 | } | ||
| 1147 | } else if (child.stderr) |stderr| { | ||
| 1148 | stderr_bytes = try stderr.reader().readAllAlloc(arena, self.max_stdio_size); | ||
| 1149 | stderr_null = false; | ||
| 1150 | } | ||
| 1151 | |||
| 1152 | if (!stderr_null and stderr_bytes.len > 0) { | ||
| 1153 | // Treat stderr as an error message. | ||
| 1154 | const stderr_is_diagnostic = self.captured_stderr == null and switch (self.stdio) { | ||
| 1155 | .check => |checks| !checksContainStderr(checks.items), | ||
| 1156 | else => true, | ||
| 1157 | }; | ||
| 1158 | if (stderr_is_diagnostic) { | ||
| 1159 | try self.step.result_error_msgs.append(arena, stderr_bytes); | ||
| 1160 | } | ||
| 1161 | } | ||
| 1162 | |||
| 1163 | return .{ | ||
| 1164 | .stdout = stdout_bytes, | ||
| 1165 | .stderr = stderr_bytes, | ||
| 1166 | .stdout_null = stdout_null, | ||
| 1167 | .stderr_null = stderr_null, | ||
| 1168 | .test_results = .{}, | ||
| 1169 | }; | ||
| 515 | } | 1170 | } |
| 516 | 1171 | ||
| 517 | /// This should only be used for internal usage, this is called automatically | 1172 | fn addPathForDynLibs(self: *RunStep, artifact: *CompileStep) void { |
| 518 | /// for the user. | 1173 | const b = self.step.owner; |
| 519 | pub fn addPathForDynLibsInternal(step: *Step, builder: *std.Build, artifact: *CompileStep) void { | ||
| 520 | for (artifact.link_objects.items) |link_object| { | 1174 | for (artifact.link_objects.items) |link_object| { |
| 521 | switch (link_object) { | 1175 | switch (link_object) { |
| 522 | .other_step => |other| { | 1176 | .other_step => |other| { |
| 523 | if (other.target.isWindows() and other.isDynamicLibrary()) { | 1177 | if (other.target.isWindows() and other.isDynamicLibrary()) { |
| 524 | addPathDirInternal(step, builder, fs.path.dirname(other.getOutputSource().getPath(builder)).?); | 1178 | addPathDir(self, fs.path.dirname(other.getOutputSource().getPath(b)).?); |
| 525 | addPathForDynLibsInternal(step, builder, other); | 1179 | addPathForDynLibs(self, other); |
| 526 | } | 1180 | } |
| 527 | }, | 1181 | }, |
| 528 | else => {}, | 1182 | else => {}, |
| 529 | } | 1183 | } |
| 530 | } | 1184 | } |
| 531 | } | 1185 | } |
| 1186 | |||
| 1187 | fn failForeign( | ||
| 1188 | self: *RunStep, | ||
| 1189 | suggested_flag: []const u8, | ||
| 1190 | argv0: []const u8, | ||
| 1191 | exe: *CompileStep, | ||
| 1192 | ) error{ MakeFailed, MakeSkipped, OutOfMemory } { | ||
| 1193 | switch (self.stdio) { | ||
| 1194 | .check, .zig_test => { | ||
| 1195 | if (self.skip_foreign_checks) | ||
| 1196 | return error.MakeSkipped; | ||
| 1197 | |||
| 1198 | const b = self.step.owner; | ||
| 1199 | const host_name = try b.host.target.zigTriple(b.allocator); | ||
| 1200 | const foreign_name = try exe.target.zigTriple(b.allocator); | ||
| 1201 | |||
| 1202 | return self.step.fail( | ||
| 1203 | \\unable to spawn foreign binary '{s}' ({s}) on host system ({s}) | ||
| 1204 | \\ consider using {s} or enabling skip_foreign_checks in the Run step | ||
| 1205 | , .{ argv0, foreign_name, host_name, suggested_flag }); | ||
| 1206 | }, | ||
| 1207 | else => { | ||
| 1208 | return self.step.fail("unable to spawn foreign binary '{s}'", .{argv0}); | ||
| 1209 | }, | ||
| 1210 | } | ||
| 1211 | } | ||
| 1212 | |||
| 1213 | fn hashStdIo(hh: *std.Build.Cache.HashHelper, stdio: StdIo) void { | ||
| 1214 | switch (stdio) { | ||
| 1215 | .infer_from_args, .inherit, .zig_test => {}, | ||
| 1216 | .check => |checks| for (checks.items) |check| { | ||
| 1217 | hh.add(@as(std.meta.Tag(StdIo.Check), check)); | ||
| 1218 | switch (check) { | ||
| 1219 | .expect_stderr_exact, | ||
| 1220 | .expect_stderr_match, | ||
| 1221 | .expect_stdout_exact, | ||
| 1222 | .expect_stdout_match, | ||
| 1223 | => |s| hh.addBytes(s), | ||
| 1224 | |||
| 1225 | .expect_term => |term| { | ||
| 1226 | hh.add(@as(std.meta.Tag(std.process.Child.Term), term)); | ||
| 1227 | switch (term) { | ||
| 1228 | .Exited => |x| hh.add(x), | ||
| 1229 | .Signal, .Stopped, .Unknown => |x| hh.add(x), | ||
| 1230 | } | ||
| 1231 | }, | ||
| 1232 | } | ||
| 1233 | }, | ||
| 1234 | } | ||
| 1235 | } |
lib/std/Build/Step.zig+467-25| ... | @@ -1,9 +1,77 @@ | ... | @@ -1,9 +1,77 @@ |
| 1 | id: Id, | 1 | id: Id, |
| 2 | name: []const u8, | 2 | name: []const u8, |
| 3 | makeFn: *const fn (self: *Step) anyerror!void, | 3 | owner: *Build, |
| 4 | makeFn: MakeFn, | ||
| 5 | |||
| 4 | dependencies: std.ArrayList(*Step), | 6 | dependencies: std.ArrayList(*Step), |
| 5 | loop_flag: bool, | 7 | /// This field is empty during execution of the user's build script, and |
| 6 | done_flag: bool, | 8 | /// then populated during dependency loop checking in the build runner. |
| 9 | dependants: std.ArrayListUnmanaged(*Step), | ||
| 10 | state: State, | ||
| 11 | /// Set this field to declare an upper bound on the amount of bytes of memory it will | ||
| 12 | /// take to run the step. Zero means no limit. | ||
| 13 | /// | ||
| 14 | /// The idea to annotate steps that might use a high amount of RAM with an | ||
| 15 | /// upper bound. For example, perhaps a particular set of unit tests require 4 | ||
| 16 | /// GiB of RAM, and those tests will be run under 4 different build | ||
| 17 | /// configurations at once. This would potentially require 16 GiB of memory on | ||
| 18 | /// the system if all 4 steps executed simultaneously, which could easily be | ||
| 19 | /// greater than what is actually available, potentially causing the system to | ||
| 20 | /// crash when using `zig build` at the default concurrency level. | ||
| 21 | /// | ||
| 22 | /// This field causes the build runner to do two things: | ||
| 23 | /// 1. ulimit child processes, so that they will fail if it would exceed this | ||
| 24 | /// memory limit. This serves to enforce that this upper bound value is | ||
| 25 | /// correct. | ||
| 26 | /// 2. Ensure that the set of concurrent steps at any given time have a total | ||
| 27 | /// max_rss value that does not exceed the `max_total_rss` value of the build | ||
| 28 | /// runner. This value is configurable on the command line, and defaults to the | ||
| 29 | /// total system memory available. | ||
| 30 | max_rss: usize, | ||
| 31 | |||
| 32 | result_error_msgs: std.ArrayListUnmanaged([]const u8), | ||
| 33 | result_error_bundle: std.zig.ErrorBundle, | ||
| 34 | result_cached: bool, | ||
| 35 | result_duration_ns: ?u64, | ||
| 36 | /// 0 means unavailable or not reported. | ||
| 37 | result_peak_rss: usize, | ||
| 38 | test_results: TestResults, | ||
| 39 | |||
| 40 | /// The return addresss associated with creation of this step that can be useful | ||
| 41 | /// to print along with debugging messages. | ||
| 42 | debug_stack_trace: [n_debug_stack_frames]usize, | ||
| 43 | |||
| 44 | pub const TestResults = struct { | ||
| 45 | fail_count: u32 = 0, | ||
| 46 | skip_count: u32 = 0, | ||
| 47 | leak_count: u32 = 0, | ||
| 48 | test_count: u32 = 0, | ||
| 49 | |||
| 50 | pub fn isSuccess(tr: TestResults) bool { | ||
| 51 | return tr.fail_count == 0 and tr.leak_count == 0; | ||
| 52 | } | ||
| 53 | |||
| 54 | pub fn passCount(tr: TestResults) u32 { | ||
| 55 | return tr.test_count - tr.fail_count - tr.skip_count; | ||
| 56 | } | ||
| 57 | }; | ||
| 58 | |||
| 59 | pub const MakeFn = *const fn (self: *Step, prog_node: *std.Progress.Node) anyerror!void; | ||
| 60 | |||
| 61 | const n_debug_stack_frames = 4; | ||
| 62 | |||
| 63 | pub const State = enum { | ||
| 64 | precheck_unstarted, | ||
| 65 | precheck_started, | ||
| 66 | precheck_done, | ||
| 67 | running, | ||
| 68 | dependency_failure, | ||
| 69 | success, | ||
| 70 | failure, | ||
| 71 | /// This state indicates that the step did not complete, however, it also did not fail, | ||
| 72 | /// and it is safe to continue executing its dependencies. | ||
| 73 | skipped, | ||
| 74 | }; | ||
| 7 | 75 | ||
| 8 | pub const Id = enum { | 76 | pub const Id = enum { |
| 9 | top_level, | 77 | top_level, |
| ... | @@ -17,7 +85,6 @@ pub const Id = enum { | ... | @@ -17,7 +85,6 @@ pub const Id = enum { |
| 17 | translate_c, | 85 | translate_c, |
| 18 | write_file, | 86 | write_file, |
| 19 | run, | 87 | run, |
| 20 | emulatable_run, | ||
| 21 | check_file, | 88 | check_file, |
| 22 | check_object, | 89 | check_object, |
| 23 | config_header, | 90 | config_header, |
| ... | @@ -38,7 +105,6 @@ pub const Id = enum { | ... | @@ -38,7 +105,6 @@ pub const Id = enum { |
| 38 | .translate_c => Build.TranslateCStep, | 105 | .translate_c => Build.TranslateCStep, |
| 39 | .write_file => Build.WriteFileStep, | 106 | .write_file => Build.WriteFileStep, |
| 40 | .run => Build.RunStep, | 107 | .run => Build.RunStep, |
| 41 | .emulatable_run => Build.EmulatableRunStep, | ||
| 42 | .check_file => Build.CheckFileStep, | 108 | .check_file => Build.CheckFileStep, |
| 43 | .check_object => Build.CheckObjectStep, | 109 | .check_object => Build.CheckObjectStep, |
| 44 | .config_header => Build.ConfigHeaderStep, | 110 | .config_header => Build.ConfigHeaderStep, |
| ... | @@ -49,39 +115,99 @@ pub const Id = enum { | ... | @@ -49,39 +115,99 @@ pub const Id = enum { |
| 49 | } | 115 | } |
| 50 | }; | 116 | }; |
| 51 | 117 | ||
| 52 | pub fn init( | 118 | pub const Options = struct { |
| 53 | id: Id, | 119 | id: Id, |
| 54 | name: []const u8, | 120 | name: []const u8, |
| 55 | allocator: Allocator, | 121 | owner: *Build, |
| 56 | makeFn: *const fn (self: *Step) anyerror!void, | 122 | makeFn: MakeFn = makeNoOp, |
| 57 | ) Step { | 123 | first_ret_addr: ?usize = null, |
| 58 | return Step{ | 124 | max_rss: usize = 0, |
| 59 | .id = id, | 125 | }; |
| 60 | .name = allocator.dupe(u8, name) catch @panic("OOM"), | 126 | |
| 61 | .makeFn = makeFn, | 127 | pub fn init(options: Options) Step { |
| 62 | .dependencies = std.ArrayList(*Step).init(allocator), | 128 | const arena = options.owner.allocator; |
| 63 | .loop_flag = false, | 129 | |
| 64 | .done_flag = false, | 130 | var addresses = [1]usize{0} ** n_debug_stack_frames; |
| 131 | const first_ret_addr = options.first_ret_addr orelse @returnAddress(); | ||
| 132 | var stack_trace = std.builtin.StackTrace{ | ||
| 133 | .instruction_addresses = &addresses, | ||
| 134 | .index = 0, | ||
| 65 | }; | 135 | }; |
| 66 | } | 136 | std.debug.captureStackTrace(first_ret_addr, &stack_trace); |
| 67 | 137 | ||
| 68 | pub fn initNoOp(id: Id, name: []const u8, allocator: Allocator) Step { | 138 | return .{ |
| 69 | return init(id, name, allocator, makeNoOp); | 139 | .id = options.id, |
| 140 | .name = arena.dupe(u8, options.name) catch @panic("OOM"), | ||
| 141 | .owner = options.owner, | ||
| 142 | .makeFn = options.makeFn, | ||
| 143 | .dependencies = std.ArrayList(*Step).init(arena), | ||
| 144 | .dependants = .{}, | ||
| 145 | .state = .precheck_unstarted, | ||
| 146 | .max_rss = options.max_rss, | ||
| 147 | .debug_stack_trace = addresses, | ||
| 148 | .result_error_msgs = .{}, | ||
| 149 | .result_error_bundle = std.zig.ErrorBundle.empty, | ||
| 150 | .result_cached = false, | ||
| 151 | .result_duration_ns = null, | ||
| 152 | .result_peak_rss = 0, | ||
| 153 | .test_results = .{}, | ||
| 154 | }; | ||
| 70 | } | 155 | } |
| 71 | 156 | ||
| 72 | pub fn make(self: *Step) !void { | 157 | /// If the Step's `make` function reports `error.MakeFailed`, it indicates they |
| 73 | if (self.done_flag) return; | 158 | /// have already reported the error. Otherwise, we add a simple error report |
| 159 | /// here. | ||
| 160 | pub fn make(s: *Step, prog_node: *std.Progress.Node) error{ MakeFailed, MakeSkipped }!void { | ||
| 161 | const arena = s.owner.allocator; | ||
| 162 | |||
| 163 | s.makeFn(s, prog_node) catch |err| switch (err) { | ||
| 164 | error.MakeFailed => return error.MakeFailed, | ||
| 165 | error.MakeSkipped => return error.MakeSkipped, | ||
| 166 | else => { | ||
| 167 | s.result_error_msgs.append(arena, @errorName(err)) catch @panic("OOM"); | ||
| 168 | return error.MakeFailed; | ||
| 169 | }, | ||
| 170 | }; | ||
| 171 | |||
| 172 | if (!s.test_results.isSuccess()) { | ||
| 173 | return error.MakeFailed; | ||
| 174 | } | ||
| 74 | 175 | ||
| 75 | try self.makeFn(self); | 176 | if (s.max_rss != 0 and s.result_peak_rss > s.max_rss) { |
| 76 | self.done_flag = true; | 177 | const msg = std.fmt.allocPrint(arena, "memory usage peaked at {d} bytes, exceeding the declared upper bound of {d}", .{ |
| 178 | s.result_peak_rss, s.max_rss, | ||
| 179 | }) catch @panic("OOM"); | ||
| 180 | s.result_error_msgs.append(arena, msg) catch @panic("OOM"); | ||
| 181 | return error.MakeFailed; | ||
| 182 | } | ||
| 77 | } | 183 | } |
| 78 | 184 | ||
| 79 | pub fn dependOn(self: *Step, other: *Step) void { | 185 | pub fn dependOn(self: *Step, other: *Step) void { |
| 80 | self.dependencies.append(other) catch @panic("OOM"); | 186 | self.dependencies.append(other) catch @panic("OOM"); |
| 81 | } | 187 | } |
| 82 | 188 | ||
| 83 | fn makeNoOp(self: *Step) anyerror!void { | 189 | pub fn getStackTrace(s: *Step) std.builtin.StackTrace { |
| 84 | _ = self; | 190 | const stack_addresses = &s.debug_stack_trace; |
| 191 | var len: usize = 0; | ||
| 192 | while (len < n_debug_stack_frames and stack_addresses[len] != 0) { | ||
| 193 | len += 1; | ||
| 194 | } | ||
| 195 | return .{ | ||
| 196 | .instruction_addresses = stack_addresses, | ||
| 197 | .index = len, | ||
| 198 | }; | ||
| 199 | } | ||
| 200 | |||
| 201 | fn makeNoOp(step: *Step, prog_node: *std.Progress.Node) anyerror!void { | ||
| 202 | _ = prog_node; | ||
| 203 | |||
| 204 | var all_cached = true; | ||
| 205 | |||
| 206 | for (step.dependencies.items) |dep| { | ||
| 207 | all_cached = all_cached and dep.result_cached; | ||
| 208 | } | ||
| 209 | |||
| 210 | step.result_cached = all_cached; | ||
| 85 | } | 211 | } |
| 86 | 212 | ||
| 87 | pub fn cast(step: *Step, comptime T: type) ?*T { | 213 | pub fn cast(step: *Step, comptime T: type) ?*T { |
| ... | @@ -91,7 +217,323 @@ pub fn cast(step: *Step, comptime T: type) ?*T { | ... | @@ -91,7 +217,323 @@ pub fn cast(step: *Step, comptime T: type) ?*T { |
| 91 | return null; | 217 | return null; |
| 92 | } | 218 | } |
| 93 | 219 | ||
| 220 | /// For debugging purposes, prints identifying information about this Step. | ||
| 221 | pub fn dump(step: *Step) void { | ||
| 222 | std.debug.getStderrMutex().lock(); | ||
| 223 | defer std.debug.getStderrMutex().unlock(); | ||
| 224 | |||
| 225 | const stderr = std.io.getStdErr(); | ||
| 226 | const w = stderr.writer(); | ||
| 227 | const tty_config = std.debug.detectTTYConfig(stderr); | ||
| 228 | const debug_info = std.debug.getSelfDebugInfo() catch |err| { | ||
| 229 | w.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{ | ||
| 230 | @errorName(err), | ||
| 231 | }) catch {}; | ||
| 232 | return; | ||
| 233 | }; | ||
| 234 | const ally = debug_info.allocator; | ||
| 235 | w.print("name: '{s}'. creation stack trace:\n", .{step.name}) catch {}; | ||
| 236 | std.debug.writeStackTrace(step.getStackTrace(), w, ally, debug_info, tty_config) catch |err| { | ||
| 237 | stderr.writer().print("Unable to dump stack trace: {s}\n", .{@errorName(err)}) catch {}; | ||
| 238 | return; | ||
| 239 | }; | ||
| 240 | } | ||
| 241 | |||
| 94 | const Step = @This(); | 242 | const Step = @This(); |
| 95 | const std = @import("../std.zig"); | 243 | const std = @import("../std.zig"); |
| 96 | const Build = std.Build; | 244 | const Build = std.Build; |
| 97 | const Allocator = std.mem.Allocator; | 245 | const Allocator = std.mem.Allocator; |
| 246 | const assert = std.debug.assert; | ||
| 247 | const builtin = @import("builtin"); | ||
| 248 | |||
| 249 | pub fn evalChildProcess(s: *Step, argv: []const []const u8) !void { | ||
| 250 | const arena = s.owner.allocator; | ||
| 251 | |||
| 252 | try handleChildProcUnsupported(s, null, argv); | ||
| 253 | try handleVerbose(s.owner, null, argv); | ||
| 254 | |||
| 255 | const result = std.ChildProcess.exec(.{ | ||
| 256 | .allocator = arena, | ||
| 257 | .argv = argv, | ||
| 258 | }) catch |err| return s.fail("unable to spawn {s}: {s}", .{ argv[0], @errorName(err) }); | ||
| 259 | |||
| 260 | if (result.stderr.len > 0) { | ||
| 261 | try s.result_error_msgs.append(arena, result.stderr); | ||
| 262 | } | ||
| 263 | |||
| 264 | try handleChildProcessTerm(s, result.term, null, argv); | ||
| 265 | } | ||
| 266 | |||
| 267 | pub fn fail(step: *Step, comptime fmt: []const u8, args: anytype) error{ OutOfMemory, MakeFailed } { | ||
| 268 | try step.addError(fmt, args); | ||
| 269 | return error.MakeFailed; | ||
| 270 | } | ||
| 271 | |||
| 272 | pub fn addError(step: *Step, comptime fmt: []const u8, args: anytype) error{OutOfMemory}!void { | ||
| 273 | const arena = step.owner.allocator; | ||
| 274 | const msg = try std.fmt.allocPrint(arena, fmt, args); | ||
| 275 | try step.result_error_msgs.append(arena, msg); | ||
| 276 | } | ||
| 277 | |||
| 278 | /// Assumes that argv contains `--listen=-` and that the process being spawned | ||
| 279 | /// is the zig compiler - the same version that compiled the build runner. | ||
| 280 | pub fn evalZigProcess( | ||
| 281 | s: *Step, | ||
| 282 | argv: []const []const u8, | ||
| 283 | prog_node: *std.Progress.Node, | ||
| 284 | ) ![]const u8 { | ||
| 285 | assert(argv.len != 0); | ||
| 286 | const b = s.owner; | ||
| 287 | const arena = b.allocator; | ||
| 288 | const gpa = arena; | ||
| 289 | |||
| 290 | try handleChildProcUnsupported(s, null, argv); | ||
| 291 | try handleVerbose(s.owner, null, argv); | ||
| 292 | |||
| 293 | var child = std.ChildProcess.init(argv, arena); | ||
| 294 | child.env_map = b.env_map; | ||
| 295 | child.stdin_behavior = .Pipe; | ||
| 296 | child.stdout_behavior = .Pipe; | ||
| 297 | child.stderr_behavior = .Pipe; | ||
| 298 | child.request_resource_usage_statistics = true; | ||
| 299 | |||
| 300 | child.spawn() catch |err| return s.fail("unable to spawn {s}: {s}", .{ | ||
| 301 | argv[0], @errorName(err), | ||
| 302 | }); | ||
| 303 | var timer = try std.time.Timer.start(); | ||
| 304 | |||
| 305 | var poller = std.io.poll(gpa, enum { stdout, stderr }, .{ | ||
| 306 | .stdout = child.stdout.?, | ||
| 307 | .stderr = child.stderr.?, | ||
| 308 | }); | ||
| 309 | defer poller.deinit(); | ||
| 310 | |||
| 311 | try sendMessage(child.stdin.?, .update); | ||
| 312 | try sendMessage(child.stdin.?, .exit); | ||
| 313 | |||
| 314 | const Header = std.zig.Server.Message.Header; | ||
| 315 | var result: ?[]const u8 = null; | ||
| 316 | |||
| 317 | var node_name: std.ArrayListUnmanaged(u8) = .{}; | ||
| 318 | defer node_name.deinit(gpa); | ||
| 319 | var sub_prog_node = prog_node.start("", 0); | ||
| 320 | defer sub_prog_node.end(); | ||
| 321 | |||
| 322 | const stdout = poller.fifo(.stdout); | ||
| 323 | |||
| 324 | poll: while (true) { | ||
| 325 | while (stdout.readableLength() < @sizeOf(Header)) { | ||
| 326 | if (!(try poller.poll())) break :poll; | ||
| 327 | } | ||
| 328 | const header = stdout.reader().readStruct(Header) catch unreachable; | ||
| 329 | while (stdout.readableLength() < header.bytes_len) { | ||
| 330 | if (!(try poller.poll())) break :poll; | ||
| 331 | } | ||
| 332 | const body = stdout.readableSliceOfLen(header.bytes_len); | ||
| 333 | |||
| 334 | switch (header.tag) { | ||
| 335 | .zig_version => { | ||
| 336 | if (!std.mem.eql(u8, builtin.zig_version_string, body)) { | ||
| 337 | return s.fail( | ||
| 338 | "zig version mismatch build runner vs compiler: '{s}' vs '{s}'", | ||
| 339 | .{ builtin.zig_version_string, body }, | ||
| 340 | ); | ||
| 341 | } | ||
| 342 | }, | ||
| 343 | .error_bundle => { | ||
| 344 | const EbHdr = std.zig.Server.Message.ErrorBundle; | ||
| 345 | const eb_hdr = @ptrCast(*align(1) const EbHdr, body); | ||
| 346 | const extra_bytes = | ||
| 347 | body[@sizeOf(EbHdr)..][0 .. @sizeOf(u32) * eb_hdr.extra_len]; | ||
| 348 | const string_bytes = | ||
| 349 | body[@sizeOf(EbHdr) + extra_bytes.len ..][0..eb_hdr.string_bytes_len]; | ||
| 350 | // TODO: use @ptrCast when the compiler supports it | ||
| 351 | const unaligned_extra = std.mem.bytesAsSlice(u32, extra_bytes); | ||
| 352 | const extra_array = try arena.alloc(u32, unaligned_extra.len); | ||
| 353 | // TODO: use @memcpy when it supports slices | ||
| 354 | for (extra_array, unaligned_extra) |*dst, src| dst.* = src; | ||
| 355 | s.result_error_bundle = .{ | ||
| 356 | .string_bytes = try arena.dupe(u8, string_bytes), | ||
| 357 | .extra = extra_array, | ||
| 358 | }; | ||
| 359 | }, | ||
| 360 | .progress => { | ||
| 361 | node_name.clearRetainingCapacity(); | ||
| 362 | try node_name.appendSlice(gpa, body); | ||
| 363 | sub_prog_node.setName(node_name.items); | ||
| 364 | }, | ||
| 365 | .emit_bin_path => { | ||
| 366 | const EbpHdr = std.zig.Server.Message.EmitBinPath; | ||
| 367 | const ebp_hdr = @ptrCast(*align(1) const EbpHdr, body); | ||
| 368 | s.result_cached = ebp_hdr.flags.cache_hit; | ||
| 369 | result = try arena.dupe(u8, body[@sizeOf(EbpHdr)..]); | ||
| 370 | }, | ||
| 371 | else => {}, // ignore other messages | ||
| 372 | } | ||
| 373 | |||
| 374 | stdout.discard(body.len); | ||
| 375 | } | ||
| 376 | |||
| 377 | const stderr = poller.fifo(.stderr); | ||
| 378 | if (stderr.readableLength() > 0) { | ||
| 379 | try s.result_error_msgs.append(arena, try stderr.toOwnedSlice()); | ||
| 380 | } | ||
| 381 | |||
| 382 | // Send EOF to stdin. | ||
| 383 | child.stdin.?.close(); | ||
| 384 | child.stdin = null; | ||
| 385 | |||
| 386 | const term = child.wait() catch |err| { | ||
| 387 | return s.fail("unable to wait for {s}: {s}", .{ argv[0], @errorName(err) }); | ||
| 388 | }; | ||
| 389 | s.result_duration_ns = timer.read(); | ||
| 390 | s.result_peak_rss = child.resource_usage_statistics.getMaxRss() orelse 0; | ||
| 391 | |||
| 392 | // Special handling for CompileStep that is expecting compile errors. | ||
| 393 | if (s.cast(Build.CompileStep)) |compile| switch (term) { | ||
| 394 | .Exited => { | ||
| 395 | // Note that the exit code may be 0 in this case due to the | ||
| 396 | // compiler server protocol. | ||
| 397 | if (compile.expect_errors.len != 0 and s.result_error_bundle.errorMessageCount() > 0) { | ||
| 398 | return error.NeedCompileErrorCheck; | ||
| 399 | } | ||
| 400 | }, | ||
| 401 | else => {}, | ||
| 402 | }; | ||
| 403 | |||
| 404 | try handleChildProcessTerm(s, term, null, argv); | ||
| 405 | |||
| 406 | if (s.result_error_bundle.errorMessageCount() > 0) { | ||
| 407 | return s.fail("the following command failed with {d} compilation errors:\n{s}", .{ | ||
| 408 | s.result_error_bundle.errorMessageCount(), | ||
| 409 | try allocPrintCmd(arena, null, argv), | ||
| 410 | }); | ||
| 411 | } | ||
| 412 | |||
| 413 | return result orelse return s.fail( | ||
| 414 | "the following command failed to communicate the compilation result:\n{s}", | ||
| 415 | .{try allocPrintCmd(arena, null, argv)}, | ||
| 416 | ); | ||
| 417 | } | ||
| 418 | |||
| 419 | fn sendMessage(file: std.fs.File, tag: std.zig.Client.Message.Tag) !void { | ||
| 420 | const header: std.zig.Client.Message.Header = .{ | ||
| 421 | .tag = tag, | ||
| 422 | .bytes_len = 0, | ||
| 423 | }; | ||
| 424 | try file.writeAll(std.mem.asBytes(&header)); | ||
| 425 | } | ||
| 426 | |||
| 427 | pub fn handleVerbose( | ||
| 428 | b: *Build, | ||
| 429 | opt_cwd: ?[]const u8, | ||
| 430 | argv: []const []const u8, | ||
| 431 | ) error{OutOfMemory}!void { | ||
| 432 | return handleVerbose2(b, opt_cwd, null, argv); | ||
| 433 | } | ||
| 434 | |||
| 435 | pub fn handleVerbose2( | ||
| 436 | b: *Build, | ||
| 437 | opt_cwd: ?[]const u8, | ||
| 438 | opt_env: ?*const std.process.EnvMap, | ||
| 439 | argv: []const []const u8, | ||
| 440 | ) error{OutOfMemory}!void { | ||
| 441 | if (b.verbose) { | ||
| 442 | // Intention of verbose is to print all sub-process command lines to | ||
| 443 | // stderr before spawning them. | ||
| 444 | const text = try allocPrintCmd2(b.allocator, opt_cwd, opt_env, argv); | ||
| 445 | std.debug.print("{s}\n", .{text}); | ||
| 446 | } | ||
| 447 | } | ||
| 448 | |||
| 449 | pub inline fn handleChildProcUnsupported( | ||
| 450 | s: *Step, | ||
| 451 | opt_cwd: ?[]const u8, | ||
| 452 | argv: []const []const u8, | ||
| 453 | ) error{ OutOfMemory, MakeFailed }!void { | ||
| 454 | if (!std.process.can_spawn) { | ||
| 455 | return s.fail( | ||
| 456 | "unable to execute the following command: host cannot spawn child processes\n{s}", | ||
| 457 | .{try allocPrintCmd(s.owner.allocator, opt_cwd, argv)}, | ||
| 458 | ); | ||
| 459 | } | ||
| 460 | } | ||
| 461 | |||
| 462 | pub fn handleChildProcessTerm( | ||
| 463 | s: *Step, | ||
| 464 | term: std.ChildProcess.Term, | ||
| 465 | opt_cwd: ?[]const u8, | ||
| 466 | argv: []const []const u8, | ||
| 467 | ) error{ MakeFailed, OutOfMemory }!void { | ||
| 468 | const arena = s.owner.allocator; | ||
| 469 | switch (term) { | ||
| 470 | .Exited => |code| { | ||
| 471 | if (code != 0) { | ||
| 472 | return s.fail( | ||
| 473 | "the following command exited with error code {d}:\n{s}", | ||
| 474 | .{ code, try allocPrintCmd(arena, opt_cwd, argv) }, | ||
| 475 | ); | ||
| 476 | } | ||
| 477 | }, | ||
| 478 | .Signal, .Stopped, .Unknown => { | ||
| 479 | return s.fail( | ||
| 480 | "the following command terminated unexpectedly:\n{s}", | ||
| 481 | .{try allocPrintCmd(arena, opt_cwd, argv)}, | ||
| 482 | ); | ||
| 483 | }, | ||
| 484 | } | ||
| 485 | } | ||
| 486 | |||
| 487 | pub fn allocPrintCmd( | ||
| 488 | arena: Allocator, | ||
| 489 | opt_cwd: ?[]const u8, | ||
| 490 | argv: []const []const u8, | ||
| 491 | ) Allocator.Error![]u8 { | ||
| 492 | return allocPrintCmd2(arena, opt_cwd, null, argv); | ||
| 493 | } | ||
| 494 | |||
| 495 | pub fn allocPrintCmd2( | ||
| 496 | arena: Allocator, | ||
| 497 | opt_cwd: ?[]const u8, | ||
| 498 | opt_env: ?*const std.process.EnvMap, | ||
| 499 | argv: []const []const u8, | ||
| 500 | ) Allocator.Error![]u8 { | ||
| 501 | var buf: std.ArrayListUnmanaged(u8) = .{}; | ||
| 502 | if (opt_cwd) |cwd| try buf.writer(arena).print("cd {s} && ", .{cwd}); | ||
| 503 | if (opt_env) |env| { | ||
| 504 | const process_env_map = std.process.getEnvMap(arena) catch std.process.EnvMap.init(arena); | ||
| 505 | var it = env.iterator(); | ||
| 506 | while (it.next()) |entry| { | ||
| 507 | const key = entry.key_ptr.*; | ||
| 508 | const value = entry.value_ptr.*; | ||
| 509 | if (process_env_map.get(key)) |process_value| { | ||
| 510 | if (std.mem.eql(u8, value, process_value)) continue; | ||
| 511 | } | ||
| 512 | try buf.writer(arena).print("{s}={s} ", .{ key, value }); | ||
| 513 | } | ||
| 514 | } | ||
| 515 | for (argv) |arg| { | ||
| 516 | try buf.writer(arena).print("{s} ", .{arg}); | ||
| 517 | } | ||
| 518 | return buf.toOwnedSlice(arena); | ||
| 519 | } | ||
| 520 | |||
| 521 | pub fn cacheHit(s: *Step, man: *std.Build.Cache.Manifest) !bool { | ||
| 522 | s.result_cached = man.hit() catch |err| return failWithCacheError(s, man, err); | ||
| 523 | return s.result_cached; | ||
| 524 | } | ||
| 525 | |||
| 526 | fn failWithCacheError(s: *Step, man: *const std.Build.Cache.Manifest, err: anyerror) anyerror { | ||
| 527 | const i = man.failed_file_index orelse return err; | ||
| 528 | const pp = man.files.items[i].prefixed_path orelse return err; | ||
| 529 | const prefix = man.cache.prefixes()[pp.prefix].path orelse ""; | ||
| 530 | return s.fail("{s}: {s}/{s}", .{ @errorName(err), prefix, pp.sub_path }); | ||
| 531 | } | ||
| 532 | |||
| 533 | pub fn writeManifest(s: *Step, man: *std.Build.Cache.Manifest) !void { | ||
| 534 | if (s.test_results.isSuccess()) { | ||
| 535 | man.writeManifest() catch |err| { | ||
| 536 | try s.addError("unable to write cache manifest: {s}", .{@errorName(err)}); | ||
| 537 | }; | ||
| 538 | } | ||
| 539 | } |
lib/std/Build/TranslateCStep.zig+30-22| ... | @@ -11,7 +11,6 @@ const TranslateCStep = @This(); | ... | @@ -11,7 +11,6 @@ const TranslateCStep = @This(); |
| 11 | pub const base_id = .translate_c; | 11 | pub const base_id = .translate_c; |
| 12 | 12 | ||
| 13 | step: Step, | 13 | step: Step, |
| 14 | builder: *std.Build, | ||
| 15 | source: std.Build.FileSource, | 14 | source: std.Build.FileSource, |
| 16 | include_dirs: std.ArrayList([]const u8), | 15 | include_dirs: std.ArrayList([]const u8), |
| 17 | c_macros: std.ArrayList([]const u8), | 16 | c_macros: std.ArrayList([]const u8), |
| ... | @@ -26,15 +25,19 @@ pub const Options = struct { | ... | @@ -26,15 +25,19 @@ pub const Options = struct { |
| 26 | optimize: std.builtin.OptimizeMode, | 25 | optimize: std.builtin.OptimizeMode, |
| 27 | }; | 26 | }; |
| 28 | 27 | ||
| 29 | pub fn create(builder: *std.Build, options: Options) *TranslateCStep { | 28 | pub fn create(owner: *std.Build, options: Options) *TranslateCStep { |
| 30 | const self = builder.allocator.create(TranslateCStep) catch @panic("OOM"); | 29 | const self = owner.allocator.create(TranslateCStep) catch @panic("OOM"); |
| 31 | const source = options.source_file.dupe(builder); | 30 | const source = options.source_file.dupe(owner); |
| 32 | self.* = TranslateCStep{ | 31 | self.* = TranslateCStep{ |
| 33 | .step = Step.init(.translate_c, "translate-c", builder.allocator, make), | 32 | .step = Step.init(.{ |
| 34 | .builder = builder, | 33 | .id = .translate_c, |
| 34 | .name = "translate-c", | ||
| 35 | .owner = owner, | ||
| 36 | .makeFn = make, | ||
| 37 | }), | ||
| 35 | .source = source, | 38 | .source = source, |
| 36 | .include_dirs = std.ArrayList([]const u8).init(builder.allocator), | 39 | .include_dirs = std.ArrayList([]const u8).init(owner.allocator), |
| 37 | .c_macros = std.ArrayList([]const u8).init(builder.allocator), | 40 | .c_macros = std.ArrayList([]const u8).init(owner.allocator), |
| 38 | .out_basename = undefined, | 41 | .out_basename = undefined, |
| 39 | .target = options.target, | 42 | .target = options.target, |
| 40 | .optimize = options.optimize, | 43 | .optimize = options.optimize, |
| ... | @@ -54,7 +57,7 @@ pub const AddExecutableOptions = struct { | ... | @@ -54,7 +57,7 @@ pub const AddExecutableOptions = struct { |
| 54 | 57 | ||
| 55 | /// Creates a step to build an executable from the translated source. | 58 | /// Creates a step to build an executable from the translated source. |
| 56 | pub fn addExecutable(self: *TranslateCStep, options: AddExecutableOptions) *CompileStep { | 59 | pub fn addExecutable(self: *TranslateCStep, options: AddExecutableOptions) *CompileStep { |
| 57 | return self.builder.addExecutable(.{ | 60 | return self.step.owner.addExecutable(.{ |
| 58 | .root_source_file = .{ .generated = &self.output_file }, | 61 | .root_source_file = .{ .generated = &self.output_file }, |
| 59 | .name = options.name orelse "translated_c", | 62 | .name = options.name orelse "translated_c", |
| 60 | .version = options.version, | 63 | .version = options.version, |
| ... | @@ -65,43 +68,49 @@ pub fn addExecutable(self: *TranslateCStep, options: AddExecutableOptions) *Comp | ... | @@ -65,43 +68,49 @@ pub fn addExecutable(self: *TranslateCStep, options: AddExecutableOptions) *Comp |
| 65 | } | 68 | } |
| 66 | 69 | ||
| 67 | pub fn addIncludeDir(self: *TranslateCStep, include_dir: []const u8) void { | 70 | pub fn addIncludeDir(self: *TranslateCStep, include_dir: []const u8) void { |
| 68 | self.include_dirs.append(self.builder.dupePath(include_dir)) catch @panic("OOM"); | 71 | self.include_dirs.append(self.step.owner.dupePath(include_dir)) catch @panic("OOM"); |
| 69 | } | 72 | } |
| 70 | 73 | ||
| 71 | pub fn addCheckFile(self: *TranslateCStep, expected_matches: []const []const u8) *CheckFileStep { | 74 | pub fn addCheckFile(self: *TranslateCStep, expected_matches: []const []const u8) *CheckFileStep { |
| 72 | return CheckFileStep.create(self.builder, .{ .generated = &self.output_file }, self.builder.dupeStrings(expected_matches)); | 75 | return CheckFileStep.create( |
| 76 | self.step.owner, | ||
| 77 | .{ .generated = &self.output_file }, | ||
| 78 | .{ .expected_matches = expected_matches }, | ||
| 79 | ); | ||
| 73 | } | 80 | } |
| 74 | 81 | ||
| 75 | /// If the value is omitted, it is set to 1. | 82 | /// If the value is omitted, it is set to 1. |
| 76 | /// `name` and `value` need not live longer than the function call. | 83 | /// `name` and `value` need not live longer than the function call. |
| 77 | pub fn defineCMacro(self: *TranslateCStep, name: []const u8, value: ?[]const u8) void { | 84 | pub fn defineCMacro(self: *TranslateCStep, name: []const u8, value: ?[]const u8) void { |
| 78 | const macro = std.Build.constructCMacro(self.builder.allocator, name, value); | 85 | const macro = std.Build.constructCMacro(self.step.owner.allocator, name, value); |
| 79 | self.c_macros.append(macro) catch @panic("OOM"); | 86 | self.c_macros.append(macro) catch @panic("OOM"); |
| 80 | } | 87 | } |
| 81 | 88 | ||
| 82 | /// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1. | 89 | /// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1. |
| 83 | pub fn defineCMacroRaw(self: *TranslateCStep, name_and_value: []const u8) void { | 90 | pub fn defineCMacroRaw(self: *TranslateCStep, name_and_value: []const u8) void { |
| 84 | self.c_macros.append(self.builder.dupe(name_and_value)) catch @panic("OOM"); | 91 | self.c_macros.append(self.step.owner.dupe(name_and_value)) catch @panic("OOM"); |
| 85 | } | 92 | } |
| 86 | 93 | ||
| 87 | fn make(step: *Step) !void { | 94 | fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 95 | const b = step.owner; | ||
| 88 | const self = @fieldParentPtr(TranslateCStep, "step", step); | 96 | const self = @fieldParentPtr(TranslateCStep, "step", step); |
| 89 | 97 | ||
| 90 | var argv_list = std.ArrayList([]const u8).init(self.builder.allocator); | 98 | var argv_list = std.ArrayList([]const u8).init(b.allocator); |
| 91 | try argv_list.append(self.builder.zig_exe); | 99 | try argv_list.append(b.zig_exe); |
| 92 | try argv_list.append("translate-c"); | 100 | try argv_list.append("translate-c"); |
| 93 | try argv_list.append("-lc"); | 101 | try argv_list.append("-lc"); |
| 94 | 102 | ||
| 95 | try argv_list.append("--enable-cache"); | 103 | try argv_list.append("--enable-cache"); |
| 104 | try argv_list.append("--listen=-"); | ||
| 96 | 105 | ||
| 97 | if (!self.target.isNative()) { | 106 | if (!self.target.isNative()) { |
| 98 | try argv_list.append("-target"); | 107 | try argv_list.append("-target"); |
| 99 | try argv_list.append(try self.target.zigTriple(self.builder.allocator)); | 108 | try argv_list.append(try self.target.zigTriple(b.allocator)); |
| 100 | } | 109 | } |
| 101 | 110 | ||
| 102 | switch (self.optimize) { | 111 | switch (self.optimize) { |
| 103 | .Debug => {}, // Skip since it's the default. | 112 | .Debug => {}, // Skip since it's the default. |
| 104 | else => try argv_list.append(self.builder.fmt("-O{s}", .{@tagName(self.optimize)})), | 113 | else => try argv_list.append(b.fmt("-O{s}", .{@tagName(self.optimize)})), |
| 105 | } | 114 | } |
| 106 | 115 | ||
| 107 | for (self.include_dirs.items) |include_dir| { | 116 | for (self.include_dirs.items) |include_dir| { |
| ... | @@ -114,16 +123,15 @@ fn make(step: *Step) !void { | ... | @@ -114,16 +123,15 @@ fn make(step: *Step) !void { |
| 114 | try argv_list.append(c_macro); | 123 | try argv_list.append(c_macro); |
| 115 | } | 124 | } |
| 116 | 125 | ||
| 117 | try argv_list.append(self.source.getPath(self.builder)); | 126 | try argv_list.append(self.source.getPath(b)); |
| 118 | 127 | ||
| 119 | const output_path_nl = try self.builder.execFromStep(argv_list.items, &self.step); | 128 | const output_path = try step.evalZigProcess(argv_list.items, prog_node); |
| 120 | const output_path = mem.trimRight(u8, output_path_nl, "\r\n"); | ||
| 121 | 129 | ||
| 122 | self.out_basename = fs.path.basename(output_path); | 130 | self.out_basename = fs.path.basename(output_path); |
| 123 | const output_dir = fs.path.dirname(output_path).?; | 131 | const output_dir = fs.path.dirname(output_path).?; |
| 124 | 132 | ||
| 125 | self.output_file.path = try fs.path.join( | 133 | self.output_file.path = try fs.path.join( |
| 126 | self.builder.allocator, | 134 | b.allocator, |
| 127 | &[_][]const u8{ output_dir, self.out_basename }, | 135 | &[_][]const u8{ output_dir, self.out_basename }, |
| 128 | ); | 136 | ); |
| 129 | } | 137 | } |
lib/std/Build/WriteFileStep.zig+145-67| ... | @@ -10,11 +10,11 @@ | ... | @@ -10,11 +10,11 @@ |
| 10 | //! control. | 10 | //! control. |
| 11 | 11 | ||
| 12 | step: Step, | 12 | step: Step, |
| 13 | builder: *std.Build, | ||
| 14 | /// The elements here are pointers because we need stable pointers for the | 13 | /// The elements here are pointers because we need stable pointers for the |
| 15 | /// GeneratedFile field. | 14 | /// GeneratedFile field. |
| 16 | files: std.ArrayListUnmanaged(*File), | 15 | files: std.ArrayListUnmanaged(*File), |
| 17 | output_source_files: std.ArrayListUnmanaged(OutputSourceFile), | 16 | output_source_files: std.ArrayListUnmanaged(OutputSourceFile), |
| 17 | generated_directory: std.Build.GeneratedFile, | ||
| 18 | 18 | ||
| 19 | pub const base_id = .write_file; | 19 | pub const base_id = .write_file; |
| 20 | 20 | ||
| ... | @@ -34,24 +34,34 @@ pub const Contents = union(enum) { | ... | @@ -34,24 +34,34 @@ pub const Contents = union(enum) { |
| 34 | copy: std.Build.FileSource, | 34 | copy: std.Build.FileSource, |
| 35 | }; | 35 | }; |
| 36 | 36 | ||
| 37 | pub fn init(builder: *std.Build) WriteFileStep { | 37 | pub fn create(owner: *std.Build) *WriteFileStep { |
| 38 | return .{ | 38 | const wf = owner.allocator.create(WriteFileStep) catch @panic("OOM"); |
| 39 | .builder = builder, | 39 | wf.* = .{ |
| 40 | .step = Step.init(.write_file, "writefile", builder.allocator, make), | 40 | .step = Step.init(.{ |
| 41 | .id = .write_file, | ||
| 42 | .name = "WriteFile", | ||
| 43 | .owner = owner, | ||
| 44 | .makeFn = make, | ||
| 45 | }), | ||
| 41 | .files = .{}, | 46 | .files = .{}, |
| 42 | .output_source_files = .{}, | 47 | .output_source_files = .{}, |
| 48 | .generated_directory = .{ .step = &wf.step }, | ||
| 43 | }; | 49 | }; |
| 50 | return wf; | ||
| 44 | } | 51 | } |
| 45 | 52 | ||
| 46 | pub fn add(wf: *WriteFileStep, sub_path: []const u8, bytes: []const u8) void { | 53 | pub fn add(wf: *WriteFileStep, sub_path: []const u8, bytes: []const u8) void { |
| 47 | const gpa = wf.builder.allocator; | 54 | const b = wf.step.owner; |
| 55 | const gpa = b.allocator; | ||
| 48 | const file = gpa.create(File) catch @panic("OOM"); | 56 | const file = gpa.create(File) catch @panic("OOM"); |
| 49 | file.* = .{ | 57 | file.* = .{ |
| 50 | .generated_file = .{ .step = &wf.step }, | 58 | .generated_file = .{ .step = &wf.step }, |
| 51 | .sub_path = wf.builder.dupePath(sub_path), | 59 | .sub_path = b.dupePath(sub_path), |
| 52 | .contents = .{ .bytes = wf.builder.dupe(bytes) }, | 60 | .contents = .{ .bytes = b.dupe(bytes) }, |
| 53 | }; | 61 | }; |
| 54 | wf.files.append(gpa, file) catch @panic("OOM"); | 62 | wf.files.append(gpa, file) catch @panic("OOM"); |
| 63 | |||
| 64 | wf.maybeUpdateName(); | ||
| 55 | } | 65 | } |
| 56 | 66 | ||
| 57 | /// Place the file into the generated directory within the local cache, | 67 | /// Place the file into the generated directory within the local cache, |
| ... | @@ -62,14 +72,18 @@ pub fn add(wf: *WriteFileStep, sub_path: []const u8, bytes: []const u8) void { | ... | @@ -62,14 +72,18 @@ pub fn add(wf: *WriteFileStep, sub_path: []const u8, bytes: []const u8) void { |
| 62 | /// required sub-path exists. | 72 | /// required sub-path exists. |
| 63 | /// This is the option expected to be used most commonly with `addCopyFile`. | 73 | /// This is the option expected to be used most commonly with `addCopyFile`. |
| 64 | pub fn addCopyFile(wf: *WriteFileStep, source: std.Build.FileSource, sub_path: []const u8) void { | 74 | pub fn addCopyFile(wf: *WriteFileStep, source: std.Build.FileSource, sub_path: []const u8) void { |
| 65 | const gpa = wf.builder.allocator; | 75 | const b = wf.step.owner; |
| 76 | const gpa = b.allocator; | ||
| 66 | const file = gpa.create(File) catch @panic("OOM"); | 77 | const file = gpa.create(File) catch @panic("OOM"); |
| 67 | file.* = .{ | 78 | file.* = .{ |
| 68 | .generated_file = .{ .step = &wf.step }, | 79 | .generated_file = .{ .step = &wf.step }, |
| 69 | .sub_path = wf.builder.dupePath(sub_path), | 80 | .sub_path = b.dupePath(sub_path), |
| 70 | .contents = .{ .copy = source }, | 81 | .contents = .{ .copy = source }, |
| 71 | }; | 82 | }; |
| 72 | wf.files.append(gpa, file) catch @panic("OOM"); | 83 | wf.files.append(gpa, file) catch @panic("OOM"); |
| 84 | |||
| 85 | wf.maybeUpdateName(); | ||
| 86 | source.addStepDependencies(&wf.step); | ||
| 73 | } | 87 | } |
| 74 | 88 | ||
| 75 | /// A path relative to the package root. | 89 | /// A path relative to the package root. |
| ... | @@ -79,10 +93,26 @@ pub fn addCopyFile(wf: *WriteFileStep, source: std.Build.FileSource, sub_path: [ | ... | @@ -79,10 +93,26 @@ pub fn addCopyFile(wf: *WriteFileStep, source: std.Build.FileSource, sub_path: [ |
| 79 | /// those changes to version control. | 93 | /// those changes to version control. |
| 80 | /// A file added this way is not available with `getFileSource`. | 94 | /// A file added this way is not available with `getFileSource`. |
| 81 | pub fn addCopyFileToSource(wf: *WriteFileStep, source: std.Build.FileSource, sub_path: []const u8) void { | 95 | pub fn addCopyFileToSource(wf: *WriteFileStep, source: std.Build.FileSource, sub_path: []const u8) void { |
| 82 | wf.output_source_files.append(wf.builder.allocator, .{ | 96 | const b = wf.step.owner; |
| 97 | wf.output_source_files.append(b.allocator, .{ | ||
| 83 | .contents = .{ .copy = source }, | 98 | .contents = .{ .copy = source }, |
| 84 | .sub_path = sub_path, | 99 | .sub_path = sub_path, |
| 85 | }) catch @panic("OOM"); | 100 | }) catch @panic("OOM"); |
| 101 | source.addStepDependencies(&wf.step); | ||
| 102 | } | ||
| 103 | |||
| 104 | /// A path relative to the package root. | ||
| 105 | /// Be careful with this because it updates source files. This should not be | ||
| 106 | /// used as part of the normal build process, but as a utility occasionally | ||
| 107 | /// run by a developer with intent to modify source files and then commit | ||
| 108 | /// those changes to version control. | ||
| 109 | /// A file added this way is not available with `getFileSource`. | ||
| 110 | pub fn addBytesToSource(wf: *WriteFileStep, bytes: []const u8, sub_path: []const u8) void { | ||
| 111 | const b = wf.step.owner; | ||
| 112 | wf.output_source_files.append(b.allocator, .{ | ||
| 113 | .contents = .{ .bytes = bytes }, | ||
| 114 | .sub_path = sub_path, | ||
| 115 | }) catch @panic("OOM"); | ||
| 86 | } | 116 | } |
| 87 | 117 | ||
| 88 | /// Gets a file source for the given sub_path. If the file does not exist, returns `null`. | 118 | /// Gets a file source for the given sub_path. If the file does not exist, returns `null`. |
| ... | @@ -95,21 +125,63 @@ pub fn getFileSource(wf: *WriteFileStep, sub_path: []const u8) ?std.Build.FileSo | ... | @@ -95,21 +125,63 @@ pub fn getFileSource(wf: *WriteFileStep, sub_path: []const u8) ?std.Build.FileSo |
| 95 | return null; | 125 | return null; |
| 96 | } | 126 | } |
| 97 | 127 | ||
| 98 | fn make(step: *Step) !void { | 128 | /// Returns a `FileSource` representing the base directory that contains all the |
| 129 | /// files from this `WriteFileStep`. | ||
| 130 | pub fn getDirectorySource(wf: *WriteFileStep) std.Build.FileSource { | ||
| 131 | return .{ .generated = &wf.generated_directory }; | ||
| 132 | } | ||
| 133 | |||
| 134 | fn maybeUpdateName(wf: *WriteFileStep) void { | ||
| 135 | if (wf.files.items.len == 1) { | ||
| 136 | // First time adding a file; update name. | ||
| 137 | if (std.mem.eql(u8, wf.step.name, "WriteFile")) { | ||
| 138 | wf.step.name = wf.step.owner.fmt("WriteFile {s}", .{wf.files.items[0].sub_path}); | ||
| 139 | } | ||
| 140 | } | ||
| 141 | } | ||
| 142 | |||
| 143 | fn make(step: *Step, prog_node: *std.Progress.Node) !void { | ||
| 144 | _ = prog_node; | ||
| 145 | const b = step.owner; | ||
| 99 | const wf = @fieldParentPtr(WriteFileStep, "step", step); | 146 | const wf = @fieldParentPtr(WriteFileStep, "step", step); |
| 100 | 147 | ||
| 101 | // Writing to source files is kind of an extra capability of this | 148 | // Writing to source files is kind of an extra capability of this |
| 102 | // WriteFileStep - arguably it should be a different step. But anyway here | 149 | // WriteFileStep - arguably it should be a different step. But anyway here |
| 103 | // it is, it happens unconditionally and does not interact with the other | 150 | // it is, it happens unconditionally and does not interact with the other |
| 104 | // files here. | 151 | // files here. |
| 152 | var any_miss = false; | ||
| 105 | for (wf.output_source_files.items) |output_source_file| { | 153 | for (wf.output_source_files.items) |output_source_file| { |
| 106 | const basename = fs.path.basename(output_source_file.sub_path); | ||
| 107 | if (fs.path.dirname(output_source_file.sub_path)) |dirname| { | 154 | if (fs.path.dirname(output_source_file.sub_path)) |dirname| { |
| 108 | var dir = try wf.builder.build_root.handle.makeOpenPath(dirname, .{}); | 155 | b.build_root.handle.makePath(dirname) catch |err| { |
| 109 | defer dir.close(); | 156 | return step.fail("unable to make path '{}{s}': {s}", .{ |
| 110 | try writeFile(wf, dir, output_source_file.contents, basename); | 157 | b.build_root, dirname, @errorName(err), |
| 111 | } else { | 158 | }); |
| 112 | try writeFile(wf, wf.builder.build_root.handle, output_source_file.contents, basename); | 159 | }; |
| 160 | } | ||
| 161 | switch (output_source_file.contents) { | ||
| 162 | .bytes => |bytes| { | ||
| 163 | b.build_root.handle.writeFile(output_source_file.sub_path, bytes) catch |err| { | ||
| 164 | return step.fail("unable to write file '{}{s}': {s}", .{ | ||
| 165 | b.build_root, output_source_file.sub_path, @errorName(err), | ||
| 166 | }); | ||
| 167 | }; | ||
| 168 | any_miss = true; | ||
| 169 | }, | ||
| 170 | .copy => |file_source| { | ||
| 171 | const source_path = file_source.getPath(b); | ||
| 172 | const prev_status = fs.Dir.updateFile( | ||
| 173 | fs.cwd(), | ||
| 174 | source_path, | ||
| 175 | b.build_root.handle, | ||
| 176 | output_source_file.sub_path, | ||
| 177 | .{}, | ||
| 178 | ) catch |err| { | ||
| 179 | return step.fail("unable to update file from '{s}' to '{}{s}': {s}", .{ | ||
| 180 | source_path, b.build_root, output_source_file.sub_path, @errorName(err), | ||
| 181 | }); | ||
| 182 | }; | ||
| 183 | any_miss = any_miss or prev_status == .stale; | ||
| 184 | }, | ||
| 113 | } | 185 | } |
| 114 | } | 186 | } |
| 115 | 187 | ||
| ... | @@ -120,7 +192,7 @@ fn make(step: *Step) !void { | ... | @@ -120,7 +192,7 @@ fn make(step: *Step) !void { |
| 120 | // If, for example, a hard-coded path was used as the location to put WriteFileStep | 192 | // If, for example, a hard-coded path was used as the location to put WriteFileStep |
| 121 | // files, then two WriteFileSteps executing in parallel might clobber each other. | 193 | // files, then two WriteFileSteps executing in parallel might clobber each other. |
| 122 | 194 | ||
| 123 | var man = wf.builder.cache.obtain(); | 195 | var man = b.cache.obtain(); |
| 124 | defer man.deinit(); | 196 | defer man.deinit(); |
| 125 | 197 | ||
| 126 | // Random bytes to make WriteFileStep unique. Refresh this with | 198 | // Random bytes to make WriteFileStep unique. Refresh this with |
| ... | @@ -135,76 +207,82 @@ fn make(step: *Step) !void { | ... | @@ -135,76 +207,82 @@ fn make(step: *Step) !void { |
| 135 | man.hash.addBytes(bytes); | 207 | man.hash.addBytes(bytes); |
| 136 | }, | 208 | }, |
| 137 | .copy => |file_source| { | 209 | .copy => |file_source| { |
| 138 | _ = try man.addFile(file_source.getPath(wf.builder), null); | 210 | _ = try man.addFile(file_source.getPath(b), null); |
| 139 | }, | 211 | }, |
| 140 | } | 212 | } |
| 141 | } | 213 | } |
| 142 | 214 | ||
| 143 | if (man.hit() catch |err| failWithCacheError(man, err)) { | 215 | if (try step.cacheHit(&man)) { |
| 144 | // Cache hit, skip writing file data. | ||
| 145 | const digest = man.final(); | 216 | const digest = man.final(); |
| 146 | for (wf.files.items) |file| { | 217 | for (wf.files.items) |file| { |
| 147 | file.generated_file.path = try wf.builder.cache_root.join( | 218 | file.generated_file.path = try b.cache_root.join(b.allocator, &.{ |
| 148 | wf.builder.allocator, | 219 | "o", &digest, file.sub_path, |
| 149 | &.{ "o", &digest, file.sub_path }, | 220 | }); |
| 150 | ); | ||
| 151 | } | 221 | } |
| 222 | wf.generated_directory.path = try b.cache_root.join(b.allocator, &.{ "o", &digest }); | ||
| 152 | return; | 223 | return; |
| 153 | } | 224 | } |
| 154 | 225 | ||
| 155 | const digest = man.final(); | 226 | const digest = man.final(); |
| 156 | const cache_path = "o" ++ fs.path.sep_str ++ digest; | 227 | const cache_path = "o" ++ fs.path.sep_str ++ digest; |
| 157 | 228 | ||
| 158 | var cache_dir = wf.builder.cache_root.handle.makeOpenPath(cache_path, .{}) catch |err| { | 229 | wf.generated_directory.path = try b.cache_root.join(b.allocator, &.{ "o", &digest }); |
| 159 | std.debug.print("unable to make path {s}: {s}\n", .{ cache_path, @errorName(err) }); | 230 | |
| 160 | return err; | 231 | var cache_dir = b.cache_root.handle.makeOpenPath(cache_path, .{}) catch |err| { |
| 232 | return step.fail("unable to make path '{}{s}': {s}", .{ | ||
| 233 | b.cache_root, cache_path, @errorName(err), | ||
| 234 | }); | ||
| 161 | }; | 235 | }; |
| 162 | defer cache_dir.close(); | 236 | defer cache_dir.close(); |
| 163 | 237 | ||
| 164 | for (wf.files.items) |file| { | 238 | for (wf.files.items) |file| { |
| 165 | const basename = fs.path.basename(file.sub_path); | ||
| 166 | if (fs.path.dirname(file.sub_path)) |dirname| { | 239 | if (fs.path.dirname(file.sub_path)) |dirname| { |
| 167 | var dir = try wf.builder.cache_root.handle.makeOpenPath(dirname, .{}); | 240 | cache_dir.makePath(dirname) catch |err| { |
| 168 | defer dir.close(); | 241 | return step.fail("unable to make path '{}{s}{c}{s}': {s}", .{ |
| 169 | try writeFile(wf, dir, file.contents, basename); | 242 | b.cache_root, cache_path, fs.path.sep, dirname, @errorName(err), |
| 170 | } else { | 243 | }); |
| 171 | try writeFile(wf, cache_dir, file.contents, basename); | 244 | }; |
| 245 | } | ||
| 246 | switch (file.contents) { | ||
| 247 | .bytes => |bytes| { | ||
| 248 | cache_dir.writeFile(file.sub_path, bytes) catch |err| { | ||
| 249 | return step.fail("unable to write file '{}{s}{c}{s}': {s}", .{ | ||
| 250 | b.cache_root, cache_path, fs.path.sep, file.sub_path, @errorName(err), | ||
| 251 | }); | ||
| 252 | }; | ||
| 253 | }, | ||
| 254 | .copy => |file_source| { | ||
| 255 | const source_path = file_source.getPath(b); | ||
| 256 | const prev_status = fs.Dir.updateFile( | ||
| 257 | fs.cwd(), | ||
| 258 | source_path, | ||
| 259 | cache_dir, | ||
| 260 | file.sub_path, | ||
| 261 | .{}, | ||
| 262 | ) catch |err| { | ||
| 263 | return step.fail("unable to update file from '{s}' to '{}{s}{c}{s}': {s}", .{ | ||
| 264 | source_path, | ||
| 265 | b.cache_root, | ||
| 266 | cache_path, | ||
| 267 | fs.path.sep, | ||
| 268 | file.sub_path, | ||
| 269 | @errorName(err), | ||
| 270 | }); | ||
| 271 | }; | ||
| 272 | // At this point we already will mark the step as a cache miss. | ||
| 273 | // But this is kind of a partial cache hit since individual | ||
| 274 | // file copies may be avoided. Oh well, this information is | ||
| 275 | // discarded. | ||
| 276 | _ = prev_status; | ||
| 277 | }, | ||
| 172 | } | 278 | } |
| 173 | 279 | ||
| 174 | file.generated_file.path = try wf.builder.cache_root.join( | 280 | file.generated_file.path = try b.cache_root.join(b.allocator, &.{ |
| 175 | wf.builder.allocator, | 281 | cache_path, file.sub_path, |
| 176 | &.{ cache_path, file.sub_path }, | 282 | }); |
| 177 | ); | ||
| 178 | } | ||
| 179 | |||
| 180 | try man.writeManifest(); | ||
| 181 | } | ||
| 182 | |||
| 183 | fn writeFile(wf: *WriteFileStep, dir: fs.Dir, contents: Contents, basename: []const u8) !void { | ||
| 184 | // TODO after landing concurrency PR, improve error reporting here | ||
| 185 | switch (contents) { | ||
| 186 | .bytes => |bytes| return dir.writeFile(basename, bytes), | ||
| 187 | .copy => |file_source| { | ||
| 188 | const source_path = file_source.getPath(wf.builder); | ||
| 189 | const prev_status = try fs.Dir.updateFile(fs.cwd(), source_path, dir, basename, .{}); | ||
| 190 | _ = prev_status; // TODO logging (affected by open PR regarding concurrency) | ||
| 191 | }, | ||
| 192 | } | 283 | } |
| 193 | } | ||
| 194 | |||
| 195 | /// TODO consolidate this with the same function in RunStep? | ||
| 196 | /// Also properly deal with concurrency (see open PR) | ||
| 197 | fn failWithCacheError(man: std.Build.Cache.Manifest, err: anyerror) noreturn { | ||
| 198 | const i = man.failed_file_index orelse failWithSimpleError(err); | ||
| 199 | const pp = man.files.items[i].prefixed_path orelse failWithSimpleError(err); | ||
| 200 | const prefix = man.cache.prefixes()[pp.prefix].path orelse ""; | ||
| 201 | std.debug.print("{s}: {s}/{s}\n", .{ @errorName(err), prefix, pp.sub_path }); | ||
| 202 | std.process.exit(1); | ||
| 203 | } | ||
| 204 | 284 | ||
| 205 | fn failWithSimpleError(err: anyerror) noreturn { | 285 | try step.writeManifest(&man); |
| 206 | std.debug.print("{s}\n", .{@errorName(err)}); | ||
| 207 | std.process.exit(1); | ||
| 208 | } | 286 | } |
| 209 | 287 | ||
| 210 | const std = @import("../std.zig"); | 288 | const std = @import("../std.zig"); |
lib/std/Progress.zig+72-26| ... | @@ -126,6 +126,21 @@ pub const Node = struct { | ... | @@ -126,6 +126,21 @@ pub const Node = struct { |
| 126 | } | 126 | } |
| 127 | } | 127 | } |
| 128 | 128 | ||
| 129 | /// Thread-safe. | ||
| 130 | pub fn setName(self: *Node, name: []const u8) void { | ||
| 131 | const progress = self.context; | ||
| 132 | progress.update_mutex.lock(); | ||
| 133 | defer progress.update_mutex.unlock(); | ||
| 134 | self.name = name; | ||
| 135 | if (self.parent) |parent| { | ||
| 136 | @atomicStore(?*Node, &parent.recently_updated_child, self, .Release); | ||
| 137 | if (parent.parent) |grand_parent| { | ||
| 138 | @atomicStore(?*Node, &grand_parent.recently_updated_child, parent, .Release); | ||
| 139 | } | ||
| 140 | if (progress.timer) |*timer| progress.maybeRefreshWithHeldLock(timer); | ||
| 141 | } | ||
| 142 | } | ||
| 143 | |||
| 129 | /// Thread-safe. 0 means unknown. | 144 | /// Thread-safe. 0 means unknown. |
| 130 | pub fn setEstimatedTotalItems(self: *Node, count: usize) void { | 145 | pub fn setEstimatedTotalItems(self: *Node, count: usize) void { |
| 131 | @atomicStore(usize, &self.unprotected_estimated_total_items, count, .Monotonic); | 146 | @atomicStore(usize, &self.unprotected_estimated_total_items, count, .Monotonic); |
| ... | @@ -174,16 +189,20 @@ pub fn maybeRefresh(self: *Progress) void { | ... | @@ -174,16 +189,20 @@ pub fn maybeRefresh(self: *Progress) void { |
| 174 | if (self.timer) |*timer| { | 189 | if (self.timer) |*timer| { |
| 175 | if (!self.update_mutex.tryLock()) return; | 190 | if (!self.update_mutex.tryLock()) return; |
| 176 | defer self.update_mutex.unlock(); | 191 | defer self.update_mutex.unlock(); |
| 177 | const now = timer.read(); | 192 | maybeRefreshWithHeldLock(self, timer); |
| 178 | if (now < self.initial_delay_ns) return; | ||
| 179 | // TODO I have observed this to happen sometimes. I think we need to follow Rust's | ||
| 180 | // lead and guarantee monotonically increasing times in the std lib itself. | ||
| 181 | if (now < self.prev_refresh_timestamp) return; | ||
| 182 | if (now - self.prev_refresh_timestamp < self.refresh_rate_ns) return; | ||
| 183 | return self.refreshWithHeldLock(); | ||
| 184 | } | 193 | } |
| 185 | } | 194 | } |
| 186 | 195 | ||
| 196 | fn maybeRefreshWithHeldLock(self: *Progress, timer: *std.time.Timer) void { | ||
| 197 | const now = timer.read(); | ||
| 198 | if (now < self.initial_delay_ns) return; | ||
| 199 | // TODO I have observed this to happen sometimes. I think we need to follow Rust's | ||
| 200 | // lead and guarantee monotonically increasing times in the std lib itself. | ||
| 201 | if (now < self.prev_refresh_timestamp) return; | ||
| 202 | if (now - self.prev_refresh_timestamp < self.refresh_rate_ns) return; | ||
| 203 | return self.refreshWithHeldLock(); | ||
| 204 | } | ||
| 205 | |||
| 187 | /// Updates the terminal and resets `self.next_refresh_timestamp`. Thread-safe. | 206 | /// Updates the terminal and resets `self.next_refresh_timestamp`. Thread-safe. |
| 188 | pub fn refresh(self: *Progress) void { | 207 | pub fn refresh(self: *Progress) void { |
| 189 | if (!self.update_mutex.tryLock()) return; | 208 | if (!self.update_mutex.tryLock()) return; |
| ... | @@ -192,32 +211,28 @@ pub fn refresh(self: *Progress) void { | ... | @@ -192,32 +211,28 @@ pub fn refresh(self: *Progress) void { |
| 192 | return self.refreshWithHeldLock(); | 211 | return self.refreshWithHeldLock(); |
| 193 | } | 212 | } |
| 194 | 213 | ||
| 195 | fn refreshWithHeldLock(self: *Progress) void { | 214 | fn clearWithHeldLock(p: *Progress, end_ptr: *usize) void { |
| 196 | const is_dumb = !self.supports_ansi_escape_codes and !self.is_windows_terminal; | 215 | const file = p.terminal orelse return; |
| 197 | if (is_dumb and self.dont_print_on_dumb) return; | 216 | var end = end_ptr.*; |
| 198 | 217 | if (p.columns_written > 0) { | |
| 199 | const file = self.terminal orelse return; | ||
| 200 | |||
| 201 | var end: usize = 0; | ||
| 202 | if (self.columns_written > 0) { | ||
| 203 | // restore the cursor position by moving the cursor | 218 | // restore the cursor position by moving the cursor |
| 204 | // `columns_written` cells to the left, then clear the rest of the | 219 | // `columns_written` cells to the left, then clear the rest of the |
| 205 | // line | 220 | // line |
| 206 | if (self.supports_ansi_escape_codes) { | 221 | if (p.supports_ansi_escape_codes) { |
| 207 | end += (std.fmt.bufPrint(self.output_buffer[end..], "\x1b[{d}D", .{self.columns_written}) catch unreachable).len; | 222 | end += (std.fmt.bufPrint(p.output_buffer[end..], "\x1b[{d}D", .{p.columns_written}) catch unreachable).len; |
| 208 | end += (std.fmt.bufPrint(self.output_buffer[end..], "\x1b[0K", .{}) catch unreachable).len; | 223 | end += (std.fmt.bufPrint(p.output_buffer[end..], "\x1b[0K", .{}) catch unreachable).len; |
| 209 | } else if (builtin.os.tag == .windows) winapi: { | 224 | } else if (builtin.os.tag == .windows) winapi: { |
| 210 | std.debug.assert(self.is_windows_terminal); | 225 | std.debug.assert(p.is_windows_terminal); |
| 211 | 226 | ||
| 212 | var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined; | 227 | var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined; |
| 213 | if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) != windows.TRUE) { | 228 | if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) != windows.TRUE) { |
| 214 | // stop trying to write to this file | 229 | // stop trying to write to this file |
| 215 | self.terminal = null; | 230 | p.terminal = null; |
| 216 | break :winapi; | 231 | break :winapi; |
| 217 | } | 232 | } |
| 218 | 233 | ||
| 219 | var cursor_pos = windows.COORD{ | 234 | var cursor_pos = windows.COORD{ |
| 220 | .X = info.dwCursorPosition.X - @intCast(windows.SHORT, self.columns_written), | 235 | .X = info.dwCursorPosition.X - @intCast(windows.SHORT, p.columns_written), |
| 221 | .Y = info.dwCursorPosition.Y, | 236 | .Y = info.dwCursorPosition.Y, |
| 222 | }; | 237 | }; |
| 223 | 238 | ||
| ... | @@ -235,7 +250,7 @@ fn refreshWithHeldLock(self: *Progress) void { | ... | @@ -235,7 +250,7 @@ fn refreshWithHeldLock(self: *Progress) void { |
| 235 | &written, | 250 | &written, |
| 236 | ) != windows.TRUE) { | 251 | ) != windows.TRUE) { |
| 237 | // stop trying to write to this file | 252 | // stop trying to write to this file |
| 238 | self.terminal = null; | 253 | p.terminal = null; |
| 239 | break :winapi; | 254 | break :winapi; |
| 240 | } | 255 | } |
| 241 | if (windows.kernel32.FillConsoleOutputCharacterW( | 256 | if (windows.kernel32.FillConsoleOutputCharacterW( |
| ... | @@ -246,22 +261,33 @@ fn refreshWithHeldLock(self: *Progress) void { | ... | @@ -246,22 +261,33 @@ fn refreshWithHeldLock(self: *Progress) void { |
| 246 | &written, | 261 | &written, |
| 247 | ) != windows.TRUE) { | 262 | ) != windows.TRUE) { |
| 248 | // stop trying to write to this file | 263 | // stop trying to write to this file |
| 249 | self.terminal = null; | 264 | p.terminal = null; |
| 250 | break :winapi; | 265 | break :winapi; |
| 251 | } | 266 | } |
| 252 | if (windows.kernel32.SetConsoleCursorPosition(file.handle, cursor_pos) != windows.TRUE) { | 267 | if (windows.kernel32.SetConsoleCursorPosition(file.handle, cursor_pos) != windows.TRUE) { |
| 253 | // stop trying to write to this file | 268 | // stop trying to write to this file |
| 254 | self.terminal = null; | 269 | p.terminal = null; |
| 255 | break :winapi; | 270 | break :winapi; |
| 256 | } | 271 | } |
| 257 | } else { | 272 | } else { |
| 258 | // we are in a "dumb" terminal like in acme or writing to a file | 273 | // we are in a "dumb" terminal like in acme or writing to a file |
| 259 | self.output_buffer[end] = '\n'; | 274 | p.output_buffer[end] = '\n'; |
| 260 | end += 1; | 275 | end += 1; |
| 261 | } | 276 | } |
| 262 | 277 | ||
| 263 | self.columns_written = 0; | 278 | p.columns_written = 0; |
| 264 | } | 279 | } |
| 280 | end_ptr.* = end; | ||
| 281 | } | ||
| 282 | |||
| 283 | fn refreshWithHeldLock(self: *Progress) void { | ||
| 284 | const is_dumb = !self.supports_ansi_escape_codes and !self.is_windows_terminal; | ||
| 285 | if (is_dumb and self.dont_print_on_dumb) return; | ||
| 286 | |||
| 287 | const file = self.terminal orelse return; | ||
| 288 | |||
| 289 | var end: usize = 0; | ||
| 290 | clearWithHeldLock(self, &end); | ||
| 265 | 291 | ||
| 266 | if (!self.done) { | 292 | if (!self.done) { |
| 267 | var need_ellipse = false; | 293 | var need_ellipse = false; |
| ... | @@ -318,6 +344,26 @@ pub fn log(self: *Progress, comptime format: []const u8, args: anytype) void { | ... | @@ -318,6 +344,26 @@ pub fn log(self: *Progress, comptime format: []const u8, args: anytype) void { |
| 318 | self.columns_written = 0; | 344 | self.columns_written = 0; |
| 319 | } | 345 | } |
| 320 | 346 | ||
| 347 | /// Allows the caller to freely write to stderr until unlock_stderr() is called. | ||
| 348 | /// During the lock, the progress information is cleared from the terminal. | ||
| 349 | pub fn lock_stderr(p: *Progress) void { | ||
| 350 | p.update_mutex.lock(); | ||
| 351 | if (p.terminal) |file| { | ||
| 352 | var end: usize = 0; | ||
| 353 | clearWithHeldLock(p, &end); | ||
| 354 | _ = file.write(p.output_buffer[0..end]) catch { | ||
| 355 | // stop trying to write to this file | ||
| 356 | p.terminal = null; | ||
| 357 | }; | ||
| 358 | } | ||
| 359 | std.debug.getStderrMutex().lock(); | ||
| 360 | } | ||
| 361 | |||
| 362 | pub fn unlock_stderr(p: *Progress) void { | ||
| 363 | std.debug.getStderrMutex().unlock(); | ||
| 364 | p.update_mutex.unlock(); | ||
| 365 | } | ||
| 366 | |||
| 321 | fn bufWrite(self: *Progress, end: *usize, comptime format: []const u8, args: anytype) void { | 367 | fn bufWrite(self: *Progress, end: *usize, comptime format: []const u8, args: anytype) void { |
| 322 | if (std.fmt.bufPrint(self.output_buffer[end.*..], format, args)) |written| { | 368 | if (std.fmt.bufPrint(self.output_buffer[end.*..], format, args)) |written| { |
| 323 | const amt = written.len; | 369 | const amt = written.len; |
lib/std/Thread.zig+2| ... | @@ -16,6 +16,8 @@ pub const Mutex = @import("Thread/Mutex.zig"); | ... | @@ -16,6 +16,8 @@ pub const Mutex = @import("Thread/Mutex.zig"); |
| 16 | pub const Semaphore = @import("Thread/Semaphore.zig"); | 16 | pub const Semaphore = @import("Thread/Semaphore.zig"); |
| 17 | pub const Condition = @import("Thread/Condition.zig"); | 17 | pub const Condition = @import("Thread/Condition.zig"); |
| 18 | pub const RwLock = @import("Thread/RwLock.zig"); | 18 | pub const RwLock = @import("Thread/RwLock.zig"); |
| 19 | pub const Pool = @import("Thread/Pool.zig"); | ||
| 20 | pub const WaitGroup = @import("Thread/WaitGroup.zig"); | ||
| 19 | 21 | ||
| 20 | pub const use_pthreads = target.os.tag != .windows and target.os.tag != .wasi and builtin.link_libc; | 22 | pub const use_pthreads = target.os.tag != .windows and target.os.tag != .wasi and builtin.link_libc; |
| 21 | const is_gnu = target.abi.isGnu(); | 23 | const is_gnu = target.abi.isGnu(); |
lib/std/Thread/Pool.zig created+159| ... | @@ -0,0 +1,159 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const builtin = @import("builtin"); | ||
| 3 | const Pool = @This(); | ||
| 4 | const WaitGroup = @import("WaitGroup.zig"); | ||
| 5 | |||
| 6 | mutex: std.Thread.Mutex = .{}, | ||
| 7 | cond: std.Thread.Condition = .{}, | ||
| 8 | run_queue: RunQueue = .{}, | ||
| 9 | is_running: bool = true, | ||
| 10 | allocator: std.mem.Allocator, | ||
| 11 | threads: []std.Thread, | ||
| 12 | |||
| 13 | const RunQueue = std.SinglyLinkedList(Runnable); | ||
| 14 | const Runnable = struct { | ||
| 15 | runFn: RunProto, | ||
| 16 | }; | ||
| 17 | |||
| 18 | const RunProto = *const fn (*Runnable) void; | ||
| 19 | |||
| 20 | pub const Options = struct { | ||
| 21 | allocator: std.mem.Allocator, | ||
| 22 | n_jobs: ?u32 = null, | ||
| 23 | }; | ||
| 24 | |||
| 25 | pub fn init(pool: *Pool, options: Options) !void { | ||
| 26 | const allocator = options.allocator; | ||
| 27 | |||
| 28 | pool.* = .{ | ||
| 29 | .allocator = allocator, | ||
| 30 | .threads = &[_]std.Thread{}, | ||
| 31 | }; | ||
| 32 | |||
| 33 | if (builtin.single_threaded) { | ||
| 34 | return; | ||
| 35 | } | ||
| 36 | |||
| 37 | const thread_count = options.n_jobs orelse @max(1, std.Thread.getCpuCount() catch 1); | ||
| 38 | pool.threads = try allocator.alloc(std.Thread, thread_count); | ||
| 39 | errdefer allocator.free(pool.threads); | ||
| 40 | |||
| 41 | // kill and join any threads we spawned previously on error. | ||
| 42 | var spawned: usize = 0; | ||
| 43 | errdefer pool.join(spawned); | ||
| 44 | |||
| 45 | for (pool.threads) |*thread| { | ||
| 46 | thread.* = try std.Thread.spawn(.{}, worker, .{pool}); | ||
| 47 | spawned += 1; | ||
| 48 | } | ||
| 49 | } | ||
| 50 | |||
| 51 | pub fn deinit(pool: *Pool) void { | ||
| 52 | pool.join(pool.threads.len); // kill and join all threads. | ||
| 53 | pool.* = undefined; | ||
| 54 | } | ||
| 55 | |||
| 56 | fn join(pool: *Pool, spawned: usize) void { | ||
| 57 | if (builtin.single_threaded) { | ||
| 58 | return; | ||
| 59 | } | ||
| 60 | |||
| 61 | { | ||
| 62 | pool.mutex.lock(); | ||
| 63 | defer pool.mutex.unlock(); | ||
| 64 | |||
| 65 | // ensure future worker threads exit the dequeue loop | ||
| 66 | pool.is_running = false; | ||
| 67 | } | ||
| 68 | |||
| 69 | // wake up any sleeping threads (this can be done outside the mutex) | ||
| 70 | // then wait for all the threads we know are spawned to complete. | ||
| 71 | pool.cond.broadcast(); | ||
| 72 | for (pool.threads[0..spawned]) |thread| { | ||
| 73 | thread.join(); | ||
| 74 | } | ||
| 75 | |||
| 76 | pool.allocator.free(pool.threads); | ||
| 77 | } | ||
| 78 | |||
| 79 | pub fn spawn(pool: *Pool, comptime func: anytype, args: anytype) !void { | ||
| 80 | if (builtin.single_threaded) { | ||
| 81 | @call(.auto, func, args); | ||
| 82 | return; | ||
| 83 | } | ||
| 84 | |||
| 85 | const Args = @TypeOf(args); | ||
| 86 | const Closure = struct { | ||
| 87 | arguments: Args, | ||
| 88 | pool: *Pool, | ||
| 89 | run_node: RunQueue.Node = .{ .data = .{ .runFn = runFn } }, | ||
| 90 | |||
| 91 | fn runFn(runnable: *Runnable) void { | ||
| 92 | const run_node = @fieldParentPtr(RunQueue.Node, "data", runnable); | ||
| 93 | const closure = @fieldParentPtr(@This(), "run_node", run_node); | ||
| 94 | @call(.auto, func, closure.arguments); | ||
| 95 | |||
| 96 | // The thread pool's allocator is protected by the mutex. | ||
| 97 | const mutex = &closure.pool.mutex; | ||
| 98 | mutex.lock(); | ||
| 99 | defer mutex.unlock(); | ||
| 100 | |||
| 101 | closure.pool.allocator.destroy(closure); | ||
| 102 | } | ||
| 103 | }; | ||
| 104 | |||
| 105 | { | ||
| 106 | pool.mutex.lock(); | ||
| 107 | defer pool.mutex.unlock(); | ||
| 108 | |||
| 109 | const closure = try pool.allocator.create(Closure); | ||
| 110 | closure.* = .{ | ||
| 111 | .arguments = args, | ||
| 112 | .pool = pool, | ||
| 113 | }; | ||
| 114 | |||
| 115 | pool.run_queue.prepend(&closure.run_node); | ||
| 116 | } | ||
| 117 | |||
| 118 | // Notify waiting threads outside the lock to try and keep the critical section small. | ||
| 119 | pool.cond.signal(); | ||
| 120 | } | ||
| 121 | |||
| 122 | fn worker(pool: *Pool) void { | ||
| 123 | pool.mutex.lock(); | ||
| 124 | defer pool.mutex.unlock(); | ||
| 125 | |||
| 126 | while (true) { | ||
| 127 | while (pool.run_queue.popFirst()) |run_node| { | ||
| 128 | // Temporarily unlock the mutex in order to execute the run_node | ||
| 129 | pool.mutex.unlock(); | ||
| 130 | defer pool.mutex.lock(); | ||
| 131 | |||
| 132 | const runFn = run_node.data.runFn; | ||
| 133 | runFn(&run_node.data); | ||
| 134 | } | ||
| 135 | |||
| 136 | // Stop executing instead of waiting if the thread pool is no longer running. | ||
| 137 | if (pool.is_running) { | ||
| 138 | pool.cond.wait(&pool.mutex); | ||
| 139 | } else { | ||
| 140 | break; | ||
| 141 | } | ||
| 142 | } | ||
| 143 | } | ||
| 144 | |||
| 145 | pub fn waitAndWork(pool: *Pool, wait_group: *WaitGroup) void { | ||
| 146 | while (!wait_group.isDone()) { | ||
| 147 | if (blk: { | ||
| 148 | pool.mutex.lock(); | ||
| 149 | defer pool.mutex.unlock(); | ||
| 150 | break :blk pool.run_queue.popFirst(); | ||
| 151 | }) |run_node| { | ||
| 152 | run_node.data.runFn(&run_node.data); | ||
| 153 | continue; | ||
| 154 | } | ||
| 155 | |||
| 156 | wait_group.wait(); | ||
| 157 | return; | ||
| 158 | } | ||
| 159 | } | ||
lib/std/Thread/WaitGroup.zig created+46| ... | @@ -0,0 +1,46 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const Atomic = std.atomic.Atomic; | ||
| 3 | const assert = std.debug.assert; | ||
| 4 | const WaitGroup = @This(); | ||
| 5 | |||
| 6 | const is_waiting: usize = 1 << 0; | ||
| 7 | const one_pending: usize = 1 << 1; | ||
| 8 | |||
| 9 | state: Atomic(usize) = Atomic(usize).init(0), | ||
| 10 | event: std.Thread.ResetEvent = .{}, | ||
| 11 | |||
| 12 | pub fn start(self: *WaitGroup) void { | ||
| 13 | const state = self.state.fetchAdd(one_pending, .Monotonic); | ||
| 14 | assert((state / one_pending) < (std.math.maxInt(usize) / one_pending)); | ||
| 15 | } | ||
| 16 | |||
| 17 | pub fn finish(self: *WaitGroup) void { | ||
| 18 | const state = self.state.fetchSub(one_pending, .Release); | ||
| 19 | assert((state / one_pending) > 0); | ||
| 20 | |||
| 21 | if (state == (one_pending | is_waiting)) { | ||
| 22 | self.state.fence(.Acquire); | ||
| 23 | self.event.set(); | ||
| 24 | } | ||
| 25 | } | ||
| 26 | |||
| 27 | pub fn wait(self: *WaitGroup) void { | ||
| 28 | var state = self.state.fetchAdd(is_waiting, .Acquire); | ||
| 29 | assert(state & is_waiting == 0); | ||
| 30 | |||
| 31 | if ((state / one_pending) > 0) { | ||
| 32 | self.event.wait(); | ||
| 33 | } | ||
| 34 | } | ||
| 35 | |||
| 36 | pub fn reset(self: *WaitGroup) void { | ||
| 37 | self.state.store(0, .Monotonic); | ||
| 38 | self.event.reset(); | ||
| 39 | } | ||
| 40 | |||
| 41 | pub fn isDone(wg: *WaitGroup) bool { | ||
| 42 | const state = wg.state.load(.Acquire); | ||
| 43 | assert(state & is_waiting == 0); | ||
| 44 | |||
| 45 | return (state / one_pending) == 0; | ||
| 46 | } | ||
lib/std/c.zig+2-1| ... | @@ -153,7 +153,8 @@ pub extern "c" fn linkat(oldfd: c.fd_t, oldpath: [*:0]const u8, newfd: c.fd_t, n | ... | @@ -153,7 +153,8 @@ pub extern "c" fn linkat(oldfd: c.fd_t, oldpath: [*:0]const u8, newfd: c.fd_t, n |
| 153 | pub extern "c" fn unlink(path: [*:0]const u8) c_int; | 153 | pub extern "c" fn unlink(path: [*:0]const u8) c_int; |
| 154 | pub extern "c" fn unlinkat(dirfd: c.fd_t, path: [*:0]const u8, flags: c_uint) c_int; | 154 | pub extern "c" fn unlinkat(dirfd: c.fd_t, path: [*:0]const u8, flags: c_uint) c_int; |
| 155 | pub extern "c" fn getcwd(buf: [*]u8, size: usize) ?[*]u8; | 155 | pub extern "c" fn getcwd(buf: [*]u8, size: usize) ?[*]u8; |
| 156 | pub extern "c" fn waitpid(pid: c.pid_t, stat_loc: ?*c_int, options: c_int) c.pid_t; | 156 | pub extern "c" fn waitpid(pid: c.pid_t, status: ?*c_int, options: c_int) c.pid_t; |
| 157 | pub extern "c" fn wait4(pid: c.pid_t, status: ?*c_int, options: c_int, ru: ?*c.rusage) c.pid_t; | ||
| 157 | pub extern "c" fn fork() c_int; | 158 | pub extern "c" fn fork() c_int; |
| 158 | pub extern "c" fn access(path: [*:0]const u8, mode: c_uint) c_int; | 159 | pub extern "c" fn access(path: [*:0]const u8, mode: c_uint) c_int; |
| 159 | pub extern "c" fn faccessat(dirfd: c.fd_t, path: [*:0]const u8, mode: c_uint, flags: c_uint) c_int; | 160 | pub extern "c" fn faccessat(dirfd: c.fd_t, path: [*:0]const u8, mode: c_uint, flags: c_uint) c_int; |
lib/std/child_process.zig+49-1| ... | @@ -17,10 +17,12 @@ const Os = std.builtin.Os; | ... | @@ -17,10 +17,12 @@ const Os = std.builtin.Os; |
| 17 | const TailQueue = std.TailQueue; | 17 | const TailQueue = std.TailQueue; |
| 18 | const maxInt = std.math.maxInt; | 18 | const maxInt = std.math.maxInt; |
| 19 | const assert = std.debug.assert; | 19 | const assert = std.debug.assert; |
| 20 | const is_darwin = builtin.target.isDarwin(); | ||
| 20 | 21 | ||
| 21 | pub const ChildProcess = struct { | 22 | pub const ChildProcess = struct { |
| 22 | pub const Id = switch (builtin.os.tag) { | 23 | pub const Id = switch (builtin.os.tag) { |
| 23 | .windows => windows.HANDLE, | 24 | .windows => windows.HANDLE, |
| 25 | .wasi => void, | ||
| 24 | else => os.pid_t, | 26 | else => os.pid_t, |
| 25 | }; | 27 | }; |
| 26 | 28 | ||
| ... | @@ -70,6 +72,43 @@ pub const ChildProcess = struct { | ... | @@ -70,6 +72,43 @@ pub const ChildProcess = struct { |
| 70 | /// Darwin-only. Start child process in suspended state as if SIGSTOP was sent. | 72 | /// Darwin-only. Start child process in suspended state as if SIGSTOP was sent. |
| 71 | start_suspended: bool = false, | 73 | start_suspended: bool = false, |
| 72 | 74 | ||
| 75 | /// Set to true to obtain rusage information for the child process. | ||
| 76 | /// Depending on the target platform and implementation status, the | ||
| 77 | /// requested statistics may or may not be available. If they are | ||
| 78 | /// available, then the `resource_usage_statistics` field will be populated | ||
| 79 | /// after calling `wait`. | ||
| 80 | /// On Linux, this obtains rusage statistics from wait4(). | ||
| 81 | request_resource_usage_statistics: bool = false, | ||
| 82 | |||
| 83 | /// This is available after calling wait if | ||
| 84 | /// `request_resource_usage_statistics` was set to `true` before calling | ||
| 85 | /// `spawn`. | ||
| 86 | resource_usage_statistics: ResourceUsageStatistics = .{}, | ||
| 87 | |||
| 88 | pub const ResourceUsageStatistics = struct { | ||
| 89 | rusage: @TypeOf(rusage_init) = rusage_init, | ||
| 90 | |||
| 91 | /// Returns the peak resident set size of the child process, in bytes, | ||
| 92 | /// if available. | ||
| 93 | pub inline fn getMaxRss(rus: ResourceUsageStatistics) ?usize { | ||
| 94 | switch (builtin.os.tag) { | ||
| 95 | .linux => { | ||
| 96 | if (rus.rusage) |ru| { | ||
| 97 | return @intCast(usize, ru.maxrss) * 1024; | ||
| 98 | } else { | ||
| 99 | return null; | ||
| 100 | } | ||
| 101 | }, | ||
| 102 | else => return null, | ||
| 103 | } | ||
| 104 | } | ||
| 105 | |||
| 106 | const rusage_init = switch (builtin.os.tag) { | ||
| 107 | .linux => @as(?std.os.rusage, null), | ||
| 108 | else => {}, | ||
| 109 | }; | ||
| 110 | }; | ||
| 111 | |||
| 73 | pub const Arg0Expand = os.Arg0Expand; | 112 | pub const Arg0Expand = os.Arg0Expand; |
| 74 | 113 | ||
| 75 | pub const SpawnError = error{ | 114 | pub const SpawnError = error{ |
| ... | @@ -332,7 +371,16 @@ pub const ChildProcess = struct { | ... | @@ -332,7 +371,16 @@ pub const ChildProcess = struct { |
| 332 | } | 371 | } |
| 333 | 372 | ||
| 334 | fn waitUnwrapped(self: *ChildProcess) !void { | 373 | fn waitUnwrapped(self: *ChildProcess) !void { |
| 335 | const res: os.WaitPidResult = os.waitpid(self.id, 0); | 374 | const res: os.WaitPidResult = res: { |
| 375 | if (builtin.os.tag == .linux and self.request_resource_usage_statistics) { | ||
| 376 | var ru: std.os.rusage = undefined; | ||
| 377 | const res = os.wait4(self.id, 0, &ru); | ||
| 378 | self.resource_usage_statistics.rusage = ru; | ||
| 379 | break :res res; | ||
| 380 | } | ||
| 381 | |||
| 382 | break :res os.waitpid(self.id, 0); | ||
| 383 | }; | ||
| 336 | const status = res.status; | 384 | const status = res.status; |
| 337 | self.cleanupStreams(); | 385 | self.cleanupStreams(); |
| 338 | self.handleWaitResult(status); | 386 | self.handleWaitResult(status); |
lib/std/debug.zig+33| ... | @@ -635,6 +635,7 @@ pub const TTY = struct { | ... | @@ -635,6 +635,7 @@ pub const TTY = struct { |
| 635 | pub const Color = enum { | 635 | pub const Color = enum { |
| 636 | Red, | 636 | Red, |
| 637 | Green, | 637 | Green, |
| 638 | Yellow, | ||
| 638 | Cyan, | 639 | Cyan, |
| 639 | White, | 640 | White, |
| 640 | Dim, | 641 | Dim, |
| ... | @@ -659,6 +660,7 @@ pub const TTY = struct { | ... | @@ -659,6 +660,7 @@ pub const TTY = struct { |
| 659 | const color_string = switch (color) { | 660 | const color_string = switch (color) { |
| 660 | .Red => "\x1b[31;1m", | 661 | .Red => "\x1b[31;1m", |
| 661 | .Green => "\x1b[32;1m", | 662 | .Green => "\x1b[32;1m", |
| 663 | .Yellow => "\x1b[33;1m", | ||
| 662 | .Cyan => "\x1b[36;1m", | 664 | .Cyan => "\x1b[36;1m", |
| 663 | .White => "\x1b[37;1m", | 665 | .White => "\x1b[37;1m", |
| 664 | .Bold => "\x1b[1m", | 666 | .Bold => "\x1b[1m", |
| ... | @@ -671,6 +673,7 @@ pub const TTY = struct { | ... | @@ -671,6 +673,7 @@ pub const TTY = struct { |
| 671 | const attributes = switch (color) { | 673 | const attributes = switch (color) { |
| 672 | .Red => windows.FOREGROUND_RED | windows.FOREGROUND_INTENSITY, | 674 | .Red => windows.FOREGROUND_RED | windows.FOREGROUND_INTENSITY, |
| 673 | .Green => windows.FOREGROUND_GREEN | windows.FOREGROUND_INTENSITY, | 675 | .Green => windows.FOREGROUND_GREEN | windows.FOREGROUND_INTENSITY, |
| 676 | .Yellow => windows.FOREGROUND_RED | windows.FOREGROUND_GREEN | windows.FOREGROUND_INTENSITY, | ||
| 674 | .Cyan => windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY, | 677 | .Cyan => windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY, |
| 675 | .White, .Bold => windows.FOREGROUND_RED | windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY, | 678 | .White, .Bold => windows.FOREGROUND_RED | windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY, |
| 676 | .Dim => windows.FOREGROUND_INTENSITY, | 679 | .Dim => windows.FOREGROUND_INTENSITY, |
| ... | @@ -682,6 +685,36 @@ pub const TTY = struct { | ... | @@ -682,6 +685,36 @@ pub const TTY = struct { |
| 682 | }, | 685 | }, |
| 683 | }; | 686 | }; |
| 684 | } | 687 | } |
| 688 | |||
| 689 | pub fn writeDEC(conf: Config, writer: anytype, codepoint: u8) !void { | ||
| 690 | const bytes = switch (conf) { | ||
| 691 | .no_color, .windows_api => switch (codepoint) { | ||
| 692 | 0x50...0x5e => @as(*const [1]u8, &codepoint), | ||
| 693 | 0x6a => "+", // ┘ | ||
| 694 | 0x6b => "+", // ┐ | ||
| 695 | 0x6c => "+", // ┌ | ||
| 696 | 0x6d => "+", // └ | ||
| 697 | 0x6e => "+", // ┼ | ||
| 698 | 0x71 => "-", // ─ | ||
| 699 | 0x74 => "+", // ├ | ||
| 700 | 0x75 => "+", // ┤ | ||
| 701 | 0x76 => "+", // ┴ | ||
| 702 | 0x77 => "+", // ┬ | ||
| 703 | 0x78 => "|", // │ | ||
| 704 | else => " ", // TODO | ||
| 705 | }, | ||
| 706 | .escape_codes => switch (codepoint) { | ||
| 707 | // Here we avoid writing the DEC beginning sequence and | ||
| 708 | // ending sequence in separate syscalls by putting the | ||
| 709 | // beginning and ending sequence into the same string | ||
| 710 | // literals, to prevent terminals ending up in bad states | ||
| 711 | // in case a crash happens between syscalls. | ||
| 712 | inline 0x50...0x7f => |x| "\x1B\x28\x30" ++ [1]u8{x} ++ "\x1B\x28\x42", | ||
| 713 | else => unreachable, | ||
| 714 | }, | ||
| 715 | }; | ||
| 716 | return writer.writeAll(bytes); | ||
| 717 | } | ||
| 685 | }; | 718 | }; |
| 686 | }; | 719 | }; |
| 687 | 720 |
lib/std/fifo.zig+27| ... | @@ -164,6 +164,17 @@ pub fn LinearFifo( | ... | @@ -164,6 +164,17 @@ pub fn LinearFifo( |
| 164 | return self.readableSliceMut(offset); | 164 | return self.readableSliceMut(offset); |
| 165 | } | 165 | } |
| 166 | 166 | ||
| 167 | pub fn readableSliceOfLen(self: *Self, len: usize) []const T { | ||
| 168 | assert(len <= self.count); | ||
| 169 | const buf = self.readableSlice(0); | ||
| 170 | if (buf.len >= len) { | ||
| 171 | return buf[0..len]; | ||
| 172 | } else { | ||
| 173 | self.realign(); | ||
| 174 | return self.readableSlice(0)[0..len]; | ||
| 175 | } | ||
| 176 | } | ||
| 177 | |||
| 167 | /// Discard first `count` items in the fifo | 178 | /// Discard first `count` items in the fifo |
| 168 | pub fn discard(self: *Self, count: usize) void { | 179 | pub fn discard(self: *Self, count: usize) void { |
| 169 | assert(count <= self.count); | 180 | assert(count <= self.count); |
| ... | @@ -383,6 +394,22 @@ pub fn LinearFifo( | ... | @@ -383,6 +394,22 @@ pub fn LinearFifo( |
| 383 | self.discard(try dest_writer.write(self.readableSlice(0))); | 394 | self.discard(try dest_writer.write(self.readableSlice(0))); |
| 384 | } | 395 | } |
| 385 | } | 396 | } |
| 397 | |||
| 398 | pub fn toOwnedSlice(self: *Self) Allocator.Error![]T { | ||
| 399 | if (self.head != 0) self.realign(); | ||
| 400 | assert(self.head == 0); | ||
| 401 | assert(self.count <= self.buf.len); | ||
| 402 | const allocator = self.allocator; | ||
| 403 | if (allocator.resize(self.buf, self.count)) { | ||
| 404 | const result = self.buf[0..self.count]; | ||
| 405 | self.* = Self.init(allocator); | ||
| 406 | return result; | ||
| 407 | } | ||
| 408 | const new_memory = try allocator.dupe(T, self.buf[0..self.count]); | ||
| 409 | allocator.free(self.buf); | ||
| 410 | self.* = Self.init(allocator); | ||
| 411 | return new_memory; | ||
| 412 | } | ||
| 386 | }; | 413 | }; |
| 387 | } | 414 | } |
| 388 | 415 |
lib/std/fs/file.zig+33-5| ... | @@ -1048,12 +1048,27 @@ pub const File = struct { | ... | @@ -1048,12 +1048,27 @@ pub const File = struct { |
| 1048 | /// Returns the number of bytes read. If the number read is smaller than the total bytes | 1048 | /// Returns the number of bytes read. If the number read is smaller than the total bytes |
| 1049 | /// from all the buffers, it means the file reached the end. Reaching the end of a file | 1049 | /// from all the buffers, it means the file reached the end. Reaching the end of a file |
| 1050 | /// is not an error condition. | 1050 | /// is not an error condition. |
| 1051 | /// The `iovecs` parameter is mutable because this function needs to mutate the fields in | 1051 | /// |
| 1052 | /// order to handle partial reads from the underlying OS layer. | 1052 | /// The `iovecs` parameter is mutable because: |
| 1053 | /// See https://github.com/ziglang/zig/issues/7699 | 1053 | /// * This function needs to mutate the fields in order to handle partial |
| 1054 | /// reads from the underlying OS layer. | ||
| 1055 | /// * The OS layer expects pointer addresses to be inside the application's address space | ||
| 1056 | /// even if the length is zero. Meanwhile, in Zig, slices may have undefined pointer | ||
| 1057 | /// addresses when the length is zero. So this function modifies the iov_base fields | ||
| 1058 | /// when the length is zero. | ||
| 1059 | /// | ||
| 1060 | /// Related open issue: https://github.com/ziglang/zig/issues/7699 | ||
| 1054 | pub fn readvAll(self: File, iovecs: []os.iovec) ReadError!usize { | 1061 | pub fn readvAll(self: File, iovecs: []os.iovec) ReadError!usize { |
| 1055 | if (iovecs.len == 0) return 0; | 1062 | if (iovecs.len == 0) return 0; |
| 1056 | 1063 | ||
| 1064 | // We use the address of this local variable for all zero-length | ||
| 1065 | // vectors so that the OS does not complain that we are giving it | ||
| 1066 | // addresses outside the application's address space. | ||
| 1067 | var garbage: [1]u8 = undefined; | ||
| 1068 | for (iovecs) |*v| { | ||
| 1069 | if (v.iov_len == 0) v.iov_base = &garbage; | ||
| 1070 | } | ||
| 1071 | |||
| 1057 | var i: usize = 0; | 1072 | var i: usize = 0; |
| 1058 | var off: usize = 0; | 1073 | var off: usize = 0; |
| 1059 | while (true) { | 1074 | while (true) { |
| ... | @@ -1181,13 +1196,26 @@ pub const File = struct { | ... | @@ -1181,13 +1196,26 @@ pub const File = struct { |
| 1181 | } | 1196 | } |
| 1182 | } | 1197 | } |
| 1183 | 1198 | ||
| 1184 | /// The `iovecs` parameter is mutable because this function needs to mutate the fields in | 1199 | /// The `iovecs` parameter is mutable because: |
| 1185 | /// order to handle partial writes from the underlying OS layer. | 1200 | /// * This function needs to mutate the fields in order to handle partial |
| 1201 | /// writes from the underlying OS layer. | ||
| 1202 | /// * The OS layer expects pointer addresses to be inside the application's address space | ||
| 1203 | /// even if the length is zero. Meanwhile, in Zig, slices may have undefined pointer | ||
| 1204 | /// addresses when the length is zero. So this function modifies the iov_base fields | ||
| 1205 | /// when the length is zero. | ||
| 1186 | /// See https://github.com/ziglang/zig/issues/7699 | 1206 | /// See https://github.com/ziglang/zig/issues/7699 |
| 1187 | /// See equivalent function: `std.net.Stream.writevAll`. | 1207 | /// See equivalent function: `std.net.Stream.writevAll`. |
| 1188 | pub fn writevAll(self: File, iovecs: []os.iovec_const) WriteError!void { | 1208 | pub fn writevAll(self: File, iovecs: []os.iovec_const) WriteError!void { |
| 1189 | if (iovecs.len == 0) return; | 1209 | if (iovecs.len == 0) return; |
| 1190 | 1210 | ||
| 1211 | // We use the address of this local variable for all zero-length | ||
| 1212 | // vectors so that the OS does not complain that we are giving it | ||
| 1213 | // addresses outside the application's address space. | ||
| 1214 | var garbage: [1]u8 = undefined; | ||
| 1215 | for (iovecs) |*v| { | ||
| 1216 | if (v.iov_len == 0) v.iov_base = &garbage; | ||
| 1217 | } | ||
| 1218 | |||
| 1191 | var i: usize = 0; | 1219 | var i: usize = 0; |
| 1192 | while (true) { | 1220 | while (true) { |
| 1193 | var amt = try self.writev(iovecs[i..]); | 1221 | var amt = try self.writev(iovecs[i..]); |
lib/std/fs/test.zig+22-8| ... | @@ -1124,17 +1124,31 @@ test "open file with exclusive lock twice, make sure second lock waits" { | ... | @@ -1124,17 +1124,31 @@ test "open file with exclusive lock twice, make sure second lock waits" { |
| 1124 | test "open file with exclusive nonblocking lock twice (absolute paths)" { | 1124 | test "open file with exclusive nonblocking lock twice (absolute paths)" { |
| 1125 | if (builtin.os.tag == .wasi) return error.SkipZigTest; | 1125 | if (builtin.os.tag == .wasi) return error.SkipZigTest; |
| 1126 | 1126 | ||
| 1127 | const allocator = testing.allocator; | 1127 | var random_bytes: [12]u8 = undefined; |
| 1128 | std.crypto.random.bytes(&random_bytes); | ||
| 1128 | 1129 | ||
| 1129 | const cwd = try std.process.getCwdAlloc(allocator); | 1130 | var random_b64: [fs.base64_encoder.calcSize(random_bytes.len)]u8 = undefined; |
| 1130 | defer allocator.free(cwd); | 1131 | _ = fs.base64_encoder.encode(&random_b64, &random_bytes); |
| 1131 | const file_paths: [2][]const u8 = .{ cwd, "zig-test-absolute-paths.txt" }; | ||
| 1132 | const filename = try fs.path.resolve(allocator, &file_paths); | ||
| 1133 | defer allocator.free(filename); | ||
| 1134 | 1132 | ||
| 1135 | const file1 = try fs.createFileAbsolute(filename, .{ .lock = .Exclusive, .lock_nonblocking = true }); | 1133 | const sub_path = random_b64 ++ "-zig-test-absolute-paths.txt"; |
| 1136 | 1134 | ||
| 1137 | const file2 = fs.createFileAbsolute(filename, .{ .lock = .Exclusive, .lock_nonblocking = true }); | 1135 | const gpa = testing.allocator; |
| 1136 | |||
| 1137 | const cwd = try std.process.getCwdAlloc(gpa); | ||
| 1138 | defer gpa.free(cwd); | ||
| 1139 | |||
| 1140 | const filename = try fs.path.resolve(gpa, &[_][]const u8{ cwd, sub_path }); | ||
| 1141 | defer gpa.free(filename); | ||
| 1142 | |||
| 1143 | const file1 = try fs.createFileAbsolute(filename, .{ | ||
| 1144 | .lock = .Exclusive, | ||
| 1145 | .lock_nonblocking = true, | ||
| 1146 | }); | ||
| 1147 | |||
| 1148 | const file2 = fs.createFileAbsolute(filename, .{ | ||
| 1149 | .lock = .Exclusive, | ||
| 1150 | .lock_nonblocking = true, | ||
| 1151 | }); | ||
| 1138 | file1.close(); | 1152 | file1.close(); |
| 1139 | try testing.expectError(error.WouldBlock, file2); | 1153 | try testing.expectError(error.WouldBlock, file2); |
| 1140 | 1154 |
lib/std/heap.zig+1| ... | @@ -19,6 +19,7 @@ pub const GeneralPurposeAllocator = @import("heap/general_purpose_allocator.zig" | ... | @@ -19,6 +19,7 @@ pub const GeneralPurposeAllocator = @import("heap/general_purpose_allocator.zig" |
| 19 | pub const WasmAllocator = @import("heap/WasmAllocator.zig"); | 19 | pub const WasmAllocator = @import("heap/WasmAllocator.zig"); |
| 20 | pub const WasmPageAllocator = @import("heap/WasmPageAllocator.zig"); | 20 | pub const WasmPageAllocator = @import("heap/WasmPageAllocator.zig"); |
| 21 | pub const PageAllocator = @import("heap/PageAllocator.zig"); | 21 | pub const PageAllocator = @import("heap/PageAllocator.zig"); |
| 22 | pub const ThreadSafeAllocator = @import("heap/ThreadSafeAllocator.zig"); | ||
| 22 | 23 | ||
| 23 | const memory_pool = @import("heap/memory_pool.zig"); | 24 | const memory_pool = @import("heap/memory_pool.zig"); |
| 24 | pub const MemoryPool = memory_pool.MemoryPool; | 25 | pub const MemoryPool = memory_pool.MemoryPool; |
lib/std/heap/ThreadSafeAllocator.zig created+45| ... | @@ -0,0 +1,45 @@ | ||
| 1 | //! Wraps a non-thread-safe allocator and makes it thread-safe. | ||
| 2 | |||
| 3 | child_allocator: Allocator, | ||
| 4 | mutex: std.Thread.Mutex = .{}, | ||
| 5 | |||
| 6 | pub fn allocator(self: *ThreadSafeAllocator) Allocator { | ||
| 7 | return .{ | ||
| 8 | .ptr = self, | ||
| 9 | .vtable = &.{ | ||
| 10 | .alloc = alloc, | ||
| 11 | .resize = resize, | ||
| 12 | .free = free, | ||
| 13 | }, | ||
| 14 | }; | ||
| 15 | } | ||
| 16 | |||
| 17 | fn alloc(ctx: *anyopaque, n: usize, log2_ptr_align: u8, ra: usize) ?[*]u8 { | ||
| 18 | const self = @ptrCast(*ThreadSafeAllocator, @alignCast(@alignOf(ThreadSafeAllocator), ctx)); | ||
| 19 | self.mutex.lock(); | ||
| 20 | defer self.mutex.unlock(); | ||
| 21 | |||
| 22 | return self.child_allocator.rawAlloc(n, log2_ptr_align, ra); | ||
| 23 | } | ||
| 24 | |||
| 25 | fn resize(ctx: *anyopaque, buf: []u8, log2_buf_align: u8, new_len: usize, ret_addr: usize) bool { | ||
| 26 | const self = @ptrCast(*ThreadSafeAllocator, @alignCast(@alignOf(ThreadSafeAllocator), ctx)); | ||
| 27 | |||
| 28 | self.mutex.lock(); | ||
| 29 | defer self.mutex.unlock(); | ||
| 30 | |||
| 31 | return self.child_allocator.rawResize(buf, log2_buf_align, new_len, ret_addr); | ||
| 32 | } | ||
| 33 | |||
| 34 | fn free(ctx: *anyopaque, buf: []u8, log2_buf_align: u8, ret_addr: usize) void { | ||
| 35 | const self = @ptrCast(*ThreadSafeAllocator, @alignCast(@alignOf(ThreadSafeAllocator), ctx)); | ||
| 36 | |||
| 37 | self.mutex.lock(); | ||
| 38 | defer self.mutex.unlock(); | ||
| 39 | |||
| 40 | return self.child_allocator.rawFree(buf, log2_buf_align, ret_addr); | ||
| 41 | } | ||
| 42 | |||
| 43 | const std = @import("../std.zig"); | ||
| 44 | const ThreadSafeAllocator = @This(); | ||
| 45 | const Allocator = std.mem.Allocator; | ||
lib/std/mem.zig+4-9| ... | @@ -196,13 +196,8 @@ test "Allocator.resize" { | ... | @@ -196,13 +196,8 @@ test "Allocator.resize" { |
| 196 | /// dest.len must be >= source.len. | 196 | /// dest.len must be >= source.len. |
| 197 | /// If the slices overlap, dest.ptr must be <= src.ptr. | 197 | /// If the slices overlap, dest.ptr must be <= src.ptr. |
| 198 | pub fn copy(comptime T: type, dest: []T, source: []const T) void { | 198 | pub fn copy(comptime T: type, dest: []T, source: []const T) void { |
| 199 | // TODO instead of manually doing this check for the whole array | 199 | for (dest[0..source.len], source) |*d, s| |
| 200 | // and turning off runtime safety, the compiler should detect loops like | 200 | d.* = s; |
| 201 | // this and automatically omit safety checks for loops | ||
| 202 | @setRuntimeSafety(false); | ||
| 203 | assert(dest.len >= source.len); | ||
| 204 | for (source, 0..) |s, i| | ||
| 205 | dest[i] = s; | ||
| 206 | } | 201 | } |
| 207 | 202 | ||
| 208 | /// Copy all of source into dest at position 0. | 203 | /// Copy all of source into dest at position 0. |
| ... | @@ -611,8 +606,8 @@ test "lessThan" { | ... | @@ -611,8 +606,8 @@ test "lessThan" { |
| 611 | pub fn eql(comptime T: type, a: []const T, b: []const T) bool { | 606 | pub fn eql(comptime T: type, a: []const T, b: []const T) bool { |
| 612 | if (a.len != b.len) return false; | 607 | if (a.len != b.len) return false; |
| 613 | if (a.ptr == b.ptr) return true; | 608 | if (a.ptr == b.ptr) return true; |
| 614 | for (a, 0..) |item, index| { | 609 | for (a, b) |a_elem, b_elem| { |
| 615 | if (b[index] != item) return false; | 610 | if (a_elem != b_elem) return false; |
| 616 | } | 611 | } |
| 617 | return true; | 612 | return true; |
| 618 | } | 613 | } |
lib/std/os.zig+27-1| ... | @@ -766,6 +766,9 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize { | ... | @@ -766,6 +766,9 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize { |
| 766 | /// This operation is non-atomic on the following systems: | 766 | /// This operation is non-atomic on the following systems: |
| 767 | /// * Windows | 767 | /// * Windows |
| 768 | /// On these systems, the read races with concurrent writes to the same file descriptor. | 768 | /// On these systems, the read races with concurrent writes to the same file descriptor. |
| 769 | /// | ||
| 770 | /// This function assumes that all vectors, including zero-length vectors, have | ||
| 771 | /// a pointer within the address space of the application. | ||
| 769 | pub fn readv(fd: fd_t, iov: []const iovec) ReadError!usize { | 772 | pub fn readv(fd: fd_t, iov: []const iovec) ReadError!usize { |
| 770 | if (builtin.os.tag == .windows) { | 773 | if (builtin.os.tag == .windows) { |
| 771 | // TODO improve this to use ReadFileScatter | 774 | // TODO improve this to use ReadFileScatter |
| ... | @@ -1167,6 +1170,9 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize { | ... | @@ -1167,6 +1170,9 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize { |
| 1167 | /// used to perform the I/O. `error.WouldBlock` is not possible on Windows. | 1170 | /// used to perform the I/O. `error.WouldBlock` is not possible on Windows. |
| 1168 | /// | 1171 | /// |
| 1169 | /// If `iov.len` is larger than `IOV_MAX`, a partial write will occur. | 1172 | /// If `iov.len` is larger than `IOV_MAX`, a partial write will occur. |
| 1173 | /// | ||
| 1174 | /// This function assumes that all vectors, including zero-length vectors, have | ||
| 1175 | /// a pointer within the address space of the application. | ||
| 1170 | pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!usize { | 1176 | pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!usize { |
| 1171 | if (builtin.os.tag == .windows) { | 1177 | if (builtin.os.tag == .windows) { |
| 1172 | // TODO improve this to use WriteFileScatter | 1178 | // TODO improve this to use WriteFileScatter |
| ... | @@ -4000,8 +4006,28 @@ pub const WaitPidResult = struct { | ... | @@ -4000,8 +4006,28 @@ pub const WaitPidResult = struct { |
| 4000 | pub fn waitpid(pid: pid_t, flags: u32) WaitPidResult { | 4006 | pub fn waitpid(pid: pid_t, flags: u32) WaitPidResult { |
| 4001 | const Status = if (builtin.link_libc) c_int else u32; | 4007 | const Status = if (builtin.link_libc) c_int else u32; |
| 4002 | var status: Status = undefined; | 4008 | var status: Status = undefined; |
| 4009 | const coerced_flags = if (builtin.link_libc) @intCast(c_int, flags) else flags; | ||
| 4010 | while (true) { | ||
| 4011 | const rc = system.waitpid(pid, &status, coerced_flags); | ||
| 4012 | switch (errno(rc)) { | ||
| 4013 | .SUCCESS => return .{ | ||
| 4014 | .pid = @intCast(pid_t, rc), | ||
| 4015 | .status = @bitCast(u32, status), | ||
| 4016 | }, | ||
| 4017 | .INTR => continue, | ||
| 4018 | .CHILD => unreachable, // The process specified does not exist. It would be a race condition to handle this error. | ||
| 4019 | .INVAL => unreachable, // Invalid flags. | ||
| 4020 | else => unreachable, | ||
| 4021 | } | ||
| 4022 | } | ||
| 4023 | } | ||
| 4024 | |||
| 4025 | pub fn wait4(pid: pid_t, flags: u32, ru: ?*rusage) WaitPidResult { | ||
| 4026 | const Status = if (builtin.link_libc) c_int else u32; | ||
| 4027 | var status: Status = undefined; | ||
| 4028 | const coerced_flags = if (builtin.link_libc) @intCast(c_int, flags) else flags; | ||
| 4003 | while (true) { | 4029 | while (true) { |
| 4004 | const rc = system.waitpid(pid, &status, if (builtin.link_libc) @intCast(c_int, flags) else flags); | 4030 | const rc = system.wait4(pid, &status, coerced_flags, ru); |
| 4005 | switch (errno(rc)) { | 4031 | switch (errno(rc)) { |
| 4006 | .SUCCESS => return .{ | 4032 | .SUCCESS => return .{ |
| 4007 | .pid = @intCast(pid_t, rc), | 4033 | .pid = @intCast(pid_t, rc), |
lib/std/os/linux.zig+74-10| ... | @@ -944,6 +944,16 @@ pub fn waitpid(pid: pid_t, status: *u32, flags: u32) usize { | ... | @@ -944,6 +944,16 @@ pub fn waitpid(pid: pid_t, status: *u32, flags: u32) usize { |
| 944 | return syscall4(.wait4, @bitCast(usize, @as(isize, pid)), @ptrToInt(status), flags, 0); | 944 | return syscall4(.wait4, @bitCast(usize, @as(isize, pid)), @ptrToInt(status), flags, 0); |
| 945 | } | 945 | } |
| 946 | 946 | ||
| 947 | pub fn wait4(pid: pid_t, status: *u32, flags: u32, usage: ?*rusage) usize { | ||
| 948 | return syscall4( | ||
| 949 | .wait4, | ||
| 950 | @bitCast(usize, @as(isize, pid)), | ||
| 951 | @ptrToInt(status), | ||
| 952 | flags, | ||
| 953 | @ptrToInt(usage), | ||
| 954 | ); | ||
| 955 | } | ||
| 956 | |||
| 947 | pub fn waitid(id_type: P, id: i32, infop: *siginfo_t, flags: u32) usize { | 957 | pub fn waitid(id_type: P, id: i32, infop: *siginfo_t, flags: u32) usize { |
| 948 | return syscall5(.waitid, @enumToInt(id_type), @bitCast(usize, @as(isize, id)), @ptrToInt(infop), flags, 0); | 958 | return syscall5(.waitid, @enumToInt(id_type), @bitCast(usize, @as(isize, id)), @ptrToInt(infop), flags, 0); |
| 949 | } | 959 | } |
| ... | @@ -1716,26 +1726,26 @@ pub fn pidfd_send_signal(pidfd: fd_t, sig: i32, info: ?*siginfo_t, flags: u32) u | ... | @@ -1716,26 +1726,26 @@ pub fn pidfd_send_signal(pidfd: fd_t, sig: i32, info: ?*siginfo_t, flags: u32) u |
| 1716 | ); | 1726 | ); |
| 1717 | } | 1727 | } |
| 1718 | 1728 | ||
| 1719 | pub fn process_vm_readv(pid: pid_t, local: [*]const iovec, local_count: usize, remote: [*]const iovec, remote_count: usize, flags: usize) usize { | 1729 | pub fn process_vm_readv(pid: pid_t, local: []iovec, remote: []const iovec_const, flags: usize) usize { |
| 1720 | return syscall6( | 1730 | return syscall6( |
| 1721 | .process_vm_readv, | 1731 | .process_vm_readv, |
| 1722 | @bitCast(usize, @as(isize, pid)), | 1732 | @bitCast(usize, @as(isize, pid)), |
| 1723 | @ptrToInt(local), | 1733 | @ptrToInt(local.ptr), |
| 1724 | local_count, | 1734 | local.len, |
| 1725 | @ptrToInt(remote), | 1735 | @ptrToInt(remote.ptr), |
| 1726 | remote_count, | 1736 | remote.len, |
| 1727 | flags, | 1737 | flags, |
| 1728 | ); | 1738 | ); |
| 1729 | } | 1739 | } |
| 1730 | 1740 | ||
| 1731 | pub fn process_vm_writev(pid: pid_t, local: [*]const iovec, local_count: usize, remote: [*]const iovec, remote_count: usize, flags: usize) usize { | 1741 | pub fn process_vm_writev(pid: pid_t, local: []const iovec_const, remote: []const iovec_const, flags: usize) usize { |
| 1732 | return syscall6( | 1742 | return syscall6( |
| 1733 | .process_vm_writev, | 1743 | .process_vm_writev, |
| 1734 | @bitCast(usize, @as(isize, pid)), | 1744 | @bitCast(usize, @as(isize, pid)), |
| 1735 | @ptrToInt(local), | 1745 | @ptrToInt(local.ptr), |
| 1736 | local_count, | 1746 | local.len, |
| 1737 | @ptrToInt(remote), | 1747 | @ptrToInt(remote.ptr), |
| 1738 | remote_count, | 1748 | remote.len, |
| 1739 | flags, | 1749 | flags, |
| 1740 | ); | 1750 | ); |
| 1741 | } | 1751 | } |
| ... | @@ -1820,6 +1830,23 @@ pub fn seccomp(operation: u32, flags: u32, args: ?*const anyopaque) usize { | ... | @@ -1820,6 +1830,23 @@ pub fn seccomp(operation: u32, flags: u32, args: ?*const anyopaque) usize { |
| 1820 | return syscall3(.seccomp, operation, flags, @ptrToInt(args)); | 1830 | return syscall3(.seccomp, operation, flags, @ptrToInt(args)); |
| 1821 | } | 1831 | } |
| 1822 | 1832 | ||
| 1833 | pub fn ptrace( | ||
| 1834 | req: u32, | ||
| 1835 | pid: pid_t, | ||
| 1836 | addr: usize, | ||
| 1837 | data: usize, | ||
| 1838 | addr2: usize, | ||
| 1839 | ) usize { | ||
| 1840 | return syscall5( | ||
| 1841 | .ptrace, | ||
| 1842 | req, | ||
| 1843 | @bitCast(usize, @as(isize, pid)), | ||
| 1844 | addr, | ||
| 1845 | data, | ||
| 1846 | addr2, | ||
| 1847 | ); | ||
| 1848 | } | ||
| 1849 | |||
| 1823 | pub const E = switch (native_arch) { | 1850 | pub const E = switch (native_arch) { |
| 1824 | .mips, .mipsel => @import("linux/errno/mips.zig").E, | 1851 | .mips, .mipsel => @import("linux/errno/mips.zig").E, |
| 1825 | .sparc, .sparcel, .sparc64 => @import("linux/errno/sparc.zig").E, | 1852 | .sparc, .sparcel, .sparc64 => @import("linux/errno/sparc.zig").E, |
| ... | @@ -5721,3 +5748,40 @@ pub const AUDIT = struct { | ... | @@ -5721,3 +5748,40 @@ pub const AUDIT = struct { |
| 5721 | } | 5748 | } |
| 5722 | }; | 5749 | }; |
| 5723 | }; | 5750 | }; |
| 5751 | |||
| 5752 | pub const PTRACE = struct { | ||
| 5753 | pub const TRACEME = 0; | ||
| 5754 | pub const PEEKTEXT = 1; | ||
| 5755 | pub const PEEKDATA = 2; | ||
| 5756 | pub const PEEKUSER = 3; | ||
| 5757 | pub const POKETEXT = 4; | ||
| 5758 | pub const POKEDATA = 5; | ||
| 5759 | pub const POKEUSER = 6; | ||
| 5760 | pub const CONT = 7; | ||
| 5761 | pub const KILL = 8; | ||
| 5762 | pub const SINGLESTEP = 9; | ||
| 5763 | pub const GETREGS = 12; | ||
| 5764 | pub const SETREGS = 13; | ||
| 5765 | pub const GETFPREGS = 14; | ||
| 5766 | pub const SETFPREGS = 15; | ||
| 5767 | pub const ATTACH = 16; | ||
| 5768 | pub const DETACH = 17; | ||
| 5769 | pub const GETFPXREGS = 18; | ||
| 5770 | pub const SETFPXREGS = 19; | ||
| 5771 | pub const SYSCALL = 24; | ||
| 5772 | pub const SETOPTIONS = 0x4200; | ||
| 5773 | pub const GETEVENTMSG = 0x4201; | ||
| 5774 | pub const GETSIGINFO = 0x4202; | ||
| 5775 | pub const SETSIGINFO = 0x4203; | ||
| 5776 | pub const GETREGSET = 0x4204; | ||
| 5777 | pub const SETREGSET = 0x4205; | ||
| 5778 | pub const SEIZE = 0x4206; | ||
| 5779 | pub const INTERRUPT = 0x4207; | ||
| 5780 | pub const LISTEN = 0x4208; | ||
| 5781 | pub const PEEKSIGINFO = 0x4209; | ||
| 5782 | pub const GETSIGMASK = 0x420a; | ||
| 5783 | pub const SETSIGMASK = 0x420b; | ||
| 5784 | pub const SECCOMP_GET_FILTER = 0x420c; | ||
| 5785 | pub const SECCOMP_GET_METADATA = 0x420d; | ||
| 5786 | pub const GET_SYSCALL_INFO = 0x420e; | ||
| 5787 | }; |
lib/std/os/linux/io_uring.zig+104-66| ... | @@ -1728,10 +1728,12 @@ test "writev/fsync/readv" { | ... | @@ -1728,10 +1728,12 @@ test "writev/fsync/readv" { |
| 1728 | }; | 1728 | }; |
| 1729 | defer ring.deinit(); | 1729 | defer ring.deinit(); |
| 1730 | 1730 | ||
| 1731 | var tmp = std.testing.tmpDir(.{}); | ||
| 1732 | defer tmp.cleanup(); | ||
| 1733 | |||
| 1731 | const path = "test_io_uring_writev_fsync_readv"; | 1734 | const path = "test_io_uring_writev_fsync_readv"; |
| 1732 | const file = try std.fs.cwd().createFile(path, .{ .read = true, .truncate = true }); | 1735 | const file = try tmp.dir.createFile(path, .{ .read = true, .truncate = true }); |
| 1733 | defer file.close(); | 1736 | defer file.close(); |
| 1734 | defer std.fs.cwd().deleteFile(path) catch {}; | ||
| 1735 | const fd = file.handle; | 1737 | const fd = file.handle; |
| 1736 | 1738 | ||
| 1737 | const buffer_write = [_]u8{42} ** 128; | 1739 | const buffer_write = [_]u8{42} ** 128; |
| ... | @@ -1796,10 +1798,11 @@ test "write/read" { | ... | @@ -1796,10 +1798,11 @@ test "write/read" { |
| 1796 | }; | 1798 | }; |
| 1797 | defer ring.deinit(); | 1799 | defer ring.deinit(); |
| 1798 | 1800 | ||
| 1801 | var tmp = std.testing.tmpDir(.{}); | ||
| 1802 | defer tmp.cleanup(); | ||
| 1799 | const path = "test_io_uring_write_read"; | 1803 | const path = "test_io_uring_write_read"; |
| 1800 | const file = try std.fs.cwd().createFile(path, .{ .read = true, .truncate = true }); | 1804 | const file = try tmp.dir.createFile(path, .{ .read = true, .truncate = true }); |
| 1801 | defer file.close(); | 1805 | defer file.close(); |
| 1802 | defer std.fs.cwd().deleteFile(path) catch {}; | ||
| 1803 | const fd = file.handle; | 1806 | const fd = file.handle; |
| 1804 | 1807 | ||
| 1805 | const buffer_write = [_]u8{97} ** 20; | 1808 | const buffer_write = [_]u8{97} ** 20; |
| ... | @@ -1842,10 +1845,12 @@ test "write_fixed/read_fixed" { | ... | @@ -1842,10 +1845,12 @@ test "write_fixed/read_fixed" { |
| 1842 | }; | 1845 | }; |
| 1843 | defer ring.deinit(); | 1846 | defer ring.deinit(); |
| 1844 | 1847 | ||
| 1848 | var tmp = std.testing.tmpDir(.{}); | ||
| 1849 | defer tmp.cleanup(); | ||
| 1850 | |||
| 1845 | const path = "test_io_uring_write_read_fixed"; | 1851 | const path = "test_io_uring_write_read_fixed"; |
| 1846 | const file = try std.fs.cwd().createFile(path, .{ .read = true, .truncate = true }); | 1852 | const file = try tmp.dir.createFile(path, .{ .read = true, .truncate = true }); |
| 1847 | defer file.close(); | 1853 | defer file.close(); |
| 1848 | defer std.fs.cwd().deleteFile(path) catch {}; | ||
| 1849 | const fd = file.handle; | 1854 | const fd = file.handle; |
| 1850 | 1855 | ||
| 1851 | var raw_buffers: [2][11]u8 = undefined; | 1856 | var raw_buffers: [2][11]u8 = undefined; |
| ... | @@ -1899,8 +1904,10 @@ test "openat" { | ... | @@ -1899,8 +1904,10 @@ test "openat" { |
| 1899 | }; | 1904 | }; |
| 1900 | defer ring.deinit(); | 1905 | defer ring.deinit(); |
| 1901 | 1906 | ||
| 1907 | var tmp = std.testing.tmpDir(.{}); | ||
| 1908 | defer tmp.cleanup(); | ||
| 1909 | |||
| 1902 | const path = "test_io_uring_openat"; | 1910 | const path = "test_io_uring_openat"; |
| 1903 | defer std.fs.cwd().deleteFile(path) catch {}; | ||
| 1904 | 1911 | ||
| 1905 | // Workaround for LLVM bug: https://github.com/ziglang/zig/issues/12014 | 1912 | // Workaround for LLVM bug: https://github.com/ziglang/zig/issues/12014 |
| 1906 | const path_addr = if (builtin.zig_backend == .stage2_llvm) p: { | 1913 | const path_addr = if (builtin.zig_backend == .stage2_llvm) p: { |
| ... | @@ -1910,12 +1917,12 @@ test "openat" { | ... | @@ -1910,12 +1917,12 @@ test "openat" { |
| 1910 | 1917 | ||
| 1911 | const flags: u32 = os.O.CLOEXEC | os.O.RDWR | os.O.CREAT; | 1918 | const flags: u32 = os.O.CLOEXEC | os.O.RDWR | os.O.CREAT; |
| 1912 | const mode: os.mode_t = 0o666; | 1919 | const mode: os.mode_t = 0o666; |
| 1913 | const sqe_openat = try ring.openat(0x33333333, linux.AT.FDCWD, path, flags, mode); | 1920 | const sqe_openat = try ring.openat(0x33333333, tmp.dir.fd, path, flags, mode); |
| 1914 | try testing.expectEqual(linux.io_uring_sqe{ | 1921 | try testing.expectEqual(linux.io_uring_sqe{ |
| 1915 | .opcode = .OPENAT, | 1922 | .opcode = .OPENAT, |
| 1916 | .flags = 0, | 1923 | .flags = 0, |
| 1917 | .ioprio = 0, | 1924 | .ioprio = 0, |
| 1918 | .fd = linux.AT.FDCWD, | 1925 | .fd = tmp.dir.fd, |
| 1919 | .off = 0, | 1926 | .off = 0, |
| 1920 | .addr = path_addr, | 1927 | .addr = path_addr, |
| 1921 | .len = mode, | 1928 | .len = mode, |
| ... | @@ -1931,12 +1938,6 @@ test "openat" { | ... | @@ -1931,12 +1938,6 @@ test "openat" { |
| 1931 | const cqe_openat = try ring.copy_cqe(); | 1938 | const cqe_openat = try ring.copy_cqe(); |
| 1932 | try testing.expectEqual(@as(u64, 0x33333333), cqe_openat.user_data); | 1939 | try testing.expectEqual(@as(u64, 0x33333333), cqe_openat.user_data); |
| 1933 | if (cqe_openat.err() == .INVAL) return error.SkipZigTest; | 1940 | if (cqe_openat.err() == .INVAL) return error.SkipZigTest; |
| 1934 | // AT.FDCWD is not fully supported before kernel 5.6: | ||
| 1935 | // See https://lore.kernel.org/io-uring/20200207155039.12819-1-axboe@kernel.dk/T/ | ||
| 1936 | // We use IORING_FEAT_RW_CUR_POS to know if we are pre-5.6 since that feature was added in 5.6. | ||
| 1937 | if (cqe_openat.err() == .BADF and (ring.features & linux.IORING_FEAT_RW_CUR_POS) == 0) { | ||
| 1938 | return error.SkipZigTest; | ||
| 1939 | } | ||
| 1940 | if (cqe_openat.res <= 0) std.debug.print("\ncqe_openat.res={}\n", .{cqe_openat.res}); | 1941 | if (cqe_openat.res <= 0) std.debug.print("\ncqe_openat.res={}\n", .{cqe_openat.res}); |
| 1941 | try testing.expect(cqe_openat.res > 0); | 1942 | try testing.expect(cqe_openat.res > 0); |
| 1942 | try testing.expectEqual(@as(u32, 0), cqe_openat.flags); | 1943 | try testing.expectEqual(@as(u32, 0), cqe_openat.flags); |
| ... | @@ -1954,10 +1955,12 @@ test "close" { | ... | @@ -1954,10 +1955,12 @@ test "close" { |
| 1954 | }; | 1955 | }; |
| 1955 | defer ring.deinit(); | 1956 | defer ring.deinit(); |
| 1956 | 1957 | ||
| 1958 | var tmp = std.testing.tmpDir(.{}); | ||
| 1959 | defer tmp.cleanup(); | ||
| 1960 | |||
| 1957 | const path = "test_io_uring_close"; | 1961 | const path = "test_io_uring_close"; |
| 1958 | const file = try std.fs.cwd().createFile(path, .{}); | 1962 | const file = try tmp.dir.createFile(path, .{}); |
| 1959 | errdefer file.close(); | 1963 | errdefer file.close(); |
| 1960 | defer std.fs.cwd().deleteFile(path) catch {}; | ||
| 1961 | 1964 | ||
| 1962 | const sqe_close = try ring.close(0x44444444, file.handle); | 1965 | const sqe_close = try ring.close(0x44444444, file.handle); |
| 1963 | try testing.expectEqual(linux.IORING_OP.CLOSE, sqe_close.opcode); | 1966 | try testing.expectEqual(linux.IORING_OP.CLOSE, sqe_close.opcode); |
| ... | @@ -1976,6 +1979,11 @@ test "close" { | ... | @@ -1976,6 +1979,11 @@ test "close" { |
| 1976 | test "accept/connect/send/recv" { | 1979 | test "accept/connect/send/recv" { |
| 1977 | if (builtin.os.tag != .linux) return error.SkipZigTest; | 1980 | if (builtin.os.tag != .linux) return error.SkipZigTest; |
| 1978 | 1981 | ||
| 1982 | if (true) { | ||
| 1983 | // https://github.com/ziglang/zig/issues/14907 | ||
| 1984 | return error.SkipZigTest; | ||
| 1985 | } | ||
| 1986 | |||
| 1979 | var ring = IO_Uring.init(16, 0) catch |err| switch (err) { | 1987 | var ring = IO_Uring.init(16, 0) catch |err| switch (err) { |
| 1980 | error.SystemOutdated => return error.SkipZigTest, | 1988 | error.SystemOutdated => return error.SkipZigTest, |
| 1981 | error.PermissionDenied => return error.SkipZigTest, | 1989 | error.PermissionDenied => return error.SkipZigTest, |
| ... | @@ -2017,6 +2025,11 @@ test "accept/connect/send/recv" { | ... | @@ -2017,6 +2025,11 @@ test "accept/connect/send/recv" { |
| 2017 | test "sendmsg/recvmsg" { | 2025 | test "sendmsg/recvmsg" { |
| 2018 | if (builtin.os.tag != .linux) return error.SkipZigTest; | 2026 | if (builtin.os.tag != .linux) return error.SkipZigTest; |
| 2019 | 2027 | ||
| 2028 | if (true) { | ||
| 2029 | // https://github.com/ziglang/zig/issues/14907 | ||
| 2030 | return error.SkipZigTest; | ||
| 2031 | } | ||
| 2032 | |||
| 2020 | var ring = IO_Uring.init(2, 0) catch |err| switch (err) { | 2033 | var ring = IO_Uring.init(2, 0) catch |err| switch (err) { |
| 2021 | error.SystemOutdated => return error.SkipZigTest, | 2034 | error.SystemOutdated => return error.SkipZigTest, |
| 2022 | error.PermissionDenied => return error.SkipZigTest, | 2035 | error.PermissionDenied => return error.SkipZigTest, |
| ... | @@ -2024,6 +2037,7 @@ test "sendmsg/recvmsg" { | ... | @@ -2024,6 +2037,7 @@ test "sendmsg/recvmsg" { |
| 2024 | }; | 2037 | }; |
| 2025 | defer ring.deinit(); | 2038 | defer ring.deinit(); |
| 2026 | 2039 | ||
| 2040 | if (true) @compileError("don't hard code port numbers in unit tests"); // https://github.com/ziglang/zig/issues/14907 | ||
| 2027 | const address_server = try net.Address.parseIp4("127.0.0.1", 3131); | 2041 | const address_server = try net.Address.parseIp4("127.0.0.1", 3131); |
| 2028 | 2042 | ||
| 2029 | const server = try os.socket(address_server.any.family, os.SOCK.DGRAM, 0); | 2043 | const server = try os.socket(address_server.any.family, os.SOCK.DGRAM, 0); |
| ... | @@ -2223,6 +2237,11 @@ test "timeout_remove" { | ... | @@ -2223,6 +2237,11 @@ test "timeout_remove" { |
| 2223 | test "accept/connect/recv/link_timeout" { | 2237 | test "accept/connect/recv/link_timeout" { |
| 2224 | if (builtin.os.tag != .linux) return error.SkipZigTest; | 2238 | if (builtin.os.tag != .linux) return error.SkipZigTest; |
| 2225 | 2239 | ||
| 2240 | if (true) { | ||
| 2241 | // https://github.com/ziglang/zig/issues/14907 | ||
| 2242 | return error.SkipZigTest; | ||
| 2243 | } | ||
| 2244 | |||
| 2226 | var ring = IO_Uring.init(16, 0) catch |err| switch (err) { | 2245 | var ring = IO_Uring.init(16, 0) catch |err| switch (err) { |
| 2227 | error.SystemOutdated => return error.SkipZigTest, | 2246 | error.SystemOutdated => return error.SkipZigTest, |
| 2228 | error.PermissionDenied => return error.SkipZigTest, | 2247 | error.PermissionDenied => return error.SkipZigTest, |
| ... | @@ -2279,10 +2298,12 @@ test "fallocate" { | ... | @@ -2279,10 +2298,12 @@ test "fallocate" { |
| 2279 | }; | 2298 | }; |
| 2280 | defer ring.deinit(); | 2299 | defer ring.deinit(); |
| 2281 | 2300 | ||
| 2301 | var tmp = std.testing.tmpDir(.{}); | ||
| 2302 | defer tmp.cleanup(); | ||
| 2303 | |||
| 2282 | const path = "test_io_uring_fallocate"; | 2304 | const path = "test_io_uring_fallocate"; |
| 2283 | const file = try std.fs.cwd().createFile(path, .{ .truncate = true, .mode = 0o666 }); | 2305 | const file = try tmp.dir.createFile(path, .{ .truncate = true, .mode = 0o666 }); |
| 2284 | defer file.close(); | 2306 | defer file.close(); |
| 2285 | defer std.fs.cwd().deleteFile(path) catch {}; | ||
| 2286 | 2307 | ||
| 2287 | try testing.expectEqual(@as(u64, 0), (try file.stat()).size); | 2308 | try testing.expectEqual(@as(u64, 0), (try file.stat()).size); |
| 2288 | 2309 | ||
| ... | @@ -2323,10 +2344,11 @@ test "statx" { | ... | @@ -2323,10 +2344,11 @@ test "statx" { |
| 2323 | }; | 2344 | }; |
| 2324 | defer ring.deinit(); | 2345 | defer ring.deinit(); |
| 2325 | 2346 | ||
| 2347 | var tmp = std.testing.tmpDir(.{}); | ||
| 2348 | defer tmp.cleanup(); | ||
| 2326 | const path = "test_io_uring_statx"; | 2349 | const path = "test_io_uring_statx"; |
| 2327 | const file = try std.fs.cwd().createFile(path, .{ .truncate = true, .mode = 0o666 }); | 2350 | const file = try tmp.dir.createFile(path, .{ .truncate = true, .mode = 0o666 }); |
| 2328 | defer file.close(); | 2351 | defer file.close(); |
| 2329 | defer std.fs.cwd().deleteFile(path) catch {}; | ||
| 2330 | 2352 | ||
| 2331 | try testing.expectEqual(@as(u64, 0), (try file.stat()).size); | 2353 | try testing.expectEqual(@as(u64, 0), (try file.stat()).size); |
| 2332 | 2354 | ||
| ... | @@ -2335,14 +2357,14 @@ test "statx" { | ... | @@ -2335,14 +2357,14 @@ test "statx" { |
| 2335 | var buf: linux.Statx = undefined; | 2357 | var buf: linux.Statx = undefined; |
| 2336 | const sqe = try ring.statx( | 2358 | const sqe = try ring.statx( |
| 2337 | 0xaaaaaaaa, | 2359 | 0xaaaaaaaa, |
| 2338 | linux.AT.FDCWD, | 2360 | tmp.dir.fd, |
| 2339 | path, | 2361 | path, |
| 2340 | 0, | 2362 | 0, |
| 2341 | linux.STATX_SIZE, | 2363 | linux.STATX_SIZE, |
| 2342 | &buf, | 2364 | &buf, |
| 2343 | ); | 2365 | ); |
| 2344 | try testing.expectEqual(linux.IORING_OP.STATX, sqe.opcode); | 2366 | try testing.expectEqual(linux.IORING_OP.STATX, sqe.opcode); |
| 2345 | try testing.expectEqual(@as(i32, linux.AT.FDCWD), sqe.fd); | 2367 | try testing.expectEqual(@as(i32, tmp.dir.fd), sqe.fd); |
| 2346 | try testing.expectEqual(@as(u32, 1), try ring.submit()); | 2368 | try testing.expectEqual(@as(u32, 1), try ring.submit()); |
| 2347 | 2369 | ||
| 2348 | const cqe = try ring.copy_cqe(); | 2370 | const cqe = try ring.copy_cqe(); |
| ... | @@ -2355,8 +2377,6 @@ test "statx" { | ... | @@ -2355,8 +2377,6 @@ test "statx" { |
| 2355 | // The filesystem containing the file referred to by fd does not support this operation; | 2377 | // The filesystem containing the file referred to by fd does not support this operation; |
| 2356 | // or the mode is not supported by the filesystem containing the file referred to by fd: | 2378 | // or the mode is not supported by the filesystem containing the file referred to by fd: |
| 2357 | .OPNOTSUPP => return error.SkipZigTest, | 2379 | .OPNOTSUPP => return error.SkipZigTest, |
| 2358 | // The kernel is too old to support FDCWD for dir_fd | ||
| 2359 | .BADF => return error.SkipZigTest, | ||
| 2360 | else => |errno| std.debug.panic("unhandled errno: {}", .{errno}), | 2380 | else => |errno| std.debug.panic("unhandled errno: {}", .{errno}), |
| 2361 | } | 2381 | } |
| 2362 | try testing.expectEqual(linux.io_uring_cqe{ | 2382 | try testing.expectEqual(linux.io_uring_cqe{ |
| ... | @@ -2372,6 +2392,11 @@ test "statx" { | ... | @@ -2372,6 +2392,11 @@ test "statx" { |
| 2372 | test "accept/connect/recv/cancel" { | 2392 | test "accept/connect/recv/cancel" { |
| 2373 | if (builtin.os.tag != .linux) return error.SkipZigTest; | 2393 | if (builtin.os.tag != .linux) return error.SkipZigTest; |
| 2374 | 2394 | ||
| 2395 | if (true) { | ||
| 2396 | // https://github.com/ziglang/zig/issues/14907 | ||
| 2397 | return error.SkipZigTest; | ||
| 2398 | } | ||
| 2399 | |||
| 2375 | var ring = IO_Uring.init(16, 0) catch |err| switch (err) { | 2400 | var ring = IO_Uring.init(16, 0) catch |err| switch (err) { |
| 2376 | error.SystemOutdated => return error.SkipZigTest, | 2401 | error.SystemOutdated => return error.SkipZigTest, |
| 2377 | error.PermissionDenied => return error.SkipZigTest, | 2402 | error.PermissionDenied => return error.SkipZigTest, |
| ... | @@ -2509,6 +2534,11 @@ test "register_files_update" { | ... | @@ -2509,6 +2534,11 @@ test "register_files_update" { |
| 2509 | test "shutdown" { | 2534 | test "shutdown" { |
| 2510 | if (builtin.os.tag != .linux) return error.SkipZigTest; | 2535 | if (builtin.os.tag != .linux) return error.SkipZigTest; |
| 2511 | 2536 | ||
| 2537 | if (true) { | ||
| 2538 | // https://github.com/ziglang/zig/issues/14907 | ||
| 2539 | return error.SkipZigTest; | ||
| 2540 | } | ||
| 2541 | |||
| 2512 | var ring = IO_Uring.init(16, 0) catch |err| switch (err) { | 2542 | var ring = IO_Uring.init(16, 0) catch |err| switch (err) { |
| 2513 | error.SystemOutdated => return error.SkipZigTest, | 2543 | error.SystemOutdated => return error.SkipZigTest, |
| 2514 | error.PermissionDenied => return error.SkipZigTest, | 2544 | error.PermissionDenied => return error.SkipZigTest, |
| ... | @@ -2516,6 +2546,7 @@ test "shutdown" { | ... | @@ -2516,6 +2546,7 @@ test "shutdown" { |
| 2516 | }; | 2546 | }; |
| 2517 | defer ring.deinit(); | 2547 | defer ring.deinit(); |
| 2518 | 2548 | ||
| 2549 | if (true) @compileError("don't hard code port numbers in unit tests"); // https://github.com/ziglang/zig/issues/14907 | ||
| 2519 | const address = try net.Address.parseIp4("127.0.0.1", 3131); | 2550 | const address = try net.Address.parseIp4("127.0.0.1", 3131); |
| 2520 | 2551 | ||
| 2521 | // Socket bound, expect shutdown to work | 2552 | // Socket bound, expect shutdown to work |
| ... | @@ -2579,28 +2610,28 @@ test "renameat" { | ... | @@ -2579,28 +2610,28 @@ test "renameat" { |
| 2579 | const old_path = "test_io_uring_renameat_old"; | 2610 | const old_path = "test_io_uring_renameat_old"; |
| 2580 | const new_path = "test_io_uring_renameat_new"; | 2611 | const new_path = "test_io_uring_renameat_new"; |
| 2581 | 2612 | ||
| 2613 | var tmp = std.testing.tmpDir(.{}); | ||
| 2614 | defer tmp.cleanup(); | ||
| 2615 | |||
| 2582 | // Write old file with data | 2616 | // Write old file with data |
| 2583 | 2617 | ||
| 2584 | const old_file = try std.fs.cwd().createFile(old_path, .{ .truncate = true, .mode = 0o666 }); | 2618 | const old_file = try tmp.dir.createFile(old_path, .{ .truncate = true, .mode = 0o666 }); |
| 2585 | defer { | 2619 | defer old_file.close(); |
| 2586 | old_file.close(); | ||
| 2587 | std.fs.cwd().deleteFile(new_path) catch {}; | ||
| 2588 | } | ||
| 2589 | try old_file.writeAll("hello"); | 2620 | try old_file.writeAll("hello"); |
| 2590 | 2621 | ||
| 2591 | // Submit renameat | 2622 | // Submit renameat |
| 2592 | 2623 | ||
| 2593 | var sqe = try ring.renameat( | 2624 | var sqe = try ring.renameat( |
| 2594 | 0x12121212, | 2625 | 0x12121212, |
| 2595 | linux.AT.FDCWD, | 2626 | tmp.dir.fd, |
| 2596 | old_path, | 2627 | old_path, |
| 2597 | linux.AT.FDCWD, | 2628 | tmp.dir.fd, |
| 2598 | new_path, | 2629 | new_path, |
| 2599 | 0, | 2630 | 0, |
| 2600 | ); | 2631 | ); |
| 2601 | try testing.expectEqual(linux.IORING_OP.RENAMEAT, sqe.opcode); | 2632 | try testing.expectEqual(linux.IORING_OP.RENAMEAT, sqe.opcode); |
| 2602 | try testing.expectEqual(@as(i32, linux.AT.FDCWD), sqe.fd); | 2633 | try testing.expectEqual(@as(i32, tmp.dir.fd), sqe.fd); |
| 2603 | try testing.expectEqual(@as(i32, linux.AT.FDCWD), @bitCast(i32, sqe.len)); | 2634 | try testing.expectEqual(@as(i32, tmp.dir.fd), @bitCast(i32, sqe.len)); |
| 2604 | try testing.expectEqual(@as(u32, 1), try ring.submit()); | 2635 | try testing.expectEqual(@as(u32, 1), try ring.submit()); |
| 2605 | 2636 | ||
| 2606 | const cqe = try ring.copy_cqe(); | 2637 | const cqe = try ring.copy_cqe(); |
| ... | @@ -2618,7 +2649,7 @@ test "renameat" { | ... | @@ -2618,7 +2649,7 @@ test "renameat" { |
| 2618 | 2649 | ||
| 2619 | // Validate that the old file doesn't exist anymore | 2650 | // Validate that the old file doesn't exist anymore |
| 2620 | { | 2651 | { |
| 2621 | _ = std.fs.cwd().openFile(old_path, .{}) catch |err| switch (err) { | 2652 | _ = tmp.dir.openFile(old_path, .{}) catch |err| switch (err) { |
| 2622 | error.FileNotFound => {}, | 2653 | error.FileNotFound => {}, |
| 2623 | else => std.debug.panic("unexpected error: {}", .{err}), | 2654 | else => std.debug.panic("unexpected error: {}", .{err}), |
| 2624 | }; | 2655 | }; |
| ... | @@ -2626,7 +2657,7 @@ test "renameat" { | ... | @@ -2626,7 +2657,7 @@ test "renameat" { |
| 2626 | 2657 | ||
| 2627 | // Validate that the new file exists with the proper content | 2658 | // Validate that the new file exists with the proper content |
| 2628 | { | 2659 | { |
| 2629 | const new_file = try std.fs.cwd().openFile(new_path, .{}); | 2660 | const new_file = try tmp.dir.openFile(new_path, .{}); |
| 2630 | defer new_file.close(); | 2661 | defer new_file.close(); |
| 2631 | 2662 | ||
| 2632 | var new_file_data: [16]u8 = undefined; | 2663 | var new_file_data: [16]u8 = undefined; |
| ... | @@ -2647,22 +2678,24 @@ test "unlinkat" { | ... | @@ -2647,22 +2678,24 @@ test "unlinkat" { |
| 2647 | 2678 | ||
| 2648 | const path = "test_io_uring_unlinkat"; | 2679 | const path = "test_io_uring_unlinkat"; |
| 2649 | 2680 | ||
| 2681 | var tmp = std.testing.tmpDir(.{}); | ||
| 2682 | defer tmp.cleanup(); | ||
| 2683 | |||
| 2650 | // Write old file with data | 2684 | // Write old file with data |
| 2651 | 2685 | ||
| 2652 | const file = try std.fs.cwd().createFile(path, .{ .truncate = true, .mode = 0o666 }); | 2686 | const file = try tmp.dir.createFile(path, .{ .truncate = true, .mode = 0o666 }); |
| 2653 | defer file.close(); | 2687 | defer file.close(); |
| 2654 | defer std.fs.cwd().deleteFile(path) catch {}; | ||
| 2655 | 2688 | ||
| 2656 | // Submit unlinkat | 2689 | // Submit unlinkat |
| 2657 | 2690 | ||
| 2658 | var sqe = try ring.unlinkat( | 2691 | var sqe = try ring.unlinkat( |
| 2659 | 0x12121212, | 2692 | 0x12121212, |
| 2660 | linux.AT.FDCWD, | 2693 | tmp.dir.fd, |
| 2661 | path, | 2694 | path, |
| 2662 | 0, | 2695 | 0, |
| 2663 | ); | 2696 | ); |
| 2664 | try testing.expectEqual(linux.IORING_OP.UNLINKAT, sqe.opcode); | 2697 | try testing.expectEqual(linux.IORING_OP.UNLINKAT, sqe.opcode); |
| 2665 | try testing.expectEqual(@as(i32, linux.AT.FDCWD), sqe.fd); | 2698 | try testing.expectEqual(@as(i32, tmp.dir.fd), sqe.fd); |
| 2666 | try testing.expectEqual(@as(u32, 1), try ring.submit()); | 2699 | try testing.expectEqual(@as(u32, 1), try ring.submit()); |
| 2667 | 2700 | ||
| 2668 | const cqe = try ring.copy_cqe(); | 2701 | const cqe = try ring.copy_cqe(); |
| ... | @@ -2679,7 +2712,7 @@ test "unlinkat" { | ... | @@ -2679,7 +2712,7 @@ test "unlinkat" { |
| 2679 | }, cqe); | 2712 | }, cqe); |
| 2680 | 2713 | ||
| 2681 | // Validate that the file doesn't exist anymore | 2714 | // Validate that the file doesn't exist anymore |
| 2682 | _ = std.fs.cwd().openFile(path, .{}) catch |err| switch (err) { | 2715 | _ = tmp.dir.openFile(path, .{}) catch |err| switch (err) { |
| 2683 | error.FileNotFound => {}, | 2716 | error.FileNotFound => {}, |
| 2684 | else => std.debug.panic("unexpected error: {}", .{err}), | 2717 | else => std.debug.panic("unexpected error: {}", .{err}), |
| 2685 | }; | 2718 | }; |
| ... | @@ -2695,20 +2728,21 @@ test "mkdirat" { | ... | @@ -2695,20 +2728,21 @@ test "mkdirat" { |
| 2695 | }; | 2728 | }; |
| 2696 | defer ring.deinit(); | 2729 | defer ring.deinit(); |
| 2697 | 2730 | ||
| 2698 | const path = "test_io_uring_mkdirat"; | 2731 | var tmp = std.testing.tmpDir(.{}); |
| 2732 | defer tmp.cleanup(); | ||
| 2699 | 2733 | ||
| 2700 | defer std.fs.cwd().deleteDir(path) catch {}; | 2734 | const path = "test_io_uring_mkdirat"; |
| 2701 | 2735 | ||
| 2702 | // Submit mkdirat | 2736 | // Submit mkdirat |
| 2703 | 2737 | ||
| 2704 | var sqe = try ring.mkdirat( | 2738 | var sqe = try ring.mkdirat( |
| 2705 | 0x12121212, | 2739 | 0x12121212, |
| 2706 | linux.AT.FDCWD, | 2740 | tmp.dir.fd, |
| 2707 | path, | 2741 | path, |
| 2708 | 0o0755, | 2742 | 0o0755, |
| 2709 | ); | 2743 | ); |
| 2710 | try testing.expectEqual(linux.IORING_OP.MKDIRAT, sqe.opcode); | 2744 | try testing.expectEqual(linux.IORING_OP.MKDIRAT, sqe.opcode); |
| 2711 | try testing.expectEqual(@as(i32, linux.AT.FDCWD), sqe.fd); | 2745 | try testing.expectEqual(@as(i32, tmp.dir.fd), sqe.fd); |
| 2712 | try testing.expectEqual(@as(u32, 1), try ring.submit()); | 2746 | try testing.expectEqual(@as(u32, 1), try ring.submit()); |
| 2713 | 2747 | ||
| 2714 | const cqe = try ring.copy_cqe(); | 2748 | const cqe = try ring.copy_cqe(); |
| ... | @@ -2725,7 +2759,7 @@ test "mkdirat" { | ... | @@ -2725,7 +2759,7 @@ test "mkdirat" { |
| 2725 | }, cqe); | 2759 | }, cqe); |
| 2726 | 2760 | ||
| 2727 | // Validate that the directory exist | 2761 | // Validate that the directory exist |
| 2728 | _ = try std.fs.cwd().openDir(path, .{}); | 2762 | _ = try tmp.dir.openDir(path, .{}); |
| 2729 | } | 2763 | } |
| 2730 | 2764 | ||
| 2731 | test "symlinkat" { | 2765 | test "symlinkat" { |
| ... | @@ -2738,26 +2772,25 @@ test "symlinkat" { | ... | @@ -2738,26 +2772,25 @@ test "symlinkat" { |
| 2738 | }; | 2772 | }; |
| 2739 | defer ring.deinit(); | 2773 | defer ring.deinit(); |
| 2740 | 2774 | ||
| 2775 | var tmp = std.testing.tmpDir(.{}); | ||
| 2776 | defer tmp.cleanup(); | ||
| 2777 | |||
| 2741 | const path = "test_io_uring_symlinkat"; | 2778 | const path = "test_io_uring_symlinkat"; |
| 2742 | const link_path = "test_io_uring_symlinkat_link"; | 2779 | const link_path = "test_io_uring_symlinkat_link"; |
| 2743 | 2780 | ||
| 2744 | const file = try std.fs.cwd().createFile(path, .{ .truncate = true, .mode = 0o666 }); | 2781 | const file = try tmp.dir.createFile(path, .{ .truncate = true, .mode = 0o666 }); |
| 2745 | defer { | 2782 | defer file.close(); |
| 2746 | file.close(); | ||
| 2747 | std.fs.cwd().deleteFile(path) catch {}; | ||
| 2748 | std.fs.cwd().deleteFile(link_path) catch {}; | ||
| 2749 | } | ||
| 2750 | 2783 | ||
| 2751 | // Submit symlinkat | 2784 | // Submit symlinkat |
| 2752 | 2785 | ||
| 2753 | var sqe = try ring.symlinkat( | 2786 | var sqe = try ring.symlinkat( |
| 2754 | 0x12121212, | 2787 | 0x12121212, |
| 2755 | path, | 2788 | path, |
| 2756 | linux.AT.FDCWD, | 2789 | tmp.dir.fd, |
| 2757 | link_path, | 2790 | link_path, |
| 2758 | ); | 2791 | ); |
| 2759 | try testing.expectEqual(linux.IORING_OP.SYMLINKAT, sqe.opcode); | 2792 | try testing.expectEqual(linux.IORING_OP.SYMLINKAT, sqe.opcode); |
| 2760 | try testing.expectEqual(@as(i32, linux.AT.FDCWD), sqe.fd); | 2793 | try testing.expectEqual(@as(i32, tmp.dir.fd), sqe.fd); |
| 2761 | try testing.expectEqual(@as(u32, 1), try ring.submit()); | 2794 | try testing.expectEqual(@as(u32, 1), try ring.submit()); |
| 2762 | 2795 | ||
| 2763 | const cqe = try ring.copy_cqe(); | 2796 | const cqe = try ring.copy_cqe(); |
| ... | @@ -2774,7 +2807,7 @@ test "symlinkat" { | ... | @@ -2774,7 +2807,7 @@ test "symlinkat" { |
| 2774 | }, cqe); | 2807 | }, cqe); |
| 2775 | 2808 | ||
| 2776 | // Validate that the symlink exist | 2809 | // Validate that the symlink exist |
| 2777 | _ = try std.fs.cwd().openFile(link_path, .{}); | 2810 | _ = try tmp.dir.openFile(link_path, .{}); |
| 2778 | } | 2811 | } |
| 2779 | 2812 | ||
| 2780 | test "linkat" { | 2813 | test "linkat" { |
| ... | @@ -2787,32 +2820,31 @@ test "linkat" { | ... | @@ -2787,32 +2820,31 @@ test "linkat" { |
| 2787 | }; | 2820 | }; |
| 2788 | defer ring.deinit(); | 2821 | defer ring.deinit(); |
| 2789 | 2822 | ||
| 2823 | var tmp = std.testing.tmpDir(.{}); | ||
| 2824 | defer tmp.cleanup(); | ||
| 2825 | |||
| 2790 | const first_path = "test_io_uring_linkat_first"; | 2826 | const first_path = "test_io_uring_linkat_first"; |
| 2791 | const second_path = "test_io_uring_linkat_second"; | 2827 | const second_path = "test_io_uring_linkat_second"; |
| 2792 | 2828 | ||
| 2793 | // Write file with data | 2829 | // Write file with data |
| 2794 | 2830 | ||
| 2795 | const first_file = try std.fs.cwd().createFile(first_path, .{ .truncate = true, .mode = 0o666 }); | 2831 | const first_file = try tmp.dir.createFile(first_path, .{ .truncate = true, .mode = 0o666 }); |
| 2796 | defer { | 2832 | defer first_file.close(); |
| 2797 | first_file.close(); | ||
| 2798 | std.fs.cwd().deleteFile(first_path) catch {}; | ||
| 2799 | std.fs.cwd().deleteFile(second_path) catch {}; | ||
| 2800 | } | ||
| 2801 | try first_file.writeAll("hello"); | 2833 | try first_file.writeAll("hello"); |
| 2802 | 2834 | ||
| 2803 | // Submit linkat | 2835 | // Submit linkat |
| 2804 | 2836 | ||
| 2805 | var sqe = try ring.linkat( | 2837 | var sqe = try ring.linkat( |
| 2806 | 0x12121212, | 2838 | 0x12121212, |
| 2807 | linux.AT.FDCWD, | 2839 | tmp.dir.fd, |
| 2808 | first_path, | 2840 | first_path, |
| 2809 | linux.AT.FDCWD, | 2841 | tmp.dir.fd, |
| 2810 | second_path, | 2842 | second_path, |
| 2811 | 0, | 2843 | 0, |
| 2812 | ); | 2844 | ); |
| 2813 | try testing.expectEqual(linux.IORING_OP.LINKAT, sqe.opcode); | 2845 | try testing.expectEqual(linux.IORING_OP.LINKAT, sqe.opcode); |
| 2814 | try testing.expectEqual(@as(i32, linux.AT.FDCWD), sqe.fd); | 2846 | try testing.expectEqual(@as(i32, tmp.dir.fd), sqe.fd); |
| 2815 | try testing.expectEqual(@as(i32, linux.AT.FDCWD), @bitCast(i32, sqe.len)); | 2847 | try testing.expectEqual(@as(i32, tmp.dir.fd), @bitCast(i32, sqe.len)); |
| 2816 | try testing.expectEqual(@as(u32, 1), try ring.submit()); | 2848 | try testing.expectEqual(@as(u32, 1), try ring.submit()); |
| 2817 | 2849 | ||
| 2818 | const cqe = try ring.copy_cqe(); | 2850 | const cqe = try ring.copy_cqe(); |
| ... | @@ -2829,7 +2861,7 @@ test "linkat" { | ... | @@ -2829,7 +2861,7 @@ test "linkat" { |
| 2829 | }, cqe); | 2861 | }, cqe); |
| 2830 | 2862 | ||
| 2831 | // Validate the second file | 2863 | // Validate the second file |
| 2832 | const second_file = try std.fs.cwd().openFile(second_path, .{}); | 2864 | const second_file = try tmp.dir.openFile(second_path, .{}); |
| 2833 | defer second_file.close(); | 2865 | defer second_file.close(); |
| 2834 | 2866 | ||
| 2835 | var second_file_data: [16]u8 = undefined; | 2867 | var second_file_data: [16]u8 = undefined; |
| ... | @@ -3060,6 +3092,11 @@ test "remove_buffers" { | ... | @@ -3060,6 +3092,11 @@ test "remove_buffers" { |
| 3060 | test "provide_buffers: accept/connect/send/recv" { | 3092 | test "provide_buffers: accept/connect/send/recv" { |
| 3061 | if (builtin.os.tag != .linux) return error.SkipZigTest; | 3093 | if (builtin.os.tag != .linux) return error.SkipZigTest; |
| 3062 | 3094 | ||
| 3095 | if (true) { | ||
| 3096 | // https://github.com/ziglang/zig/issues/14907 | ||
| 3097 | return error.SkipZigTest; | ||
| 3098 | } | ||
| 3099 | |||
| 3063 | var ring = IO_Uring.init(16, 0) catch |err| switch (err) { | 3100 | var ring = IO_Uring.init(16, 0) catch |err| switch (err) { |
| 3064 | error.SystemOutdated => return error.SkipZigTest, | 3101 | error.SystemOutdated => return error.SkipZigTest, |
| 3065 | error.PermissionDenied => return error.SkipZigTest, | 3102 | error.PermissionDenied => return error.SkipZigTest, |
| ... | @@ -3236,6 +3273,7 @@ const SocketTestHarness = struct { | ... | @@ -3236,6 +3273,7 @@ const SocketTestHarness = struct { |
| 3236 | fn createSocketTestHarness(ring: *IO_Uring) !SocketTestHarness { | 3273 | fn createSocketTestHarness(ring: *IO_Uring) !SocketTestHarness { |
| 3237 | // Create a TCP server socket | 3274 | // Create a TCP server socket |
| 3238 | 3275 | ||
| 3276 | if (true) @compileError("don't hard code port numbers in unit tests"); // https://github.com/ziglang/zig/issues/14907 | ||
| 3239 | const address = try net.Address.parseIp4("127.0.0.1", 3131); | 3277 | const address = try net.Address.parseIp4("127.0.0.1", 3131); |
| 3240 | const kernel_backlog = 1; | 3278 | const kernel_backlog = 1; |
| 3241 | const listener_socket = try os.socket(address.any.family, os.SOCK.STREAM | os.SOCK.CLOEXEC, 0); | 3279 | const listener_socket = try os.socket(address.any.family, os.SOCK.STREAM | os.SOCK.CLOEXEC, 0); |
lib/std/os/linux/test.zig+15-18| ... | @@ -8,10 +8,12 @@ const expectEqual = std.testing.expectEqual; | ... | @@ -8,10 +8,12 @@ const expectEqual = std.testing.expectEqual; |
| 8 | const fs = std.fs; | 8 | const fs = std.fs; |
| 9 | 9 | ||
| 10 | test "fallocate" { | 10 | test "fallocate" { |
| 11 | var tmp = std.testing.tmpDir(.{}); | ||
| 12 | defer tmp.cleanup(); | ||
| 13 | |||
| 11 | const path = "test_fallocate"; | 14 | const path = "test_fallocate"; |
| 12 | const file = try fs.cwd().createFile(path, .{ .truncate = true, .mode = 0o666 }); | 15 | const file = try tmp.dir.createFile(path, .{ .truncate = true, .mode = 0o666 }); |
| 13 | defer file.close(); | 16 | defer file.close(); |
| 14 | defer fs.cwd().deleteFile(path) catch {}; | ||
| 15 | 17 | ||
| 16 | try expect((try file.stat()).size == 0); | 18 | try expect((try file.stat()).size == 0); |
| 17 | 19 | ||
| ... | @@ -67,12 +69,12 @@ test "timer" { | ... | @@ -67,12 +69,12 @@ test "timer" { |
| 67 | } | 69 | } |
| 68 | 70 | ||
| 69 | test "statx" { | 71 | test "statx" { |
| 72 | var tmp = std.testing.tmpDir(.{}); | ||
| 73 | defer tmp.cleanup(); | ||
| 74 | |||
| 70 | const tmp_file_name = "just_a_temporary_file.txt"; | 75 | const tmp_file_name = "just_a_temporary_file.txt"; |
| 71 | var file = try fs.cwd().createFile(tmp_file_name, .{}); | 76 | var file = try tmp.dir.createFile(tmp_file_name, .{}); |
| 72 | defer { | 77 | defer file.close(); |
| 73 | file.close(); | ||
| 74 | fs.cwd().deleteFile(tmp_file_name) catch {}; | ||
| 75 | } | ||
| 76 | 78 | ||
| 77 | var statx_buf: linux.Statx = undefined; | 79 | var statx_buf: linux.Statx = undefined; |
| 78 | switch (linux.getErrno(linux.statx(file.handle, "", linux.AT.EMPTY_PATH, linux.STATX_BASIC_STATS, &statx_buf))) { | 80 | switch (linux.getErrno(linux.statx(file.handle, "", linux.AT.EMPTY_PATH, linux.STATX_BASIC_STATS, &statx_buf))) { |
| ... | @@ -105,21 +107,16 @@ test "user and group ids" { | ... | @@ -105,21 +107,16 @@ test "user and group ids" { |
| 105 | } | 107 | } |
| 106 | 108 | ||
| 107 | test "fadvise" { | 109 | test "fadvise" { |
| 110 | var tmp = std.testing.tmpDir(.{}); | ||
| 111 | defer tmp.cleanup(); | ||
| 112 | |||
| 108 | const tmp_file_name = "temp_posix_fadvise.txt"; | 113 | const tmp_file_name = "temp_posix_fadvise.txt"; |
| 109 | var file = try fs.cwd().createFile(tmp_file_name, .{}); | 114 | var file = try tmp.dir.createFile(tmp_file_name, .{}); |
| 110 | defer { | 115 | defer file.close(); |
| 111 | file.close(); | ||
| 112 | fs.cwd().deleteFile(tmp_file_name) catch {}; | ||
| 113 | } | ||
| 114 | 116 | ||
| 115 | var buf: [2048]u8 = undefined; | 117 | var buf: [2048]u8 = undefined; |
| 116 | try file.writeAll(&buf); | 118 | try file.writeAll(&buf); |
| 117 | 119 | ||
| 118 | const ret = linux.fadvise( | 120 | const ret = linux.fadvise(file.handle, 0, 0, linux.POSIX_FADV.SEQUENTIAL); |
| 119 | file.handle, | ||
| 120 | 0, | ||
| 121 | 0, | ||
| 122 | linux.POSIX_FADV.SEQUENTIAL, | ||
| 123 | ); | ||
| 124 | try expectEqual(@as(usize, 0), ret); | 121 | try expectEqual(@as(usize, 0), ret); |
| 125 | } | 122 | } |
lib/std/os/windows.zig+47-35| ... | @@ -105,41 +105,53 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN | ... | @@ -105,41 +105,53 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN |
| 105 | // If we're not following symlinks, we need to ensure we don't pass in any synchronization flags such as FILE_SYNCHRONOUS_IO_NONALERT. | 105 | // If we're not following symlinks, we need to ensure we don't pass in any synchronization flags such as FILE_SYNCHRONOUS_IO_NONALERT. |
| 106 | const flags: ULONG = if (options.follow_symlinks) file_or_dir_flag | blocking_flag else file_or_dir_flag | FILE_OPEN_REPARSE_POINT; | 106 | const flags: ULONG = if (options.follow_symlinks) file_or_dir_flag | blocking_flag else file_or_dir_flag | FILE_OPEN_REPARSE_POINT; |
| 107 | 107 | ||
| 108 | const rc = ntdll.NtCreateFile( | 108 | while (true) { |
| 109 | &result, | 109 | const rc = ntdll.NtCreateFile( |
| 110 | options.access_mask, | 110 | &result, |
| 111 | &attr, | 111 | options.access_mask, |
| 112 | &io, | 112 | &attr, |
| 113 | null, | 113 | &io, |
| 114 | FILE_ATTRIBUTE_NORMAL, | 114 | null, |
| 115 | options.share_access, | 115 | FILE_ATTRIBUTE_NORMAL, |
| 116 | options.creation, | 116 | options.share_access, |
| 117 | flags, | 117 | options.creation, |
| 118 | null, | 118 | flags, |
| 119 | 0, | 119 | null, |
| 120 | ); | 120 | 0, |
| 121 | switch (rc) { | 121 | ); |
| 122 | .SUCCESS => { | 122 | switch (rc) { |
| 123 | if (std.io.is_async and options.io_mode == .evented) { | 123 | .SUCCESS => { |
| 124 | _ = CreateIoCompletionPort(result, std.event.Loop.instance.?.os_data.io_port, undefined, undefined) catch undefined; | 124 | if (std.io.is_async and options.io_mode == .evented) { |
| 125 | } | 125 | _ = CreateIoCompletionPort(result, std.event.Loop.instance.?.os_data.io_port, undefined, undefined) catch undefined; |
| 126 | return result; | 126 | } |
| 127 | }, | 127 | return result; |
| 128 | .OBJECT_NAME_INVALID => unreachable, | 128 | }, |
| 129 | .OBJECT_NAME_NOT_FOUND => return error.FileNotFound, | 129 | .OBJECT_NAME_INVALID => unreachable, |
| 130 | .OBJECT_PATH_NOT_FOUND => return error.FileNotFound, | 130 | .OBJECT_NAME_NOT_FOUND => return error.FileNotFound, |
| 131 | .NO_MEDIA_IN_DEVICE => return error.NoDevice, | 131 | .OBJECT_PATH_NOT_FOUND => return error.FileNotFound, |
| 132 | .INVALID_PARAMETER => unreachable, | 132 | .NO_MEDIA_IN_DEVICE => return error.NoDevice, |
| 133 | .SHARING_VIOLATION => return error.AccessDenied, | 133 | .INVALID_PARAMETER => unreachable, |
| 134 | .ACCESS_DENIED => return error.AccessDenied, | 134 | .SHARING_VIOLATION => return error.AccessDenied, |
| 135 | .PIPE_BUSY => return error.PipeBusy, | 135 | .ACCESS_DENIED => return error.AccessDenied, |
| 136 | .OBJECT_PATH_SYNTAX_BAD => unreachable, | 136 | .PIPE_BUSY => return error.PipeBusy, |
| 137 | .OBJECT_NAME_COLLISION => return error.PathAlreadyExists, | 137 | .OBJECT_PATH_SYNTAX_BAD => unreachable, |
| 138 | .FILE_IS_A_DIRECTORY => return error.IsDir, | 138 | .OBJECT_NAME_COLLISION => return error.PathAlreadyExists, |
| 139 | .NOT_A_DIRECTORY => return error.NotDir, | 139 | .FILE_IS_A_DIRECTORY => return error.IsDir, |
| 140 | .USER_MAPPED_FILE => return error.AccessDenied, | 140 | .NOT_A_DIRECTORY => return error.NotDir, |
| 141 | .INVALID_HANDLE => unreachable, | 141 | .USER_MAPPED_FILE => return error.AccessDenied, |
| 142 | else => return unexpectedStatus(rc), | 142 | .INVALID_HANDLE => unreachable, |
| 143 | .DELETE_PENDING => { | ||
| 144 | // This error means that there *was* a file in this location on | ||
| 145 | // the file system, but it was deleted. However, the OS is not | ||
| 146 | // finished with the deletion operation, and so this CreateFile | ||
| 147 | // call has failed. There is not really a sane way to handle | ||
| 148 | // this other than retrying the creation after the OS finishes | ||
| 149 | // the deletion. | ||
| 150 | std.time.sleep(std.time.ns_per_ms); | ||
| 151 | continue; | ||
| 152 | }, | ||
| 153 | else => return unexpectedStatus(rc), | ||
| 154 | } | ||
| 143 | } | 155 | } |
| 144 | } | 156 | } |
| 145 | 157 |
lib/std/os/windows/kernel32.zig+3| ... | @@ -67,6 +67,7 @@ const RUNTIME_FUNCTION = windows.RUNTIME_FUNCTION; | ... | @@ -67,6 +67,7 @@ const RUNTIME_FUNCTION = windows.RUNTIME_FUNCTION; |
| 67 | const KNONVOLATILE_CONTEXT_POINTERS = windows.KNONVOLATILE_CONTEXT_POINTERS; | 67 | const KNONVOLATILE_CONTEXT_POINTERS = windows.KNONVOLATILE_CONTEXT_POINTERS; |
| 68 | const EXCEPTION_ROUTINE = windows.EXCEPTION_ROUTINE; | 68 | const EXCEPTION_ROUTINE = windows.EXCEPTION_ROUTINE; |
| 69 | const MODULEENTRY32 = windows.MODULEENTRY32; | 69 | const MODULEENTRY32 = windows.MODULEENTRY32; |
| 70 | const ULONGLONG = windows.ULONGLONG; | ||
| 70 | 71 | ||
| 71 | pub extern "kernel32" fn AddVectoredExceptionHandler(First: c_ulong, Handler: ?VECTORED_EXCEPTION_HANDLER) callconv(WINAPI) ?*anyopaque; | 72 | pub extern "kernel32" fn AddVectoredExceptionHandler(First: c_ulong, Handler: ?VECTORED_EXCEPTION_HANDLER) callconv(WINAPI) ?*anyopaque; |
| 72 | pub extern "kernel32" fn RemoveVectoredExceptionHandler(Handle: HANDLE) callconv(WINAPI) c_ulong; | 73 | pub extern "kernel32" fn RemoveVectoredExceptionHandler(Handle: HANDLE) callconv(WINAPI) c_ulong; |
| ... | @@ -457,3 +458,5 @@ pub extern "kernel32" fn RegOpenKeyExW( | ... | @@ -457,3 +458,5 @@ pub extern "kernel32" fn RegOpenKeyExW( |
| 457 | samDesired: REGSAM, | 458 | samDesired: REGSAM, |
| 458 | phkResult: *HKEY, | 459 | phkResult: *HKEY, |
| 459 | ) callconv(WINAPI) LSTATUS; | 460 | ) callconv(WINAPI) LSTATUS; |
| 461 | |||
| 462 | pub extern "kernel32" fn GetPhysicallyInstalledSystemMemory(TotalMemoryInKilobytes: *ULONGLONG) BOOL; |
lib/std/process.zig+48-18| ... | @@ -828,24 +828,6 @@ pub fn argsWithAllocator(allocator: Allocator) ArgIterator.InitError!ArgIterator | ... | @@ -828,24 +828,6 @@ pub fn argsWithAllocator(allocator: Allocator) ArgIterator.InitError!ArgIterator |
| 828 | return ArgIterator.initWithAllocator(allocator); | 828 | return ArgIterator.initWithAllocator(allocator); |
| 829 | } | 829 | } |
| 830 | 830 | ||
| 831 | test "args iterator" { | ||
| 832 | var ga = std.testing.allocator; | ||
| 833 | var it = try argsWithAllocator(ga); | ||
| 834 | defer it.deinit(); // no-op unless WASI or Windows | ||
| 835 | |||
| 836 | const prog_name = it.next() orelse unreachable; | ||
| 837 | const expected_suffix = switch (builtin.os.tag) { | ||
| 838 | .wasi => "test.wasm", | ||
| 839 | .windows => "test.exe", | ||
| 840 | else => "test", | ||
| 841 | }; | ||
| 842 | const given_suffix = std.fs.path.basename(prog_name); | ||
| 843 | |||
| 844 | try testing.expect(mem.eql(u8, expected_suffix, given_suffix)); | ||
| 845 | try testing.expect(it.next() == null); | ||
| 846 | try testing.expect(!it.skip()); | ||
| 847 | } | ||
| 848 | |||
| 849 | /// Caller must call argsFree on result. | 831 | /// Caller must call argsFree on result. |
| 850 | pub fn argsAlloc(allocator: Allocator) ![][:0]u8 { | 832 | pub fn argsAlloc(allocator: Allocator) ![][:0]u8 { |
| 851 | // TODO refactor to only make 1 allocation. | 833 | // TODO refactor to only make 1 allocation. |
| ... | @@ -1169,3 +1151,51 @@ pub fn execve( | ... | @@ -1169,3 +1151,51 @@ pub fn execve( |
| 1169 | 1151 | ||
| 1170 | return os.execvpeZ_expandArg0(.no_expand, argv_buf.ptr[0].?, argv_buf.ptr, envp); | 1152 | return os.execvpeZ_expandArg0(.no_expand, argv_buf.ptr[0].?, argv_buf.ptr, envp); |
| 1171 | } | 1153 | } |
| 1154 | |||
| 1155 | pub const TotalSystemMemoryError = error{ | ||
| 1156 | UnknownTotalSystemMemory, | ||
| 1157 | }; | ||
| 1158 | |||
| 1159 | /// Returns the total system memory, in bytes. | ||
| 1160 | pub fn totalSystemMemory() TotalSystemMemoryError!usize { | ||
| 1161 | switch (builtin.os.tag) { | ||
| 1162 | .linux => { | ||
| 1163 | return totalSystemMemoryLinux() catch return error.UnknownTotalSystemMemory; | ||
| 1164 | }, | ||
| 1165 | .windows => { | ||
| 1166 | var kilobytes: std.os.windows.ULONGLONG = undefined; | ||
| 1167 | assert(std.os.windows.kernel32.GetPhysicallyInstalledSystemMemory(&kilobytes) == std.os.windows.TRUE); | ||
| 1168 | return kilobytes * 1024; | ||
| 1169 | }, | ||
| 1170 | else => return error.UnknownTotalSystemMemory, | ||
| 1171 | } | ||
| 1172 | } | ||
| 1173 | |||
| 1174 | fn totalSystemMemoryLinux() !usize { | ||
| 1175 | var file = try std.fs.openFileAbsoluteZ("/proc/meminfo", .{}); | ||
| 1176 | defer file.close(); | ||
| 1177 | var buf: [50]u8 = undefined; | ||
| 1178 | const amt = try file.read(&buf); | ||
| 1179 | if (amt != 50) return error.Unexpected; | ||
| 1180 | var it = std.mem.tokenize(u8, buf[0..amt], " \n"); | ||
| 1181 | const label = it.next().?; | ||
| 1182 | if (!std.mem.eql(u8, label, "MemTotal:")) return error.Unexpected; | ||
| 1183 | const int_text = it.next() orelse return error.Unexpected; | ||
| 1184 | const units = it.next() orelse return error.Unexpected; | ||
| 1185 | if (!std.mem.eql(u8, units, "kB")) return error.Unexpected; | ||
| 1186 | const kilobytes = try std.fmt.parseInt(usize, int_text, 10); | ||
| 1187 | return kilobytes * 1024; | ||
| 1188 | } | ||
| 1189 | |||
| 1190 | /// Indicate that we are now terminating with a successful exit code. | ||
| 1191 | /// In debug builds, this is a no-op, so that the calling code's | ||
| 1192 | /// cleanup mechanisms are tested and so that external tools that | ||
| 1193 | /// check for resource leaks can be accurate. In release builds, this | ||
| 1194 | /// calls exit(0), and does not return. | ||
| 1195 | pub fn cleanExit() void { | ||
| 1196 | if (builtin.mode == .Debug) { | ||
| 1197 | return; | ||
| 1198 | } else { | ||
| 1199 | exit(0); | ||
| 1200 | } | ||
| 1201 | } |
lib/std/zig.zig+3| ... | @@ -3,6 +3,9 @@ const tokenizer = @import("zig/tokenizer.zig"); | ... | @@ -3,6 +3,9 @@ const tokenizer = @import("zig/tokenizer.zig"); |
| 3 | const fmt = @import("zig/fmt.zig"); | 3 | const fmt = @import("zig/fmt.zig"); |
| 4 | const assert = std.debug.assert; | 4 | const assert = std.debug.assert; |
| 5 | 5 | ||
| 6 | pub const ErrorBundle = @import("zig/ErrorBundle.zig"); | ||
| 7 | pub const Server = @import("zig/Server.zig"); | ||
| 8 | pub const Client = @import("zig/Client.zig"); | ||
| 6 | pub const Token = tokenizer.Token; | 9 | pub const Token = tokenizer.Token; |
| 7 | pub const Tokenizer = tokenizer.Tokenizer; | 10 | pub const Tokenizer = tokenizer.Tokenizer; |
| 8 | pub const fmtId = fmt.fmtId; | 11 | pub const fmtId = fmt.fmtId; |
lib/std/zig/Client.zig created+39| ... | @@ -0,0 +1,39 @@ | ||
| 1 | pub const Message = struct { | ||
| 2 | pub const Header = extern struct { | ||
| 3 | tag: Tag, | ||
| 4 | /// Size of the body only; does not include this Header. | ||
| 5 | bytes_len: u32, | ||
| 6 | }; | ||
| 7 | |||
| 8 | pub const Tag = enum(u32) { | ||
| 9 | /// Tells the compiler to shut down cleanly. | ||
| 10 | /// No body. | ||
| 11 | exit, | ||
| 12 | /// Tells the compiler to detect changes in source files and update the | ||
| 13 | /// affected output compilation artifacts. | ||
| 14 | /// If one of the compilation artifacts is an executable that is | ||
| 15 | /// running as a child process, the compiler will wait for it to exit | ||
| 16 | /// before performing the update. | ||
| 17 | /// No body. | ||
| 18 | update, | ||
| 19 | /// Tells the compiler to execute the executable as a child process. | ||
| 20 | /// No body. | ||
| 21 | run, | ||
| 22 | /// Tells the compiler to detect changes in source files and update the | ||
| 23 | /// affected output compilation artifacts. | ||
| 24 | /// If one of the compilation artifacts is an executable that is | ||
| 25 | /// running as a child process, the compiler will perform a hot code | ||
| 26 | /// swap. | ||
| 27 | /// No body. | ||
| 28 | hot_update, | ||
| 29 | /// Ask the test runner for metadata about all the unit tests that can | ||
| 30 | /// be run. Server will respond with a `test_metadata` message. | ||
| 31 | /// No body. | ||
| 32 | query_test_metadata, | ||
| 33 | /// Ask the test runner to run a particular test. | ||
| 34 | /// The message body is a u32 test index. | ||
| 35 | run_test, | ||
| 36 | |||
| 37 | _, | ||
| 38 | }; | ||
| 39 | }; | ||
lib/std/zig/ErrorBundle.zig created+515| ... | @@ -0,0 +1,515 @@ | ||
| 1 | //! To support incremental compilation, errors are stored in various places | ||
| 2 | //! so that they can be created and destroyed appropriately. This structure | ||
| 3 | //! is used to collect all the errors from the various places into one | ||
| 4 | //! convenient place for API users to consume. | ||
| 5 | //! | ||
| 6 | //! There is one special encoding for this data structure. If both arrays are | ||
| 7 | //! empty, it means there are no errors. This special encoding exists so that | ||
| 8 | //! heap allocation is not needed in the common case of no errors. | ||
| 9 | |||
| 10 | string_bytes: []const u8, | ||
| 11 | /// The first thing in this array is an `ErrorMessageList`. | ||
| 12 | extra: []const u32, | ||
| 13 | |||
| 14 | /// Special encoding when there are no errors. | ||
| 15 | pub const empty: ErrorBundle = .{ | ||
| 16 | .string_bytes = &.{}, | ||
| 17 | .extra = &.{}, | ||
| 18 | }; | ||
| 19 | |||
| 20 | // An index into `extra` pointing at an `ErrorMessage`. | ||
| 21 | pub const MessageIndex = enum(u32) { | ||
| 22 | _, | ||
| 23 | }; | ||
| 24 | |||
| 25 | // An index into `extra` pointing at an `SourceLocation`. | ||
| 26 | pub const SourceLocationIndex = enum(u32) { | ||
| 27 | none = 0, | ||
| 28 | _, | ||
| 29 | }; | ||
| 30 | |||
| 31 | /// There will be a MessageIndex for each len at start. | ||
| 32 | pub const ErrorMessageList = struct { | ||
| 33 | len: u32, | ||
| 34 | start: u32, | ||
| 35 | /// null-terminated string index. 0 means no compile log text. | ||
| 36 | compile_log_text: u32, | ||
| 37 | }; | ||
| 38 | |||
| 39 | /// Trailing: | ||
| 40 | /// * ReferenceTrace for each reference_trace_len | ||
| 41 | pub const SourceLocation = struct { | ||
| 42 | /// null terminated string index | ||
| 43 | src_path: u32, | ||
| 44 | line: u32, | ||
| 45 | column: u32, | ||
| 46 | /// byte offset of starting token | ||
| 47 | span_start: u32, | ||
| 48 | /// byte offset of main error location | ||
| 49 | span_main: u32, | ||
| 50 | /// byte offset of end of last token | ||
| 51 | span_end: u32, | ||
| 52 | /// null terminated string index, possibly null. | ||
| 53 | /// Does not include the trailing newline. | ||
| 54 | source_line: u32 = 0, | ||
| 55 | reference_trace_len: u32 = 0, | ||
| 56 | }; | ||
| 57 | |||
| 58 | /// Trailing: | ||
| 59 | /// * MessageIndex for each notes_len. | ||
| 60 | pub const ErrorMessage = struct { | ||
| 61 | /// null terminated string index | ||
| 62 | msg: u32, | ||
| 63 | /// Usually one, but incremented for redundant messages. | ||
| 64 | count: u32 = 1, | ||
| 65 | src_loc: SourceLocationIndex = .none, | ||
| 66 | notes_len: u32 = 0, | ||
| 67 | }; | ||
| 68 | |||
| 69 | pub const ReferenceTrace = struct { | ||
| 70 | /// null terminated string index | ||
| 71 | /// Except for the sentinel ReferenceTrace element, in which case: | ||
| 72 | /// * 0 means remaining references hidden | ||
| 73 | /// * >0 means N references hidden | ||
| 74 | decl_name: u32, | ||
| 75 | /// Index into extra of a SourceLocation | ||
| 76 | /// If this is 0, this is the sentinel ReferenceTrace element. | ||
| 77 | src_loc: SourceLocationIndex, | ||
| 78 | }; | ||
| 79 | |||
| 80 | pub fn deinit(eb: *ErrorBundle, gpa: Allocator) void { | ||
| 81 | gpa.free(eb.string_bytes); | ||
| 82 | gpa.free(eb.extra); | ||
| 83 | eb.* = undefined; | ||
| 84 | } | ||
| 85 | |||
| 86 | pub fn errorMessageCount(eb: ErrorBundle) u32 { | ||
| 87 | if (eb.extra.len == 0) return 0; | ||
| 88 | return eb.getErrorMessageList().len; | ||
| 89 | } | ||
| 90 | |||
| 91 | pub fn getErrorMessageList(eb: ErrorBundle) ErrorMessageList { | ||
| 92 | return eb.extraData(ErrorMessageList, 0).data; | ||
| 93 | } | ||
| 94 | |||
| 95 | pub fn getMessages(eb: ErrorBundle) []const MessageIndex { | ||
| 96 | const list = eb.getErrorMessageList(); | ||
| 97 | return @ptrCast([]const MessageIndex, eb.extra[list.start..][0..list.len]); | ||
| 98 | } | ||
| 99 | |||
| 100 | pub fn getErrorMessage(eb: ErrorBundle, index: MessageIndex) ErrorMessage { | ||
| 101 | return eb.extraData(ErrorMessage, @enumToInt(index)).data; | ||
| 102 | } | ||
| 103 | |||
| 104 | pub fn getSourceLocation(eb: ErrorBundle, index: SourceLocationIndex) SourceLocation { | ||
| 105 | assert(index != .none); | ||
| 106 | return eb.extraData(SourceLocation, @enumToInt(index)).data; | ||
| 107 | } | ||
| 108 | |||
| 109 | pub fn getNotes(eb: ErrorBundle, index: MessageIndex) []const MessageIndex { | ||
| 110 | const notes_len = eb.getErrorMessage(index).notes_len; | ||
| 111 | const start = @enumToInt(index) + @typeInfo(ErrorMessage).Struct.fields.len; | ||
| 112 | return @ptrCast([]const MessageIndex, eb.extra[start..][0..notes_len]); | ||
| 113 | } | ||
| 114 | |||
| 115 | pub fn getCompileLogOutput(eb: ErrorBundle) [:0]const u8 { | ||
| 116 | return nullTerminatedString(eb, getErrorMessageList(eb).compile_log_text); | ||
| 117 | } | ||
| 118 | |||
| 119 | /// Returns the requested data, as well as the new index which is at the start of the | ||
| 120 | /// trailers for the object. | ||
| 121 | fn extraData(eb: ErrorBundle, comptime T: type, index: usize) struct { data: T, end: usize } { | ||
| 122 | const fields = @typeInfo(T).Struct.fields; | ||
| 123 | var i: usize = index; | ||
| 124 | var result: T = undefined; | ||
| 125 | inline for (fields) |field| { | ||
| 126 | @field(result, field.name) = switch (field.type) { | ||
| 127 | u32 => eb.extra[i], | ||
| 128 | MessageIndex => @intToEnum(MessageIndex, eb.extra[i]), | ||
| 129 | SourceLocationIndex => @intToEnum(SourceLocationIndex, eb.extra[i]), | ||
| 130 | else => @compileError("bad field type"), | ||
| 131 | }; | ||
| 132 | i += 1; | ||
| 133 | } | ||
| 134 | return .{ | ||
| 135 | .data = result, | ||
| 136 | .end = i, | ||
| 137 | }; | ||
| 138 | } | ||
| 139 | |||
| 140 | /// Given an index into `string_bytes` returns the null-terminated string found there. | ||
| 141 | pub fn nullTerminatedString(eb: ErrorBundle, index: usize) [:0]const u8 { | ||
| 142 | const string_bytes = eb.string_bytes; | ||
| 143 | var end: usize = index; | ||
| 144 | while (string_bytes[end] != 0) { | ||
| 145 | end += 1; | ||
| 146 | } | ||
| 147 | return string_bytes[index..end :0]; | ||
| 148 | } | ||
| 149 | |||
| 150 | pub const RenderOptions = struct { | ||
| 151 | ttyconf: std.debug.TTY.Config, | ||
| 152 | include_reference_trace: bool = true, | ||
| 153 | include_source_line: bool = true, | ||
| 154 | include_log_text: bool = true, | ||
| 155 | }; | ||
| 156 | |||
| 157 | pub fn renderToStdErr(eb: ErrorBundle, options: RenderOptions) void { | ||
| 158 | std.debug.getStderrMutex().lock(); | ||
| 159 | defer std.debug.getStderrMutex().unlock(); | ||
| 160 | const stderr = std.io.getStdErr(); | ||
| 161 | return renderToWriter(eb, options, stderr.writer()) catch return; | ||
| 162 | } | ||
| 163 | |||
| 164 | pub fn renderToWriter(eb: ErrorBundle, options: RenderOptions, writer: anytype) anyerror!void { | ||
| 165 | for (eb.getMessages()) |err_msg| { | ||
| 166 | try renderErrorMessageToWriter(eb, options, err_msg, writer, "error", .Red, 0); | ||
| 167 | } | ||
| 168 | |||
| 169 | if (options.include_log_text) { | ||
| 170 | const log_text = eb.getCompileLogOutput(); | ||
| 171 | if (log_text.len != 0) { | ||
| 172 | try writer.writeAll("\nCompile Log Output:\n"); | ||
| 173 | try writer.writeAll(log_text); | ||
| 174 | } | ||
| 175 | } | ||
| 176 | } | ||
| 177 | |||
| 178 | fn renderErrorMessageToWriter( | ||
| 179 | eb: ErrorBundle, | ||
| 180 | options: RenderOptions, | ||
| 181 | err_msg_index: MessageIndex, | ||
| 182 | stderr: anytype, | ||
| 183 | kind: []const u8, | ||
| 184 | color: std.debug.TTY.Color, | ||
| 185 | indent: usize, | ||
| 186 | ) anyerror!void { | ||
| 187 | const ttyconf = options.ttyconf; | ||
| 188 | var counting_writer = std.io.countingWriter(stderr); | ||
| 189 | const counting_stderr = counting_writer.writer(); | ||
| 190 | const err_msg = eb.getErrorMessage(err_msg_index); | ||
| 191 | if (err_msg.src_loc != .none) { | ||
| 192 | const src = eb.extraData(SourceLocation, @enumToInt(err_msg.src_loc)); | ||
| 193 | try counting_stderr.writeByteNTimes(' ', indent); | ||
| 194 | try ttyconf.setColor(stderr, .Bold); | ||
| 195 | try counting_stderr.print("{s}:{d}:{d}: ", .{ | ||
| 196 | eb.nullTerminatedString(src.data.src_path), | ||
| 197 | src.data.line + 1, | ||
| 198 | src.data.column + 1, | ||
| 199 | }); | ||
| 200 | try ttyconf.setColor(stderr, color); | ||
| 201 | try counting_stderr.writeAll(kind); | ||
| 202 | try counting_stderr.writeAll(": "); | ||
| 203 | // This is the length of the part before the error message: | ||
| 204 | // e.g. "file.zig:4:5: error: " | ||
| 205 | const prefix_len = @intCast(usize, counting_stderr.context.bytes_written); | ||
| 206 | try ttyconf.setColor(stderr, .Reset); | ||
| 207 | try ttyconf.setColor(stderr, .Bold); | ||
| 208 | if (err_msg.count == 1) { | ||
| 209 | try writeMsg(eb, err_msg, stderr, prefix_len); | ||
| 210 | try stderr.writeByte('\n'); | ||
| 211 | } else { | ||
| 212 | try writeMsg(eb, err_msg, stderr, prefix_len); | ||
| 213 | try ttyconf.setColor(stderr, .Dim); | ||
| 214 | try stderr.print(" ({d} times)\n", .{err_msg.count}); | ||
| 215 | } | ||
| 216 | try ttyconf.setColor(stderr, .Reset); | ||
| 217 | if (src.data.source_line != 0 and options.include_source_line) { | ||
| 218 | const line = eb.nullTerminatedString(src.data.source_line); | ||
| 219 | for (line) |b| switch (b) { | ||
| 220 | '\t' => try stderr.writeByte(' '), | ||
| 221 | else => try stderr.writeByte(b), | ||
| 222 | }; | ||
| 223 | try stderr.writeByte('\n'); | ||
| 224 | // TODO basic unicode code point monospace width | ||
| 225 | const before_caret = src.data.span_main - src.data.span_start; | ||
| 226 | // -1 since span.main includes the caret | ||
| 227 | const after_caret = src.data.span_end - src.data.span_main -| 1; | ||
| 228 | try stderr.writeByteNTimes(' ', src.data.column - before_caret); | ||
| 229 | try ttyconf.setColor(stderr, .Green); | ||
| 230 | try stderr.writeByteNTimes('~', before_caret); | ||
| 231 | try stderr.writeByte('^'); | ||
| 232 | try stderr.writeByteNTimes('~', after_caret); | ||
| 233 | try stderr.writeByte('\n'); | ||
| 234 | try ttyconf.setColor(stderr, .Reset); | ||
| 235 | } | ||
| 236 | for (eb.getNotes(err_msg_index)) |note| { | ||
| 237 | try renderErrorMessageToWriter(eb, options, note, stderr, "note", .Cyan, indent); | ||
| 238 | } | ||
| 239 | if (src.data.reference_trace_len > 0 and options.include_reference_trace) { | ||
| 240 | try ttyconf.setColor(stderr, .Reset); | ||
| 241 | try ttyconf.setColor(stderr, .Dim); | ||
| 242 | try stderr.print("referenced by:\n", .{}); | ||
| 243 | var ref_index = src.end; | ||
| 244 | for (0..src.data.reference_trace_len) |_| { | ||
| 245 | const ref_trace = eb.extraData(ReferenceTrace, ref_index); | ||
| 246 | ref_index = ref_trace.end; | ||
| 247 | if (ref_trace.data.src_loc != .none) { | ||
| 248 | const ref_src = eb.getSourceLocation(ref_trace.data.src_loc); | ||
| 249 | try stderr.print(" {s}: {s}:{d}:{d}\n", .{ | ||
| 250 | eb.nullTerminatedString(ref_trace.data.decl_name), | ||
| 251 | eb.nullTerminatedString(ref_src.src_path), | ||
| 252 | ref_src.line + 1, | ||
| 253 | ref_src.column + 1, | ||
| 254 | }); | ||
| 255 | } else if (ref_trace.data.decl_name != 0) { | ||
| 256 | const count = ref_trace.data.decl_name; | ||
| 257 | try stderr.print( | ||
| 258 | " {d} reference(s) hidden; use '-freference-trace={d}' to see all references\n", | ||
| 259 | .{ count, count + src.data.reference_trace_len - 1 }, | ||
| 260 | ); | ||
| 261 | } else { | ||
| 262 | try stderr.print( | ||
| 263 | " remaining reference traces hidden; use '-freference-trace' to see all reference traces\n", | ||
| 264 | .{}, | ||
| 265 | ); | ||
| 266 | } | ||
| 267 | } | ||
| 268 | try stderr.writeByte('\n'); | ||
| 269 | try ttyconf.setColor(stderr, .Reset); | ||
| 270 | } | ||
| 271 | } else { | ||
| 272 | try ttyconf.setColor(stderr, color); | ||
| 273 | try stderr.writeByteNTimes(' ', indent); | ||
| 274 | try stderr.writeAll(kind); | ||
| 275 | try stderr.writeAll(": "); | ||
| 276 | try ttyconf.setColor(stderr, .Reset); | ||
| 277 | const msg = eb.nullTerminatedString(err_msg.msg); | ||
| 278 | if (err_msg.count == 1) { | ||
| 279 | try stderr.print("{s}\n", .{msg}); | ||
| 280 | } else { | ||
| 281 | try stderr.print("{s}", .{msg}); | ||
| 282 | try ttyconf.setColor(stderr, .Dim); | ||
| 283 | try stderr.print(" ({d} times)\n", .{err_msg.count}); | ||
| 284 | } | ||
| 285 | try ttyconf.setColor(stderr, .Reset); | ||
| 286 | for (eb.getNotes(err_msg_index)) |note| { | ||
| 287 | try renderErrorMessageToWriter(eb, options, note, stderr, "note", .Cyan, indent + 4); | ||
| 288 | } | ||
| 289 | } | ||
| 290 | } | ||
| 291 | |||
| 292 | /// Splits the error message up into lines to properly indent them | ||
| 293 | /// to allow for long, good-looking error messages. | ||
| 294 | /// | ||
| 295 | /// This is used to split the message in `@compileError("hello\nworld")` for example. | ||
| 296 | fn writeMsg(eb: ErrorBundle, err_msg: ErrorMessage, stderr: anytype, indent: usize) !void { | ||
| 297 | var lines = std.mem.split(u8, eb.nullTerminatedString(err_msg.msg), "\n"); | ||
| 298 | while (lines.next()) |line| { | ||
| 299 | try stderr.writeAll(line); | ||
| 300 | if (lines.index == null) break; | ||
| 301 | try stderr.writeByte('\n'); | ||
| 302 | try stderr.writeByteNTimes(' ', indent); | ||
| 303 | } | ||
| 304 | } | ||
| 305 | |||
| 306 | const std = @import("std"); | ||
| 307 | const ErrorBundle = @This(); | ||
| 308 | const Allocator = std.mem.Allocator; | ||
| 309 | const assert = std.debug.assert; | ||
| 310 | |||
| 311 | pub const Wip = struct { | ||
| 312 | gpa: Allocator, | ||
| 313 | string_bytes: std.ArrayListUnmanaged(u8), | ||
| 314 | /// The first thing in this array is a ErrorMessageList. | ||
| 315 | extra: std.ArrayListUnmanaged(u32), | ||
| 316 | root_list: std.ArrayListUnmanaged(MessageIndex), | ||
| 317 | |||
| 318 | pub fn init(wip: *Wip, gpa: Allocator) !void { | ||
| 319 | wip.* = .{ | ||
| 320 | .gpa = gpa, | ||
| 321 | .string_bytes = .{}, | ||
| 322 | .extra = .{}, | ||
| 323 | .root_list = .{}, | ||
| 324 | }; | ||
| 325 | |||
| 326 | // So that 0 can be used to indicate a null string. | ||
| 327 | try wip.string_bytes.append(gpa, 0); | ||
| 328 | |||
| 329 | assert(0 == try addExtra(wip, ErrorMessageList{ | ||
| 330 | .len = 0, | ||
| 331 | .start = 0, | ||
| 332 | .compile_log_text = 0, | ||
| 333 | })); | ||
| 334 | } | ||
| 335 | |||
| 336 | pub fn deinit(wip: *Wip) void { | ||
| 337 | const gpa = wip.gpa; | ||
| 338 | wip.root_list.deinit(gpa); | ||
| 339 | wip.string_bytes.deinit(gpa); | ||
| 340 | wip.extra.deinit(gpa); | ||
| 341 | wip.* = undefined; | ||
| 342 | } | ||
| 343 | |||
| 344 | pub fn toOwnedBundle(wip: *Wip, compile_log_text: []const u8) !ErrorBundle { | ||
| 345 | const gpa = wip.gpa; | ||
| 346 | if (wip.root_list.items.len == 0) { | ||
| 347 | assert(compile_log_text.len == 0); | ||
| 348 | // Special encoding when there are no errors. | ||
| 349 | wip.deinit(); | ||
| 350 | wip.* = .{ | ||
| 351 | .gpa = gpa, | ||
| 352 | .string_bytes = .{}, | ||
| 353 | .extra = .{}, | ||
| 354 | .root_list = .{}, | ||
| 355 | }; | ||
| 356 | return empty; | ||
| 357 | } | ||
| 358 | |||
| 359 | const compile_log_str_index = if (compile_log_text.len == 0) 0 else str: { | ||
| 360 | const str = @intCast(u32, wip.string_bytes.items.len); | ||
| 361 | try wip.string_bytes.ensureUnusedCapacity(gpa, compile_log_text.len + 1); | ||
| 362 | wip.string_bytes.appendSliceAssumeCapacity(compile_log_text); | ||
| 363 | wip.string_bytes.appendAssumeCapacity(0); | ||
| 364 | break :str str; | ||
| 365 | }; | ||
| 366 | |||
| 367 | wip.setExtra(0, ErrorMessageList{ | ||
| 368 | .len = @intCast(u32, wip.root_list.items.len), | ||
| 369 | .start = @intCast(u32, wip.extra.items.len), | ||
| 370 | .compile_log_text = compile_log_str_index, | ||
| 371 | }); | ||
| 372 | try wip.extra.appendSlice(gpa, @ptrCast([]const u32, wip.root_list.items)); | ||
| 373 | wip.root_list.clearAndFree(gpa); | ||
| 374 | return .{ | ||
| 375 | .string_bytes = try wip.string_bytes.toOwnedSlice(gpa), | ||
| 376 | .extra = try wip.extra.toOwnedSlice(gpa), | ||
| 377 | }; | ||
| 378 | } | ||
| 379 | |||
| 380 | pub fn tmpBundle(wip: Wip) ErrorBundle { | ||
| 381 | return .{ | ||
| 382 | .string_bytes = wip.string_bytes.items, | ||
| 383 | .extra = wip.extra.items, | ||
| 384 | }; | ||
| 385 | } | ||
| 386 | |||
| 387 | pub fn addString(wip: *Wip, s: []const u8) !u32 { | ||
| 388 | const gpa = wip.gpa; | ||
| 389 | const index = @intCast(u32, wip.string_bytes.items.len); | ||
| 390 | try wip.string_bytes.ensureUnusedCapacity(gpa, s.len + 1); | ||
| 391 | wip.string_bytes.appendSliceAssumeCapacity(s); | ||
| 392 | wip.string_bytes.appendAssumeCapacity(0); | ||
| 393 | return index; | ||
| 394 | } | ||
| 395 | |||
| 396 | pub fn printString(wip: *Wip, comptime fmt: []const u8, args: anytype) !u32 { | ||
| 397 | const gpa = wip.gpa; | ||
| 398 | const index = @intCast(u32, wip.string_bytes.items.len); | ||
| 399 | try wip.string_bytes.writer(gpa).print(fmt, args); | ||
| 400 | try wip.string_bytes.append(gpa, 0); | ||
| 401 | return index; | ||
| 402 | } | ||
| 403 | |||
| 404 | pub fn addRootErrorMessage(wip: *Wip, em: ErrorMessage) !void { | ||
| 405 | try wip.root_list.ensureUnusedCapacity(wip.gpa, 1); | ||
| 406 | wip.root_list.appendAssumeCapacity(try addErrorMessage(wip, em)); | ||
| 407 | } | ||
| 408 | |||
| 409 | pub fn addErrorMessage(wip: *Wip, em: ErrorMessage) !MessageIndex { | ||
| 410 | return @intToEnum(MessageIndex, try addExtra(wip, em)); | ||
| 411 | } | ||
| 412 | |||
| 413 | pub fn addErrorMessageAssumeCapacity(wip: *Wip, em: ErrorMessage) MessageIndex { | ||
| 414 | return @intToEnum(MessageIndex, addExtraAssumeCapacity(wip, em)); | ||
| 415 | } | ||
| 416 | |||
| 417 | pub fn addSourceLocation(wip: *Wip, sl: SourceLocation) !SourceLocationIndex { | ||
| 418 | return @intToEnum(SourceLocationIndex, try addExtra(wip, sl)); | ||
| 419 | } | ||
| 420 | |||
| 421 | pub fn addReferenceTrace(wip: *Wip, rt: ReferenceTrace) !void { | ||
| 422 | _ = try addExtra(wip, rt); | ||
| 423 | } | ||
| 424 | |||
| 425 | pub fn addBundle(wip: *Wip, other: ErrorBundle) !void { | ||
| 426 | const gpa = wip.gpa; | ||
| 427 | |||
| 428 | try wip.string_bytes.ensureUnusedCapacity(gpa, other.string_bytes.len); | ||
| 429 | try wip.extra.ensureUnusedCapacity(gpa, other.extra.len); | ||
| 430 | |||
| 431 | const other_list = other.getMessages(); | ||
| 432 | |||
| 433 | // The ensureUnusedCapacity call above guarantees this. | ||
| 434 | const notes_start = wip.reserveNotes(@intCast(u32, other_list.len)) catch unreachable; | ||
| 435 | for (notes_start.., other_list) |note, message| { | ||
| 436 | wip.extra.items[note] = @enumToInt(wip.addOtherMessage(other, message) catch unreachable); | ||
| 437 | } | ||
| 438 | } | ||
| 439 | |||
| 440 | pub fn reserveNotes(wip: *Wip, notes_len: u32) !u32 { | ||
| 441 | try wip.extra.ensureUnusedCapacity(wip.gpa, notes_len + | ||
| 442 | notes_len * @typeInfo(ErrorBundle.ErrorMessage).Struct.fields.len); | ||
| 443 | wip.extra.items.len += notes_len; | ||
| 444 | return @intCast(u32, wip.extra.items.len - notes_len); | ||
| 445 | } | ||
| 446 | |||
| 447 | fn addOtherMessage(wip: *Wip, other: ErrorBundle, msg_index: MessageIndex) !MessageIndex { | ||
| 448 | const other_msg = other.getErrorMessage(msg_index); | ||
| 449 | const src_loc = try wip.addOtherSourceLocation(other, other_msg.src_loc); | ||
| 450 | const msg = try wip.addErrorMessage(.{ | ||
| 451 | .msg = try wip.addString(other.nullTerminatedString(other_msg.msg)), | ||
| 452 | .count = other_msg.count, | ||
| 453 | .src_loc = src_loc, | ||
| 454 | .notes_len = other_msg.notes_len, | ||
| 455 | }); | ||
| 456 | const notes_start = try wip.reserveNotes(other_msg.notes_len); | ||
| 457 | for (notes_start.., other.getNotes(msg_index)) |note, other_note| { | ||
| 458 | wip.extra.items[note] = @enumToInt(try wip.addOtherMessage(other, other_note)); | ||
| 459 | } | ||
| 460 | return msg; | ||
| 461 | } | ||
| 462 | |||
| 463 | fn addOtherSourceLocation( | ||
| 464 | wip: *Wip, | ||
| 465 | other: ErrorBundle, | ||
| 466 | index: SourceLocationIndex, | ||
| 467 | ) !SourceLocationIndex { | ||
| 468 | if (index == .none) return .none; | ||
| 469 | const other_sl = other.getSourceLocation(index); | ||
| 470 | |||
| 471 | const src_loc = try wip.addSourceLocation(.{ | ||
| 472 | .src_path = try wip.addString(other.nullTerminatedString(other_sl.src_path)), | ||
| 473 | .line = other_sl.line, | ||
| 474 | .column = other_sl.column, | ||
| 475 | .span_start = other_sl.span_start, | ||
| 476 | .span_main = other_sl.span_main, | ||
| 477 | .span_end = other_sl.span_end, | ||
| 478 | .source_line = try wip.addString(other.nullTerminatedString(other_sl.source_line)), | ||
| 479 | .reference_trace_len = other_sl.reference_trace_len, | ||
| 480 | }); | ||
| 481 | |||
| 482 | // TODO: also add the reference trace | ||
| 483 | |||
| 484 | return src_loc; | ||
| 485 | } | ||
| 486 | |||
| 487 | fn addExtra(wip: *Wip, extra: anytype) Allocator.Error!u32 { | ||
| 488 | const gpa = wip.gpa; | ||
| 489 | const fields = @typeInfo(@TypeOf(extra)).Struct.fields; | ||
| 490 | try wip.extra.ensureUnusedCapacity(gpa, fields.len); | ||
| 491 | return addExtraAssumeCapacity(wip, extra); | ||
| 492 | } | ||
| 493 | |||
| 494 | fn addExtraAssumeCapacity(wip: *Wip, extra: anytype) u32 { | ||
| 495 | const fields = @typeInfo(@TypeOf(extra)).Struct.fields; | ||
| 496 | const result = @intCast(u32, wip.extra.items.len); | ||
| 497 | wip.extra.items.len += fields.len; | ||
| 498 | setExtra(wip, result, extra); | ||
| 499 | return result; | ||
| 500 | } | ||
| 501 | |||
| 502 | fn setExtra(wip: *Wip, index: usize, extra: anytype) void { | ||
| 503 | const fields = @typeInfo(@TypeOf(extra)).Struct.fields; | ||
| 504 | var i = index; | ||
| 505 | inline for (fields) |field| { | ||
| 506 | wip.extra.items[i] = switch (field.type) { | ||
| 507 | u32 => @field(extra, field.name), | ||
| 508 | MessageIndex => @enumToInt(@field(extra, field.name)), | ||
| 509 | SourceLocationIndex => @enumToInt(@field(extra, field.name)), | ||
| 510 | else => @compileError("bad field type"), | ||
| 511 | }; | ||
| 512 | i += 1; | ||
| 513 | } | ||
| 514 | } | ||
| 515 | }; | ||
lib/std/zig/Server.zig created+305| ... | @@ -0,0 +1,305 @@ | ||
| 1 | in: std.fs.File, | ||
| 2 | out: std.fs.File, | ||
| 3 | receive_fifo: std.fifo.LinearFifo(u8, .Dynamic), | ||
| 4 | |||
| 5 | pub const Message = struct { | ||
| 6 | pub const Header = extern struct { | ||
| 7 | tag: Tag, | ||
| 8 | /// Size of the body only; does not include this Header. | ||
| 9 | bytes_len: u32, | ||
| 10 | }; | ||
| 11 | |||
| 12 | pub const Tag = enum(u32) { | ||
| 13 | /// Body is a UTF-8 string. | ||
| 14 | zig_version, | ||
| 15 | /// Body is an ErrorBundle. | ||
| 16 | error_bundle, | ||
| 17 | /// Body is a UTF-8 string. | ||
| 18 | progress, | ||
| 19 | /// Body is a EmitBinPath. | ||
| 20 | emit_bin_path, | ||
| 21 | /// Body is a TestMetadata | ||
| 22 | test_metadata, | ||
| 23 | /// Body is a TestResults | ||
| 24 | test_results, | ||
| 25 | |||
| 26 | _, | ||
| 27 | }; | ||
| 28 | |||
| 29 | /// Trailing: | ||
| 30 | /// * extra: [extra_len]u32, | ||
| 31 | /// * string_bytes: [string_bytes_len]u8, | ||
| 32 | /// See `std.zig.ErrorBundle`. | ||
| 33 | pub const ErrorBundle = extern struct { | ||
| 34 | extra_len: u32, | ||
| 35 | string_bytes_len: u32, | ||
| 36 | }; | ||
| 37 | |||
| 38 | /// Trailing: | ||
| 39 | /// * name: [tests_len]u32 | ||
| 40 | /// - null-terminated string_bytes index | ||
| 41 | /// * async_frame_len: [tests_len]u32, | ||
| 42 | /// - 0 means not async | ||
| 43 | /// * expected_panic_msg: [tests_len]u32, | ||
| 44 | /// - null-terminated string_bytes index | ||
| 45 | /// - 0 means does not expect pani | ||
| 46 | /// * string_bytes: [string_bytes_len]u8, | ||
| 47 | pub const TestMetadata = extern struct { | ||
| 48 | string_bytes_len: u32, | ||
| 49 | tests_len: u32, | ||
| 50 | }; | ||
| 51 | |||
| 52 | pub const TestResults = extern struct { | ||
| 53 | index: u32, | ||
| 54 | flags: Flags, | ||
| 55 | |||
| 56 | pub const Flags = packed struct(u8) { | ||
| 57 | fail: bool, | ||
| 58 | skip: bool, | ||
| 59 | leak: bool, | ||
| 60 | |||
| 61 | reserved: u5 = 0, | ||
| 62 | }; | ||
| 63 | }; | ||
| 64 | |||
| 65 | /// Trailing: | ||
| 66 | /// * the file system path the emitted binary can be found | ||
| 67 | pub const EmitBinPath = extern struct { | ||
| 68 | flags: Flags, | ||
| 69 | |||
| 70 | pub const Flags = packed struct(u8) { | ||
| 71 | cache_hit: bool, | ||
| 72 | reserved: u7 = 0, | ||
| 73 | }; | ||
| 74 | }; | ||
| 75 | }; | ||
| 76 | |||
| 77 | pub const Options = struct { | ||
| 78 | gpa: Allocator, | ||
| 79 | in: std.fs.File, | ||
| 80 | out: std.fs.File, | ||
| 81 | zig_version: []const u8, | ||
| 82 | }; | ||
| 83 | |||
| 84 | pub fn init(options: Options) !Server { | ||
| 85 | var s: Server = .{ | ||
| 86 | .in = options.in, | ||
| 87 | .out = options.out, | ||
| 88 | .receive_fifo = std.fifo.LinearFifo(u8, .Dynamic).init(options.gpa), | ||
| 89 | }; | ||
| 90 | try s.serveStringMessage(.zig_version, options.zig_version); | ||
| 91 | return s; | ||
| 92 | } | ||
| 93 | |||
| 94 | pub fn deinit(s: *Server) void { | ||
| 95 | s.receive_fifo.deinit(); | ||
| 96 | s.* = undefined; | ||
| 97 | } | ||
| 98 | |||
| 99 | pub fn receiveMessage(s: *Server) !InMessage.Header { | ||
| 100 | const Header = InMessage.Header; | ||
| 101 | const fifo = &s.receive_fifo; | ||
| 102 | |||
| 103 | while (true) { | ||
| 104 | const buf = fifo.readableSlice(0); | ||
| 105 | assert(fifo.readableLength() == buf.len); | ||
| 106 | if (buf.len >= @sizeOf(Header)) { | ||
| 107 | const header = @ptrCast(*align(1) const Header, buf[0..@sizeOf(Header)]); | ||
| 108 | // workaround for https://github.com/ziglang/zig/issues/14904 | ||
| 109 | const bytes_len = bswap_and_workaround_u32(&header.bytes_len); | ||
| 110 | // workaround for https://github.com/ziglang/zig/issues/14904 | ||
| 111 | const tag = bswap_and_workaround_tag(&header.tag); | ||
| 112 | |||
| 113 | if (buf.len - @sizeOf(Header) >= bytes_len) { | ||
| 114 | fifo.discard(@sizeOf(Header)); | ||
| 115 | return .{ | ||
| 116 | .tag = tag, | ||
| 117 | .bytes_len = bytes_len, | ||
| 118 | }; | ||
| 119 | } else { | ||
| 120 | const needed = bytes_len - (buf.len - @sizeOf(Header)); | ||
| 121 | const write_buffer = try fifo.writableWithSize(needed); | ||
| 122 | const amt = try s.in.read(write_buffer); | ||
| 123 | fifo.update(amt); | ||
| 124 | continue; | ||
| 125 | } | ||
| 126 | } | ||
| 127 | |||
| 128 | const write_buffer = try fifo.writableWithSize(256); | ||
| 129 | const amt = try s.in.read(write_buffer); | ||
| 130 | fifo.update(amt); | ||
| 131 | } | ||
| 132 | } | ||
| 133 | |||
| 134 | pub fn receiveBody_u32(s: *Server) !u32 { | ||
| 135 | const fifo = &s.receive_fifo; | ||
| 136 | const buf = fifo.readableSlice(0); | ||
| 137 | const result = @ptrCast(*align(1) const u32, buf[0..4]).*; | ||
| 138 | fifo.discard(4); | ||
| 139 | return bswap(result); | ||
| 140 | } | ||
| 141 | |||
| 142 | pub fn serveStringMessage(s: *Server, tag: OutMessage.Tag, msg: []const u8) !void { | ||
| 143 | return s.serveMessage(.{ | ||
| 144 | .tag = tag, | ||
| 145 | .bytes_len = @intCast(u32, msg.len), | ||
| 146 | }, &.{msg}); | ||
| 147 | } | ||
| 148 | |||
| 149 | pub fn serveMessage( | ||
| 150 | s: *const Server, | ||
| 151 | header: OutMessage.Header, | ||
| 152 | bufs: []const []const u8, | ||
| 153 | ) !void { | ||
| 154 | var iovecs: [10]std.os.iovec_const = undefined; | ||
| 155 | const header_le = bswap(header); | ||
| 156 | iovecs[0] = .{ | ||
| 157 | .iov_base = @ptrCast([*]const u8, &header_le), | ||
| 158 | .iov_len = @sizeOf(OutMessage.Header), | ||
| 159 | }; | ||
| 160 | for (bufs, iovecs[1 .. bufs.len + 1]) |buf, *iovec| { | ||
| 161 | iovec.* = .{ | ||
| 162 | .iov_base = buf.ptr, | ||
| 163 | .iov_len = buf.len, | ||
| 164 | }; | ||
| 165 | } | ||
| 166 | try s.out.writevAll(iovecs[0 .. bufs.len + 1]); | ||
| 167 | } | ||
| 168 | |||
| 169 | pub fn serveEmitBinPath( | ||
| 170 | s: *Server, | ||
| 171 | fs_path: []const u8, | ||
| 172 | header: OutMessage.EmitBinPath, | ||
| 173 | ) !void { | ||
| 174 | try s.serveMessage(.{ | ||
| 175 | .tag = .emit_bin_path, | ||
| 176 | .bytes_len = @intCast(u32, fs_path.len + @sizeOf(OutMessage.EmitBinPath)), | ||
| 177 | }, &.{ | ||
| 178 | std.mem.asBytes(&header), | ||
| 179 | fs_path, | ||
| 180 | }); | ||
| 181 | } | ||
| 182 | |||
| 183 | pub fn serveTestResults( | ||
| 184 | s: *Server, | ||
| 185 | msg: OutMessage.TestResults, | ||
| 186 | ) !void { | ||
| 187 | const msg_le = bswap(msg); | ||
| 188 | try s.serveMessage(.{ | ||
| 189 | .tag = .test_results, | ||
| 190 | .bytes_len = @intCast(u32, @sizeOf(OutMessage.TestResults)), | ||
| 191 | }, &.{ | ||
| 192 | std.mem.asBytes(&msg_le), | ||
| 193 | }); | ||
| 194 | } | ||
| 195 | |||
| 196 | pub fn serveErrorBundle(s: *Server, error_bundle: std.zig.ErrorBundle) !void { | ||
| 197 | const eb_hdr: OutMessage.ErrorBundle = .{ | ||
| 198 | .extra_len = @intCast(u32, error_bundle.extra.len), | ||
| 199 | .string_bytes_len = @intCast(u32, error_bundle.string_bytes.len), | ||
| 200 | }; | ||
| 201 | const bytes_len = @sizeOf(OutMessage.ErrorBundle) + | ||
| 202 | 4 * error_bundle.extra.len + error_bundle.string_bytes.len; | ||
| 203 | try s.serveMessage(.{ | ||
| 204 | .tag = .error_bundle, | ||
| 205 | .bytes_len = @intCast(u32, bytes_len), | ||
| 206 | }, &.{ | ||
| 207 | std.mem.asBytes(&eb_hdr), | ||
| 208 | // TODO: implement @ptrCast between slices changing the length | ||
| 209 | std.mem.sliceAsBytes(error_bundle.extra), | ||
| 210 | error_bundle.string_bytes, | ||
| 211 | }); | ||
| 212 | } | ||
| 213 | |||
| 214 | pub const TestMetadata = struct { | ||
| 215 | names: []u32, | ||
| 216 | async_frame_sizes: []u32, | ||
| 217 | expected_panic_msgs: []u32, | ||
| 218 | string_bytes: []const u8, | ||
| 219 | }; | ||
| 220 | |||
| 221 | pub fn serveTestMetadata(s: *Server, test_metadata: TestMetadata) !void { | ||
| 222 | const header: OutMessage.TestMetadata = .{ | ||
| 223 | .tests_len = bswap(@intCast(u32, test_metadata.names.len)), | ||
| 224 | .string_bytes_len = bswap(@intCast(u32, test_metadata.string_bytes.len)), | ||
| 225 | }; | ||
| 226 | const bytes_len = @sizeOf(OutMessage.TestMetadata) + | ||
| 227 | 3 * 4 * test_metadata.names.len + test_metadata.string_bytes.len; | ||
| 228 | |||
| 229 | if (need_bswap) { | ||
| 230 | bswap_u32_array(test_metadata.names); | ||
| 231 | bswap_u32_array(test_metadata.async_frame_sizes); | ||
| 232 | bswap_u32_array(test_metadata.expected_panic_msgs); | ||
| 233 | } | ||
| 234 | defer if (need_bswap) { | ||
| 235 | bswap_u32_array(test_metadata.names); | ||
| 236 | bswap_u32_array(test_metadata.async_frame_sizes); | ||
| 237 | bswap_u32_array(test_metadata.expected_panic_msgs); | ||
| 238 | }; | ||
| 239 | |||
| 240 | return s.serveMessage(.{ | ||
| 241 | .tag = .test_metadata, | ||
| 242 | .bytes_len = @intCast(u32, bytes_len), | ||
| 243 | }, &.{ | ||
| 244 | std.mem.asBytes(&header), | ||
| 245 | // TODO: implement @ptrCast between slices changing the length | ||
| 246 | std.mem.sliceAsBytes(test_metadata.names), | ||
| 247 | std.mem.sliceAsBytes(test_metadata.async_frame_sizes), | ||
| 248 | std.mem.sliceAsBytes(test_metadata.expected_panic_msgs), | ||
| 249 | test_metadata.string_bytes, | ||
| 250 | }); | ||
| 251 | } | ||
| 252 | |||
| 253 | fn bswap(x: anytype) @TypeOf(x) { | ||
| 254 | if (!need_bswap) return x; | ||
| 255 | |||
| 256 | const T = @TypeOf(x); | ||
| 257 | switch (@typeInfo(T)) { | ||
| 258 | .Enum => return @intToEnum(T, @byteSwap(@enumToInt(x))), | ||
| 259 | .Int => return @byteSwap(x), | ||
| 260 | .Struct => |info| switch (info.layout) { | ||
| 261 | .Extern => { | ||
| 262 | var result: T = undefined; | ||
| 263 | inline for (info.fields) |field| { | ||
| 264 | @field(result, field.name) = bswap(@field(x, field.name)); | ||
| 265 | } | ||
| 266 | return result; | ||
| 267 | }, | ||
| 268 | .Packed => { | ||
| 269 | const I = info.backing_integer.?; | ||
| 270 | return @bitCast(T, @byteSwap(@bitCast(I, x))); | ||
| 271 | }, | ||
| 272 | .Auto => @compileError("auto layout struct"), | ||
| 273 | }, | ||
| 274 | else => @compileError("bswap on type " ++ @typeName(T)), | ||
| 275 | } | ||
| 276 | } | ||
| 277 | |||
| 278 | fn bswap_u32_array(slice: []u32) void { | ||
| 279 | comptime assert(need_bswap); | ||
| 280 | for (slice) |*elem| elem.* = @byteSwap(elem.*); | ||
| 281 | } | ||
| 282 | |||
| 283 | /// workaround for https://github.com/ziglang/zig/issues/14904 | ||
| 284 | fn bswap_and_workaround_u32(x: *align(1) const u32) u32 { | ||
| 285 | const bytes_ptr = @ptrCast(*const [4]u8, x); | ||
| 286 | return std.mem.readIntLittle(u32, bytes_ptr); | ||
| 287 | } | ||
| 288 | |||
| 289 | /// workaround for https://github.com/ziglang/zig/issues/14904 | ||
| 290 | fn bswap_and_workaround_tag(x: *align(1) const InMessage.Tag) InMessage.Tag { | ||
| 291 | const bytes_ptr = @ptrCast(*const [4]u8, x); | ||
| 292 | const int = std.mem.readIntLittle(u32, bytes_ptr); | ||
| 293 | return @intToEnum(InMessage.Tag, int); | ||
| 294 | } | ||
| 295 | |||
| 296 | const OutMessage = std.zig.Server.Message; | ||
| 297 | const InMessage = std.zig.Client.Message; | ||
| 298 | |||
| 299 | const Server = @This(); | ||
| 300 | const builtin = @import("builtin"); | ||
| 301 | const std = @import("std"); | ||
| 302 | const Allocator = std.mem.Allocator; | ||
| 303 | const assert = std.debug.assert; | ||
| 304 | const native_endian = builtin.target.cpu.arch.endian(); | ||
| 305 | const need_bswap = native_endian != .Little; | ||
lib/std/zig/system/NativeTargetInfo.zig+5| ... | @@ -1090,6 +1090,11 @@ pub fn getExternalExecutor( | ... | @@ -1090,6 +1090,11 @@ pub fn getExternalExecutor( |
| 1090 | switch (candidate.target.os.tag) { | 1090 | switch (candidate.target.os.tag) { |
| 1091 | .windows => { | 1091 | .windows => { |
| 1092 | if (options.allow_wine) { | 1092 | if (options.allow_wine) { |
| 1093 | // x86_64 wine does not support emulating aarch64-windows and | ||
| 1094 | // vice versa. | ||
| 1095 | if (candidate.target.cpu.arch != builtin.cpu.arch) { | ||
| 1096 | return bad_result; | ||
| 1097 | } | ||
| 1093 | switch (candidate.target.cpu.arch.ptrBitWidth()) { | 1098 | switch (candidate.target.cpu.arch.ptrBitWidth()) { |
| 1094 | 32 => return Executor{ .wine = "wine" }, | 1099 | 32 => return Executor{ .wine = "wine" }, |
| 1095 | 64 => return Executor{ .wine = "wine64" }, | 1100 | 64 => return Executor{ .wine = "wine64" }, |
lib/test_runner.zig+123-45| ... | @@ -8,14 +8,126 @@ pub const std_options = struct { | ... | @@ -8,14 +8,126 @@ pub const std_options = struct { |
| 8 | }; | 8 | }; |
| 9 | 9 | ||
| 10 | var log_err_count: usize = 0; | 10 | var log_err_count: usize = 0; |
| 11 | var cmdline_buffer: [4096]u8 = undefined; | ||
| 12 | var fba = std.heap.FixedBufferAllocator.init(&cmdline_buffer); | ||
| 11 | 13 | ||
| 12 | pub fn main() void { | 14 | pub fn main() void { |
| 13 | if (builtin.zig_backend != .stage1 and | 15 | if (builtin.zig_backend == .stage2_wasm or |
| 14 | builtin.zig_backend != .stage2_llvm and | 16 | builtin.zig_backend == .stage2_x86_64 or |
| 15 | builtin.zig_backend != .stage2_c) | 17 | builtin.zig_backend == .stage2_aarch64) |
| 16 | { | 18 | { |
| 17 | return main2() catch @panic("test failure"); | 19 | return mainSimple() catch @panic("test failure"); |
| 20 | } | ||
| 21 | |||
| 22 | const args = std.process.argsAlloc(fba.allocator()) catch | ||
| 23 | @panic("unable to parse command line args"); | ||
| 24 | |||
| 25 | var listen = false; | ||
| 26 | |||
| 27 | for (args[1..]) |arg| { | ||
| 28 | if (std.mem.eql(u8, arg, "--listen=-")) { | ||
| 29 | listen = true; | ||
| 30 | } else { | ||
| 31 | @panic("unrecognized command line argument"); | ||
| 32 | } | ||
| 33 | } | ||
| 34 | |||
| 35 | if (listen) { | ||
| 36 | return mainServer() catch @panic("internal test runner failure"); | ||
| 37 | } else { | ||
| 38 | return mainTerminal(); | ||
| 39 | } | ||
| 40 | } | ||
| 41 | |||
| 42 | fn mainServer() !void { | ||
| 43 | var server = try std.zig.Server.init(.{ | ||
| 44 | .gpa = fba.allocator(), | ||
| 45 | .in = std.io.getStdIn(), | ||
| 46 | .out = std.io.getStdOut(), | ||
| 47 | .zig_version = builtin.zig_version_string, | ||
| 48 | }); | ||
| 49 | defer server.deinit(); | ||
| 50 | |||
| 51 | while (true) { | ||
| 52 | const hdr = try server.receiveMessage(); | ||
| 53 | switch (hdr.tag) { | ||
| 54 | .exit => { | ||
| 55 | return std.process.exit(0); | ||
| 56 | }, | ||
| 57 | .query_test_metadata => { | ||
| 58 | std.testing.allocator_instance = .{}; | ||
| 59 | defer if (std.testing.allocator_instance.deinit()) { | ||
| 60 | @panic("internal test runner memory leak"); | ||
| 61 | }; | ||
| 62 | |||
| 63 | var string_bytes: std.ArrayListUnmanaged(u8) = .{}; | ||
| 64 | defer string_bytes.deinit(std.testing.allocator); | ||
| 65 | try string_bytes.append(std.testing.allocator, 0); // Reserve 0 for null. | ||
| 66 | |||
| 67 | const test_fns = builtin.test_functions; | ||
| 68 | const names = try std.testing.allocator.alloc(u32, test_fns.len); | ||
| 69 | defer std.testing.allocator.free(names); | ||
| 70 | const async_frame_sizes = try std.testing.allocator.alloc(u32, test_fns.len); | ||
| 71 | defer std.testing.allocator.free(async_frame_sizes); | ||
| 72 | const expected_panic_msgs = try std.testing.allocator.alloc(u32, test_fns.len); | ||
| 73 | defer std.testing.allocator.free(expected_panic_msgs); | ||
| 74 | |||
| 75 | for (test_fns, names, async_frame_sizes, expected_panic_msgs) |test_fn, *name, *async_frame_size, *expected_panic_msg| { | ||
| 76 | name.* = @intCast(u32, string_bytes.items.len); | ||
| 77 | try string_bytes.ensureUnusedCapacity(std.testing.allocator, test_fn.name.len + 1); | ||
| 78 | string_bytes.appendSliceAssumeCapacity(test_fn.name); | ||
| 79 | string_bytes.appendAssumeCapacity(0); | ||
| 80 | |||
| 81 | async_frame_size.* = @intCast(u32, test_fn.async_frame_size orelse 0); | ||
| 82 | expected_panic_msg.* = 0; | ||
| 83 | } | ||
| 84 | |||
| 85 | try server.serveTestMetadata(.{ | ||
| 86 | .names = names, | ||
| 87 | .async_frame_sizes = async_frame_sizes, | ||
| 88 | .expected_panic_msgs = expected_panic_msgs, | ||
| 89 | .string_bytes = string_bytes.items, | ||
| 90 | }); | ||
| 91 | }, | ||
| 92 | |||
| 93 | .run_test => { | ||
| 94 | std.testing.allocator_instance = .{}; | ||
| 95 | const index = try server.receiveBody_u32(); | ||
| 96 | const test_fn = builtin.test_functions[index]; | ||
| 97 | if (test_fn.async_frame_size != null) | ||
| 98 | @panic("TODO test runner implement async tests"); | ||
| 99 | var fail = false; | ||
| 100 | var skip = false; | ||
| 101 | var leak = false; | ||
| 102 | test_fn.func() catch |err| switch (err) { | ||
| 103 | error.SkipZigTest => skip = true, | ||
| 104 | else => { | ||
| 105 | fail = true; | ||
| 106 | if (@errorReturnTrace()) |trace| { | ||
| 107 | std.debug.dumpStackTrace(trace.*); | ||
| 108 | } | ||
| 109 | }, | ||
| 110 | }; | ||
| 111 | leak = std.testing.allocator_instance.deinit(); | ||
| 112 | try server.serveTestResults(.{ | ||
| 113 | .index = index, | ||
| 114 | .flags = .{ | ||
| 115 | .fail = fail, | ||
| 116 | .skip = skip, | ||
| 117 | .leak = leak, | ||
| 118 | }, | ||
| 119 | }); | ||
| 120 | }, | ||
| 121 | |||
| 122 | else => { | ||
| 123 | std.debug.print("unsupported message: {x}", .{@enumToInt(hdr.tag)}); | ||
| 124 | std.process.exit(1); | ||
| 125 | }, | ||
| 126 | } | ||
| 18 | } | 127 | } |
| 128 | } | ||
| 129 | |||
| 130 | fn mainTerminal() void { | ||
| 19 | const test_fn_list = builtin.test_functions; | 131 | const test_fn_list = builtin.test_functions; |
| 20 | var ok_count: usize = 0; | 132 | var ok_count: usize = 0; |
| 21 | var skip_count: usize = 0; | 133 | var skip_count: usize = 0; |
| ... | @@ -118,51 +230,17 @@ pub fn log( | ... | @@ -118,51 +230,17 @@ pub fn log( |
| 118 | } | 230 | } |
| 119 | } | 231 | } |
| 120 | 232 | ||
| 121 | pub fn main2() anyerror!void { | 233 | /// Simpler main(), exercising fewer language features, so that |
| 122 | var skipped: usize = 0; | 234 | /// work-in-progress backends can handle it. |
| 123 | var failed: usize = 0; | 235 | pub fn mainSimple() anyerror!void { |
| 124 | // Simpler main(), exercising fewer language features, so that stage2 can handle it. | 236 | //const stderr = std.io.getStdErr(); |
| 125 | for (builtin.test_functions) |test_fn| { | 237 | for (builtin.test_functions) |test_fn| { |
| 126 | test_fn.func() catch |err| { | 238 | test_fn.func() catch |err| { |
| 127 | if (err != error.SkipZigTest) { | 239 | if (err != error.SkipZigTest) { |
| 128 | failed += 1; | 240 | //stderr.writeAll(test_fn.name) catch {}; |
| 129 | } else { | 241 | //stderr.writeAll("\n") catch {}; |
| 130 | skipped += 1; | 242 | return err; |
| 131 | } | 243 | } |
| 132 | }; | 244 | }; |
| 133 | } | 245 | } |
| 134 | if (builtin.zig_backend == .stage2_wasm or | ||
| 135 | builtin.zig_backend == .stage2_x86_64 or | ||
| 136 | builtin.zig_backend == .stage2_aarch64 or | ||
| 137 | builtin.zig_backend == .stage2_llvm or | ||
| 138 | builtin.zig_backend == .stage2_c) | ||
| 139 | { | ||
| 140 | const passed = builtin.test_functions.len - skipped - failed; | ||
| 141 | const stderr = std.io.getStdErr(); | ||
| 142 | writeInt(stderr, passed) catch {}; | ||
| 143 | stderr.writeAll(" passed; ") catch {}; | ||
| 144 | writeInt(stderr, skipped) catch {}; | ||
| 145 | stderr.writeAll(" skipped; ") catch {}; | ||
| 146 | writeInt(stderr, failed) catch {}; | ||
| 147 | stderr.writeAll(" failed.\n") catch {}; | ||
| 148 | } | ||
| 149 | if (failed != 0) { | ||
| 150 | return error.TestsFailed; | ||
| 151 | } | ||
| 152 | } | ||
| 153 | |||
| 154 | fn writeInt(stderr: std.fs.File, int: usize) anyerror!void { | ||
| 155 | const base = 10; | ||
| 156 | var buf: [100]u8 = undefined; | ||
| 157 | var a: usize = int; | ||
| 158 | var index: usize = buf.len; | ||
| 159 | while (true) { | ||
| 160 | const digit = a % base; | ||
| 161 | index -= 1; | ||
| 162 | buf[index] = std.fmt.digitToChar(@intCast(u8, digit), .lower); | ||
| 163 | a /= base; | ||
| 164 | if (a == 0) break; | ||
| 165 | } | ||
| 166 | const slice = buf[index..]; | ||
| 167 | try stderr.writeAll(slice); | ||
| 168 | } | 246 | } |
src/AstGen.zig+88-40| ... | @@ -148,18 +148,24 @@ pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir { | ... | @@ -148,18 +148,24 @@ pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir { |
| 148 | }; | 148 | }; |
| 149 | defer gz_instructions.deinit(gpa); | 149 | defer gz_instructions.deinit(gpa); |
| 150 | 150 | ||
| 151 | if (AstGen.structDeclInner( | 151 | // The AST -> ZIR lowering process assumes an AST that does not have any |
| 152 | &gen_scope, | 152 | // parse errors. |
| 153 | &gen_scope.base, | 153 | if (tree.errors.len == 0) { |
| 154 | 0, | 154 | if (AstGen.structDeclInner( |
| 155 | tree.containerDeclRoot(), | 155 | &gen_scope, |
| 156 | .Auto, | 156 | &gen_scope.base, |
| 157 | 0, | 157 | 0, |
| 158 | )) |struct_decl_ref| { | 158 | tree.containerDeclRoot(), |
| 159 | assert(refToIndex(struct_decl_ref).? == 0); | 159 | .Auto, |
| 160 | } else |err| switch (err) { | 160 | 0, |
| 161 | error.OutOfMemory => return error.OutOfMemory, | 161 | )) |struct_decl_ref| { |
| 162 | error.AnalysisFail => {}, // Handled via compile_errors below. | 162 | assert(refToIndex(struct_decl_ref).? == 0); |
| 163 | } else |err| switch (err) { | ||
| 164 | error.OutOfMemory => return error.OutOfMemory, | ||
| 165 | error.AnalysisFail => {}, // Handled via compile_errors below. | ||
| 166 | } | ||
| 167 | } else { | ||
| 168 | try lowerAstErrors(&astgen); | ||
| 163 | } | 169 | } |
| 164 | 170 | ||
| 165 | const err_index = @enumToInt(Zir.ExtraIndex.compile_errors); | 171 | const err_index = @enumToInt(Zir.ExtraIndex.compile_errors); |
| ... | @@ -10380,7 +10386,7 @@ fn appendErrorTok( | ... | @@ -10380,7 +10386,7 @@ fn appendErrorTok( |
| 10380 | comptime format: []const u8, | 10386 | comptime format: []const u8, |
| 10381 | args: anytype, | 10387 | args: anytype, |
| 10382 | ) !void { | 10388 | ) !void { |
| 10383 | try astgen.appendErrorTokNotes(token, format, args, &[0]u32{}); | 10389 | try astgen.appendErrorTokNotesOff(token, 0, format, args, &[0]u32{}); |
| 10384 | } | 10390 | } |
| 10385 | 10391 | ||
| 10386 | fn failTokNotes( | 10392 | fn failTokNotes( |
| ... | @@ -10390,7 +10396,7 @@ fn failTokNotes( | ... | @@ -10390,7 +10396,7 @@ fn failTokNotes( |
| 10390 | args: anytype, | 10396 | args: anytype, |
| 10391 | notes: []const u32, | 10397 | notes: []const u32, |
| 10392 | ) InnerError { | 10398 | ) InnerError { |
| 10393 | try appendErrorTokNotes(astgen, token, format, args, notes); | 10399 | try appendErrorTokNotesOff(astgen, token, 0, format, args, notes); |
| 10394 | return error.AnalysisFail; | 10400 | return error.AnalysisFail; |
| 10395 | } | 10401 | } |
| 10396 | 10402 | ||
| ... | @@ -10401,27 +10407,11 @@ fn appendErrorTokNotes( | ... | @@ -10401,27 +10407,11 @@ fn appendErrorTokNotes( |
| 10401 | args: anytype, | 10407 | args: anytype, |
| 10402 | notes: []const u32, | 10408 | notes: []const u32, |
| 10403 | ) !void { | 10409 | ) !void { |
| 10404 | @setCold(true); | 10410 | return appendErrorTokNotesOff(astgen, token, 0, format, args, notes); |
| 10405 | const string_bytes = &astgen.string_bytes; | ||
| 10406 | const msg = @intCast(u32, string_bytes.items.len); | ||
| 10407 | try string_bytes.writer(astgen.gpa).print(format ++ "\x00", args); | ||
| 10408 | const notes_index: u32 = if (notes.len != 0) blk: { | ||
| 10409 | const notes_start = astgen.extra.items.len; | ||
| 10410 | try astgen.extra.ensureTotalCapacity(astgen.gpa, notes_start + 1 + notes.len); | ||
| 10411 | astgen.extra.appendAssumeCapacity(@intCast(u32, notes.len)); | ||
| 10412 | astgen.extra.appendSliceAssumeCapacity(notes); | ||
| 10413 | break :blk @intCast(u32, notes_start); | ||
| 10414 | } else 0; | ||
| 10415 | try astgen.compile_errors.append(astgen.gpa, .{ | ||
| 10416 | .msg = msg, | ||
| 10417 | .node = 0, | ||
| 10418 | .token = token, | ||
| 10419 | .byte_offset = 0, | ||
| 10420 | .notes = notes_index, | ||
| 10421 | }); | ||
| 10422 | } | 10411 | } |
| 10423 | 10412 | ||
| 10424 | /// Same as `fail`, except given an absolute byte offset. | 10413 | /// Same as `fail`, except given a token plus an offset from its starting byte |
| 10414 | /// offset. | ||
| 10425 | fn failOff( | 10415 | fn failOff( |
| 10426 | astgen: *AstGen, | 10416 | astgen: *AstGen, |
| 10427 | token: Ast.TokenIndex, | 10417 | token: Ast.TokenIndex, |
| ... | @@ -10429,27 +10419,36 @@ fn failOff( | ... | @@ -10429,27 +10419,36 @@ fn failOff( |
| 10429 | comptime format: []const u8, | 10419 | comptime format: []const u8, |
| 10430 | args: anytype, | 10420 | args: anytype, |
| 10431 | ) InnerError { | 10421 | ) InnerError { |
| 10432 | try appendErrorOff(astgen, token, byte_offset, format, args); | 10422 | try appendErrorTokNotesOff(astgen, token, byte_offset, format, args, &.{}); |
| 10433 | return error.AnalysisFail; | 10423 | return error.AnalysisFail; |
| 10434 | } | 10424 | } |
| 10435 | 10425 | ||
| 10436 | fn appendErrorOff( | 10426 | fn appendErrorTokNotesOff( |
| 10437 | astgen: *AstGen, | 10427 | astgen: *AstGen, |
| 10438 | token: Ast.TokenIndex, | 10428 | token: Ast.TokenIndex, |
| 10439 | byte_offset: u32, | 10429 | byte_offset: u32, |
| 10440 | comptime format: []const u8, | 10430 | comptime format: []const u8, |
| 10441 | args: anytype, | 10431 | args: anytype, |
| 10442 | ) Allocator.Error!void { | 10432 | notes: []const u32, |
| 10433 | ) !void { | ||
| 10443 | @setCold(true); | 10434 | @setCold(true); |
| 10435 | const gpa = astgen.gpa; | ||
| 10444 | const string_bytes = &astgen.string_bytes; | 10436 | const string_bytes = &astgen.string_bytes; |
| 10445 | const msg = @intCast(u32, string_bytes.items.len); | 10437 | const msg = @intCast(u32, string_bytes.items.len); |
| 10446 | try string_bytes.writer(astgen.gpa).print(format ++ "\x00", args); | 10438 | try string_bytes.writer(gpa).print(format ++ "\x00", args); |
| 10447 | try astgen.compile_errors.append(astgen.gpa, .{ | 10439 | const notes_index: u32 = if (notes.len != 0) blk: { |
| 10440 | const notes_start = astgen.extra.items.len; | ||
| 10441 | try astgen.extra.ensureTotalCapacity(gpa, notes_start + 1 + notes.len); | ||
| 10442 | astgen.extra.appendAssumeCapacity(@intCast(u32, notes.len)); | ||
| 10443 | astgen.extra.appendSliceAssumeCapacity(notes); | ||
| 10444 | break :blk @intCast(u32, notes_start); | ||
| 10445 | } else 0; | ||
| 10446 | try astgen.compile_errors.append(gpa, .{ | ||
| 10448 | .msg = msg, | 10447 | .msg = msg, |
| 10449 | .node = 0, | 10448 | .node = 0, |
| 10450 | .token = token, | 10449 | .token = token, |
| 10451 | .byte_offset = byte_offset, | 10450 | .byte_offset = byte_offset, |
| 10452 | .notes = 0, | 10451 | .notes = notes_index, |
| 10453 | }); | 10452 | }); |
| 10454 | } | 10453 | } |
| 10455 | 10454 | ||
| ... | @@ -10458,6 +10457,16 @@ fn errNoteTok( | ... | @@ -10458,6 +10457,16 @@ fn errNoteTok( |
| 10458 | token: Ast.TokenIndex, | 10457 | token: Ast.TokenIndex, |
| 10459 | comptime format: []const u8, | 10458 | comptime format: []const u8, |
| 10460 | args: anytype, | 10459 | args: anytype, |
| 10460 | ) Allocator.Error!u32 { | ||
| 10461 | return errNoteTokOff(astgen, token, 0, format, args); | ||
| 10462 | } | ||
| 10463 | |||
| 10464 | fn errNoteTokOff( | ||
| 10465 | astgen: *AstGen, | ||
| 10466 | token: Ast.TokenIndex, | ||
| 10467 | byte_offset: u32, | ||
| 10468 | comptime format: []const u8, | ||
| 10469 | args: anytype, | ||
| 10461 | ) Allocator.Error!u32 { | 10470 | ) Allocator.Error!u32 { |
| 10462 | @setCold(true); | 10471 | @setCold(true); |
| 10463 | const string_bytes = &astgen.string_bytes; | 10472 | const string_bytes = &astgen.string_bytes; |
| ... | @@ -10467,7 +10476,7 @@ fn errNoteTok( | ... | @@ -10467,7 +10476,7 @@ fn errNoteTok( |
| 10467 | .msg = msg, | 10476 | .msg = msg, |
| 10468 | .node = 0, | 10477 | .node = 0, |
| 10469 | .token = token, | 10478 | .token = token, |
| 10470 | .byte_offset = 0, | 10479 | .byte_offset = byte_offset, |
| 10471 | .notes = 0, | 10480 | .notes = 0, |
| 10472 | }); | 10481 | }); |
| 10473 | } | 10482 | } |
| ... | @@ -12634,3 +12643,42 @@ fn emitDbgStmt(gz: *GenZir, line: u32, column: u32) !void { | ... | @@ -12634,3 +12643,42 @@ fn emitDbgStmt(gz: *GenZir, line: u32, column: u32) !void { |
| 12634 | }, | 12643 | }, |
| 12635 | } }); | 12644 | } }); |
| 12636 | } | 12645 | } |
| 12646 | |||
| 12647 | fn lowerAstErrors(astgen: *AstGen) !void { | ||
| 12648 | const tree = astgen.tree; | ||
| 12649 | assert(tree.errors.len > 0); | ||
| 12650 | |||
| 12651 | const gpa = astgen.gpa; | ||
| 12652 | const parse_err = tree.errors[0]; | ||
| 12653 | |||
| 12654 | var msg: std.ArrayListUnmanaged(u8) = .{}; | ||
| 12655 | defer msg.deinit(gpa); | ||
| 12656 | |||
| 12657 | const token_starts = tree.tokens.items(.start); | ||
| 12658 | const token_tags = tree.tokens.items(.tag); | ||
| 12659 | |||
| 12660 | var notes: std.ArrayListUnmanaged(u32) = .{}; | ||
| 12661 | defer notes.deinit(gpa); | ||
| 12662 | |||
| 12663 | if (token_tags[parse_err.token + @boolToInt(parse_err.token_is_prev)] == .invalid) { | ||
| 12664 | const tok = parse_err.token + @boolToInt(parse_err.token_is_prev); | ||
| 12665 | const bad_off = @intCast(u32, tree.tokenSlice(parse_err.token + @boolToInt(parse_err.token_is_prev)).len); | ||
| 12666 | const byte_abs = token_starts[parse_err.token + @boolToInt(parse_err.token_is_prev)] + bad_off; | ||
| 12667 | try notes.append(gpa, try astgen.errNoteTokOff(tok, bad_off, "invalid byte: '{'}'", .{ | ||
| 12668 | std.zig.fmtEscapes(tree.source[byte_abs..][0..1]), | ||
| 12669 | })); | ||
| 12670 | } | ||
| 12671 | |||
| 12672 | for (tree.errors[1..]) |note| { | ||
| 12673 | if (!note.is_note) break; | ||
| 12674 | |||
| 12675 | msg.clearRetainingCapacity(); | ||
| 12676 | try tree.renderError(note, msg.writer(gpa)); | ||
| 12677 | try notes.append(gpa, try astgen.errNoteTok(note.token, "{s}", .{msg.items})); | ||
| 12678 | } | ||
| 12679 | |||
| 12680 | const extra_offset = tree.errorOffset(parse_err); | ||
| 12681 | msg.clearRetainingCapacity(); | ||
| 12682 | try tree.renderError(parse_err, msg.writer(gpa)); | ||
| 12683 | try astgen.appendErrorTokNotesOff(parse_err.token, extra_offset, "{s}", .{msg.items}, notes.items); | ||
| 12684 | } |
src/Compilation.zig+424-608| ... | @@ -7,6 +7,9 @@ const Allocator = std.mem.Allocator; | ... | @@ -7,6 +7,9 @@ const Allocator = std.mem.Allocator; |
| 7 | const assert = std.debug.assert; | 7 | const assert = std.debug.assert; |
| 8 | const log = std.log.scoped(.compilation); | 8 | const log = std.log.scoped(.compilation); |
| 9 | const Target = std.Target; | 9 | const Target = std.Target; |
| 10 | const ThreadPool = std.Thread.Pool; | ||
| 11 | const WaitGroup = std.Thread.WaitGroup; | ||
| 12 | const ErrorBundle = std.zig.ErrorBundle; | ||
| 10 | 13 | ||
| 11 | const Value = @import("value.zig").Value; | 14 | const Value = @import("value.zig").Value; |
| 12 | const Type = @import("type.zig").Type; | 15 | const Type = @import("type.zig").Type; |
| ... | @@ -30,8 +33,6 @@ const Cache = std.Build.Cache; | ... | @@ -30,8 +33,6 @@ const Cache = std.Build.Cache; |
| 30 | const translate_c = @import("translate_c.zig"); | 33 | const translate_c = @import("translate_c.zig"); |
| 31 | const clang = @import("clang.zig"); | 34 | const clang = @import("clang.zig"); |
| 32 | const c_codegen = @import("codegen/c.zig"); | 35 | const c_codegen = @import("codegen/c.zig"); |
| 33 | const ThreadPool = @import("ThreadPool.zig"); | ||
| 34 | const WaitGroup = @import("WaitGroup.zig"); | ||
| 35 | const libtsan = @import("libtsan.zig"); | 36 | const libtsan = @import("libtsan.zig"); |
| 36 | const Zir = @import("Zir.zig"); | 37 | const Zir = @import("Zir.zig"); |
| 37 | const Autodoc = @import("Autodoc.zig"); | 38 | const Autodoc = @import("Autodoc.zig"); |
| ... | @@ -99,6 +100,7 @@ job_queued_compiler_rt_lib: bool = false, | ... | @@ -99,6 +100,7 @@ job_queued_compiler_rt_lib: bool = false, |
| 99 | job_queued_compiler_rt_obj: bool = false, | 100 | job_queued_compiler_rt_obj: bool = false, |
| 100 | alloc_failure_occurred: bool = false, | 101 | alloc_failure_occurred: bool = false, |
| 101 | formatted_panics: bool = false, | 102 | formatted_panics: bool = false, |
| 103 | last_update_was_cache_hit: bool = false, | ||
| 102 | 104 | ||
| 103 | c_source_files: []const CSourceFile, | 105 | c_source_files: []const CSourceFile, |
| 104 | clang_argv: []const []const u8, | 106 | clang_argv: []const []const u8, |
| ... | @@ -334,12 +336,41 @@ pub const MiscTask = enum { | ... | @@ -334,12 +336,41 @@ pub const MiscTask = enum { |
| 334 | libssp, | 336 | libssp, |
| 335 | zig_libc, | 337 | zig_libc, |
| 336 | analyze_pkg, | 338 | analyze_pkg, |
| 339 | |||
| 340 | @"musl crti.o", | ||
| 341 | @"musl crtn.o", | ||
| 342 | @"musl crt1.o", | ||
| 343 | @"musl rcrt1.o", | ||
| 344 | @"musl Scrt1.o", | ||
| 345 | @"musl libc.a", | ||
| 346 | @"musl libc.so", | ||
| 347 | |||
| 348 | @"wasi crt1-reactor.o", | ||
| 349 | @"wasi crt1-command.o", | ||
| 350 | @"wasi libc.a", | ||
| 351 | @"libwasi-emulated-process-clocks.a", | ||
| 352 | @"libwasi-emulated-getpid.a", | ||
| 353 | @"libwasi-emulated-mman.a", | ||
| 354 | @"libwasi-emulated-signal.a", | ||
| 355 | |||
| 356 | @"glibc crti.o", | ||
| 357 | @"glibc crtn.o", | ||
| 358 | @"glibc Scrt1.o", | ||
| 359 | @"glibc libc_nonshared.a", | ||
| 360 | @"glibc shared object", | ||
| 361 | |||
| 362 | @"mingw-w64 crt2.o", | ||
| 363 | @"mingw-w64 dllcrt2.o", | ||
| 364 | @"mingw-w64 mingw32.lib", | ||
| 365 | @"mingw-w64 msvcrt-os.lib", | ||
| 366 | @"mingw-w64 mingwex.lib", | ||
| 367 | @"mingw-w64 uuid.lib", | ||
| 337 | }; | 368 | }; |
| 338 | 369 | ||
| 339 | pub const MiscError = struct { | 370 | pub const MiscError = struct { |
| 340 | /// Allocated with gpa. | 371 | /// Allocated with gpa. |
| 341 | msg: []u8, | 372 | msg: []u8, |
| 342 | children: ?AllErrors = null, | 373 | children: ?ErrorBundle = null, |
| 343 | 374 | ||
| 344 | pub fn deinit(misc_err: *MiscError, gpa: Allocator) void { | 375 | pub fn deinit(misc_err: *MiscError, gpa: Allocator) void { |
| 345 | gpa.free(misc_err.msg); | 376 | gpa.free(misc_err.msg); |
| ... | @@ -365,448 +396,6 @@ pub const LldError = struct { | ... | @@ -365,448 +396,6 @@ pub const LldError = struct { |
| 365 | } | 396 | } |
| 366 | }; | 397 | }; |
| 367 | 398 | ||
| 368 | /// To support incremental compilation, errors are stored in various places | ||
| 369 | /// so that they can be created and destroyed appropriately. This structure | ||
| 370 | /// is used to collect all the errors from the various places into one | ||
| 371 | /// convenient place for API users to consume. It is allocated into 1 arena | ||
| 372 | /// and freed all at once. | ||
| 373 | pub const AllErrors = struct { | ||
| 374 | arena: std.heap.ArenaAllocator.State, | ||
| 375 | list: []const Message, | ||
| 376 | |||
| 377 | pub const Message = union(enum) { | ||
| 378 | src: struct { | ||
| 379 | msg: []const u8, | ||
| 380 | src_path: []const u8, | ||
| 381 | line: u32, | ||
| 382 | column: u32, | ||
| 383 | span: Module.SrcLoc.Span, | ||
| 384 | /// Usually one, but incremented for redundant messages. | ||
| 385 | count: u32 = 1, | ||
| 386 | /// Does not include the trailing newline. | ||
| 387 | source_line: ?[]const u8, | ||
| 388 | notes: []const Message = &.{}, | ||
| 389 | reference_trace: []Message = &.{}, | ||
| 390 | |||
| 391 | /// Splits the error message up into lines to properly indent them | ||
| 392 | /// to allow for long, good-looking error messages. | ||
| 393 | /// | ||
| 394 | /// This is used to split the message in `@compileError("hello\nworld")` for example. | ||
| 395 | fn writeMsg(src: @This(), stderr: anytype, indent: usize) !void { | ||
| 396 | var lines = mem.split(u8, src.msg, "\n"); | ||
| 397 | while (lines.next()) |line| { | ||
| 398 | try stderr.writeAll(line); | ||
| 399 | if (lines.index == null) break; | ||
| 400 | try stderr.writeByte('\n'); | ||
| 401 | try stderr.writeByteNTimes(' ', indent); | ||
| 402 | } | ||
| 403 | } | ||
| 404 | }, | ||
| 405 | plain: struct { | ||
| 406 | msg: []const u8, | ||
| 407 | notes: []Message = &.{}, | ||
| 408 | /// Usually one, but incremented for redundant messages. | ||
| 409 | count: u32 = 1, | ||
| 410 | }, | ||
| 411 | |||
| 412 | pub fn incrementCount(msg: *Message) void { | ||
| 413 | switch (msg.*) { | ||
| 414 | .src => |*src| { | ||
| 415 | src.count += 1; | ||
| 416 | }, | ||
| 417 | .plain => |*plain| { | ||
| 418 | plain.count += 1; | ||
| 419 | }, | ||
| 420 | } | ||
| 421 | } | ||
| 422 | |||
| 423 | pub fn renderToStdErr(msg: Message, ttyconf: std.debug.TTY.Config) void { | ||
| 424 | std.debug.getStderrMutex().lock(); | ||
| 425 | defer std.debug.getStderrMutex().unlock(); | ||
| 426 | const stderr = std.io.getStdErr(); | ||
| 427 | return msg.renderToWriter(ttyconf, stderr.writer(), "error", .Red, 0) catch return; | ||
| 428 | } | ||
| 429 | |||
| 430 | pub fn renderToWriter( | ||
| 431 | msg: Message, | ||
| 432 | ttyconf: std.debug.TTY.Config, | ||
| 433 | stderr: anytype, | ||
| 434 | kind: []const u8, | ||
| 435 | color: std.debug.TTY.Color, | ||
| 436 | indent: usize, | ||
| 437 | ) anyerror!void { | ||
| 438 | var counting_writer = std.io.countingWriter(stderr); | ||
| 439 | const counting_stderr = counting_writer.writer(); | ||
| 440 | switch (msg) { | ||
| 441 | .src => |src| { | ||
| 442 | try counting_stderr.writeByteNTimes(' ', indent); | ||
| 443 | try ttyconf.setColor(stderr, .Bold); | ||
| 444 | try counting_stderr.print("{s}:{d}:{d}: ", .{ | ||
| 445 | src.src_path, | ||
| 446 | src.line + 1, | ||
| 447 | src.column + 1, | ||
| 448 | }); | ||
| 449 | try ttyconf.setColor(stderr, color); | ||
| 450 | try counting_stderr.writeAll(kind); | ||
| 451 | try counting_stderr.writeAll(": "); | ||
| 452 | // This is the length of the part before the error message: | ||
| 453 | // e.g. "file.zig:4:5: error: " | ||
| 454 | const prefix_len = @intCast(usize, counting_stderr.context.bytes_written); | ||
| 455 | try ttyconf.setColor(stderr, .Reset); | ||
| 456 | try ttyconf.setColor(stderr, .Bold); | ||
| 457 | if (src.count == 1) { | ||
| 458 | try src.writeMsg(stderr, prefix_len); | ||
| 459 | try stderr.writeByte('\n'); | ||
| 460 | } else { | ||
| 461 | try src.writeMsg(stderr, prefix_len); | ||
| 462 | try ttyconf.setColor(stderr, .Dim); | ||
| 463 | try stderr.print(" ({d} times)\n", .{src.count}); | ||
| 464 | } | ||
| 465 | try ttyconf.setColor(stderr, .Reset); | ||
| 466 | if (src.source_line) |line| { | ||
| 467 | for (line) |b| switch (b) { | ||
| 468 | '\t' => try stderr.writeByte(' '), | ||
| 469 | else => try stderr.writeByte(b), | ||
| 470 | }; | ||
| 471 | try stderr.writeByte('\n'); | ||
| 472 | // TODO basic unicode code point monospace width | ||
| 473 | const before_caret = src.span.main - src.span.start; | ||
| 474 | // -1 since span.main includes the caret | ||
| 475 | const after_caret = src.span.end - src.span.main -| 1; | ||
| 476 | try stderr.writeByteNTimes(' ', src.column - before_caret); | ||
| 477 | try ttyconf.setColor(stderr, .Green); | ||
| 478 | try stderr.writeByteNTimes('~', before_caret); | ||
| 479 | try stderr.writeByte('^'); | ||
| 480 | try stderr.writeByteNTimes('~', after_caret); | ||
| 481 | try stderr.writeByte('\n'); | ||
| 482 | try ttyconf.setColor(stderr, .Reset); | ||
| 483 | } | ||
| 484 | for (src.notes) |note| { | ||
| 485 | try note.renderToWriter(ttyconf, stderr, "note", .Cyan, indent); | ||
| 486 | } | ||
| 487 | if (src.reference_trace.len != 0) { | ||
| 488 | try ttyconf.setColor(stderr, .Reset); | ||
| 489 | try ttyconf.setColor(stderr, .Dim); | ||
| 490 | try stderr.print("referenced by:\n", .{}); | ||
| 491 | for (src.reference_trace) |reference| { | ||
| 492 | switch (reference) { | ||
| 493 | .src => |ref_src| try stderr.print(" {s}: {s}:{d}:{d}\n", .{ | ||
| 494 | ref_src.msg, | ||
| 495 | ref_src.src_path, | ||
| 496 | ref_src.line + 1, | ||
| 497 | ref_src.column + 1, | ||
| 498 | }), | ||
| 499 | .plain => |plain| if (plain.count != 0) { | ||
| 500 | try stderr.print( | ||
| 501 | " {d} reference(s) hidden; use '-freference-trace={d}' to see all references\n", | ||
| 502 | .{ plain.count, plain.count + src.reference_trace.len - 1 }, | ||
| 503 | ); | ||
| 504 | } else { | ||
| 505 | try stderr.print( | ||
| 506 | " remaining reference traces hidden; use '-freference-trace' to see all reference traces\n", | ||
| 507 | .{}, | ||
| 508 | ); | ||
| 509 | }, | ||
| 510 | } | ||
| 511 | } | ||
| 512 | try stderr.writeByte('\n'); | ||
| 513 | try ttyconf.setColor(stderr, .Reset); | ||
| 514 | } | ||
| 515 | }, | ||
| 516 | .plain => |plain| { | ||
| 517 | try ttyconf.setColor(stderr, color); | ||
| 518 | try stderr.writeByteNTimes(' ', indent); | ||
| 519 | try stderr.writeAll(kind); | ||
| 520 | try stderr.writeAll(": "); | ||
| 521 | try ttyconf.setColor(stderr, .Reset); | ||
| 522 | if (plain.count == 1) { | ||
| 523 | try stderr.print("{s}\n", .{plain.msg}); | ||
| 524 | } else { | ||
| 525 | try stderr.print("{s}", .{plain.msg}); | ||
| 526 | try ttyconf.setColor(stderr, .Dim); | ||
| 527 | try stderr.print(" ({d} times)\n", .{plain.count}); | ||
| 528 | } | ||
| 529 | try ttyconf.setColor(stderr, .Reset); | ||
| 530 | for (plain.notes) |note| { | ||
| 531 | try note.renderToWriter(ttyconf, stderr, "note", .Cyan, indent + 4); | ||
| 532 | } | ||
| 533 | }, | ||
| 534 | } | ||
| 535 | } | ||
| 536 | |||
| 537 | pub const HashContext = struct { | ||
| 538 | pub fn hash(ctx: HashContext, key: *Message) u64 { | ||
| 539 | _ = ctx; | ||
| 540 | var hasher = std.hash.Wyhash.init(0); | ||
| 541 | |||
| 542 | switch (key.*) { | ||
| 543 | .src => |src| { | ||
| 544 | hasher.update(src.msg); | ||
| 545 | hasher.update(src.src_path); | ||
| 546 | std.hash.autoHash(&hasher, src.line); | ||
| 547 | std.hash.autoHash(&hasher, src.column); | ||
| 548 | std.hash.autoHash(&hasher, src.span.main); | ||
| 549 | }, | ||
| 550 | .plain => |plain| { | ||
| 551 | hasher.update(plain.msg); | ||
| 552 | }, | ||
| 553 | } | ||
| 554 | |||
| 555 | return hasher.final(); | ||
| 556 | } | ||
| 557 | |||
| 558 | pub fn eql(ctx: HashContext, a: *Message, b: *Message) bool { | ||
| 559 | _ = ctx; | ||
| 560 | switch (a.*) { | ||
| 561 | .src => |a_src| switch (b.*) { | ||
| 562 | .src => |b_src| { | ||
| 563 | return mem.eql(u8, a_src.msg, b_src.msg) and | ||
| 564 | mem.eql(u8, a_src.src_path, b_src.src_path) and | ||
| 565 | a_src.line == b_src.line and | ||
| 566 | a_src.column == b_src.column and | ||
| 567 | a_src.span.main == b_src.span.main; | ||
| 568 | }, | ||
| 569 | .plain => return false, | ||
| 570 | }, | ||
| 571 | .plain => |a_plain| switch (b.*) { | ||
| 572 | .src => return false, | ||
| 573 | .plain => |b_plain| { | ||
| 574 | return mem.eql(u8, a_plain.msg, b_plain.msg); | ||
| 575 | }, | ||
| 576 | }, | ||
| 577 | } | ||
| 578 | } | ||
| 579 | }; | ||
| 580 | }; | ||
| 581 | |||
| 582 | pub fn deinit(self: *AllErrors, gpa: Allocator) void { | ||
| 583 | self.arena.promote(gpa).deinit(); | ||
| 584 | } | ||
| 585 | |||
| 586 | pub fn add( | ||
| 587 | module: *Module, | ||
| 588 | arena: *std.heap.ArenaAllocator, | ||
| 589 | errors: *std.ArrayList(Message), | ||
| 590 | module_err_msg: Module.ErrorMsg, | ||
| 591 | ) !void { | ||
| 592 | const allocator = arena.allocator(); | ||
| 593 | |||
| 594 | const notes_buf = try allocator.alloc(Message, module_err_msg.notes.len); | ||
| 595 | var note_i: usize = 0; | ||
| 596 | |||
| 597 | // De-duplicate error notes. The main use case in mind for this is | ||
| 598 | // too many "note: called from here" notes when eval branch quota is reached. | ||
| 599 | var seen_notes = std.HashMap( | ||
| 600 | *Message, | ||
| 601 | void, | ||
| 602 | Message.HashContext, | ||
| 603 | std.hash_map.default_max_load_percentage, | ||
| 604 | ).init(allocator); | ||
| 605 | const err_source = module_err_msg.src_loc.file_scope.getSource(module.gpa) catch |err| { | ||
| 606 | const file_path = try module_err_msg.src_loc.file_scope.fullPath(allocator); | ||
| 607 | try errors.append(.{ | ||
| 608 | .plain = .{ | ||
| 609 | .msg = try std.fmt.allocPrint(allocator, "unable to load '{s}': {s}", .{ | ||
| 610 | file_path, @errorName(err), | ||
| 611 | }), | ||
| 612 | }, | ||
| 613 | }); | ||
| 614 | return; | ||
| 615 | }; | ||
| 616 | const err_span = try module_err_msg.src_loc.span(module.gpa); | ||
| 617 | const err_loc = std.zig.findLineColumn(err_source.bytes, err_span.main); | ||
| 618 | |||
| 619 | for (module_err_msg.notes) |module_note| { | ||
| 620 | const source = try module_note.src_loc.file_scope.getSource(module.gpa); | ||
| 621 | const span = try module_note.src_loc.span(module.gpa); | ||
| 622 | const loc = std.zig.findLineColumn(source.bytes, span.main); | ||
| 623 | const file_path = try module_note.src_loc.file_scope.fullPath(allocator); | ||
| 624 | const note = &notes_buf[note_i]; | ||
| 625 | note.* = .{ | ||
| 626 | .src = .{ | ||
| 627 | .src_path = file_path, | ||
| 628 | .msg = try allocator.dupe(u8, module_note.msg), | ||
| 629 | .span = span, | ||
| 630 | .line = @intCast(u32, loc.line), | ||
| 631 | .column = @intCast(u32, loc.column), | ||
| 632 | .source_line = if (err_loc.eql(loc)) null else try allocator.dupe(u8, loc.source_line), | ||
| 633 | }, | ||
| 634 | }; | ||
| 635 | const gop = try seen_notes.getOrPut(note); | ||
| 636 | if (gop.found_existing) { | ||
| 637 | gop.key_ptr.*.incrementCount(); | ||
| 638 | } else { | ||
| 639 | note_i += 1; | ||
| 640 | } | ||
| 641 | } | ||
| 642 | |||
| 643 | const reference_trace = try allocator.alloc(Message, module_err_msg.reference_trace.len); | ||
| 644 | for (reference_trace, 0..) |*reference, i| { | ||
| 645 | const module_reference = module_err_msg.reference_trace[i]; | ||
| 646 | if (module_reference.hidden != 0) { | ||
| 647 | reference.* = .{ .plain = .{ .msg = undefined, .count = module_reference.hidden } }; | ||
| 648 | break; | ||
| 649 | } else if (module_reference.decl == null) { | ||
| 650 | reference.* = .{ .plain = .{ .msg = undefined, .count = 0 } }; | ||
| 651 | break; | ||
| 652 | } | ||
| 653 | const source = try module_reference.src_loc.file_scope.getSource(module.gpa); | ||
| 654 | const span = try module_reference.src_loc.span(module.gpa); | ||
| 655 | const loc = std.zig.findLineColumn(source.bytes, span.main); | ||
| 656 | const file_path = try module_reference.src_loc.file_scope.fullPath(allocator); | ||
| 657 | reference.* = .{ | ||
| 658 | .src = .{ | ||
| 659 | .src_path = file_path, | ||
| 660 | .msg = try allocator.dupe(u8, std.mem.sliceTo(module_reference.decl.?, 0)), | ||
| 661 | .span = span, | ||
| 662 | .line = @intCast(u32, loc.line), | ||
| 663 | .column = @intCast(u32, loc.column), | ||
| 664 | .source_line = null, | ||
| 665 | }, | ||
| 666 | }; | ||
| 667 | } | ||
| 668 | const file_path = try module_err_msg.src_loc.file_scope.fullPath(allocator); | ||
| 669 | try errors.append(.{ | ||
| 670 | .src = .{ | ||
| 671 | .src_path = file_path, | ||
| 672 | .msg = try allocator.dupe(u8, module_err_msg.msg), | ||
| 673 | .span = err_span, | ||
| 674 | .line = @intCast(u32, err_loc.line), | ||
| 675 | .column = @intCast(u32, err_loc.column), | ||
| 676 | .notes = notes_buf[0..note_i], | ||
| 677 | .reference_trace = reference_trace, | ||
| 678 | .source_line = if (module_err_msg.src_loc.lazy == .entire_file) null else try allocator.dupe(u8, err_loc.source_line), | ||
| 679 | }, | ||
| 680 | }); | ||
| 681 | } | ||
| 682 | |||
| 683 | pub fn addZir( | ||
| 684 | arena: Allocator, | ||
| 685 | errors: *std.ArrayList(Message), | ||
| 686 | file: *Module.File, | ||
| 687 | ) !void { | ||
| 688 | assert(file.zir_loaded); | ||
| 689 | assert(file.tree_loaded); | ||
| 690 | assert(file.source_loaded); | ||
| 691 | const payload_index = file.zir.extra[@enumToInt(Zir.ExtraIndex.compile_errors)]; | ||
| 692 | assert(payload_index != 0); | ||
| 693 | |||
| 694 | const header = file.zir.extraData(Zir.Inst.CompileErrors, payload_index); | ||
| 695 | const items_len = header.data.items_len; | ||
| 696 | var extra_index = header.end; | ||
| 697 | var item_i: usize = 0; | ||
| 698 | while (item_i < items_len) : (item_i += 1) { | ||
| 699 | const item = file.zir.extraData(Zir.Inst.CompileErrors.Item, extra_index); | ||
| 700 | extra_index = item.end; | ||
| 701 | const err_span = blk: { | ||
| 702 | if (item.data.node != 0) { | ||
| 703 | break :blk Module.SrcLoc.nodeToSpan(&file.tree, item.data.node); | ||
| 704 | } | ||
| 705 | const token_starts = file.tree.tokens.items(.start); | ||
| 706 | const start = token_starts[item.data.token] + item.data.byte_offset; | ||
| 707 | const end = start + @intCast(u32, file.tree.tokenSlice(item.data.token).len) - item.data.byte_offset; | ||
| 708 | break :blk Module.SrcLoc.Span{ .start = start, .end = end, .main = start }; | ||
| 709 | }; | ||
| 710 | const err_loc = std.zig.findLineColumn(file.source, err_span.main); | ||
| 711 | |||
| 712 | var notes: []Message = &[0]Message{}; | ||
| 713 | if (item.data.notes != 0) { | ||
| 714 | const block = file.zir.extraData(Zir.Inst.Block, item.data.notes); | ||
| 715 | const body = file.zir.extra[block.end..][0..block.data.body_len]; | ||
| 716 | notes = try arena.alloc(Message, body.len); | ||
| 717 | for (notes, 0..) |*note, i| { | ||
| 718 | const note_item = file.zir.extraData(Zir.Inst.CompileErrors.Item, body[i]); | ||
| 719 | const msg = file.zir.nullTerminatedString(note_item.data.msg); | ||
| 720 | const span = blk: { | ||
| 721 | if (note_item.data.node != 0) { | ||
| 722 | break :blk Module.SrcLoc.nodeToSpan(&file.tree, note_item.data.node); | ||
| 723 | } | ||
| 724 | const token_starts = file.tree.tokens.items(.start); | ||
| 725 | const start = token_starts[note_item.data.token] + note_item.data.byte_offset; | ||
| 726 | const end = start + @intCast(u32, file.tree.tokenSlice(note_item.data.token).len) - item.data.byte_offset; | ||
| 727 | break :blk Module.SrcLoc.Span{ .start = start, .end = end, .main = start }; | ||
| 728 | }; | ||
| 729 | const loc = std.zig.findLineColumn(file.source, span.main); | ||
| 730 | |||
| 731 | note.* = .{ | ||
| 732 | .src = .{ | ||
| 733 | .src_path = try file.fullPath(arena), | ||
| 734 | .msg = try arena.dupe(u8, msg), | ||
| 735 | .span = span, | ||
| 736 | .line = @intCast(u32, loc.line), | ||
| 737 | .column = @intCast(u32, loc.column), | ||
| 738 | .notes = &.{}, // TODO rework this function to be recursive | ||
| 739 | .source_line = if (loc.eql(err_loc)) null else try arena.dupe(u8, loc.source_line), | ||
| 740 | }, | ||
| 741 | }; | ||
| 742 | } | ||
| 743 | } | ||
| 744 | |||
| 745 | const msg = file.zir.nullTerminatedString(item.data.msg); | ||
| 746 | try errors.append(.{ | ||
| 747 | .src = .{ | ||
| 748 | .src_path = try file.fullPath(arena), | ||
| 749 | .msg = try arena.dupe(u8, msg), | ||
| 750 | .span = err_span, | ||
| 751 | .line = @intCast(u32, err_loc.line), | ||
| 752 | .column = @intCast(u32, err_loc.column), | ||
| 753 | .notes = notes, | ||
| 754 | .source_line = try arena.dupe(u8, err_loc.source_line), | ||
| 755 | }, | ||
| 756 | }); | ||
| 757 | } | ||
| 758 | } | ||
| 759 | |||
| 760 | fn addPlain( | ||
| 761 | arena: *std.heap.ArenaAllocator, | ||
| 762 | errors: *std.ArrayList(Message), | ||
| 763 | msg: []const u8, | ||
| 764 | ) !void { | ||
| 765 | _ = arena; | ||
| 766 | try errors.append(.{ .plain = .{ .msg = msg } }); | ||
| 767 | } | ||
| 768 | |||
| 769 | fn addPlainWithChildren( | ||
| 770 | arena: *std.heap.ArenaAllocator, | ||
| 771 | errors: *std.ArrayList(Message), | ||
| 772 | msg: []const u8, | ||
| 773 | optional_children: ?AllErrors, | ||
| 774 | ) !void { | ||
| 775 | const allocator = arena.allocator(); | ||
| 776 | const duped_msg = try allocator.dupe(u8, msg); | ||
| 777 | if (optional_children) |*children| { | ||
| 778 | try errors.append(.{ .plain = .{ | ||
| 779 | .msg = duped_msg, | ||
| 780 | .notes = try dupeList(children.list, allocator), | ||
| 781 | } }); | ||
| 782 | } else { | ||
| 783 | try errors.append(.{ .plain = .{ .msg = duped_msg } }); | ||
| 784 | } | ||
| 785 | } | ||
| 786 | |||
| 787 | fn dupeList(list: []const Message, arena: Allocator) Allocator.Error![]Message { | ||
| 788 | const duped_list = try arena.alloc(Message, list.len); | ||
| 789 | for (list, 0..) |item, i| { | ||
| 790 | duped_list[i] = switch (item) { | ||
| 791 | .src => |src| .{ .src = .{ | ||
| 792 | .msg = try arena.dupe(u8, src.msg), | ||
| 793 | .src_path = try arena.dupe(u8, src.src_path), | ||
| 794 | .line = src.line, | ||
| 795 | .column = src.column, | ||
| 796 | .span = src.span, | ||
| 797 | .source_line = if (src.source_line) |s| try arena.dupe(u8, s) else null, | ||
| 798 | .notes = try dupeList(src.notes, arena), | ||
| 799 | } }, | ||
| 800 | .plain => |plain| .{ .plain = .{ | ||
| 801 | .msg = try arena.dupe(u8, plain.msg), | ||
| 802 | .notes = try dupeList(plain.notes, arena), | ||
| 803 | } }, | ||
| 804 | }; | ||
| 805 | } | ||
| 806 | return duped_list; | ||
| 807 | } | ||
| 808 | }; | ||
| 809 | |||
| 810 | pub const Directory = Cache.Directory; | 399 | pub const Directory = Cache.Directory; |
| 811 | 400 | ||
| 812 | pub const EmitLoc = struct { | 401 | pub const EmitLoc = struct { |
| ... | @@ -2259,12 +1848,20 @@ fn cleanupTmpArtifactDirectory( | ... | @@ -2259,12 +1848,20 @@ fn cleanupTmpArtifactDirectory( |
| 2259 | } | 1848 | } |
| 2260 | } | 1849 | } |
| 2261 | 1850 | ||
| 1851 | pub fn hotCodeSwap(comp: *Compilation, prog_node: *std.Progress.Node, pid: std.ChildProcess.Id) !void { | ||
| 1852 | comp.bin_file.child_pid = pid; | ||
| 1853 | try comp.makeBinFileWritable(); | ||
| 1854 | try comp.update(prog_node); | ||
| 1855 | try comp.makeBinFileExecutable(); | ||
| 1856 | } | ||
| 1857 | |||
| 2262 | /// Detect changes to source files, perform semantic analysis, and update the output files. | 1858 | /// Detect changes to source files, perform semantic analysis, and update the output files. |
| 2263 | pub fn update(comp: *Compilation) !void { | 1859 | pub fn update(comp: *Compilation, main_progress_node: *std.Progress.Node) !void { |
| 2264 | const tracy_trace = trace(@src()); | 1860 | const tracy_trace = trace(@src()); |
| 2265 | defer tracy_trace.end(); | 1861 | defer tracy_trace.end(); |
| 2266 | 1862 | ||
| 2267 | comp.clearMiscFailures(); | 1863 | comp.clearMiscFailures(); |
| 1864 | comp.last_update_was_cache_hit = false; | ||
| 2268 | 1865 | ||
| 2269 | var man: Cache.Manifest = undefined; | 1866 | var man: Cache.Manifest = undefined; |
| 2270 | defer if (comp.whole_cache_manifest != null) man.deinit(); | 1867 | defer if (comp.whole_cache_manifest != null) man.deinit(); |
| ... | @@ -2292,6 +1889,7 @@ pub fn update(comp: *Compilation) !void { | ... | @@ -2292,6 +1889,7 @@ pub fn update(comp: *Compilation) !void { |
| 2292 | return err; | 1889 | return err; |
| 2293 | }; | 1890 | }; |
| 2294 | if (is_hit) { | 1891 | if (is_hit) { |
| 1892 | comp.last_update_was_cache_hit = true; | ||
| 2295 | log.debug("CacheMode.whole cache hit for {s}", .{comp.bin_file.options.root_name}); | 1893 | log.debug("CacheMode.whole cache hit for {s}", .{comp.bin_file.options.root_name}); |
| 2296 | const digest = man.final(); | 1894 | const digest = man.final(); |
| 2297 | 1895 | ||
| ... | @@ -2407,21 +2005,6 @@ pub fn update(comp: *Compilation) !void { | ... | @@ -2407,21 +2005,6 @@ pub fn update(comp: *Compilation) !void { |
| 2407 | } | 2005 | } |
| 2408 | } | 2006 | } |
| 2409 | 2007 | ||
| 2410 | // If the terminal is dumb, we dont want to show the user all the output. | ||
| 2411 | var progress: std.Progress = .{ .dont_print_on_dumb = true }; | ||
| 2412 | const main_progress_node = progress.start("", 0); | ||
| 2413 | defer main_progress_node.end(); | ||
| 2414 | switch (comp.color) { | ||
| 2415 | .off => { | ||
| 2416 | progress.terminal = null; | ||
| 2417 | }, | ||
| 2418 | .on => { | ||
| 2419 | progress.terminal = std.io.getStdErr(); | ||
| 2420 | progress.supports_ansi_escape_codes = true; | ||
| 2421 | }, | ||
| 2422 | .auto => {}, | ||
| 2423 | } | ||
| 2424 | |||
| 2425 | try comp.performAllTheWork(main_progress_node); | 2008 | try comp.performAllTheWork(main_progress_node); |
| 2426 | 2009 | ||
| 2427 | if (comp.bin_file.options.module) |module| { | 2010 | if (comp.bin_file.options.module) |module| { |
| ... | @@ -2891,7 +2474,7 @@ pub fn makeBinFileWritable(self: *Compilation) !void { | ... | @@ -2891,7 +2474,7 @@ pub fn makeBinFileWritable(self: *Compilation) !void { |
| 2891 | } | 2474 | } |
| 2892 | 2475 | ||
| 2893 | /// This function is temporally single-threaded. | 2476 | /// This function is temporally single-threaded. |
| 2894 | pub fn totalErrorCount(self: *Compilation) usize { | 2477 | pub fn totalErrorCount(self: *Compilation) u32 { |
| 2895 | var total: usize = self.failed_c_objects.count() + self.misc_failures.count() + | 2478 | var total: usize = self.failed_c_objects.count() + self.misc_failures.count() + |
| 2896 | @boolToInt(self.alloc_failure_occurred) + self.lld_errors.items.len; | 2479 | @boolToInt(self.alloc_failure_occurred) + self.lld_errors.items.len; |
| 2897 | 2480 | ||
| ... | @@ -2951,17 +2534,16 @@ pub fn totalErrorCount(self: *Compilation) usize { | ... | @@ -2951,17 +2534,16 @@ pub fn totalErrorCount(self: *Compilation) usize { |
| 2951 | } | 2534 | } |
| 2952 | } | 2535 | } |
| 2953 | 2536 | ||
| 2954 | return total; | 2537 | return @intCast(u32, total); |
| 2955 | } | 2538 | } |
| 2956 | 2539 | ||
| 2957 | /// This function is temporally single-threaded. | 2540 | /// This function is temporally single-threaded. |
| 2958 | pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors { | 2541 | pub fn getAllErrorsAlloc(self: *Compilation) !ErrorBundle { |
| 2959 | var arena = std.heap.ArenaAllocator.init(self.gpa); | 2542 | const gpa = self.gpa; |
| 2960 | errdefer arena.deinit(); | ||
| 2961 | const arena_allocator = arena.allocator(); | ||
| 2962 | 2543 | ||
| 2963 | var errors = std.ArrayList(AllErrors.Message).init(self.gpa); | 2544 | var bundle: ErrorBundle.Wip = undefined; |
| 2964 | defer errors.deinit(); | 2545 | try bundle.init(gpa); |
| 2546 | defer bundle.deinit(); | ||
| 2965 | 2547 | ||
| 2966 | { | 2548 | { |
| 2967 | var it = self.failed_c_objects.iterator(); | 2549 | var it = self.failed_c_objects.iterator(); |
| ... | @@ -2970,53 +2552,58 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors { | ... | @@ -2970,53 +2552,58 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors { |
| 2970 | const err_msg = entry.value_ptr.*; | 2552 | const err_msg = entry.value_ptr.*; |
| 2971 | // TODO these fields will need to be adjusted when we have proper | 2553 | // TODO these fields will need to be adjusted when we have proper |
| 2972 | // C error reporting bubbling up. | 2554 | // C error reporting bubbling up. |
| 2973 | try errors.append(.{ | 2555 | try bundle.addRootErrorMessage(.{ |
| 2974 | .src = .{ | 2556 | .msg = try bundle.printString("unable to build C object: {s}", .{err_msg.msg}), |
| 2975 | .src_path = try arena_allocator.dupe(u8, c_object.src.src_path), | 2557 | .src_loc = try bundle.addSourceLocation(.{ |
| 2976 | .msg = try std.fmt.allocPrint(arena_allocator, "unable to build C object: {s}", .{ | 2558 | .src_path = try bundle.addString(c_object.src.src_path), |
| 2977 | err_msg.msg, | 2559 | .span_start = 0, |
| 2978 | }), | 2560 | .span_main = 0, |
| 2979 | .span = .{ .start = 0, .end = 1, .main = 0 }, | 2561 | .span_end = 1, |
| 2980 | .line = err_msg.line, | 2562 | .line = err_msg.line, |
| 2981 | .column = err_msg.column, | 2563 | .column = err_msg.column, |
| 2982 | .source_line = null, // TODO | 2564 | .source_line = 0, // TODO |
| 2983 | }, | 2565 | }), |
| 2984 | }); | 2566 | }); |
| 2985 | } | 2567 | } |
| 2986 | } | 2568 | } |
| 2569 | |||
| 2987 | for (self.lld_errors.items) |lld_error| { | 2570 | for (self.lld_errors.items) |lld_error| { |
| 2988 | const notes = try arena_allocator.alloc(AllErrors.Message, lld_error.context_lines.len); | 2571 | const notes_len = @intCast(u32, lld_error.context_lines.len); |
| 2989 | for (lld_error.context_lines, 0..) |context_line, i| { | ||
| 2990 | notes[i] = .{ .plain = .{ | ||
| 2991 | .msg = try arena_allocator.dupe(u8, context_line), | ||
| 2992 | } }; | ||
| 2993 | } | ||
| 2994 | 2572 | ||
| 2995 | try errors.append(.{ | 2573 | try bundle.addRootErrorMessage(.{ |
| 2996 | .plain = .{ | 2574 | .msg = try bundle.addString(lld_error.msg), |
| 2997 | .msg = try arena_allocator.dupe(u8, lld_error.msg), | 2575 | .notes_len = notes_len, |
| 2998 | .notes = notes, | ||
| 2999 | }, | ||
| 3000 | }); | 2576 | }); |
| 2577 | const notes_start = try bundle.reserveNotes(notes_len); | ||
| 2578 | for (notes_start.., lld_error.context_lines) |note, context_line| { | ||
| 2579 | bundle.extra.items[note] = @enumToInt(bundle.addErrorMessageAssumeCapacity(.{ | ||
| 2580 | .msg = try bundle.addString(context_line), | ||
| 2581 | })); | ||
| 2582 | } | ||
| 3001 | } | 2583 | } |
| 3002 | for (self.misc_failures.values()) |*value| { | 2584 | for (self.misc_failures.values()) |*value| { |
| 3003 | try AllErrors.addPlainWithChildren(&arena, &errors, value.msg, value.children); | 2585 | try bundle.addRootErrorMessage(.{ |
| 2586 | .msg = try bundle.addString(value.msg), | ||
| 2587 | .notes_len = if (value.children) |b| b.errorMessageCount() else 0, | ||
| 2588 | }); | ||
| 2589 | if (value.children) |b| try bundle.addBundle(b); | ||
| 3004 | } | 2590 | } |
| 3005 | if (self.alloc_failure_occurred) { | 2591 | if (self.alloc_failure_occurred) { |
| 3006 | try AllErrors.addPlain(&arena, &errors, "memory allocation failure"); | 2592 | try bundle.addRootErrorMessage(.{ |
| 2593 | .msg = try bundle.addString("memory allocation failure"), | ||
| 2594 | }); | ||
| 3007 | } | 2595 | } |
| 3008 | if (self.bin_file.options.module) |module| { | 2596 | if (self.bin_file.options.module) |module| { |
| 3009 | { | 2597 | { |
| 3010 | var it = module.failed_files.iterator(); | 2598 | var it = module.failed_files.iterator(); |
| 3011 | while (it.next()) |entry| { | 2599 | while (it.next()) |entry| { |
| 3012 | if (entry.value_ptr.*) |msg| { | 2600 | if (entry.value_ptr.*) |msg| { |
| 3013 | try AllErrors.add(module, &arena, &errors, msg.*); | 2601 | try addModuleErrorMsg(&bundle, msg.*); |
| 3014 | } else { | 2602 | } else { |
| 3015 | // Must be ZIR errors. In order for ZIR errors to exist, the parsing | 2603 | // Must be ZIR errors. Note that this may include AST errors. |
| 3016 | // must have completed successfully. | 2604 | // addZirErrorMessages asserts that the tree is loaded. |
| 3017 | const tree = try entry.key_ptr.*.getTree(module.gpa); | 2605 | _ = try entry.key_ptr.*.getTree(gpa); |
| 3018 | assert(tree.errors.len == 0); | 2606 | try addZirErrorMessages(&bundle, entry.key_ptr.*); |
| 3019 | try AllErrors.addZir(arena_allocator, &errors, entry.key_ptr.*); | ||
| 3020 | } | 2607 | } |
| 3021 | } | 2608 | } |
| 3022 | } | 2609 | } |
| ... | @@ -3024,7 +2611,7 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors { | ... | @@ -3024,7 +2611,7 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors { |
| 3024 | var it = module.failed_embed_files.iterator(); | 2611 | var it = module.failed_embed_files.iterator(); |
| 3025 | while (it.next()) |entry| { | 2612 | while (it.next()) |entry| { |
| 3026 | const msg = entry.value_ptr.*; | 2613 | const msg = entry.value_ptr.*; |
| 3027 | try AllErrors.add(module, &arena, &errors, msg.*); | 2614 | try addModuleErrorMsg(&bundle, msg.*); |
| 3028 | } | 2615 | } |
| 3029 | } | 2616 | } |
| 3030 | { | 2617 | { |
| ... | @@ -3034,23 +2621,20 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors { | ... | @@ -3034,23 +2621,20 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors { |
| 3034 | // Skip errors for Decls within files that had a parse failure. | 2621 | // Skip errors for Decls within files that had a parse failure. |
| 3035 | // We'll try again once parsing succeeds. | 2622 | // We'll try again once parsing succeeds. |
| 3036 | if (decl.getFileScope().okToReportErrors()) { | 2623 | if (decl.getFileScope().okToReportErrors()) { |
| 3037 | try AllErrors.add(module, &arena, &errors, entry.value_ptr.*.*); | 2624 | try addModuleErrorMsg(&bundle, entry.value_ptr.*.*); |
| 3038 | if (module.cimport_errors.get(entry.key_ptr.*)) |cimport_errors| for (cimport_errors) |c_error| { | 2625 | if (module.cimport_errors.get(entry.key_ptr.*)) |cimport_errors| for (cimport_errors) |c_error| { |
| 3039 | if (c_error.path) |some| | 2626 | try bundle.addRootErrorMessage(.{ |
| 3040 | try errors.append(.{ | 2627 | .msg = try bundle.addString(std.mem.span(c_error.msg)), |
| 3041 | .src = .{ | 2628 | .src_loc = if (c_error.path) |some| try bundle.addSourceLocation(.{ |
| 3042 | .src_path = try arena_allocator.dupe(u8, std.mem.span(some)), | 2629 | .src_path = try bundle.addString(std.mem.span(some)), |
| 3043 | .span = .{ .start = c_error.offset, .end = c_error.offset + 1, .main = c_error.offset }, | 2630 | .span_start = c_error.offset, |
| 3044 | .msg = try arena_allocator.dupe(u8, std.mem.span(c_error.msg)), | 2631 | .span_main = c_error.offset, |
| 3045 | .line = c_error.line, | 2632 | .span_end = c_error.offset + 1, |
| 3046 | .column = c_error.column, | 2633 | .line = c_error.line, |
| 3047 | .source_line = if (c_error.source_line) |line| try arena_allocator.dupe(u8, std.mem.span(line)) else null, | 2634 | .column = c_error.column, |
| 3048 | }, | 2635 | .source_line = if (c_error.source_line) |line| try bundle.addString(std.mem.span(line)) else 0, |
| 3049 | }) | 2636 | }) else .none, |
| 3050 | else | 2637 | }); |
| 3051 | try errors.append(.{ | ||
| 3052 | .plain = .{ .msg = try arena_allocator.dupe(u8, std.mem.span(c_error.msg)) }, | ||
| 3053 | }); | ||
| 3054 | }; | 2638 | }; |
| 3055 | } | 2639 | } |
| 3056 | } | 2640 | } |
| ... | @@ -3062,45 +2646,39 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors { | ... | @@ -3062,45 +2646,39 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors { |
| 3062 | // Skip errors for Decls within files that had a parse failure. | 2646 | // Skip errors for Decls within files that had a parse failure. |
| 3063 | // We'll try again once parsing succeeds. | 2647 | // We'll try again once parsing succeeds. |
| 3064 | if (decl.getFileScope().okToReportErrors()) { | 2648 | if (decl.getFileScope().okToReportErrors()) { |
| 3065 | try AllErrors.add(module, &arena, &errors, entry.value_ptr.*.*); | 2649 | try addModuleErrorMsg(&bundle, entry.value_ptr.*.*); |
| 3066 | } | 2650 | } |
| 3067 | } | 2651 | } |
| 3068 | } | 2652 | } |
| 3069 | for (module.failed_exports.values()) |value| { | 2653 | for (module.failed_exports.values()) |value| { |
| 3070 | try AllErrors.add(module, &arena, &errors, value.*); | 2654 | try addModuleErrorMsg(&bundle, value.*); |
| 3071 | } | 2655 | } |
| 3072 | } | 2656 | } |
| 3073 | 2657 | ||
| 3074 | if (errors.items.len == 0) { | 2658 | if (bundle.root_list.items.len == 0) { |
| 3075 | if (self.link_error_flags.no_entry_point_found) { | 2659 | if (self.link_error_flags.no_entry_point_found) { |
| 3076 | try errors.append(.{ | 2660 | try bundle.addRootErrorMessage(.{ |
| 3077 | .plain = .{ | 2661 | .msg = try bundle.addString("no entry point found"), |
| 3078 | .msg = try std.fmt.allocPrint(arena_allocator, "no entry point found", .{}), | ||
| 3079 | }, | ||
| 3080 | }); | 2662 | }); |
| 3081 | } | 2663 | } |
| 3082 | } | 2664 | } |
| 3083 | 2665 | ||
| 3084 | if (self.link_error_flags.missing_libc) { | 2666 | if (self.link_error_flags.missing_libc) { |
| 3085 | const notes = try arena_allocator.create([2]AllErrors.Message); | 2667 | try bundle.addRootErrorMessage(.{ |
| 3086 | notes.* = .{ | 2668 | .msg = try bundle.addString("libc not available"), |
| 3087 | .{ .plain = .{ | 2669 | .notes_len = 2, |
| 3088 | .msg = try arena_allocator.dupe(u8, "run 'zig libc -h' to learn about libc installations"), | ||
| 3089 | } }, | ||
| 3090 | .{ .plain = .{ | ||
| 3091 | .msg = try arena_allocator.dupe(u8, "run 'zig targets' to see the targets for which zig can always provide libc"), | ||
| 3092 | } }, | ||
| 3093 | }; | ||
| 3094 | try errors.append(.{ | ||
| 3095 | .plain = .{ | ||
| 3096 | .msg = try std.fmt.allocPrint(arena_allocator, "libc not available", .{}), | ||
| 3097 | .notes = notes, | ||
| 3098 | }, | ||
| 3099 | }); | 2670 | }); |
| 2671 | const notes_start = try bundle.reserveNotes(2); | ||
| 2672 | bundle.extra.items[notes_start + 0] = @enumToInt(try bundle.addErrorMessage(.{ | ||
| 2673 | .msg = try bundle.addString("run 'zig libc -h' to learn about libc installations"), | ||
| 2674 | })); | ||
| 2675 | bundle.extra.items[notes_start + 1] = @enumToInt(try bundle.addErrorMessage(.{ | ||
| 2676 | .msg = try bundle.addString("run 'zig targets' to see the targets for which zig can always provide libc"), | ||
| 2677 | })); | ||
| 3100 | } | 2678 | } |
| 3101 | 2679 | ||
| 3102 | if (self.bin_file.options.module) |module| { | 2680 | if (self.bin_file.options.module) |module| { |
| 3103 | if (errors.items.len == 0 and module.compile_log_decls.count() != 0) { | 2681 | if (bundle.root_list.items.len == 0 and module.compile_log_decls.count() != 0) { |
| 3104 | const keys = module.compile_log_decls.keys(); | 2682 | const keys = module.compile_log_decls.keys(); |
| 3105 | const values = module.compile_log_decls.values(); | 2683 | const values = module.compile_log_decls.values(); |
| 3106 | // First one will be the error; subsequent ones will be notes. | 2684 | // First one will be the error; subsequent ones will be notes. |
| ... | @@ -3109,9 +2687,9 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors { | ... | @@ -3109,9 +2687,9 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors { |
| 3109 | const err_msg = Module.ErrorMsg{ | 2687 | const err_msg = Module.ErrorMsg{ |
| 3110 | .src_loc = src_loc, | 2688 | .src_loc = src_loc, |
| 3111 | .msg = "found compile log statement", | 2689 | .msg = "found compile log statement", |
| 3112 | .notes = try self.gpa.alloc(Module.ErrorMsg, module.compile_log_decls.count() - 1), | 2690 | .notes = try gpa.alloc(Module.ErrorMsg, module.compile_log_decls.count() - 1), |
| 3113 | }; | 2691 | }; |
| 3114 | defer self.gpa.free(err_msg.notes); | 2692 | defer gpa.free(err_msg.notes); |
| 3115 | 2693 | ||
| 3116 | for (keys[1..], 0..) |key, i| { | 2694 | for (keys[1..], 0..) |key, i| { |
| 3117 | const note_decl = module.declPtr(key); | 2695 | const note_decl = module.declPtr(key); |
| ... | @@ -3121,21 +2699,260 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors { | ... | @@ -3121,21 +2699,260 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors { |
| 3121 | }; | 2699 | }; |
| 3122 | } | 2700 | } |
| 3123 | 2701 | ||
| 3124 | try AllErrors.add(module, &arena, &errors, err_msg); | 2702 | try addModuleErrorMsg(&bundle, err_msg); |
| 2703 | } | ||
| 2704 | } | ||
| 2705 | |||
| 2706 | assert(self.totalErrorCount() == bundle.root_list.items.len); | ||
| 2707 | |||
| 2708 | const compile_log_text = if (self.bin_file.options.module) |m| m.compile_log_text.items else ""; | ||
| 2709 | return bundle.toOwnedBundle(compile_log_text); | ||
| 2710 | } | ||
| 2711 | |||
| 2712 | pub const ErrorNoteHashContext = struct { | ||
| 2713 | eb: *const ErrorBundle.Wip, | ||
| 2714 | |||
| 2715 | pub fn hash(ctx: ErrorNoteHashContext, key: ErrorBundle.ErrorMessage) u32 { | ||
| 2716 | var hasher = std.hash.Wyhash.init(0); | ||
| 2717 | const eb = ctx.eb.tmpBundle(); | ||
| 2718 | |||
| 2719 | hasher.update(eb.nullTerminatedString(key.msg)); | ||
| 2720 | if (key.src_loc != .none) { | ||
| 2721 | const src = eb.getSourceLocation(key.src_loc); | ||
| 2722 | hasher.update(eb.nullTerminatedString(src.src_path)); | ||
| 2723 | std.hash.autoHash(&hasher, src.line); | ||
| 2724 | std.hash.autoHash(&hasher, src.column); | ||
| 2725 | std.hash.autoHash(&hasher, src.span_main); | ||
| 3125 | } | 2726 | } |
| 2727 | |||
| 2728 | return @truncate(u32, hasher.final()); | ||
| 3126 | } | 2729 | } |
| 3127 | 2730 | ||
| 3128 | assert(errors.items.len == self.totalErrorCount()); | 2731 | pub fn eql( |
| 2732 | ctx: ErrorNoteHashContext, | ||
| 2733 | a: ErrorBundle.ErrorMessage, | ||
| 2734 | b: ErrorBundle.ErrorMessage, | ||
| 2735 | b_index: usize, | ||
| 2736 | ) bool { | ||
| 2737 | _ = b_index; | ||
| 2738 | const eb = ctx.eb.tmpBundle(); | ||
| 2739 | const msg_a = eb.nullTerminatedString(a.msg); | ||
| 2740 | const msg_b = eb.nullTerminatedString(b.msg); | ||
| 2741 | if (!std.mem.eql(u8, msg_a, msg_b)) return false; | ||
| 2742 | |||
| 2743 | if (a.src_loc == .none and b.src_loc == .none) return true; | ||
| 2744 | if (a.src_loc == .none or b.src_loc == .none) return false; | ||
| 2745 | const src_a = eb.getSourceLocation(a.src_loc); | ||
| 2746 | const src_b = eb.getSourceLocation(b.src_loc); | ||
| 2747 | |||
| 2748 | const src_path_a = eb.nullTerminatedString(src_a.src_path); | ||
| 2749 | const src_path_b = eb.nullTerminatedString(src_b.src_path); | ||
| 2750 | |||
| 2751 | return std.mem.eql(u8, src_path_a, src_path_b) and | ||
| 2752 | src_a.line == src_b.line and | ||
| 2753 | src_a.column == src_b.column and | ||
| 2754 | src_a.span_main == src_b.span_main; | ||
| 2755 | } | ||
| 2756 | }; | ||
| 3129 | 2757 | ||
| 3130 | return AllErrors{ | 2758 | pub fn addModuleErrorMsg(eb: *ErrorBundle.Wip, module_err_msg: Module.ErrorMsg) !void { |
| 3131 | .list = try arena_allocator.dupe(AllErrors.Message, errors.items), | 2759 | const gpa = eb.gpa; |
| 3132 | .arena = arena.state, | 2760 | const err_source = module_err_msg.src_loc.file_scope.getSource(gpa) catch |err| { |
| 2761 | const file_path = try module_err_msg.src_loc.file_scope.fullPath(gpa); | ||
| 2762 | defer gpa.free(file_path); | ||
| 2763 | try eb.addRootErrorMessage(.{ | ||
| 2764 | .msg = try eb.printString("unable to load '{s}': {s}", .{ | ||
| 2765 | file_path, @errorName(err), | ||
| 2766 | }), | ||
| 2767 | }); | ||
| 2768 | return; | ||
| 3133 | }; | 2769 | }; |
| 2770 | const err_span = try module_err_msg.src_loc.span(gpa); | ||
| 2771 | const err_loc = std.zig.findLineColumn(err_source.bytes, err_span.main); | ||
| 2772 | const file_path = try module_err_msg.src_loc.file_scope.fullPath(gpa); | ||
| 2773 | defer gpa.free(file_path); | ||
| 2774 | |||
| 2775 | var ref_traces: std.ArrayListUnmanaged(ErrorBundle.ReferenceTrace) = .{}; | ||
| 2776 | defer ref_traces.deinit(gpa); | ||
| 2777 | |||
| 2778 | for (module_err_msg.reference_trace) |module_reference| { | ||
| 2779 | if (module_reference.hidden != 0) { | ||
| 2780 | try ref_traces.append(gpa, .{ | ||
| 2781 | .decl_name = module_reference.hidden, | ||
| 2782 | .src_loc = .none, | ||
| 2783 | }); | ||
| 2784 | break; | ||
| 2785 | } else if (module_reference.decl == null) { | ||
| 2786 | try ref_traces.append(gpa, .{ | ||
| 2787 | .decl_name = 0, | ||
| 2788 | .src_loc = .none, | ||
| 2789 | }); | ||
| 2790 | break; | ||
| 2791 | } | ||
| 2792 | const source = try module_reference.src_loc.file_scope.getSource(gpa); | ||
| 2793 | const span = try module_reference.src_loc.span(gpa); | ||
| 2794 | const loc = std.zig.findLineColumn(source.bytes, span.main); | ||
| 2795 | const rt_file_path = try module_reference.src_loc.file_scope.fullPath(gpa); | ||
| 2796 | defer gpa.free(rt_file_path); | ||
| 2797 | try ref_traces.append(gpa, .{ | ||
| 2798 | .decl_name = try eb.addString(std.mem.sliceTo(module_reference.decl.?, 0)), | ||
| 2799 | .src_loc = try eb.addSourceLocation(.{ | ||
| 2800 | .src_path = try eb.addString(rt_file_path), | ||
| 2801 | .span_start = span.start, | ||
| 2802 | .span_main = span.main, | ||
| 2803 | .span_end = span.end, | ||
| 2804 | .line = @intCast(u32, loc.line), | ||
| 2805 | .column = @intCast(u32, loc.column), | ||
| 2806 | .source_line = 0, | ||
| 2807 | }), | ||
| 2808 | }); | ||
| 2809 | } | ||
| 2810 | |||
| 2811 | const src_loc = try eb.addSourceLocation(.{ | ||
| 2812 | .src_path = try eb.addString(file_path), | ||
| 2813 | .span_start = err_span.start, | ||
| 2814 | .span_main = err_span.main, | ||
| 2815 | .span_end = err_span.end, | ||
| 2816 | .line = @intCast(u32, err_loc.line), | ||
| 2817 | .column = @intCast(u32, err_loc.column), | ||
| 2818 | .source_line = if (module_err_msg.src_loc.lazy == .entire_file) | ||
| 2819 | 0 | ||
| 2820 | else | ||
| 2821 | try eb.addString(err_loc.source_line), | ||
| 2822 | .reference_trace_len = @intCast(u32, ref_traces.items.len), | ||
| 2823 | }); | ||
| 2824 | |||
| 2825 | for (ref_traces.items) |rt| { | ||
| 2826 | try eb.addReferenceTrace(rt); | ||
| 2827 | } | ||
| 2828 | |||
| 2829 | // De-duplicate error notes. The main use case in mind for this is | ||
| 2830 | // too many "note: called from here" notes when eval branch quota is reached. | ||
| 2831 | var notes: std.ArrayHashMapUnmanaged(ErrorBundle.ErrorMessage, void, ErrorNoteHashContext, true) = .{}; | ||
| 2832 | defer notes.deinit(gpa); | ||
| 2833 | |||
| 2834 | for (module_err_msg.notes) |module_note| { | ||
| 2835 | const source = try module_note.src_loc.file_scope.getSource(gpa); | ||
| 2836 | const span = try module_note.src_loc.span(gpa); | ||
| 2837 | const loc = std.zig.findLineColumn(source.bytes, span.main); | ||
| 2838 | const note_file_path = try module_note.src_loc.file_scope.fullPath(gpa); | ||
| 2839 | defer gpa.free(note_file_path); | ||
| 2840 | |||
| 2841 | const gop = try notes.getOrPutContext(gpa, .{ | ||
| 2842 | .msg = try eb.addString(module_note.msg), | ||
| 2843 | .src_loc = try eb.addSourceLocation(.{ | ||
| 2844 | .src_path = try eb.addString(note_file_path), | ||
| 2845 | .span_start = span.start, | ||
| 2846 | .span_main = span.main, | ||
| 2847 | .span_end = span.end, | ||
| 2848 | .line = @intCast(u32, loc.line), | ||
| 2849 | .column = @intCast(u32, loc.column), | ||
| 2850 | .source_line = if (err_loc.eql(loc)) 0 else try eb.addString(loc.source_line), | ||
| 2851 | }), | ||
| 2852 | }, .{ .eb = eb }); | ||
| 2853 | if (gop.found_existing) { | ||
| 2854 | gop.key_ptr.count += 1; | ||
| 2855 | } | ||
| 2856 | } | ||
| 2857 | |||
| 2858 | const notes_len = @intCast(u32, notes.entries.len); | ||
| 2859 | |||
| 2860 | try eb.addRootErrorMessage(.{ | ||
| 2861 | .msg = try eb.addString(module_err_msg.msg), | ||
| 2862 | .src_loc = src_loc, | ||
| 2863 | .notes_len = notes_len, | ||
| 2864 | }); | ||
| 2865 | |||
| 2866 | const notes_start = try eb.reserveNotes(notes_len); | ||
| 2867 | |||
| 2868 | for (notes_start.., notes.keys()) |i, note| { | ||
| 2869 | eb.extra.items[i] = @enumToInt(try eb.addErrorMessage(note)); | ||
| 2870 | } | ||
| 3134 | } | 2871 | } |
| 3135 | 2872 | ||
| 3136 | pub fn getCompileLogOutput(self: *Compilation) []const u8 { | 2873 | pub fn addZirErrorMessages(eb: *ErrorBundle.Wip, file: *Module.File) !void { |
| 3137 | const module = self.bin_file.options.module orelse return &[0]u8{}; | 2874 | assert(file.zir_loaded); |
| 3138 | return module.compile_log_text.items; | 2875 | assert(file.tree_loaded); |
| 2876 | assert(file.source_loaded); | ||
| 2877 | const payload_index = file.zir.extra[@enumToInt(Zir.ExtraIndex.compile_errors)]; | ||
| 2878 | assert(payload_index != 0); | ||
| 2879 | const gpa = eb.gpa; | ||
| 2880 | |||
| 2881 | const header = file.zir.extraData(Zir.Inst.CompileErrors, payload_index); | ||
| 2882 | const items_len = header.data.items_len; | ||
| 2883 | var extra_index = header.end; | ||
| 2884 | for (0..items_len) |_| { | ||
| 2885 | const item = file.zir.extraData(Zir.Inst.CompileErrors.Item, extra_index); | ||
| 2886 | extra_index = item.end; | ||
| 2887 | const err_span = blk: { | ||
| 2888 | if (item.data.node != 0) { | ||
| 2889 | break :blk Module.SrcLoc.nodeToSpan(&file.tree, item.data.node); | ||
| 2890 | } | ||
| 2891 | const token_starts = file.tree.tokens.items(.start); | ||
| 2892 | const start = token_starts[item.data.token] + item.data.byte_offset; | ||
| 2893 | const end = start + @intCast(u32, file.tree.tokenSlice(item.data.token).len) - item.data.byte_offset; | ||
| 2894 | break :blk Module.SrcLoc.Span{ .start = start, .end = end, .main = start }; | ||
| 2895 | }; | ||
| 2896 | const err_loc = std.zig.findLineColumn(file.source, err_span.main); | ||
| 2897 | |||
| 2898 | { | ||
| 2899 | const msg = file.zir.nullTerminatedString(item.data.msg); | ||
| 2900 | const src_path = try file.fullPath(gpa); | ||
| 2901 | defer gpa.free(src_path); | ||
| 2902 | try eb.addRootErrorMessage(.{ | ||
| 2903 | .msg = try eb.addString(msg), | ||
| 2904 | .src_loc = try eb.addSourceLocation(.{ | ||
| 2905 | .src_path = try eb.addString(src_path), | ||
| 2906 | .span_start = err_span.start, | ||
| 2907 | .span_main = err_span.main, | ||
| 2908 | .span_end = err_span.end, | ||
| 2909 | .line = @intCast(u32, err_loc.line), | ||
| 2910 | .column = @intCast(u32, err_loc.column), | ||
| 2911 | .source_line = try eb.addString(err_loc.source_line), | ||
| 2912 | }), | ||
| 2913 | .notes_len = item.data.notesLen(file.zir), | ||
| 2914 | }); | ||
| 2915 | } | ||
| 2916 | |||
| 2917 | if (item.data.notes != 0) { | ||
| 2918 | const notes_start = try eb.reserveNotes(item.data.notes); | ||
| 2919 | const block = file.zir.extraData(Zir.Inst.Block, item.data.notes); | ||
| 2920 | const body = file.zir.extra[block.end..][0..block.data.body_len]; | ||
| 2921 | for (notes_start.., body) |note_i, body_elem| { | ||
| 2922 | const note_item = file.zir.extraData(Zir.Inst.CompileErrors.Item, body_elem); | ||
| 2923 | const msg = file.zir.nullTerminatedString(note_item.data.msg); | ||
| 2924 | const span = blk: { | ||
| 2925 | if (note_item.data.node != 0) { | ||
| 2926 | break :blk Module.SrcLoc.nodeToSpan(&file.tree, note_item.data.node); | ||
| 2927 | } | ||
| 2928 | const token_starts = file.tree.tokens.items(.start); | ||
| 2929 | const start = token_starts[note_item.data.token] + note_item.data.byte_offset; | ||
| 2930 | const end = start + @intCast(u32, file.tree.tokenSlice(note_item.data.token).len) - item.data.byte_offset; | ||
| 2931 | break :blk Module.SrcLoc.Span{ .start = start, .end = end, .main = start }; | ||
| 2932 | }; | ||
| 2933 | const loc = std.zig.findLineColumn(file.source, span.main); | ||
| 2934 | const src_path = try file.fullPath(gpa); | ||
| 2935 | defer gpa.free(src_path); | ||
| 2936 | |||
| 2937 | eb.extra.items[note_i] = @enumToInt(try eb.addErrorMessage(.{ | ||
| 2938 | .msg = try eb.addString(msg), | ||
| 2939 | .src_loc = try eb.addSourceLocation(.{ | ||
| 2940 | .src_path = try eb.addString(src_path), | ||
| 2941 | .span_start = span.start, | ||
| 2942 | .span_main = span.main, | ||
| 2943 | .span_end = span.end, | ||
| 2944 | .line = @intCast(u32, loc.line), | ||
| 2945 | .column = @intCast(u32, loc.column), | ||
| 2946 | .source_line = if (loc.eql(err_loc)) | ||
| 2947 | 0 | ||
| 2948 | else | ||
| 2949 | try eb.addString(loc.source_line), | ||
| 2950 | }), | ||
| 2951 | .notes_len = 0, // TODO rework this function to be recursive | ||
| 2952 | })); | ||
| 2953 | } | ||
| 2954 | } | ||
| 2955 | } | ||
| 3139 | } | 2956 | } |
| 3140 | 2957 | ||
| 3141 | pub fn performAllTheWork( | 2958 | pub fn performAllTheWork( |
| ... | @@ -3231,11 +3048,11 @@ pub fn performAllTheWork( | ... | @@ -3231,11 +3048,11 @@ pub fn performAllTheWork( |
| 3231 | // backend, preventing anonymous Decls from being prematurely destroyed. | 3048 | // backend, preventing anonymous Decls from being prematurely destroyed. |
| 3232 | while (true) { | 3049 | while (true) { |
| 3233 | if (comp.work_queue.readItem()) |work_item| { | 3050 | if (comp.work_queue.readItem()) |work_item| { |
| 3234 | try processOneJob(comp, work_item); | 3051 | try processOneJob(comp, work_item, main_progress_node); |
| 3235 | continue; | 3052 | continue; |
| 3236 | } | 3053 | } |
| 3237 | if (comp.anon_work_queue.readItem()) |work_item| { | 3054 | if (comp.anon_work_queue.readItem()) |work_item| { |
| 3238 | try processOneJob(comp, work_item); | 3055 | try processOneJob(comp, work_item, main_progress_node); |
| 3239 | continue; | 3056 | continue; |
| 3240 | } | 3057 | } |
| 3241 | break; | 3058 | break; |
| ... | @@ -3243,16 +3060,16 @@ pub fn performAllTheWork( | ... | @@ -3243,16 +3060,16 @@ pub fn performAllTheWork( |
| 3243 | 3060 | ||
| 3244 | if (comp.job_queued_compiler_rt_lib) { | 3061 | if (comp.job_queued_compiler_rt_lib) { |
| 3245 | comp.job_queued_compiler_rt_lib = false; | 3062 | comp.job_queued_compiler_rt_lib = false; |
| 3246 | buildCompilerRtOneShot(comp, .Lib, &comp.compiler_rt_lib); | 3063 | buildCompilerRtOneShot(comp, .Lib, &comp.compiler_rt_lib, main_progress_node); |
| 3247 | } | 3064 | } |
| 3248 | 3065 | ||
| 3249 | if (comp.job_queued_compiler_rt_obj) { | 3066 | if (comp.job_queued_compiler_rt_obj) { |
| 3250 | comp.job_queued_compiler_rt_obj = false; | 3067 | comp.job_queued_compiler_rt_obj = false; |
| 3251 | buildCompilerRtOneShot(comp, .Obj, &comp.compiler_rt_obj); | 3068 | buildCompilerRtOneShot(comp, .Obj, &comp.compiler_rt_obj, main_progress_node); |
| 3252 | } | 3069 | } |
| 3253 | } | 3070 | } |
| 3254 | 3071 | ||
| 3255 | fn processOneJob(comp: *Compilation, job: Job) !void { | 3072 | fn processOneJob(comp: *Compilation, job: Job, prog_node: *std.Progress.Node) !void { |
| 3256 | switch (job) { | 3073 | switch (job) { |
| 3257 | .codegen_decl => |decl_index| { | 3074 | .codegen_decl => |decl_index| { |
| 3258 | const module = comp.bin_file.options.module.?; | 3075 | const module = comp.bin_file.options.module.?; |
| ... | @@ -3404,7 +3221,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { | ... | @@ -3404,7 +3221,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { |
| 3404 | const named_frame = tracy.namedFrame("glibc_crt_file"); | 3221 | const named_frame = tracy.namedFrame("glibc_crt_file"); |
| 3405 | defer named_frame.end(); | 3222 | defer named_frame.end(); |
| 3406 | 3223 | ||
| 3407 | glibc.buildCRTFile(comp, crt_file) catch |err| { | 3224 | glibc.buildCRTFile(comp, crt_file, prog_node) catch |err| { |
| 3408 | // TODO Surface more error details. | 3225 | // TODO Surface more error details. |
| 3409 | comp.lockAndSetMiscFailure(.glibc_crt_file, "unable to build glibc CRT file: {s}", .{ | 3226 | comp.lockAndSetMiscFailure(.glibc_crt_file, "unable to build glibc CRT file: {s}", .{ |
| 3410 | @errorName(err), | 3227 | @errorName(err), |
| ... | @@ -3415,7 +3232,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { | ... | @@ -3415,7 +3232,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { |
| 3415 | const named_frame = tracy.namedFrame("glibc_shared_objects"); | 3232 | const named_frame = tracy.namedFrame("glibc_shared_objects"); |
| 3416 | defer named_frame.end(); | 3233 | defer named_frame.end(); |
| 3417 | 3234 | ||
| 3418 | glibc.buildSharedObjects(comp) catch |err| { | 3235 | glibc.buildSharedObjects(comp, prog_node) catch |err| { |
| 3419 | // TODO Surface more error details. | 3236 | // TODO Surface more error details. |
| 3420 | comp.lockAndSetMiscFailure( | 3237 | comp.lockAndSetMiscFailure( |
| 3421 | .glibc_shared_objects, | 3238 | .glibc_shared_objects, |
| ... | @@ -3428,7 +3245,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { | ... | @@ -3428,7 +3245,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { |
| 3428 | const named_frame = tracy.namedFrame("musl_crt_file"); | 3245 | const named_frame = tracy.namedFrame("musl_crt_file"); |
| 3429 | defer named_frame.end(); | 3246 | defer named_frame.end(); |
| 3430 | 3247 | ||
| 3431 | musl.buildCRTFile(comp, crt_file) catch |err| { | 3248 | musl.buildCRTFile(comp, crt_file, prog_node) catch |err| { |
| 3432 | // TODO Surface more error details. | 3249 | // TODO Surface more error details. |
| 3433 | comp.lockAndSetMiscFailure( | 3250 | comp.lockAndSetMiscFailure( |
| 3434 | .musl_crt_file, | 3251 | .musl_crt_file, |
| ... | @@ -3441,7 +3258,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { | ... | @@ -3441,7 +3258,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { |
| 3441 | const named_frame = tracy.namedFrame("mingw_crt_file"); | 3258 | const named_frame = tracy.namedFrame("mingw_crt_file"); |
| 3442 | defer named_frame.end(); | 3259 | defer named_frame.end(); |
| 3443 | 3260 | ||
| 3444 | mingw.buildCRTFile(comp, crt_file) catch |err| { | 3261 | mingw.buildCRTFile(comp, crt_file, prog_node) catch |err| { |
| 3445 | // TODO Surface more error details. | 3262 | // TODO Surface more error details. |
| 3446 | comp.lockAndSetMiscFailure( | 3263 | comp.lockAndSetMiscFailure( |
| 3447 | .mingw_crt_file, | 3264 | .mingw_crt_file, |
| ... | @@ -3468,7 +3285,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { | ... | @@ -3468,7 +3285,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { |
| 3468 | const named_frame = tracy.namedFrame("libunwind"); | 3285 | const named_frame = tracy.namedFrame("libunwind"); |
| 3469 | defer named_frame.end(); | 3286 | defer named_frame.end(); |
| 3470 | 3287 | ||
| 3471 | libunwind.buildStaticLib(comp) catch |err| { | 3288 | libunwind.buildStaticLib(comp, prog_node) catch |err| { |
| 3472 | // TODO Surface more error details. | 3289 | // TODO Surface more error details. |
| 3473 | comp.lockAndSetMiscFailure( | 3290 | comp.lockAndSetMiscFailure( |
| 3474 | .libunwind, | 3291 | .libunwind, |
| ... | @@ -3481,7 +3298,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { | ... | @@ -3481,7 +3298,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { |
| 3481 | const named_frame = tracy.namedFrame("libcxx"); | 3298 | const named_frame = tracy.namedFrame("libcxx"); |
| 3482 | defer named_frame.end(); | 3299 | defer named_frame.end(); |
| 3483 | 3300 | ||
| 3484 | libcxx.buildLibCXX(comp) catch |err| { | 3301 | libcxx.buildLibCXX(comp, prog_node) catch |err| { |
| 3485 | // TODO Surface more error details. | 3302 | // TODO Surface more error details. |
| 3486 | comp.lockAndSetMiscFailure( | 3303 | comp.lockAndSetMiscFailure( |
| 3487 | .libcxx, | 3304 | .libcxx, |
| ... | @@ -3494,7 +3311,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { | ... | @@ -3494,7 +3311,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { |
| 3494 | const named_frame = tracy.namedFrame("libcxxabi"); | 3311 | const named_frame = tracy.namedFrame("libcxxabi"); |
| 3495 | defer named_frame.end(); | 3312 | defer named_frame.end(); |
| 3496 | 3313 | ||
| 3497 | libcxx.buildLibCXXABI(comp) catch |err| { | 3314 | libcxx.buildLibCXXABI(comp, prog_node) catch |err| { |
| 3498 | // TODO Surface more error details. | 3315 | // TODO Surface more error details. |
| 3499 | comp.lockAndSetMiscFailure( | 3316 | comp.lockAndSetMiscFailure( |
| 3500 | .libcxxabi, | 3317 | .libcxxabi, |
| ... | @@ -3507,7 +3324,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { | ... | @@ -3507,7 +3324,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { |
| 3507 | const named_frame = tracy.namedFrame("libtsan"); | 3324 | const named_frame = tracy.namedFrame("libtsan"); |
| 3508 | defer named_frame.end(); | 3325 | defer named_frame.end(); |
| 3509 | 3326 | ||
| 3510 | libtsan.buildTsan(comp) catch |err| { | 3327 | libtsan.buildTsan(comp, prog_node) catch |err| { |
| 3511 | // TODO Surface more error details. | 3328 | // TODO Surface more error details. |
| 3512 | comp.lockAndSetMiscFailure( | 3329 | comp.lockAndSetMiscFailure( |
| 3513 | .libtsan, | 3330 | .libtsan, |
| ... | @@ -3520,7 +3337,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { | ... | @@ -3520,7 +3337,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { |
| 3520 | const named_frame = tracy.namedFrame("wasi_libc_crt_file"); | 3337 | const named_frame = tracy.namedFrame("wasi_libc_crt_file"); |
| 3521 | defer named_frame.end(); | 3338 | defer named_frame.end(); |
| 3522 | 3339 | ||
| 3523 | wasi_libc.buildCRTFile(comp, crt_file) catch |err| { | 3340 | wasi_libc.buildCRTFile(comp, crt_file, prog_node) catch |err| { |
| 3524 | // TODO Surface more error details. | 3341 | // TODO Surface more error details. |
| 3525 | comp.lockAndSetMiscFailure( | 3342 | comp.lockAndSetMiscFailure( |
| 3526 | .wasi_libc_crt_file, | 3343 | .wasi_libc_crt_file, |
| ... | @@ -3538,6 +3355,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { | ... | @@ -3538,6 +3355,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { |
| 3538 | .Lib, | 3355 | .Lib, |
| 3539 | &comp.libssp_static_lib, | 3356 | &comp.libssp_static_lib, |
| 3540 | .libssp, | 3357 | .libssp, |
| 3358 | prog_node, | ||
| 3541 | ) catch |err| switch (err) { | 3359 | ) catch |err| switch (err) { |
| 3542 | error.OutOfMemory => return error.OutOfMemory, | 3360 | error.OutOfMemory => return error.OutOfMemory, |
| 3543 | error.SubCompilationFailed => return, // error reported already | 3361 | error.SubCompilationFailed => return, // error reported already |
| ... | @@ -3557,6 +3375,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { | ... | @@ -3557,6 +3375,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { |
| 3557 | .Lib, | 3375 | .Lib, |
| 3558 | &comp.libc_static_lib, | 3376 | &comp.libc_static_lib, |
| 3559 | .zig_libc, | 3377 | .zig_libc, |
| 3378 | prog_node, | ||
| 3560 | ) catch |err| switch (err) { | 3379 | ) catch |err| switch (err) { |
| 3561 | error.OutOfMemory => return error.OutOfMemory, | 3380 | error.OutOfMemory => return error.OutOfMemory, |
| 3562 | error.SubCompilationFailed => return, // error reported already | 3381 | error.SubCompilationFailed => return, // error reported already |
| ... | @@ -3897,8 +3716,15 @@ fn buildCompilerRtOneShot( | ... | @@ -3897,8 +3716,15 @@ fn buildCompilerRtOneShot( |
| 3897 | comp: *Compilation, | 3716 | comp: *Compilation, |
| 3898 | output_mode: std.builtin.OutputMode, | 3717 | output_mode: std.builtin.OutputMode, |
| 3899 | out: *?CRTFile, | 3718 | out: *?CRTFile, |
| 3719 | prog_node: *std.Progress.Node, | ||
| 3900 | ) void { | 3720 | ) void { |
| 3901 | comp.buildOutputFromZig("compiler_rt.zig", output_mode, out, .compiler_rt) catch |err| switch (err) { | 3721 | comp.buildOutputFromZig( |
| 3722 | "compiler_rt.zig", | ||
| 3723 | output_mode, | ||
| 3724 | out, | ||
| 3725 | .compiler_rt, | ||
| 3726 | prog_node, | ||
| 3727 | ) catch |err| switch (err) { | ||
| 3902 | error.SubCompilationFailed => return, // error reported already | 3728 | error.SubCompilationFailed => return, // error reported already |
| 3903 | else => comp.lockAndSetMiscFailure( | 3729 | else => comp.lockAndSetMiscFailure( |
| 3904 | .compiler_rt, | 3730 | .compiler_rt, |
| ... | @@ -5230,7 +5056,8 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: Allocator) Alloca | ... | @@ -5230,7 +5056,8 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: Allocator) Alloca |
| 5230 | \\const std = @import("std"); | 5056 | \\const std = @import("std"); |
| 5231 | \\/// Zig version. When writing code that supports multiple versions of Zig, prefer | 5057 | \\/// Zig version. When writing code that supports multiple versions of Zig, prefer |
| 5232 | \\/// feature detection (i.e. with `@hasDecl` or `@hasField`) over version checks. | 5058 | \\/// feature detection (i.e. with `@hasDecl` or `@hasField`) over version checks. |
| 5233 | \\pub const zig_version = std.SemanticVersion.parse("{s}") catch unreachable; | 5059 | \\pub const zig_version = std.SemanticVersion.parse(zig_version_string) catch unreachable; |
| 5060 | \\pub const zig_version_string = "{s}"; | ||
| 5234 | \\pub const zig_backend = std.builtin.CompilerBackend.{}; | 5061 | \\pub const zig_backend = std.builtin.CompilerBackend.{}; |
| 5235 | \\ | 5062 | \\ |
| 5236 | \\pub const output_mode = std.builtin.OutputMode.{}; | 5063 | \\pub const output_mode = std.builtin.OutputMode.{}; |
| ... | @@ -5417,34 +5244,36 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: Allocator) Alloca | ... | @@ -5417,34 +5244,36 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: Allocator) Alloca |
| 5417 | return buffer.toOwnedSliceSentinel(0); | 5244 | return buffer.toOwnedSliceSentinel(0); |
| 5418 | } | 5245 | } |
| 5419 | 5246 | ||
| 5420 | pub fn updateSubCompilation(sub_compilation: *Compilation) !void { | 5247 | pub fn updateSubCompilation( |
| 5421 | try sub_compilation.update(); | 5248 | parent_comp: *Compilation, |
| 5422 | 5249 | sub_comp: *Compilation, | |
| 5423 | // Look for compilation errors in this sub_compilation | 5250 | misc_task: MiscTask, |
| 5424 | // TODO instead of logging these errors, handle them in the callsites | 5251 | prog_node: *std.Progress.Node, |
| 5425 | // of updateSubCompilation and attach them as sub-errors, properly | 5252 | ) !void { |
| 5426 | // surfacing the errors. You can see an example of this already | 5253 | { |
| 5427 | // done inside buildOutputFromZig. | 5254 | var sub_node = prog_node.start(@tagName(misc_task), 0); |
| 5428 | var errors = try sub_compilation.getAllErrorsAlloc(); | 5255 | sub_node.activate(); |
| 5429 | defer errors.deinit(sub_compilation.gpa); | 5256 | defer sub_node.end(); |
| 5430 | 5257 | ||
| 5431 | if (errors.list.len != 0) { | 5258 | try sub_comp.update(prog_node); |
| 5432 | for (errors.list) |full_err_msg| { | 5259 | } |
| 5433 | switch (full_err_msg) { | 5260 | |
| 5434 | .src => |src| { | 5261 | // Look for compilation errors in this sub compilation |
| 5435 | log.err("{s}:{d}:{d}: {s}", .{ | 5262 | const gpa = parent_comp.gpa; |
| 5436 | src.src_path, | 5263 | var keep_errors = false; |
| 5437 | src.line + 1, | 5264 | var errors = try sub_comp.getAllErrorsAlloc(); |
| 5438 | src.column + 1, | 5265 | defer if (!keep_errors) errors.deinit(gpa); |
| 5439 | src.msg, | 5266 | |
| 5440 | }); | 5267 | if (errors.errorMessageCount() > 0) { |
| 5441 | }, | 5268 | try parent_comp.misc_failures.ensureUnusedCapacity(gpa, 1); |
| 5442 | .plain => |plain| { | 5269 | parent_comp.misc_failures.putAssumeCapacityNoClobber(misc_task, .{ |
| 5443 | log.err("{s}", .{plain.msg}); | 5270 | .msg = try std.fmt.allocPrint(gpa, "sub-compilation of {s} failed", .{ |
| 5444 | }, | 5271 | @tagName(misc_task), |
| 5445 | } | 5272 | }), |
| 5446 | } | 5273 | .children = errors, |
| 5447 | return error.BuildingLibCObjectFailed; | 5274 | }); |
| 5275 | keep_errors = true; | ||
| 5276 | return error.SubCompilationFailed; | ||
| 5448 | } | 5277 | } |
| 5449 | } | 5278 | } |
| 5450 | 5279 | ||
| ... | @@ -5454,6 +5283,7 @@ fn buildOutputFromZig( | ... | @@ -5454,6 +5283,7 @@ fn buildOutputFromZig( |
| 5454 | output_mode: std.builtin.OutputMode, | 5283 | output_mode: std.builtin.OutputMode, |
| 5455 | out: *?CRTFile, | 5284 | out: *?CRTFile, |
| 5456 | misc_task_tag: MiscTask, | 5285 | misc_task_tag: MiscTask, |
| 5286 | prog_node: *std.Progress.Node, | ||
| 5457 | ) !void { | 5287 | ) !void { |
| 5458 | const tracy_trace = trace(@src()); | 5288 | const tracy_trace = trace(@src()); |
| 5459 | defer tracy_trace.end(); | 5289 | defer tracy_trace.end(); |
| ... | @@ -5520,23 +5350,7 @@ fn buildOutputFromZig( | ... | @@ -5520,23 +5350,7 @@ fn buildOutputFromZig( |
| 5520 | }); | 5350 | }); |
| 5521 | defer sub_compilation.destroy(); | 5351 | defer sub_compilation.destroy(); |
| 5522 | 5352 | ||
| 5523 | try sub_compilation.update(); | 5353 | try comp.updateSubCompilation(sub_compilation, misc_task_tag, prog_node); |
| 5524 | // Look for compilation errors in this sub_compilation. | ||
| 5525 | var keep_errors = false; | ||
| 5526 | var errors = try sub_compilation.getAllErrorsAlloc(); | ||
| 5527 | defer if (!keep_errors) errors.deinit(sub_compilation.gpa); | ||
| 5528 | |||
| 5529 | if (errors.list.len != 0) { | ||
| 5530 | try comp.misc_failures.ensureUnusedCapacity(comp.gpa, 1); | ||
| 5531 | comp.misc_failures.putAssumeCapacityNoClobber(misc_task_tag, .{ | ||
| 5532 | .msg = try std.fmt.allocPrint(comp.gpa, "sub-compilation of {s} failed", .{ | ||
| 5533 | @tagName(misc_task_tag), | ||
| 5534 | }), | ||
| 5535 | .children = errors, | ||
| 5536 | }); | ||
| 5537 | keep_errors = true; | ||
| 5538 | return error.SubCompilationFailed; | ||
| 5539 | } | ||
| 5540 | 5354 | ||
| 5541 | assert(out.* == null); | 5355 | assert(out.* == null); |
| 5542 | out.* = Compilation.CRTFile{ | 5356 | out.* = Compilation.CRTFile{ |
| ... | @@ -5551,6 +5365,8 @@ pub fn build_crt_file( | ... | @@ -5551,6 +5365,8 @@ pub fn build_crt_file( |
| 5551 | comp: *Compilation, | 5365 | comp: *Compilation, |
| 5552 | root_name: []const u8, | 5366 | root_name: []const u8, |
| 5553 | output_mode: std.builtin.OutputMode, | 5367 | output_mode: std.builtin.OutputMode, |
| 5368 | misc_task_tag: MiscTask, | ||
| 5369 | prog_node: *std.Progress.Node, | ||
| 5554 | c_source_files: []const Compilation.CSourceFile, | 5370 | c_source_files: []const Compilation.CSourceFile, |
| 5555 | ) !void { | 5371 | ) !void { |
| 5556 | const tracy_trace = trace(@src()); | 5372 | const tracy_trace = trace(@src()); |
| ... | @@ -5611,7 +5427,7 @@ pub fn build_crt_file( | ... | @@ -5611,7 +5427,7 @@ pub fn build_crt_file( |
| 5611 | }); | 5427 | }); |
| 5612 | defer sub_compilation.destroy(); | 5428 | defer sub_compilation.destroy(); |
| 5613 | 5429 | ||
| 5614 | try sub_compilation.updateSubCompilation(); | 5430 | try comp.updateSubCompilation(sub_compilation, misc_task_tag, prog_node); |
| 5615 | 5431 | ||
| 5616 | try comp.crt_files.ensureUnusedCapacity(comp.gpa, 1); | 5432 | try comp.crt_files.ensureUnusedCapacity(comp.gpa, 1); |
| 5617 | 5433 |
src/Module.zig+4-60| ... | @@ -3756,67 +3756,9 @@ pub fn astGenFile(mod: *Module, file: *File) !void { | ... | @@ -3756,67 +3756,9 @@ pub fn astGenFile(mod: *Module, file: *File) !void { |
| 3756 | file.source_loaded = true; | 3756 | file.source_loaded = true; |
| 3757 | 3757 | ||
| 3758 | file.tree = try Ast.parse(gpa, source, .zig); | 3758 | file.tree = try Ast.parse(gpa, source, .zig); |
| 3759 | defer if (!file.tree_loaded) file.tree.deinit(gpa); | ||
| 3760 | |||
| 3761 | if (file.tree.errors.len != 0) { | ||
| 3762 | const parse_err = file.tree.errors[0]; | ||
| 3763 | |||
| 3764 | var msg = std.ArrayList(u8).init(gpa); | ||
| 3765 | defer msg.deinit(); | ||
| 3766 | |||
| 3767 | const token_starts = file.tree.tokens.items(.start); | ||
| 3768 | const token_tags = file.tree.tokens.items(.tag); | ||
| 3769 | |||
| 3770 | const extra_offset = file.tree.errorOffset(parse_err); | ||
| 3771 | try file.tree.renderError(parse_err, msg.writer()); | ||
| 3772 | const err_msg = try gpa.create(ErrorMsg); | ||
| 3773 | err_msg.* = .{ | ||
| 3774 | .src_loc = .{ | ||
| 3775 | .file_scope = file, | ||
| 3776 | .parent_decl_node = 0, | ||
| 3777 | .lazy = if (extra_offset == 0) .{ | ||
| 3778 | .token_abs = parse_err.token, | ||
| 3779 | } else .{ | ||
| 3780 | .byte_abs = token_starts[parse_err.token] + extra_offset, | ||
| 3781 | }, | ||
| 3782 | }, | ||
| 3783 | .msg = try msg.toOwnedSlice(), | ||
| 3784 | }; | ||
| 3785 | if (token_tags[parse_err.token + @boolToInt(parse_err.token_is_prev)] == .invalid) { | ||
| 3786 | const bad_off = @intCast(u32, file.tree.tokenSlice(parse_err.token + @boolToInt(parse_err.token_is_prev)).len); | ||
| 3787 | const byte_abs = token_starts[parse_err.token + @boolToInt(parse_err.token_is_prev)] + bad_off; | ||
| 3788 | try mod.errNoteNonLazy(.{ | ||
| 3789 | .file_scope = file, | ||
| 3790 | .parent_decl_node = 0, | ||
| 3791 | .lazy = .{ .byte_abs = byte_abs }, | ||
| 3792 | }, err_msg, "invalid byte: '{'}'", .{std.zig.fmtEscapes(source[byte_abs..][0..1])}); | ||
| 3793 | } | ||
| 3794 | |||
| 3795 | for (file.tree.errors[1..]) |note| { | ||
| 3796 | if (!note.is_note) break; | ||
| 3797 | |||
| 3798 | try file.tree.renderError(note, msg.writer()); | ||
| 3799 | err_msg.notes = try mod.gpa.realloc(err_msg.notes, err_msg.notes.len + 1); | ||
| 3800 | err_msg.notes[err_msg.notes.len - 1] = .{ | ||
| 3801 | .src_loc = .{ | ||
| 3802 | .file_scope = file, | ||
| 3803 | .parent_decl_node = 0, | ||
| 3804 | .lazy = .{ .token_abs = note.token }, | ||
| 3805 | }, | ||
| 3806 | .msg = try msg.toOwnedSlice(), | ||
| 3807 | }; | ||
| 3808 | } | ||
| 3809 | |||
| 3810 | { | ||
| 3811 | comp.mutex.lock(); | ||
| 3812 | defer comp.mutex.unlock(); | ||
| 3813 | try mod.failed_files.putNoClobber(gpa, file, err_msg); | ||
| 3814 | } | ||
| 3815 | file.status = .parse_failure; | ||
| 3816 | return error.AnalysisFail; | ||
| 3817 | } | ||
| 3818 | file.tree_loaded = true; | 3759 | file.tree_loaded = true; |
| 3819 | 3760 | ||
| 3761 | // Any potential AST errors are converted to ZIR errors here. | ||
| 3820 | file.zir = try AstGen.generate(gpa, file.tree); | 3762 | file.zir = try AstGen.generate(gpa, file.tree); |
| 3821 | file.zir_loaded = true; | 3763 | file.zir_loaded = true; |
| 3822 | file.status = .success_zir; | 3764 | file.status = .success_zir; |
| ... | @@ -3925,6 +3867,9 @@ fn updateZirRefs(mod: *Module, file: *File, old_zir: Zir) !void { | ... | @@ -3925,6 +3867,9 @@ fn updateZirRefs(mod: *Module, file: *File, old_zir: Zir) !void { |
| 3925 | const gpa = mod.gpa; | 3867 | const gpa = mod.gpa; |
| 3926 | const new_zir = file.zir; | 3868 | const new_zir = file.zir; |
| 3927 | 3869 | ||
| 3870 | // The root decl will be null if the previous ZIR had AST errors. | ||
| 3871 | const root_decl = file.root_decl.unwrap() orelse return; | ||
| 3872 | |||
| 3928 | // Maps from old ZIR to new ZIR, struct_decl, enum_decl, etc. Any instruction which | 3873 | // Maps from old ZIR to new ZIR, struct_decl, enum_decl, etc. Any instruction which |
| 3929 | // creates a namespace, gets mapped from old to new here. | 3874 | // creates a namespace, gets mapped from old to new here. |
| 3930 | var inst_map: std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .{}; | 3875 | var inst_map: std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .{}; |
| ... | @@ -3942,7 +3887,6 @@ fn updateZirRefs(mod: *Module, file: *File, old_zir: Zir) !void { | ... | @@ -3942,7 +3887,6 @@ fn updateZirRefs(mod: *Module, file: *File, old_zir: Zir) !void { |
| 3942 | var decl_stack: ArrayListUnmanaged(Decl.Index) = .{}; | 3887 | var decl_stack: ArrayListUnmanaged(Decl.Index) = .{}; |
| 3943 | defer decl_stack.deinit(gpa); | 3888 | defer decl_stack.deinit(gpa); |
| 3944 | 3889 | ||
| 3945 | const root_decl = file.root_decl.unwrap().?; | ||
| 3946 | try decl_stack.append(gpa, root_decl); | 3890 | try decl_stack.append(gpa, root_decl); |
| 3947 | 3891 | ||
| 3948 | file.deleted_decls.clearRetainingCapacity(); | 3892 | file.deleted_decls.clearRetainingCapacity(); |
src/Package.zig+53-57| ... | @@ -8,11 +8,11 @@ const Allocator = mem.Allocator; | ... | @@ -8,11 +8,11 @@ const Allocator = mem.Allocator; |
| 8 | const assert = std.debug.assert; | 8 | const assert = std.debug.assert; |
| 9 | const log = std.log.scoped(.package); | 9 | const log = std.log.scoped(.package); |
| 10 | const main = @import("main.zig"); | 10 | const main = @import("main.zig"); |
| 11 | const ThreadPool = std.Thread.Pool; | ||
| 12 | const WaitGroup = std.Thread.WaitGroup; | ||
| 11 | 13 | ||
| 12 | const Compilation = @import("Compilation.zig"); | 14 | const Compilation = @import("Compilation.zig"); |
| 13 | const Module = @import("Module.zig"); | 15 | const Module = @import("Module.zig"); |
| 14 | const ThreadPool = @import("ThreadPool.zig"); | ||
| 15 | const WaitGroup = @import("WaitGroup.zig"); | ||
| 16 | const Cache = std.Build.Cache; | 16 | const Cache = std.Build.Cache; |
| 17 | const build_options = @import("build_options"); | 17 | const build_options = @import("build_options"); |
| 18 | const Manifest = @import("Manifest.zig"); | 18 | const Manifest = @import("Manifest.zig"); |
| ... | @@ -225,7 +225,7 @@ pub fn fetchAndAddDependencies( | ... | @@ -225,7 +225,7 @@ pub fn fetchAndAddDependencies( |
| 225 | dependencies_source: *std.ArrayList(u8), | 225 | dependencies_source: *std.ArrayList(u8), |
| 226 | build_roots_source: *std.ArrayList(u8), | 226 | build_roots_source: *std.ArrayList(u8), |
| 227 | name_prefix: []const u8, | 227 | name_prefix: []const u8, |
| 228 | color: main.Color, | 228 | error_bundle: *std.zig.ErrorBundle.Wip, |
| 229 | all_modules: *AllModules, | 229 | all_modules: *AllModules, |
| 230 | ) !void { | 230 | ) !void { |
| 231 | const max_bytes = 10 * 1024 * 1024; | 231 | const max_bytes = 10 * 1024 * 1024; |
| ... | @@ -250,7 +250,7 @@ pub fn fetchAndAddDependencies( | ... | @@ -250,7 +250,7 @@ pub fn fetchAndAddDependencies( |
| 250 | 250 | ||
| 251 | if (ast.errors.len > 0) { | 251 | if (ast.errors.len > 0) { |
| 252 | const file_path = try directory.join(arena, &.{Manifest.basename}); | 252 | const file_path = try directory.join(arena, &.{Manifest.basename}); |
| 253 | try main.printErrsMsgToStdErr(gpa, arena, ast, file_path, color); | 253 | try main.putAstErrorsIntoBundle(gpa, ast, file_path, error_bundle); |
| 254 | return error.PackageFetchFailed; | 254 | return error.PackageFetchFailed; |
| 255 | } | 255 | } |
| 256 | 256 | ||
| ... | @@ -258,14 +258,9 @@ pub fn fetchAndAddDependencies( | ... | @@ -258,14 +258,9 @@ pub fn fetchAndAddDependencies( |
| 258 | defer manifest.deinit(gpa); | 258 | defer manifest.deinit(gpa); |
| 259 | 259 | ||
| 260 | if (manifest.errors.len > 0) { | 260 | if (manifest.errors.len > 0) { |
| 261 | const ttyconf: std.debug.TTY.Config = switch (color) { | ||
| 262 | .auto => std.debug.detectTTYConfig(std.io.getStdErr()), | ||
| 263 | .on => .escape_codes, | ||
| 264 | .off => .no_color, | ||
| 265 | }; | ||
| 266 | const file_path = try directory.join(arena, &.{Manifest.basename}); | 261 | const file_path = try directory.join(arena, &.{Manifest.basename}); |
| 267 | for (manifest.errors) |msg| { | 262 | for (manifest.errors) |msg| { |
| 268 | Report.renderErrorMessage(ast, file_path, ttyconf, msg, &.{}); | 263 | try Report.addErrorMessage(ast, file_path, error_bundle, 0, msg); |
| 269 | } | 264 | } |
| 270 | return error.PackageFetchFailed; | 265 | return error.PackageFetchFailed; |
| 271 | } | 266 | } |
| ... | @@ -273,8 +268,7 @@ pub fn fetchAndAddDependencies( | ... | @@ -273,8 +268,7 @@ pub fn fetchAndAddDependencies( |
| 273 | const report: Report = .{ | 268 | const report: Report = .{ |
| 274 | .ast = &ast, | 269 | .ast = &ast, |
| 275 | .directory = directory, | 270 | .directory = directory, |
| 276 | .color = color, | 271 | .error_bundle = error_bundle, |
| 277 | .arena = arena, | ||
| 278 | }; | 272 | }; |
| 279 | 273 | ||
| 280 | var any_error = false; | 274 | var any_error = false; |
| ... | @@ -307,7 +301,7 @@ pub fn fetchAndAddDependencies( | ... | @@ -307,7 +301,7 @@ pub fn fetchAndAddDependencies( |
| 307 | dependencies_source, | 301 | dependencies_source, |
| 308 | build_roots_source, | 302 | build_roots_source, |
| 309 | sub_prefix, | 303 | sub_prefix, |
| 310 | color, | 304 | error_bundle, |
| 311 | all_modules, | 305 | all_modules, |
| 312 | ); | 306 | ); |
| 313 | 307 | ||
| ... | @@ -350,8 +344,7 @@ pub fn createFilePkg( | ... | @@ -350,8 +344,7 @@ pub fn createFilePkg( |
| 350 | const Report = struct { | 344 | const Report = struct { |
| 351 | ast: *const std.zig.Ast, | 345 | ast: *const std.zig.Ast, |
| 352 | directory: Compilation.Directory, | 346 | directory: Compilation.Directory, |
| 353 | color: main.Color, | 347 | error_bundle: *std.zig.ErrorBundle.Wip, |
| 354 | arena: Allocator, | ||
| 355 | 348 | ||
| 356 | fn fail( | 349 | fn fail( |
| 357 | report: Report, | 350 | report: Report, |
| ... | @@ -359,52 +352,46 @@ const Report = struct { | ... | @@ -359,52 +352,46 @@ const Report = struct { |
| 359 | comptime fmt_string: []const u8, | 352 | comptime fmt_string: []const u8, |
| 360 | fmt_args: anytype, | 353 | fmt_args: anytype, |
| 361 | ) error{ PackageFetchFailed, OutOfMemory } { | 354 | ) error{ PackageFetchFailed, OutOfMemory } { |
| 362 | return failWithNotes(report, &.{}, tok, fmt_string, fmt_args); | 355 | const gpa = report.error_bundle.gpa; |
| 363 | } | ||
| 364 | 356 | ||
| 365 | fn failWithNotes( | 357 | const file_path = try report.directory.join(gpa, &.{Manifest.basename}); |
| 366 | report: Report, | 358 | defer gpa.free(file_path); |
| 367 | notes: []const Compilation.AllErrors.Message, | 359 | |
| 368 | tok: std.zig.Ast.TokenIndex, | 360 | const msg = try std.fmt.allocPrint(gpa, fmt_string, fmt_args); |
| 369 | comptime fmt_string: []const u8, | 361 | defer gpa.free(msg); |
| 370 | fmt_args: anytype, | 362 | |
| 371 | ) error{ PackageFetchFailed, OutOfMemory } { | 363 | try addErrorMessage(report.ast.*, file_path, report.error_bundle, 0, .{ |
| 372 | const ttyconf: std.debug.TTY.Config = switch (report.color) { | ||
| 373 | .auto => std.debug.detectTTYConfig(std.io.getStdErr()), | ||
| 374 | .on => .escape_codes, | ||
| 375 | .off => .no_color, | ||
| 376 | }; | ||
| 377 | const file_path = try report.directory.join(report.arena, &.{Manifest.basename}); | ||
| 378 | renderErrorMessage(report.ast.*, file_path, ttyconf, .{ | ||
| 379 | .tok = tok, | 364 | .tok = tok, |
| 380 | .off = 0, | 365 | .off = 0, |
| 381 | .msg = try std.fmt.allocPrint(report.arena, fmt_string, fmt_args), | 366 | .msg = msg, |
| 382 | }, notes); | 367 | }); |
| 368 | |||
| 383 | return error.PackageFetchFailed; | 369 | return error.PackageFetchFailed; |
| 384 | } | 370 | } |
| 385 | 371 | ||
| 386 | fn renderErrorMessage( | 372 | fn addErrorMessage( |
| 387 | ast: std.zig.Ast, | 373 | ast: std.zig.Ast, |
| 388 | file_path: []const u8, | 374 | file_path: []const u8, |
| 389 | ttyconf: std.debug.TTY.Config, | 375 | eb: *std.zig.ErrorBundle.Wip, |
| 376 | notes_len: u32, | ||
| 390 | msg: Manifest.ErrorMessage, | 377 | msg: Manifest.ErrorMessage, |
| 391 | notes: []const Compilation.AllErrors.Message, | 378 | ) error{OutOfMemory}!void { |
| 392 | ) void { | ||
| 393 | const token_starts = ast.tokens.items(.start); | 379 | const token_starts = ast.tokens.items(.start); |
| 394 | const start_loc = ast.tokenLocation(0, msg.tok); | 380 | const start_loc = ast.tokenLocation(0, msg.tok); |
| 395 | Compilation.AllErrors.Message.renderToStdErr(.{ .src = .{ | 381 | |
| 396 | .msg = msg.msg, | 382 | try eb.addRootErrorMessage(.{ |
| 397 | .src_path = file_path, | 383 | .msg = try eb.addString(msg.msg), |
| 398 | .line = @intCast(u32, start_loc.line), | 384 | .src_loc = try eb.addSourceLocation(.{ |
| 399 | .column = @intCast(u32, start_loc.column), | 385 | .src_path = try eb.addString(file_path), |
| 400 | .span = .{ | 386 | .span_start = token_starts[msg.tok], |
| 401 | .start = token_starts[msg.tok], | 387 | .span_end = @intCast(u32, token_starts[msg.tok] + ast.tokenSlice(msg.tok).len), |
| 402 | .end = @intCast(u32, token_starts[msg.tok] + ast.tokenSlice(msg.tok).len), | 388 | .span_main = token_starts[msg.tok] + msg.off, |
| 403 | .main = token_starts[msg.tok] + msg.off, | 389 | .line = @intCast(u32, start_loc.line), |
| 404 | }, | 390 | .column = @intCast(u32, start_loc.column), |
| 405 | .source_line = ast.source[start_loc.line_start..start_loc.line_end], | 391 | .source_line = try eb.addString(ast.source[start_loc.line_start..start_loc.line_end]), |
| 406 | .notes = notes, | 392 | }), |
| 407 | } }, ttyconf); | 393 | .notes_len = notes_len, |
| 394 | }); | ||
| 408 | } | 395 | } |
| 409 | }; | 396 | }; |
| 410 | 397 | ||
| ... | @@ -504,9 +491,7 @@ fn fetchAndUnpack( | ... | @@ -504,9 +491,7 @@ fn fetchAndUnpack( |
| 504 | // by default, so the same logic applies for buffering the reader as for gzip. | 491 | // by default, so the same logic applies for buffering the reader as for gzip. |
| 505 | try unpackTarball(gpa, &req, tmp_directory.handle, std.compress.xz); | 492 | try unpackTarball(gpa, &req, tmp_directory.handle, std.compress.xz); |
| 506 | } else { | 493 | } else { |
| 507 | return report.fail(dep.url_tok, "unknown file extension for path '{s}'", .{ | 494 | return report.fail(dep.url_tok, "unknown file extension for path '{s}'", .{uri.path}); |
| 508 | uri.path, | ||
| 509 | }); | ||
| 510 | } | 495 | } |
| 511 | 496 | ||
| 512 | // TODO: delete files not included in the package prior to computing the package hash. | 497 | // TODO: delete files not included in the package prior to computing the package hash. |
| ... | @@ -533,10 +518,21 @@ fn fetchAndUnpack( | ... | @@ -533,10 +518,21 @@ fn fetchAndUnpack( |
| 533 | }); | 518 | }); |
| 534 | } | 519 | } |
| 535 | } else { | 520 | } else { |
| 536 | const notes: [1]Compilation.AllErrors.Message = .{.{ .plain = .{ | 521 | const file_path = try report.directory.join(gpa, &.{Manifest.basename}); |
| 537 | .msg = try std.fmt.allocPrint(report.arena, "expected .hash = \"{s}\",", .{&actual_hex}), | 522 | defer gpa.free(file_path); |
| 538 | } }}; | 523 | |
| 539 | return report.failWithNotes(&notes, dep.url_tok, "url field is missing corresponding hash field", .{}); | 524 | const eb = report.error_bundle; |
| 525 | const notes_len = 1; | ||
| 526 | try Report.addErrorMessage(report.ast.*, file_path, eb, notes_len, .{ | ||
| 527 | .tok = dep.url_tok, | ||
| 528 | .off = 0, | ||
| 529 | .msg = "url field is missing corresponding hash field", | ||
| 530 | }); | ||
| 531 | const notes_start = try eb.reserveNotes(notes_len); | ||
| 532 | eb.extra.items[notes_start] = @enumToInt(try eb.addErrorMessage(.{ | ||
| 533 | .msg = try eb.printString("expected .hash = \"{s}\",", .{&actual_hex}), | ||
| 534 | })); | ||
| 535 | return error.PackageFetchFailed; | ||
| 540 | } | 536 | } |
| 541 | 537 | ||
| 542 | const build_root = try global_cache_directory.join(gpa, &.{pkg_dir_sub_path}); | 538 | const build_root = try global_cache_directory.join(gpa, &.{pkg_dir_sub_path}); |
src/Sema.zig+12-14| ... | @@ -2211,29 +2211,27 @@ pub fn fail( | ... | @@ -2211,29 +2211,27 @@ pub fn fail( |
| 2211 | 2211 | ||
| 2212 | fn failWithOwnedErrorMsg(sema: *Sema, err_msg: *Module.ErrorMsg) CompileError { | 2212 | fn failWithOwnedErrorMsg(sema: *Sema, err_msg: *Module.ErrorMsg) CompileError { |
| 2213 | @setCold(true); | 2213 | @setCold(true); |
| 2214 | const gpa = sema.gpa; | ||
| 2214 | 2215 | ||
| 2215 | if (crash_report.is_enabled and sema.mod.comp.debug_compile_errors) { | 2216 | if (crash_report.is_enabled and sema.mod.comp.debug_compile_errors) { |
| 2216 | if (err_msg.src_loc.lazy == .unneeded) return error.NeededSourceLocation; | 2217 | if (err_msg.src_loc.lazy == .unneeded) return error.NeededSourceLocation; |
| 2217 | var arena = std.heap.ArenaAllocator.init(sema.gpa); | 2218 | var wip_errors: std.zig.ErrorBundle.Wip = undefined; |
| 2218 | errdefer arena.deinit(); | 2219 | wip_errors.init(gpa) catch unreachable; |
| 2219 | var errors = std.ArrayList(Compilation.AllErrors.Message).init(sema.gpa); | 2220 | Compilation.addModuleErrorMsg(&wip_errors, err_msg.*) catch unreachable; |
| 2220 | defer errors.deinit(); | ||
| 2221 | |||
| 2222 | Compilation.AllErrors.add(sema.mod, &arena, &errors, err_msg.*) catch unreachable; | ||
| 2223 | |||
| 2224 | std.debug.print("compile error during Sema:\n", .{}); | 2221 | std.debug.print("compile error during Sema:\n", .{}); |
| 2225 | Compilation.AllErrors.Message.renderToStdErr(errors.items[0], .no_color); | 2222 | var error_bundle = wip_errors.toOwnedBundle("") catch unreachable; |
| 2223 | error_bundle.renderToStdErr(.{ .ttyconf = .no_color }); | ||
| 2226 | crash_report.compilerPanic("unexpected compile error occurred", null, null); | 2224 | crash_report.compilerPanic("unexpected compile error occurred", null, null); |
| 2227 | } | 2225 | } |
| 2228 | 2226 | ||
| 2229 | const mod = sema.mod; | 2227 | const mod = sema.mod; |
| 2230 | ref: { | 2228 | ref: { |
| 2231 | errdefer err_msg.destroy(mod.gpa); | 2229 | errdefer err_msg.destroy(gpa); |
| 2232 | if (err_msg.src_loc.lazy == .unneeded) { | 2230 | if (err_msg.src_loc.lazy == .unneeded) { |
| 2233 | return error.NeededSourceLocation; | 2231 | return error.NeededSourceLocation; |
| 2234 | } | 2232 | } |
| 2235 | try mod.failed_decls.ensureUnusedCapacity(mod.gpa, 1); | 2233 | try mod.failed_decls.ensureUnusedCapacity(gpa, 1); |
| 2236 | try mod.failed_files.ensureUnusedCapacity(mod.gpa, 1); | 2234 | try mod.failed_files.ensureUnusedCapacity(gpa, 1); |
| 2237 | 2235 | ||
| 2238 | const max_references = blk: { | 2236 | const max_references = blk: { |
| 2239 | if (sema.mod.comp.reference_trace) |num| break :blk num; | 2237 | if (sema.mod.comp.reference_trace) |num| break :blk num; |
| ... | @@ -2243,11 +2241,11 @@ fn failWithOwnedErrorMsg(sema: *Sema, err_msg: *Module.ErrorMsg) CompileError { | ... | @@ -2243,11 +2241,11 @@ fn failWithOwnedErrorMsg(sema: *Sema, err_msg: *Module.ErrorMsg) CompileError { |
| 2243 | }; | 2241 | }; |
| 2244 | 2242 | ||
| 2245 | var referenced_by = if (sema.func) |some| some.owner_decl else sema.owner_decl_index; | 2243 | var referenced_by = if (sema.func) |some| some.owner_decl else sema.owner_decl_index; |
| 2246 | var reference_stack = std.ArrayList(Module.ErrorMsg.Trace).init(sema.gpa); | 2244 | var reference_stack = std.ArrayList(Module.ErrorMsg.Trace).init(gpa); |
| 2247 | defer reference_stack.deinit(); | 2245 | defer reference_stack.deinit(); |
| 2248 | 2246 | ||
| 2249 | // Avoid infinite loops. | 2247 | // Avoid infinite loops. |
| 2250 | var seen = std.AutoHashMap(Module.Decl.Index, void).init(sema.gpa); | 2248 | var seen = std.AutoHashMap(Module.Decl.Index, void).init(gpa); |
| 2251 | defer seen.deinit(); | 2249 | defer seen.deinit(); |
| 2252 | 2250 | ||
| 2253 | var cur_reference_trace: u32 = 0; | 2251 | var cur_reference_trace: u32 = 0; |
| ... | @@ -2288,7 +2286,7 @@ fn failWithOwnedErrorMsg(sema: *Sema, err_msg: *Module.ErrorMsg) CompileError { | ... | @@ -2288,7 +2286,7 @@ fn failWithOwnedErrorMsg(sema: *Sema, err_msg: *Module.ErrorMsg) CompileError { |
| 2288 | if (gop.found_existing) { | 2286 | if (gop.found_existing) { |
| 2289 | // If there are multiple errors for the same Decl, prefer the first one added. | 2287 | // If there are multiple errors for the same Decl, prefer the first one added. |
| 2290 | sema.err = null; | 2288 | sema.err = null; |
| 2291 | err_msg.destroy(mod.gpa); | 2289 | err_msg.destroy(gpa); |
| 2292 | } else { | 2290 | } else { |
| 2293 | sema.err = err_msg; | 2291 | sema.err = err_msg; |
| 2294 | gop.value_ptr.* = err_msg; | 2292 | gop.value_ptr.* = err_msg; |
src/ThreadPool.zig deleted-152| ... | @@ -1,152 +0,0 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const builtin = @import("builtin"); | ||
| 3 | const ThreadPool = @This(); | ||
| 4 | const WaitGroup = @import("WaitGroup.zig"); | ||
| 5 | |||
| 6 | mutex: std.Thread.Mutex = .{}, | ||
| 7 | cond: std.Thread.Condition = .{}, | ||
| 8 | run_queue: RunQueue = .{}, | ||
| 9 | is_running: bool = true, | ||
| 10 | allocator: std.mem.Allocator, | ||
| 11 | threads: []std.Thread, | ||
| 12 | |||
| 13 | const RunQueue = std.SinglyLinkedList(Runnable); | ||
| 14 | const Runnable = struct { | ||
| 15 | runFn: RunProto, | ||
| 16 | }; | ||
| 17 | |||
| 18 | const RunProto = *const fn (*Runnable) void; | ||
| 19 | |||
| 20 | pub fn init(pool: *ThreadPool, allocator: std.mem.Allocator) !void { | ||
| 21 | pool.* = .{ | ||
| 22 | .allocator = allocator, | ||
| 23 | .threads = &[_]std.Thread{}, | ||
| 24 | }; | ||
| 25 | |||
| 26 | if (builtin.single_threaded) { | ||
| 27 | return; | ||
| 28 | } | ||
| 29 | |||
| 30 | const thread_count = std.math.max(1, std.Thread.getCpuCount() catch 1); | ||
| 31 | pool.threads = try allocator.alloc(std.Thread, thread_count); | ||
| 32 | errdefer allocator.free(pool.threads); | ||
| 33 | |||
| 34 | // kill and join any threads we spawned previously on error. | ||
| 35 | var spawned: usize = 0; | ||
| 36 | errdefer pool.join(spawned); | ||
| 37 | |||
| 38 | for (pool.threads) |*thread| { | ||
| 39 | thread.* = try std.Thread.spawn(.{}, worker, .{pool}); | ||
| 40 | spawned += 1; | ||
| 41 | } | ||
| 42 | } | ||
| 43 | |||
| 44 | pub fn deinit(pool: *ThreadPool) void { | ||
| 45 | pool.join(pool.threads.len); // kill and join all threads. | ||
| 46 | pool.* = undefined; | ||
| 47 | } | ||
| 48 | |||
| 49 | fn join(pool: *ThreadPool, spawned: usize) void { | ||
| 50 | if (builtin.single_threaded) { | ||
| 51 | return; | ||
| 52 | } | ||
| 53 | |||
| 54 | { | ||
| 55 | pool.mutex.lock(); | ||
| 56 | defer pool.mutex.unlock(); | ||
| 57 | |||
| 58 | // ensure future worker threads exit the dequeue loop | ||
| 59 | pool.is_running = false; | ||
| 60 | } | ||
| 61 | |||
| 62 | // wake up any sleeping threads (this can be done outside the mutex) | ||
| 63 | // then wait for all the threads we know are spawned to complete. | ||
| 64 | pool.cond.broadcast(); | ||
| 65 | for (pool.threads[0..spawned]) |thread| { | ||
| 66 | thread.join(); | ||
| 67 | } | ||
| 68 | |||
| 69 | pool.allocator.free(pool.threads); | ||
| 70 | } | ||
| 71 | |||
| 72 | pub fn spawn(pool: *ThreadPool, comptime func: anytype, args: anytype) !void { | ||
| 73 | if (builtin.single_threaded) { | ||
| 74 | @call(.auto, func, args); | ||
| 75 | return; | ||
| 76 | } | ||
| 77 | |||
| 78 | const Args = @TypeOf(args); | ||
| 79 | const Closure = struct { | ||
| 80 | arguments: Args, | ||
| 81 | pool: *ThreadPool, | ||
| 82 | run_node: RunQueue.Node = .{ .data = .{ .runFn = runFn } }, | ||
| 83 | |||
| 84 | fn runFn(runnable: *Runnable) void { | ||
| 85 | const run_node = @fieldParentPtr(RunQueue.Node, "data", runnable); | ||
| 86 | const closure = @fieldParentPtr(@This(), "run_node", run_node); | ||
| 87 | @call(.auto, func, closure.arguments); | ||
| 88 | |||
| 89 | // The thread pool's allocator is protected by the mutex. | ||
| 90 | const mutex = &closure.pool.mutex; | ||
| 91 | mutex.lock(); | ||
| 92 | defer mutex.unlock(); | ||
| 93 | |||
| 94 | closure.pool.allocator.destroy(closure); | ||
| 95 | } | ||
| 96 | }; | ||
| 97 | |||
| 98 | { | ||
| 99 | pool.mutex.lock(); | ||
| 100 | defer pool.mutex.unlock(); | ||
| 101 | |||
| 102 | const closure = try pool.allocator.create(Closure); | ||
| 103 | closure.* = .{ | ||
| 104 | .arguments = args, | ||
| 105 | .pool = pool, | ||
| 106 | }; | ||
| 107 | |||
| 108 | pool.run_queue.prepend(&closure.run_node); | ||
| 109 | } | ||
| 110 | |||
| 111 | // Notify waiting threads outside the lock to try and keep the critical section small. | ||
| 112 | pool.cond.signal(); | ||
| 113 | } | ||
| 114 | |||
| 115 | fn worker(pool: *ThreadPool) void { | ||
| 116 | pool.mutex.lock(); | ||
| 117 | defer pool.mutex.unlock(); | ||
| 118 | |||
| 119 | while (true) { | ||
| 120 | while (pool.run_queue.popFirst()) |run_node| { | ||
| 121 | // Temporarily unlock the mutex in order to execute the run_node | ||
| 122 | pool.mutex.unlock(); | ||
| 123 | defer pool.mutex.lock(); | ||
| 124 | |||
| 125 | const runFn = run_node.data.runFn; | ||
| 126 | runFn(&run_node.data); | ||
| 127 | } | ||
| 128 | |||
| 129 | // Stop executing instead of waiting if the thread pool is no longer running. | ||
| 130 | if (pool.is_running) { | ||
| 131 | pool.cond.wait(&pool.mutex); | ||
| 132 | } else { | ||
| 133 | break; | ||
| 134 | } | ||
| 135 | } | ||
| 136 | } | ||
| 137 | |||
| 138 | pub fn waitAndWork(pool: *ThreadPool, wait_group: *WaitGroup) void { | ||
| 139 | while (!wait_group.isDone()) { | ||
| 140 | if (blk: { | ||
| 141 | pool.mutex.lock(); | ||
| 142 | defer pool.mutex.unlock(); | ||
| 143 | break :blk pool.run_queue.popFirst(); | ||
| 144 | }) |run_node| { | ||
| 145 | run_node.data.runFn(&run_node.data); | ||
| 146 | continue; | ||
| 147 | } | ||
| 148 | |||
| 149 | wait_group.wait(); | ||
| 150 | return; | ||
| 151 | } | ||
| 152 | } | ||
src/WaitGroup.zig deleted-46| ... | @@ -1,46 +0,0 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const Atomic = std.atomic.Atomic; | ||
| 3 | const assert = std.debug.assert; | ||
| 4 | const WaitGroup = @This(); | ||
| 5 | |||
| 6 | const is_waiting: usize = 1 << 0; | ||
| 7 | const one_pending: usize = 1 << 1; | ||
| 8 | |||
| 9 | state: Atomic(usize) = Atomic(usize).init(0), | ||
| 10 | event: std.Thread.ResetEvent = .{}, | ||
| 11 | |||
| 12 | pub fn start(self: *WaitGroup) void { | ||
| 13 | const state = self.state.fetchAdd(one_pending, .Monotonic); | ||
| 14 | assert((state / one_pending) < (std.math.maxInt(usize) / one_pending)); | ||
| 15 | } | ||
| 16 | |||
| 17 | pub fn finish(self: *WaitGroup) void { | ||
| 18 | const state = self.state.fetchSub(one_pending, .Release); | ||
| 19 | assert((state / one_pending) > 0); | ||
| 20 | |||
| 21 | if (state == (one_pending | is_waiting)) { | ||
| 22 | self.state.fence(.Acquire); | ||
| 23 | self.event.set(); | ||
| 24 | } | ||
| 25 | } | ||
| 26 | |||
| 27 | pub fn wait(self: *WaitGroup) void { | ||
| 28 | var state = self.state.fetchAdd(is_waiting, .Acquire); | ||
| 29 | assert(state & is_waiting == 0); | ||
| 30 | |||
| 31 | if ((state / one_pending) > 0) { | ||
| 32 | self.event.wait(); | ||
| 33 | } | ||
| 34 | } | ||
| 35 | |||
| 36 | pub fn reset(self: *WaitGroup) void { | ||
| 37 | self.state.store(0, .Monotonic); | ||
| 38 | self.event.reset(); | ||
| 39 | } | ||
| 40 | |||
| 41 | pub fn isDone(wg: *WaitGroup) bool { | ||
| 42 | const state = wg.state.load(.Acquire); | ||
| 43 | assert(state & is_waiting == 0); | ||
| 44 | |||
| 45 | return (state / one_pending) == 0; | ||
| 46 | } | ||
src/Zir.zig+6| ... | @@ -3594,6 +3594,12 @@ pub const Inst = struct { | ... | @@ -3594,6 +3594,12 @@ pub const Inst = struct { |
| 3594 | /// 0 or a payload index of a `Block`, each is a payload | 3594 | /// 0 or a payload index of a `Block`, each is a payload |
| 3595 | /// index of another `Item`. | 3595 | /// index of another `Item`. |
| 3596 | notes: u32, | 3596 | notes: u32, |
| 3597 | |||
| 3598 | pub fn notesLen(item: Item, zir: Zir) u32 { | ||
| 3599 | if (item.notes == 0) return 0; | ||
| 3600 | const block = zir.extraData(Block, item.notes); | ||
| 3601 | return block.data.body_len; | ||
| 3602 | } | ||
| 3597 | }; | 3603 | }; |
| 3598 | }; | 3604 | }; |
| 3599 | 3605 |
src/glibc.zig+11-8| ... | @@ -161,7 +161,7 @@ pub const CRTFile = enum { | ... | @@ -161,7 +161,7 @@ pub const CRTFile = enum { |
| 161 | libc_nonshared_a, | 161 | libc_nonshared_a, |
| 162 | }; | 162 | }; |
| 163 | 163 | ||
| 164 | pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { | 164 | pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progress.Node) !void { |
| 165 | if (!build_options.have_llvm) { | 165 | if (!build_options.have_llvm) { |
| 166 | return error.ZigCompilerNotBuiltWithLLVMExtensions; | 166 | return error.ZigCompilerNotBuiltWithLLVMExtensions; |
| 167 | } | 167 | } |
| ... | @@ -196,7 +196,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { | ... | @@ -196,7 +196,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { |
| 196 | "-DASSEMBLER", | 196 | "-DASSEMBLER", |
| 197 | "-Wa,--noexecstack", | 197 | "-Wa,--noexecstack", |
| 198 | }); | 198 | }); |
| 199 | return comp.build_crt_file("crti", .Obj, &[1]Compilation.CSourceFile{ | 199 | return comp.build_crt_file("crti", .Obj, .@"glibc crti.o", prog_node, &.{ |
| 200 | .{ | 200 | .{ |
| 201 | .src_path = try start_asm_path(comp, arena, "crti.S"), | 201 | .src_path = try start_asm_path(comp, arena, "crti.S"), |
| 202 | .cache_exempt_flags = args.items, | 202 | .cache_exempt_flags = args.items, |
| ... | @@ -215,7 +215,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { | ... | @@ -215,7 +215,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { |
| 215 | "-DASSEMBLER", | 215 | "-DASSEMBLER", |
| 216 | "-Wa,--noexecstack", | 216 | "-Wa,--noexecstack", |
| 217 | }); | 217 | }); |
| 218 | return comp.build_crt_file("crtn", .Obj, &[1]Compilation.CSourceFile{ | 218 | return comp.build_crt_file("crtn", .Obj, .@"glibc crtn.o", prog_node, &.{ |
| 219 | .{ | 219 | .{ |
| 220 | .src_path = try start_asm_path(comp, arena, "crtn.S"), | 220 | .src_path = try start_asm_path(comp, arena, "crtn.S"), |
| 221 | .cache_exempt_flags = args.items, | 221 | .cache_exempt_flags = args.items, |
| ... | @@ -265,7 +265,9 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { | ... | @@ -265,7 +265,9 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { |
| 265 | .cache_exempt_flags = args.items, | 265 | .cache_exempt_flags = args.items, |
| 266 | }; | 266 | }; |
| 267 | }; | 267 | }; |
| 268 | return comp.build_crt_file("Scrt1", .Obj, &[_]Compilation.CSourceFile{ start_o, abi_note_o }); | 268 | return comp.build_crt_file("Scrt1", .Obj, .@"glibc Scrt1.o", prog_node, &.{ |
| 269 | start_o, abi_note_o, | ||
| 270 | }); | ||
| 269 | }, | 271 | }, |
| 270 | .libc_nonshared_a => { | 272 | .libc_nonshared_a => { |
| 271 | const s = path.sep_str; | 273 | const s = path.sep_str; |
| ... | @@ -366,7 +368,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { | ... | @@ -366,7 +368,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { |
| 366 | files_index += 1; | 368 | files_index += 1; |
| 367 | } | 369 | } |
| 368 | const files = files_buf[0..files_index]; | 370 | const files = files_buf[0..files_index]; |
| 369 | return comp.build_crt_file("c_nonshared", .Lib, files); | 371 | return comp.build_crt_file("c_nonshared", .Lib, .@"glibc libc_nonshared.a", prog_node, files); |
| 370 | }, | 372 | }, |
| 371 | } | 373 | } |
| 372 | } | 374 | } |
| ... | @@ -639,7 +641,7 @@ pub const BuiltSharedObjects = struct { | ... | @@ -639,7 +641,7 @@ pub const BuiltSharedObjects = struct { |
| 639 | 641 | ||
| 640 | const all_map_basename = "all.map"; | 642 | const all_map_basename = "all.map"; |
| 641 | 643 | ||
| 642 | pub fn buildSharedObjects(comp: *Compilation) !void { | 644 | pub fn buildSharedObjects(comp: *Compilation, prog_node: *std.Progress.Node) !void { |
| 643 | const tracy = trace(@src()); | 645 | const tracy = trace(@src()); |
| 644 | defer tracy.end(); | 646 | defer tracy.end(); |
| 645 | 647 | ||
| ... | @@ -1023,7 +1025,7 @@ pub fn buildSharedObjects(comp: *Compilation) !void { | ... | @@ -1023,7 +1025,7 @@ pub fn buildSharedObjects(comp: *Compilation) !void { |
| 1023 | const asm_file_basename = std.fmt.bufPrint(&lib_name_buf, "{s}.s", .{lib.name}) catch unreachable; | 1025 | const asm_file_basename = std.fmt.bufPrint(&lib_name_buf, "{s}.s", .{lib.name}) catch unreachable; |
| 1024 | try o_directory.handle.writeFile(asm_file_basename, stubs_asm.items); | 1026 | try o_directory.handle.writeFile(asm_file_basename, stubs_asm.items); |
| 1025 | 1027 | ||
| 1026 | try buildSharedLib(comp, arena, comp.global_cache_directory, o_directory, asm_file_basename, lib); | 1028 | try buildSharedLib(comp, arena, comp.global_cache_directory, o_directory, asm_file_basename, lib, prog_node); |
| 1027 | } | 1029 | } |
| 1028 | 1030 | ||
| 1029 | man.writeManifest() catch |err| { | 1031 | man.writeManifest() catch |err| { |
| ... | @@ -1046,6 +1048,7 @@ fn buildSharedLib( | ... | @@ -1046,6 +1048,7 @@ fn buildSharedLib( |
| 1046 | bin_directory: Compilation.Directory, | 1048 | bin_directory: Compilation.Directory, |
| 1047 | asm_file_basename: []const u8, | 1049 | asm_file_basename: []const u8, |
| 1048 | lib: Lib, | 1050 | lib: Lib, |
| 1051 | prog_node: *std.Progress.Node, | ||
| 1049 | ) !void { | 1052 | ) !void { |
| 1050 | const tracy = trace(@src()); | 1053 | const tracy = trace(@src()); |
| 1051 | defer tracy.end(); | 1054 | defer tracy.end(); |
| ... | @@ -1105,7 +1108,7 @@ fn buildSharedLib( | ... | @@ -1105,7 +1108,7 @@ fn buildSharedLib( |
| 1105 | }); | 1108 | }); |
| 1106 | defer sub_compilation.destroy(); | 1109 | defer sub_compilation.destroy(); |
| 1107 | 1110 | ||
| 1108 | try sub_compilation.updateSubCompilation(); | 1111 | try comp.updateSubCompilation(sub_compilation, .@"glibc shared object", prog_node); |
| 1109 | } | 1112 | } |
| 1110 | 1113 | ||
| 1111 | // Return true if glibc has crti/crtn sources for that architecture. | 1114 | // Return true if glibc has crti/crtn sources for that architecture. |
src/libcxx.zig+4-4| ... | @@ -96,7 +96,7 @@ const libcxx_files = [_][]const u8{ | ... | @@ -96,7 +96,7 @@ const libcxx_files = [_][]const u8{ |
| 96 | "src/verbose_abort.cpp", | 96 | "src/verbose_abort.cpp", |
| 97 | }; | 97 | }; |
| 98 | 98 | ||
| 99 | pub fn buildLibCXX(comp: *Compilation) !void { | 99 | pub fn buildLibCXX(comp: *Compilation, prog_node: *std.Progress.Node) !void { |
| 100 | if (!build_options.have_llvm) { | 100 | if (!build_options.have_llvm) { |
| 101 | return error.ZigCompilerNotBuiltWithLLVMExtensions; | 101 | return error.ZigCompilerNotBuiltWithLLVMExtensions; |
| 102 | } | 102 | } |
| ... | @@ -258,7 +258,7 @@ pub fn buildLibCXX(comp: *Compilation) !void { | ... | @@ -258,7 +258,7 @@ pub fn buildLibCXX(comp: *Compilation) !void { |
| 258 | }); | 258 | }); |
| 259 | defer sub_compilation.destroy(); | 259 | defer sub_compilation.destroy(); |
| 260 | 260 | ||
| 261 | try sub_compilation.updateSubCompilation(); | 261 | try comp.updateSubCompilation(sub_compilation, .libcxx, prog_node); |
| 262 | 262 | ||
| 263 | assert(comp.libcxx_static_lib == null); | 263 | assert(comp.libcxx_static_lib == null); |
| 264 | comp.libcxx_static_lib = Compilation.CRTFile{ | 264 | comp.libcxx_static_lib = Compilation.CRTFile{ |
| ... | @@ -269,7 +269,7 @@ pub fn buildLibCXX(comp: *Compilation) !void { | ... | @@ -269,7 +269,7 @@ pub fn buildLibCXX(comp: *Compilation) !void { |
| 269 | }; | 269 | }; |
| 270 | } | 270 | } |
| 271 | 271 | ||
| 272 | pub fn buildLibCXXABI(comp: *Compilation) !void { | 272 | pub fn buildLibCXXABI(comp: *Compilation, prog_node: *std.Progress.Node) !void { |
| 273 | if (!build_options.have_llvm) { | 273 | if (!build_options.have_llvm) { |
| 274 | return error.ZigCompilerNotBuiltWithLLVMExtensions; | 274 | return error.ZigCompilerNotBuiltWithLLVMExtensions; |
| 275 | } | 275 | } |
| ... | @@ -418,7 +418,7 @@ pub fn buildLibCXXABI(comp: *Compilation) !void { | ... | @@ -418,7 +418,7 @@ pub fn buildLibCXXABI(comp: *Compilation) !void { |
| 418 | }); | 418 | }); |
| 419 | defer sub_compilation.destroy(); | 419 | defer sub_compilation.destroy(); |
| 420 | 420 | ||
| 421 | try sub_compilation.updateSubCompilation(); | 421 | try comp.updateSubCompilation(sub_compilation, .libcxxabi, prog_node); |
| 422 | 422 | ||
| 423 | assert(comp.libcxxabi_static_lib == null); | 423 | assert(comp.libcxxabi_static_lib == null); |
| 424 | comp.libcxxabi_static_lib = Compilation.CRTFile{ | 424 | comp.libcxxabi_static_lib = Compilation.CRTFile{ |
src/libtsan.zig+2-2| ... | @@ -5,7 +5,7 @@ const Compilation = @import("Compilation.zig"); | ... | @@ -5,7 +5,7 @@ const Compilation = @import("Compilation.zig"); |
| 5 | const build_options = @import("build_options"); | 5 | const build_options = @import("build_options"); |
| 6 | const trace = @import("tracy.zig").trace; | 6 | const trace = @import("tracy.zig").trace; |
| 7 | 7 | ||
| 8 | pub fn buildTsan(comp: *Compilation) !void { | 8 | pub fn buildTsan(comp: *Compilation, prog_node: *std.Progress.Node) !void { |
| 9 | if (!build_options.have_llvm) { | 9 | if (!build_options.have_llvm) { |
| 10 | return error.ZigCompilerNotBuiltWithLLVMExtensions; | 10 | return error.ZigCompilerNotBuiltWithLLVMExtensions; |
| 11 | } | 11 | } |
| ... | @@ -235,7 +235,7 @@ pub fn buildTsan(comp: *Compilation) !void { | ... | @@ -235,7 +235,7 @@ pub fn buildTsan(comp: *Compilation) !void { |
| 235 | }); | 235 | }); |
| 236 | defer sub_compilation.destroy(); | 236 | defer sub_compilation.destroy(); |
| 237 | 237 | ||
| 238 | try sub_compilation.updateSubCompilation(); | 238 | try comp.updateSubCompilation(sub_compilation, .libtsan, prog_node); |
| 239 | 239 | ||
| 240 | assert(comp.tsan_static_lib == null); | 240 | assert(comp.tsan_static_lib == null); |
| 241 | comp.tsan_static_lib = Compilation.CRTFile{ | 241 | comp.tsan_static_lib = Compilation.CRTFile{ |
src/libunwind.zig+2-2| ... | @@ -7,7 +7,7 @@ const Compilation = @import("Compilation.zig"); | ... | @@ -7,7 +7,7 @@ const Compilation = @import("Compilation.zig"); |
| 7 | const build_options = @import("build_options"); | 7 | const build_options = @import("build_options"); |
| 8 | const trace = @import("tracy.zig").trace; | 8 | const trace = @import("tracy.zig").trace; |
| 9 | 9 | ||
| 10 | pub fn buildStaticLib(comp: *Compilation) !void { | 10 | pub fn buildStaticLib(comp: *Compilation, prog_node: *std.Progress.Node) !void { |
| 11 | if (!build_options.have_llvm) { | 11 | if (!build_options.have_llvm) { |
| 12 | return error.ZigCompilerNotBuiltWithLLVMExtensions; | 12 | return error.ZigCompilerNotBuiltWithLLVMExtensions; |
| 13 | } | 13 | } |
| ... | @@ -130,7 +130,7 @@ pub fn buildStaticLib(comp: *Compilation) !void { | ... | @@ -130,7 +130,7 @@ pub fn buildStaticLib(comp: *Compilation) !void { |
| 130 | }); | 130 | }); |
| 131 | defer sub_compilation.destroy(); | 131 | defer sub_compilation.destroy(); |
| 132 | 132 | ||
| 133 | try sub_compilation.updateSubCompilation(); | 133 | try comp.updateSubCompilation(sub_compilation, .libunwind, prog_node); |
| 134 | 134 | ||
| 135 | assert(comp.libunwind_static_lib == null); | 135 | assert(comp.libunwind_static_lib == null); |
| 136 | 136 |
src/link.zig+40-3| ... | @@ -264,6 +264,8 @@ pub const File = struct { | ... | @@ -264,6 +264,8 @@ pub const File = struct { |
| 264 | /// of this linking operation. | 264 | /// of this linking operation. |
| 265 | lock: ?Cache.Lock = null, | 265 | lock: ?Cache.Lock = null, |
| 266 | 266 | ||
| 267 | child_pid: ?std.ChildProcess.Id = null, | ||
| 268 | |||
| 267 | /// Attempts incremental linking, if the file already exists. If | 269 | /// Attempts incremental linking, if the file already exists. If |
| 268 | /// incremental linking fails, falls back to truncating the file and | 270 | /// incremental linking fails, falls back to truncating the file and |
| 269 | /// rewriting it. A malicious file is detected as incremental link failure | 271 | /// rewriting it. A malicious file is detected as incremental link failure |
| ... | @@ -376,6 +378,26 @@ pub const File = struct { | ... | @@ -376,6 +378,26 @@ pub const File = struct { |
| 376 | if (build_options.only_c) unreachable; | 378 | if (build_options.only_c) unreachable; |
| 377 | if (base.file != null) return; | 379 | if (base.file != null) return; |
| 378 | const emit = base.options.emit orelse return; | 380 | const emit = base.options.emit orelse return; |
| 381 | if (base.child_pid) |pid| { | ||
| 382 | // If we try to open the output file in write mode while it is running, | ||
| 383 | // it will return ETXTBSY. So instead, we copy the file, atomically rename it | ||
| 384 | // over top of the exe path, and then proceed normally. This changes the inode, | ||
| 385 | // avoiding the error. | ||
| 386 | const tmp_sub_path = try std.fmt.allocPrint(base.allocator, "{s}-{x}", .{ | ||
| 387 | emit.sub_path, std.crypto.random.int(u32), | ||
| 388 | }); | ||
| 389 | try emit.directory.handle.copyFile(emit.sub_path, emit.directory.handle, tmp_sub_path, .{}); | ||
| 390 | try emit.directory.handle.rename(tmp_sub_path, emit.sub_path); | ||
| 391 | switch (builtin.os.tag) { | ||
| 392 | .linux => { | ||
| 393 | switch (std.os.errno(std.os.linux.ptrace(std.os.linux.PTRACE.ATTACH, pid, 0, 0, 0))) { | ||
| 394 | .SUCCESS => {}, | ||
| 395 | else => |errno| log.warn("ptrace failure: {s}", .{@tagName(errno)}), | ||
| 396 | } | ||
| 397 | }, | ||
| 398 | else => return error.HotSwapUnavailableOnHostOperatingSystem, | ||
| 399 | } | ||
| 400 | } | ||
| 379 | base.file = try emit.directory.handle.createFile(emit.sub_path, .{ | 401 | base.file = try emit.directory.handle.createFile(emit.sub_path, .{ |
| 380 | .truncate = false, | 402 | .truncate = false, |
| 381 | .read = true, | 403 | .read = true, |
| ... | @@ -424,6 +446,18 @@ pub const File = struct { | ... | @@ -424,6 +446,18 @@ pub const File = struct { |
| 424 | } | 446 | } |
| 425 | f.close(); | 447 | f.close(); |
| 426 | base.file = null; | 448 | base.file = null; |
| 449 | |||
| 450 | if (base.child_pid) |pid| { | ||
| 451 | switch (builtin.os.tag) { | ||
| 452 | .linux => { | ||
| 453 | switch (std.os.errno(std.os.linux.ptrace(std.os.linux.PTRACE.DETACH, pid, 0, 0, 0))) { | ||
| 454 | .SUCCESS => {}, | ||
| 455 | else => |errno| log.warn("ptrace failure: {s}", .{@tagName(errno)}), | ||
| 456 | } | ||
| 457 | }, | ||
| 458 | else => return error.HotSwapUnavailableOnHostOperatingSystem, | ||
| 459 | } | ||
| 460 | } | ||
| 427 | }, | 461 | }, |
| 428 | .c, .spirv, .nvptx => {}, | 462 | .c, .spirv, .nvptx => {}, |
| 429 | } | 463 | } |
| ... | @@ -462,6 +496,7 @@ pub const File = struct { | ... | @@ -462,6 +496,7 @@ pub const File = struct { |
| 462 | NetNameDeleted, | 496 | NetNameDeleted, |
| 463 | DeviceBusy, | 497 | DeviceBusy, |
| 464 | InvalidArgument, | 498 | InvalidArgument, |
| 499 | HotSwapUnavailableOnHostOperatingSystem, | ||
| 465 | }; | 500 | }; |
| 466 | 501 | ||
| 467 | /// Called from within the CodeGen to lower a local variable instantion as an unnamed | 502 | /// Called from within the CodeGen to lower a local variable instantion as an unnamed |
| ... | @@ -1053,9 +1088,11 @@ pub const File = struct { | ... | @@ -1053,9 +1088,11 @@ pub const File = struct { |
| 1053 | log.warn("failed to save archive hash digest file: {s}", .{@errorName(err)}); | 1088 | log.warn("failed to save archive hash digest file: {s}", .{@errorName(err)}); |
| 1054 | }; | 1089 | }; |
| 1055 | 1090 | ||
| 1056 | man.writeManifest() catch |err| { | 1091 | if (man.have_exclusive_lock) { |
| 1057 | log.warn("failed to write cache manifest when archiving: {s}", .{@errorName(err)}); | 1092 | man.writeManifest() catch |err| { |
| 1058 | }; | 1093 | log.warn("failed to write cache manifest when archiving: {s}", .{@errorName(err)}); |
| 1094 | }; | ||
| 1095 | } | ||
| 1059 | 1096 | ||
| 1060 | base.lock = man.toOwnedLock(); | 1097 | base.lock = man.toOwnedLock(); |
| 1061 | } | 1098 | } |
src/link/Elf.zig+50-5| ... | @@ -467,7 +467,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { | ... | @@ -467,7 +467,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { |
| 467 | .p_paddr = entry_addr, | 467 | .p_paddr = entry_addr, |
| 468 | .p_memsz = file_size, | 468 | .p_memsz = file_size, |
| 469 | .p_align = p_align, | 469 | .p_align = p_align, |
| 470 | .p_flags = elf.PF_X | elf.PF_R, | 470 | .p_flags = elf.PF_X | elf.PF_R | elf.PF_W, |
| 471 | }); | 471 | }); |
| 472 | self.entry_addr = null; | 472 | self.entry_addr = null; |
| 473 | self.phdr_table_dirty = true; | 473 | self.phdr_table_dirty = true; |
| ... | @@ -493,7 +493,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { | ... | @@ -493,7 +493,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { |
| 493 | .p_paddr = got_addr, | 493 | .p_paddr = got_addr, |
| 494 | .p_memsz = file_size, | 494 | .p_memsz = file_size, |
| 495 | .p_align = p_align, | 495 | .p_align = p_align, |
| 496 | .p_flags = elf.PF_R, | 496 | .p_flags = elf.PF_R | elf.PF_W, |
| 497 | }); | 497 | }); |
| 498 | self.phdr_table_dirty = true; | 498 | self.phdr_table_dirty = true; |
| 499 | } | 499 | } |
| ... | @@ -516,7 +516,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { | ... | @@ -516,7 +516,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { |
| 516 | .p_paddr = rodata_addr, | 516 | .p_paddr = rodata_addr, |
| 517 | .p_memsz = file_size, | 517 | .p_memsz = file_size, |
| 518 | .p_align = p_align, | 518 | .p_align = p_align, |
| 519 | .p_flags = elf.PF_R, | 519 | .p_flags = elf.PF_R | elf.PF_W, |
| 520 | }); | 520 | }); |
| 521 | self.phdr_table_dirty = true; | 521 | self.phdr_table_dirty = true; |
| 522 | } | 522 | } |
| ... | @@ -2166,7 +2166,7 @@ fn allocateAtom(self: *Elf, atom_index: Atom.Index, new_block_size: u64, alignme | ... | @@ -2166,7 +2166,7 @@ fn allocateAtom(self: *Elf, atom_index: Atom.Index, new_block_size: u64, alignme |
| 2166 | // First we look for an appropriately sized free list node. | 2166 | // First we look for an appropriately sized free list node. |
| 2167 | // The list is unordered. We'll just take the first thing that works. | 2167 | // The list is unordered. We'll just take the first thing that works. |
| 2168 | const vaddr = blk: { | 2168 | const vaddr = blk: { |
| 2169 | var i: usize = 0; | 2169 | var i: usize = if (self.base.child_pid == null) 0 else free_list.items.len; |
| 2170 | while (i < free_list.items.len) { | 2170 | while (i < free_list.items.len) { |
| 2171 | const big_atom_index = free_list.items[i]; | 2171 | const big_atom_index = free_list.items[i]; |
| 2172 | const big_atom = self.getAtom(big_atom_index); | 2172 | const big_atom = self.getAtom(big_atom_index); |
| ... | @@ -2397,7 +2397,7 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s | ... | @@ -2397,7 +2397,7 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s |
| 2397 | const atom = self.getAtom(atom_index); | 2397 | const atom = self.getAtom(atom_index); |
| 2398 | 2398 | ||
| 2399 | const shdr_index = decl_metadata.shdr; | 2399 | const shdr_index = decl_metadata.shdr; |
| 2400 | if (atom.getSymbol(self).st_size != 0) { | 2400 | if (atom.getSymbol(self).st_size != 0 and self.base.child_pid == null) { |
| 2401 | const local_sym = atom.getSymbolPtr(self); | 2401 | const local_sym = atom.getSymbolPtr(self); |
| 2402 | local_sym.st_name = try self.shstrtab.insert(gpa, decl_name); | 2402 | local_sym.st_name = try self.shstrtab.insert(gpa, decl_name); |
| 2403 | local_sym.st_info = (elf.STB_LOCAL << 4) | stt_bits; | 2403 | local_sym.st_info = (elf.STB_LOCAL << 4) | stt_bits; |
| ... | @@ -2451,6 +2451,28 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s | ... | @@ -2451,6 +2451,28 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s |
| 2451 | const phdr_index = self.sections.items(.phdr_index)[shdr_index]; | 2451 | const phdr_index = self.sections.items(.phdr_index)[shdr_index]; |
| 2452 | const section_offset = local_sym.st_value - self.program_headers.items[phdr_index].p_vaddr; | 2452 | const section_offset = local_sym.st_value - self.program_headers.items[phdr_index].p_vaddr; |
| 2453 | const file_offset = self.sections.items(.shdr)[shdr_index].sh_offset + section_offset; | 2453 | const file_offset = self.sections.items(.shdr)[shdr_index].sh_offset + section_offset; |
| 2454 | |||
| 2455 | if (self.base.child_pid) |pid| { | ||
| 2456 | switch (builtin.os.tag) { | ||
| 2457 | .linux => { | ||
| 2458 | var code_vec: [1]std.os.iovec_const = .{.{ | ||
| 2459 | .iov_base = code.ptr, | ||
| 2460 | .iov_len = code.len, | ||
| 2461 | }}; | ||
| 2462 | var remote_vec: [1]std.os.iovec_const = .{.{ | ||
| 2463 | .iov_base = @intToPtr([*]u8, @intCast(usize, local_sym.st_value)), | ||
| 2464 | .iov_len = code.len, | ||
| 2465 | }}; | ||
| 2466 | const rc = std.os.linux.process_vm_writev(pid, &code_vec, &remote_vec, 0); | ||
| 2467 | switch (std.os.errno(rc)) { | ||
| 2468 | .SUCCESS => assert(rc == code.len), | ||
| 2469 | else => |errno| log.warn("process_vm_writev failure: {s}", .{@tagName(errno)}), | ||
| 2470 | } | ||
| 2471 | }, | ||
| 2472 | else => return error.HotSwapUnavailableOnHostOperatingSystem, | ||
| 2473 | } | ||
| 2474 | } | ||
| 2475 | |||
| 2454 | try self.base.file.?.pwriteAll(code, file_offset); | 2476 | try self.base.file.?.pwriteAll(code, file_offset); |
| 2455 | 2477 | ||
| 2456 | return local_sym; | 2478 | return local_sym; |
| ... | @@ -2820,6 +2842,8 @@ fn writeOffsetTableEntry(self: *Elf, index: usize) !void { | ... | @@ -2820,6 +2842,8 @@ fn writeOffsetTableEntry(self: *Elf, index: usize) !void { |
| 2820 | const endian = self.base.options.target.cpu.arch.endian(); | 2842 | const endian = self.base.options.target.cpu.arch.endian(); |
| 2821 | const shdr = &self.sections.items(.shdr)[self.got_section_index.?]; | 2843 | const shdr = &self.sections.items(.shdr)[self.got_section_index.?]; |
| 2822 | const off = shdr.sh_offset + @as(u64, entry_size) * index; | 2844 | const off = shdr.sh_offset + @as(u64, entry_size) * index; |
| 2845 | const phdr = &self.program_headers.items[self.phdr_got_index.?]; | ||
| 2846 | const vaddr = phdr.p_vaddr + @as(u64, entry_size) * index; | ||
| 2823 | switch (entry_size) { | 2847 | switch (entry_size) { |
| 2824 | 2 => { | 2848 | 2 => { |
| 2825 | var buf: [2]u8 = undefined; | 2849 | var buf: [2]u8 = undefined; |
| ... | @@ -2835,6 +2859,27 @@ fn writeOffsetTableEntry(self: *Elf, index: usize) !void { | ... | @@ -2835,6 +2859,27 @@ fn writeOffsetTableEntry(self: *Elf, index: usize) !void { |
| 2835 | var buf: [8]u8 = undefined; | 2859 | var buf: [8]u8 = undefined; |
| 2836 | mem.writeInt(u64, &buf, self.offset_table.items[index], endian); | 2860 | mem.writeInt(u64, &buf, self.offset_table.items[index], endian); |
| 2837 | try self.base.file.?.pwriteAll(&buf, off); | 2861 | try self.base.file.?.pwriteAll(&buf, off); |
| 2862 | |||
| 2863 | if (self.base.child_pid) |pid| { | ||
| 2864 | switch (builtin.os.tag) { | ||
| 2865 | .linux => { | ||
| 2866 | var local_vec: [1]std.os.iovec_const = .{.{ | ||
| 2867 | .iov_base = &buf, | ||
| 2868 | .iov_len = buf.len, | ||
| 2869 | }}; | ||
| 2870 | var remote_vec: [1]std.os.iovec_const = .{.{ | ||
| 2871 | .iov_base = @intToPtr([*]u8, @intCast(usize, vaddr)), | ||
| 2872 | .iov_len = buf.len, | ||
| 2873 | }}; | ||
| 2874 | const rc = std.os.linux.process_vm_writev(pid, &local_vec, &remote_vec, 0); | ||
| 2875 | switch (std.os.errno(rc)) { | ||
| 2876 | .SUCCESS => assert(rc == buf.len), | ||
| 2877 | else => |errno| log.warn("process_vm_writev failure: {s}", .{@tagName(errno)}), | ||
| 2878 | } | ||
| 2879 | }, | ||
| 2880 | else => return error.HotSwapUnavailableOnHostOperatingSystem, | ||
| 2881 | } | ||
| 2882 | } | ||
| 2838 | }, | 2883 | }, |
| 2839 | else => unreachable, | 2884 | else => unreachable, |
| 2840 | } | 2885 | } |
src/link/MachO/CodeSignature.zig+2-2| ... | @@ -7,12 +7,12 @@ const log = std.log.scoped(.link); | ... | @@ -7,12 +7,12 @@ const log = std.log.scoped(.link); |
| 7 | const macho = std.macho; | 7 | const macho = std.macho; |
| 8 | const mem = std.mem; | 8 | const mem = std.mem; |
| 9 | const testing = std.testing; | 9 | const testing = std.testing; |
| 10 | const ThreadPool = std.Thread.Pool; | ||
| 11 | const WaitGroup = std.Thread.WaitGroup; | ||
| 10 | 12 | ||
| 11 | const Allocator = mem.Allocator; | 13 | const Allocator = mem.Allocator; |
| 12 | const Compilation = @import("../../Compilation.zig"); | 14 | const Compilation = @import("../../Compilation.zig"); |
| 13 | const Sha256 = std.crypto.hash.sha2.Sha256; | 15 | const Sha256 = std.crypto.hash.sha2.Sha256; |
| 14 | const ThreadPool = @import("../../ThreadPool.zig"); | ||
| 15 | const WaitGroup = @import("../../WaitGroup.zig"); | ||
| 16 | 16 | ||
| 17 | const hash_size = Sha256.digest_length; | 17 | const hash_size = Sha256.digest_length; |
| 18 | 18 |
src/main.zig+495-337| ... | @@ -9,6 +9,8 @@ const Allocator = mem.Allocator; | ... | @@ -9,6 +9,8 @@ const Allocator = mem.Allocator; |
| 9 | const ArrayList = std.ArrayList; | 9 | const ArrayList = std.ArrayList; |
| 10 | const Ast = std.zig.Ast; | 10 | const Ast = std.zig.Ast; |
| 11 | const warn = std.log.warn; | 11 | const warn = std.log.warn; |
| 12 | const ThreadPool = std.Thread.Pool; | ||
| 13 | const cleanExit = std.process.cleanExit; | ||
| 12 | 14 | ||
| 13 | const tracy = @import("tracy.zig"); | 15 | const tracy = @import("tracy.zig"); |
| 14 | const Compilation = @import("Compilation.zig"); | 16 | const Compilation = @import("Compilation.zig"); |
| ... | @@ -22,8 +24,10 @@ const translate_c = @import("translate_c.zig"); | ... | @@ -22,8 +24,10 @@ const translate_c = @import("translate_c.zig"); |
| 22 | const clang = @import("clang.zig"); | 24 | const clang = @import("clang.zig"); |
| 23 | const Cache = std.Build.Cache; | 25 | const Cache = std.Build.Cache; |
| 24 | const target_util = @import("target.zig"); | 26 | const target_util = @import("target.zig"); |
| 25 | const ThreadPool = @import("ThreadPool.zig"); | ||
| 26 | const crash_report = @import("crash_report.zig"); | 27 | const crash_report = @import("crash_report.zig"); |
| 28 | const Module = @import("Module.zig"); | ||
| 29 | const AstGen = @import("AstGen.zig"); | ||
| 30 | const Server = std.zig.Server; | ||
| 27 | 31 | ||
| 28 | pub const std_options = struct { | 32 | pub const std_options = struct { |
| 29 | pub const wasiCwd = wasi_cwd; | 33 | pub const wasiCwd = wasi_cwd; |
| ... | @@ -361,7 +365,6 @@ const usage_build_generic = | ... | @@ -361,7 +365,6 @@ const usage_build_generic = |
| 361 | \\ | 365 | \\ |
| 362 | \\General Options: | 366 | \\General Options: |
| 363 | \\ -h, --help Print this help and exit | 367 | \\ -h, --help Print this help and exit |
| 364 | \\ --watch Enable compiler REPL | ||
| 365 | \\ --color [auto|off|on] Enable or disable colored error messages | 368 | \\ --color [auto|off|on] Enable or disable colored error messages |
| 366 | \\ -femit-bin[=path] (default) Output machine code | 369 | \\ -femit-bin[=path] (default) Output machine code |
| 367 | \\ -fno-emit-bin Do not output machine code | 370 | \\ -fno-emit-bin Do not output machine code |
| ... | @@ -666,6 +669,16 @@ const ArgMode = union(enum) { | ... | @@ -666,6 +669,16 @@ const ArgMode = union(enum) { |
| 666 | run, | 669 | run, |
| 667 | }; | 670 | }; |
| 668 | 671 | ||
| 672 | /// Avoid dragging networking into zig2.c because it adds dependencies on some | ||
| 673 | /// linker symbols that are annoying to satisfy while bootstrapping. | ||
| 674 | const Ip4Address = if (build_options.omit_pkg_fetching_code) void else std.net.Ip4Address; | ||
| 675 | |||
| 676 | const Listen = union(enum) { | ||
| 677 | none, | ||
| 678 | ip4: Ip4Address, | ||
| 679 | stdio, | ||
| 680 | }; | ||
| 681 | |||
| 669 | fn buildOutputType( | 682 | fn buildOutputType( |
| 670 | gpa: Allocator, | 683 | gpa: Allocator, |
| 671 | arena: Allocator, | 684 | arena: Allocator, |
| ... | @@ -686,7 +699,7 @@ fn buildOutputType( | ... | @@ -686,7 +699,7 @@ fn buildOutputType( |
| 686 | var formatted_panics: ?bool = null; | 699 | var formatted_panics: ?bool = null; |
| 687 | var function_sections = false; | 700 | var function_sections = false; |
| 688 | var no_builtin = false; | 701 | var no_builtin = false; |
| 689 | var watch = false; | 702 | var listen: Listen = .none; |
| 690 | var debug_compile_errors = false; | 703 | var debug_compile_errors = false; |
| 691 | var verbose_link = (builtin.os.tag != .wasi or builtin.link_libc) and std.process.hasEnvVarConstant("ZIG_VERBOSE_LINK"); | 704 | var verbose_link = (builtin.os.tag != .wasi or builtin.link_libc) and std.process.hasEnvVarConstant("ZIG_VERBOSE_LINK"); |
| 692 | var verbose_cc = (builtin.os.tag != .wasi or builtin.link_libc) and std.process.hasEnvVarConstant("ZIG_VERBOSE_CC"); | 705 | var verbose_cc = (builtin.os.tag != .wasi or builtin.link_libc) and std.process.hasEnvVarConstant("ZIG_VERBOSE_CC"); |
| ... | @@ -1144,6 +1157,23 @@ fn buildOutputType( | ... | @@ -1144,6 +1157,23 @@ fn buildOutputType( |
| 1144 | } else { | 1157 | } else { |
| 1145 | try log_scopes.append(gpa, args_iter.nextOrFatal()); | 1158 | try log_scopes.append(gpa, args_iter.nextOrFatal()); |
| 1146 | } | 1159 | } |
| 1160 | } else if (mem.eql(u8, arg, "--listen")) { | ||
| 1161 | const next_arg = args_iter.nextOrFatal(); | ||
| 1162 | if (mem.eql(u8, next_arg, "-")) { | ||
| 1163 | listen = .stdio; | ||
| 1164 | } else { | ||
| 1165 | if (build_options.omit_pkg_fetching_code) unreachable; | ||
| 1166 | // example: --listen 127.0.0.1:9000 | ||
| 1167 | var it = std.mem.split(u8, next_arg, ":"); | ||
| 1168 | const host = it.next().?; | ||
| 1169 | const port_text = it.next() orelse "14735"; | ||
| 1170 | const port = std.fmt.parseInt(u16, port_text, 10) catch |err| | ||
| 1171 | fatal("invalid port number: '{s}': {s}", .{ port_text, @errorName(err) }); | ||
| 1172 | listen = .{ .ip4 = std.net.Ip4Address.parse(host, port) catch |err| | ||
| 1173 | fatal("invalid host: '{s}': {s}", .{ host, @errorName(err) }) }; | ||
| 1174 | } | ||
| 1175 | } else if (mem.eql(u8, arg, "--listen=-")) { | ||
| 1176 | listen = .stdio; | ||
| 1147 | } else if (mem.eql(u8, arg, "--debug-link-snapshot")) { | 1177 | } else if (mem.eql(u8, arg, "--debug-link-snapshot")) { |
| 1148 | if (!build_options.enable_link_snapshots) { | 1178 | if (!build_options.enable_link_snapshots) { |
| 1149 | std.log.warn("Zig was compiled without linker snapshots enabled (-Dlink-snapshot). --debug-link-snapshot has no effect.", .{}); | 1179 | std.log.warn("Zig was compiled without linker snapshots enabled (-Dlink-snapshot). --debug-link-snapshot has no effect.", .{}); |
| ... | @@ -1172,8 +1202,6 @@ fn buildOutputType( | ... | @@ -1172,8 +1202,6 @@ fn buildOutputType( |
| 1172 | test_evented_io = true; | 1202 | test_evented_io = true; |
| 1173 | } else if (mem.eql(u8, arg, "--test-no-exec")) { | 1203 | } else if (mem.eql(u8, arg, "--test-no-exec")) { |
| 1174 | test_no_exec = true; | 1204 | test_no_exec = true; |
| 1175 | } else if (mem.eql(u8, arg, "--watch")) { | ||
| 1176 | watch = true; | ||
| 1177 | } else if (mem.eql(u8, arg, "-ftime-report")) { | 1205 | } else if (mem.eql(u8, arg, "-ftime-report")) { |
| 1178 | time_report = true; | 1206 | time_report = true; |
| 1179 | } else if (mem.eql(u8, arg, "-fstack-report")) { | 1207 | } else if (mem.eql(u8, arg, "-fstack-report")) { |
| ... | @@ -2999,7 +3027,7 @@ fn buildOutputType( | ... | @@ -2999,7 +3027,7 @@ fn buildOutputType( |
| 2999 | defer zig_lib_directory.handle.close(); | 3027 | defer zig_lib_directory.handle.close(); |
| 3000 | 3028 | ||
| 3001 | var thread_pool: ThreadPool = undefined; | 3029 | var thread_pool: ThreadPool = undefined; |
| 3002 | try thread_pool.init(gpa); | 3030 | try thread_pool.init(.{ .allocator = gpa }); |
| 3003 | defer thread_pool.deinit(); | 3031 | defer thread_pool.deinit(); |
| 3004 | 3032 | ||
| 3005 | var libc_installation: ?LibCInstallation = null; | 3033 | var libc_installation: ?LibCInstallation = null; |
| ... | @@ -3259,8 +3287,52 @@ fn buildOutputType( | ... | @@ -3259,8 +3287,52 @@ fn buildOutputType( |
| 3259 | if (show_builtin) { | 3287 | if (show_builtin) { |
| 3260 | return std.io.getStdOut().writeAll(try comp.generateBuiltinZigSource(arena)); | 3288 | return std.io.getStdOut().writeAll(try comp.generateBuiltinZigSource(arena)); |
| 3261 | } | 3289 | } |
| 3290 | switch (listen) { | ||
| 3291 | .none => {}, | ||
| 3292 | .stdio => { | ||
| 3293 | if (build_options.only_c) unreachable; | ||
| 3294 | try serve( | ||
| 3295 | comp, | ||
| 3296 | std.io.getStdIn(), | ||
| 3297 | std.io.getStdOut(), | ||
| 3298 | test_exec_args.items, | ||
| 3299 | self_exe_path, | ||
| 3300 | arg_mode, | ||
| 3301 | all_args, | ||
| 3302 | runtime_args_start, | ||
| 3303 | ); | ||
| 3304 | return cleanExit(); | ||
| 3305 | }, | ||
| 3306 | .ip4 => |ip4_addr| { | ||
| 3307 | if (build_options.omit_pkg_fetching_code) unreachable; | ||
| 3308 | |||
| 3309 | var server = std.net.StreamServer.init(.{ | ||
| 3310 | .reuse_address = true, | ||
| 3311 | }); | ||
| 3312 | defer server.deinit(); | ||
| 3313 | |||
| 3314 | try server.listen(.{ .in = ip4_addr }); | ||
| 3315 | |||
| 3316 | while (true) { | ||
| 3317 | const conn = try server.accept(); | ||
| 3318 | defer conn.stream.close(); | ||
| 3319 | |||
| 3320 | try serve( | ||
| 3321 | comp, | ||
| 3322 | .{ .handle = conn.stream.handle }, | ||
| 3323 | .{ .handle = conn.stream.handle }, | ||
| 3324 | test_exec_args.items, | ||
| 3325 | self_exe_path, | ||
| 3326 | arg_mode, | ||
| 3327 | all_args, | ||
| 3328 | runtime_args_start, | ||
| 3329 | ); | ||
| 3330 | } | ||
| 3331 | }, | ||
| 3332 | } | ||
| 3333 | |||
| 3262 | if (arg_mode == .translate_c) { | 3334 | if (arg_mode == .translate_c) { |
| 3263 | return cmdTranslateC(comp, arena, have_enable_cache); | 3335 | return cmdTranslateC(comp, arena, null); |
| 3264 | } | 3336 | } |
| 3265 | 3337 | ||
| 3266 | const hook: AfterUpdateHook = blk: { | 3338 | const hook: AfterUpdateHook = blk: { |
| ... | @@ -3276,7 +3348,7 @@ fn buildOutputType( | ... | @@ -3276,7 +3348,7 @@ fn buildOutputType( |
| 3276 | }; | 3348 | }; |
| 3277 | 3349 | ||
| 3278 | updateModule(gpa, comp, hook) catch |err| switch (err) { | 3350 | updateModule(gpa, comp, hook) catch |err| switch (err) { |
| 3279 | error.SemanticAnalyzeFail => if (!watch) process.exit(1), | 3351 | error.SemanticAnalyzeFail => if (listen == .none) process.exit(1), |
| 3280 | else => |e| return e, | 3352 | else => |e| return e, |
| 3281 | }; | 3353 | }; |
| 3282 | if (build_options.only_c) return cleanExit(); | 3354 | if (build_options.only_c) return cleanExit(); |
| ... | @@ -3332,7 +3404,6 @@ fn buildOutputType( | ... | @@ -3332,7 +3404,6 @@ fn buildOutputType( |
| 3332 | self_exe_path.?, | 3404 | self_exe_path.?, |
| 3333 | arg_mode, | 3405 | arg_mode, |
| 3334 | target_info, | 3406 | target_info, |
| 3335 | watch, | ||
| 3336 | &comp_destroyed, | 3407 | &comp_destroyed, |
| 3337 | all_args, | 3408 | all_args, |
| 3338 | runtime_args_start, | 3409 | runtime_args_start, |
| ... | @@ -3340,109 +3411,215 @@ fn buildOutputType( | ... | @@ -3340,109 +3411,215 @@ fn buildOutputType( |
| 3340 | ); | 3411 | ); |
| 3341 | } | 3412 | } |
| 3342 | 3413 | ||
| 3343 | const stdin = std.io.getStdIn().reader(); | 3414 | // Skip resource deallocation in release builds; let the OS do it. |
| 3344 | const stderr = std.io.getStdErr().writer(); | 3415 | return cleanExit(); |
| 3345 | var repl_buf: [1024]u8 = undefined; | 3416 | } |
| 3417 | |||
| 3418 | fn serve( | ||
| 3419 | comp: *Compilation, | ||
| 3420 | in: fs.File, | ||
| 3421 | out: fs.File, | ||
| 3422 | test_exec_args: []const ?[]const u8, | ||
| 3423 | self_exe_path: ?[]const u8, | ||
| 3424 | arg_mode: ArgMode, | ||
| 3425 | all_args: []const []const u8, | ||
| 3426 | runtime_args_start: ?usize, | ||
| 3427 | ) !void { | ||
| 3428 | const gpa = comp.gpa; | ||
| 3346 | 3429 | ||
| 3347 | const ReplCmd = enum { | 3430 | var server = try Server.init(.{ |
| 3348 | update, | 3431 | .gpa = gpa, |
| 3349 | help, | 3432 | .in = in, |
| 3350 | run, | 3433 | .out = out, |
| 3351 | update_and_run, | 3434 | .zig_version = build_options.version, |
| 3435 | }); | ||
| 3436 | defer server.deinit(); | ||
| 3437 | |||
| 3438 | var child_pid: ?std.ChildProcess.Id = null; | ||
| 3439 | |||
| 3440 | var progress: std.Progress = .{ | ||
| 3441 | .terminal = null, | ||
| 3442 | .root = .{ | ||
| 3443 | .context = undefined, | ||
| 3444 | .parent = null, | ||
| 3445 | .name = "", | ||
| 3446 | .unprotected_estimated_total_items = 0, | ||
| 3447 | .unprotected_completed_items = 0, | ||
| 3448 | }, | ||
| 3449 | .columns_written = 0, | ||
| 3450 | .prev_refresh_timestamp = 0, | ||
| 3451 | .timer = null, | ||
| 3452 | .done = false, | ||
| 3352 | }; | 3453 | }; |
| 3454 | const main_progress_node = &progress.root; | ||
| 3455 | main_progress_node.context = &progress; | ||
| 3353 | 3456 | ||
| 3354 | var last_cmd: ReplCmd = .help; | 3457 | while (true) { |
| 3458 | const hdr = try server.receiveMessage(); | ||
| 3355 | 3459 | ||
| 3356 | while (watch) { | 3460 | switch (hdr.tag) { |
| 3357 | try stderr.print("(zig) ", .{}); | 3461 | .exit => { |
| 3358 | try comp.makeBinFileExecutable(); | 3462 | return cleanExit(); |
| 3359 | if (stdin.readUntilDelimiterOrEof(&repl_buf, '\n') catch |err| { | 3463 | }, |
| 3360 | try stderr.print("\nUnable to parse command: {s}\n", .{@errorName(err)}); | 3464 | .update => { |
| 3361 | continue; | 3465 | assert(main_progress_node.recently_updated_child == null); |
| 3362 | }) |line| { | 3466 | tracy.frameMark(); |
| 3363 | const actual_line = mem.trimRight(u8, line, "\r\n "); | 3467 | |
| 3364 | const cmd: ReplCmd = blk: { | 3468 | if (arg_mode == .translate_c) { |
| 3365 | if (mem.eql(u8, actual_line, "update")) { | 3469 | var arena_instance = std.heap.ArenaAllocator.init(gpa); |
| 3366 | break :blk .update; | 3470 | defer arena_instance.deinit(); |
| 3367 | } else if (mem.eql(u8, actual_line, "exit")) { | 3471 | const arena = arena_instance.allocator(); |
| 3368 | break; | 3472 | var output: TranslateCOutput = undefined; |
| 3369 | } else if (mem.eql(u8, actual_line, "help")) { | 3473 | try cmdTranslateC(comp, arena, &output); |
| 3370 | break :blk .help; | 3474 | try server.serveEmitBinPath(output.path, .{ |
| 3371 | } else if (mem.eql(u8, actual_line, "run")) { | 3475 | .flags = .{ .cache_hit = output.cache_hit }, |
| 3372 | break :blk .run; | 3476 | }); |
| 3373 | } else if (mem.eql(u8, actual_line, "update-and-run")) { | ||
| 3374 | break :blk .update_and_run; | ||
| 3375 | } else if (actual_line.len == 0) { | ||
| 3376 | break :blk last_cmd; | ||
| 3377 | } else { | ||
| 3378 | try stderr.print("unknown command: {s}\n", .{actual_line}); | ||
| 3379 | continue; | 3477 | continue; |
| 3380 | } | 3478 | } |
| 3381 | }; | 3479 | |
| 3382 | last_cmd = cmd; | 3480 | if (comp.bin_file.options.output_mode == .Exe) { |
| 3383 | switch (cmd) { | 3481 | try comp.makeBinFileWritable(); |
| 3384 | .update => { | 3482 | } |
| 3385 | tracy.frameMark(); | 3483 | |
| 3386 | if (output_mode == .Exe) { | 3484 | { |
| 3387 | try comp.makeBinFileWritable(); | 3485 | var reset: std.Thread.ResetEvent = .{}; |
| 3486 | |||
| 3487 | var progress_thread = try std.Thread.spawn(.{}, progressThread, .{ | ||
| 3488 | &progress, &server, &reset, | ||
| 3489 | }); | ||
| 3490 | defer { | ||
| 3491 | reset.set(); | ||
| 3492 | progress_thread.join(); | ||
| 3388 | } | 3493 | } |
| 3389 | updateModule(gpa, comp, hook) catch |err| switch (err) { | 3494 | |
| 3390 | error.SemanticAnalyzeFail => continue, | 3495 | try comp.update(main_progress_node); |
| 3391 | else => |e| return e, | 3496 | } |
| 3392 | }; | 3497 | |
| 3393 | }, | 3498 | try comp.makeBinFileExecutable(); |
| 3394 | .help => { | 3499 | try serveUpdateResults(&server, comp); |
| 3395 | try stderr.writeAll(repl_help); | 3500 | }, |
| 3396 | }, | 3501 | .run => { |
| 3397 | .run => { | 3502 | if (child_pid != null) { |
| 3398 | tracy.frameMark(); | 3503 | @panic("TODO block until the child exits"); |
| 3399 | try runOrTest( | 3504 | } |
| 3400 | comp, | 3505 | @panic("TODO call runOrTest"); |
| 3401 | gpa, | 3506 | //try runOrTest( |
| 3402 | arena, | 3507 | // comp, |
| 3403 | test_exec_args.items, | 3508 | // gpa, |
| 3404 | self_exe_path.?, | 3509 | // arena, |
| 3405 | arg_mode, | 3510 | // test_exec_args, |
| 3406 | target_info, | 3511 | // self_exe_path.?, |
| 3407 | watch, | 3512 | // arg_mode, |
| 3408 | &comp_destroyed, | 3513 | // target_info, |
| 3409 | all_args, | 3514 | // true, |
| 3410 | runtime_args_start, | 3515 | // &comp_destroyed, |
| 3411 | link_libc, | 3516 | // all_args, |
| 3412 | ); | 3517 | // runtime_args_start, |
| 3413 | }, | 3518 | // link_libc, |
| 3414 | .update_and_run => { | 3519 | //); |
| 3415 | tracy.frameMark(); | 3520 | }, |
| 3416 | if (output_mode == .Exe) { | 3521 | .hot_update => { |
| 3522 | tracy.frameMark(); | ||
| 3523 | assert(main_progress_node.recently_updated_child == null); | ||
| 3524 | if (child_pid) |pid| { | ||
| 3525 | try comp.hotCodeSwap(main_progress_node, pid); | ||
| 3526 | try serveUpdateResults(&server, comp); | ||
| 3527 | } else { | ||
| 3528 | if (comp.bin_file.options.output_mode == .Exe) { | ||
| 3417 | try comp.makeBinFileWritable(); | 3529 | try comp.makeBinFileWritable(); |
| 3418 | } | 3530 | } |
| 3419 | updateModule(gpa, comp, hook) catch |err| switch (err) { | 3531 | try comp.update(main_progress_node); |
| 3420 | error.SemanticAnalyzeFail => continue, | ||
| 3421 | else => |e| return e, | ||
| 3422 | }; | ||
| 3423 | try comp.makeBinFileExecutable(); | 3532 | try comp.makeBinFileExecutable(); |
| 3424 | try runOrTest( | 3533 | try serveUpdateResults(&server, comp); |
| 3534 | |||
| 3535 | child_pid = try runOrTestHotSwap( | ||
| 3425 | comp, | 3536 | comp, |
| 3426 | gpa, | 3537 | gpa, |
| 3427 | arena, | 3538 | test_exec_args, |
| 3428 | test_exec_args.items, | ||
| 3429 | self_exe_path.?, | 3539 | self_exe_path.?, |
| 3430 | arg_mode, | 3540 | arg_mode, |
| 3431 | target_info, | ||
| 3432 | watch, | ||
| 3433 | &comp_destroyed, | ||
| 3434 | all_args, | 3541 | all_args, |
| 3435 | runtime_args_start, | 3542 | runtime_args_start, |
| 3436 | link_libc, | ||
| 3437 | ); | 3543 | ); |
| 3438 | }, | 3544 | } |
| 3545 | }, | ||
| 3546 | else => { | ||
| 3547 | fatal("unrecognized message from client: 0x{x}", .{@enumToInt(hdr.tag)}); | ||
| 3548 | }, | ||
| 3549 | } | ||
| 3550 | } | ||
| 3551 | } | ||
| 3552 | |||
| 3553 | fn progressThread(progress: *std.Progress, server: *const Server, reset: *std.Thread.ResetEvent) void { | ||
| 3554 | while (true) { | ||
| 3555 | if (reset.timedWait(500 * std.time.ns_per_ms)) |_| { | ||
| 3556 | // The Compilation update has completed. | ||
| 3557 | return; | ||
| 3558 | } else |err| switch (err) { | ||
| 3559 | error.Timeout => {}, | ||
| 3560 | } | ||
| 3561 | |||
| 3562 | var buf: std.BoundedArray(u8, 160) = .{}; | ||
| 3563 | |||
| 3564 | { | ||
| 3565 | progress.update_mutex.lock(); | ||
| 3566 | defer progress.update_mutex.unlock(); | ||
| 3567 | |||
| 3568 | var need_ellipse = false; | ||
| 3569 | var maybe_node: ?*std.Progress.Node = &progress.root; | ||
| 3570 | while (maybe_node) |node| { | ||
| 3571 | if (need_ellipse) { | ||
| 3572 | buf.appendSlice("... ") catch {}; | ||
| 3573 | } | ||
| 3574 | need_ellipse = false; | ||
| 3575 | const eti = @atomicLoad(usize, &node.unprotected_estimated_total_items, .Monotonic); | ||
| 3576 | const completed_items = @atomicLoad(usize, &node.unprotected_completed_items, .Monotonic); | ||
| 3577 | const current_item = completed_items + 1; | ||
| 3578 | if (node.name.len != 0 or eti > 0) { | ||
| 3579 | if (node.name.len != 0) { | ||
| 3580 | buf.appendSlice(node.name) catch {}; | ||
| 3581 | need_ellipse = true; | ||
| 3582 | } | ||
| 3583 | if (eti > 0) { | ||
| 3584 | if (need_ellipse) buf.appendSlice(" ") catch {}; | ||
| 3585 | buf.writer().print("[{d}/{d}] ", .{ current_item, eti }) catch {}; | ||
| 3586 | need_ellipse = false; | ||
| 3587 | } else if (completed_items != 0) { | ||
| 3588 | if (need_ellipse) buf.appendSlice(" ") catch {}; | ||
| 3589 | buf.writer().print("[{d}] ", .{current_item}) catch {}; | ||
| 3590 | need_ellipse = false; | ||
| 3591 | } | ||
| 3592 | } | ||
| 3593 | maybe_node = @atomicLoad(?*std.Progress.Node, &node.recently_updated_child, .Acquire); | ||
| 3439 | } | 3594 | } |
| 3440 | } else { | ||
| 3441 | break; | ||
| 3442 | } | 3595 | } |
| 3596 | |||
| 3597 | const progress_string = buf.slice(); | ||
| 3598 | |||
| 3599 | server.serveMessage(.{ | ||
| 3600 | .tag = .progress, | ||
| 3601 | .bytes_len = @intCast(u32, progress_string.len), | ||
| 3602 | }, &.{ | ||
| 3603 | progress_string, | ||
| 3604 | }) catch |err| { | ||
| 3605 | fatal("unable to write to client: {s}", .{@errorName(err)}); | ||
| 3606 | }; | ||
| 3607 | } | ||
| 3608 | } | ||
| 3609 | |||
| 3610 | fn serveUpdateResults(s: *Server, comp: *Compilation) !void { | ||
| 3611 | const gpa = comp.gpa; | ||
| 3612 | var error_bundle = try comp.getAllErrorsAlloc(); | ||
| 3613 | defer error_bundle.deinit(gpa); | ||
| 3614 | if (error_bundle.errorMessageCount() > 0) { | ||
| 3615 | try s.serveErrorBundle(error_bundle); | ||
| 3616 | } else if (comp.bin_file.options.emit) |emit| { | ||
| 3617 | const full_path = try emit.directory.join(gpa, &.{emit.sub_path}); | ||
| 3618 | defer gpa.free(full_path); | ||
| 3619 | try s.serveEmitBinPath(full_path, .{ | ||
| 3620 | .flags = .{ .cache_hit = comp.last_update_was_cache_hit }, | ||
| 3621 | }); | ||
| 3443 | } | 3622 | } |
| 3444 | // Skip resource deallocation in release builds; let the OS do it. | ||
| 3445 | return cleanExit(); | ||
| 3446 | } | 3623 | } |
| 3447 | 3624 | ||
| 3448 | const ModuleDepIterator = struct { | 3625 | const ModuleDepIterator = struct { |
| ... | @@ -3530,7 +3707,6 @@ fn runOrTest( | ... | @@ -3530,7 +3707,6 @@ fn runOrTest( |
| 3530 | self_exe_path: []const u8, | 3707 | self_exe_path: []const u8, |
| 3531 | arg_mode: ArgMode, | 3708 | arg_mode: ArgMode, |
| 3532 | target_info: std.zig.system.NativeTargetInfo, | 3709 | target_info: std.zig.system.NativeTargetInfo, |
| 3533 | watch: bool, | ||
| 3534 | comp_destroyed: *bool, | 3710 | comp_destroyed: *bool, |
| 3535 | all_args: []const []const u8, | 3711 | all_args: []const []const u8, |
| 3536 | runtime_args_start: ?usize, | 3712 | runtime_args_start: ?usize, |
| ... | @@ -3561,7 +3737,7 @@ fn runOrTest( | ... | @@ -3561,7 +3737,7 @@ fn runOrTest( |
| 3561 | 3737 | ||
| 3562 | // We do not execve for tests because if the test fails we want to print | 3738 | // We do not execve for tests because if the test fails we want to print |
| 3563 | // the error message and invocation below. | 3739 | // the error message and invocation below. |
| 3564 | if (std.process.can_execv and arg_mode == .run and !watch) { | 3740 | if (std.process.can_execv and arg_mode == .run) { |
| 3565 | // execv releases the locks; no need to destroy the Compilation here. | 3741 | // execv releases the locks; no need to destroy the Compilation here. |
| 3566 | const err = std.process.execve(gpa, argv.items, &env_map); | 3742 | const err = std.process.execve(gpa, argv.items, &env_map); |
| 3567 | try warnAboutForeignBinaries(arena, arg_mode, target_info, link_libc); | 3743 | try warnAboutForeignBinaries(arena, arg_mode, target_info, link_libc); |
| ... | @@ -3574,12 +3750,10 @@ fn runOrTest( | ... | @@ -3574,12 +3750,10 @@ fn runOrTest( |
| 3574 | child.stdout_behavior = .Inherit; | 3750 | child.stdout_behavior = .Inherit; |
| 3575 | child.stderr_behavior = .Inherit; | 3751 | child.stderr_behavior = .Inherit; |
| 3576 | 3752 | ||
| 3577 | if (!watch) { | 3753 | // Here we release all the locks associated with the Compilation so |
| 3578 | // Here we release all the locks associated with the Compilation so | 3754 | // that whatever this child process wants to do won't deadlock. |
| 3579 | // that whatever this child process wants to do won't deadlock. | 3755 | comp.destroy(); |
| 3580 | comp.destroy(); | 3756 | comp_destroyed.* = true; |
| 3581 | comp_destroyed.* = true; | ||
| 3582 | } | ||
| 3583 | 3757 | ||
| 3584 | const term = child.spawnAndWait() catch |err| { | 3758 | const term = child.spawnAndWait() catch |err| { |
| 3585 | try warnAboutForeignBinaries(arena, arg_mode, target_info, link_libc); | 3759 | try warnAboutForeignBinaries(arena, arg_mode, target_info, link_libc); |
| ... | @@ -3591,19 +3765,13 @@ fn runOrTest( | ... | @@ -3591,19 +3765,13 @@ fn runOrTest( |
| 3591 | switch (term) { | 3765 | switch (term) { |
| 3592 | .Exited => |code| { | 3766 | .Exited => |code| { |
| 3593 | if (code == 0) { | 3767 | if (code == 0) { |
| 3594 | if (!watch) return cleanExit(); | 3768 | return cleanExit(); |
| 3595 | } else if (watch) { | ||
| 3596 | warn("process exited with code {d}", .{code}); | ||
| 3597 | } else { | 3769 | } else { |
| 3598 | process.exit(code); | 3770 | process.exit(code); |
| 3599 | } | 3771 | } |
| 3600 | }, | 3772 | }, |
| 3601 | else => { | 3773 | else => { |
| 3602 | if (watch) { | 3774 | process.exit(1); |
| 3603 | warn("process aborted abnormally", .{}); | ||
| 3604 | } else { | ||
| 3605 | process.exit(1); | ||
| 3606 | } | ||
| 3607 | }, | 3775 | }, |
| 3608 | } | 3776 | } |
| 3609 | }, | 3777 | }, |
| ... | @@ -3611,7 +3779,7 @@ fn runOrTest( | ... | @@ -3611,7 +3779,7 @@ fn runOrTest( |
| 3611 | switch (term) { | 3779 | switch (term) { |
| 3612 | .Exited => |code| { | 3780 | .Exited => |code| { |
| 3613 | if (code == 0) { | 3781 | if (code == 0) { |
| 3614 | if (!watch) return cleanExit(); | 3782 | return cleanExit(); |
| 3615 | } else { | 3783 | } else { |
| 3616 | const cmd = try std.mem.join(arena, " ", argv.items); | 3784 | const cmd = try std.mem.join(arena, " ", argv.items); |
| 3617 | fatal("the following test command failed with exit code {d}:\n{s}", .{ code, cmd }); | 3785 | fatal("the following test command failed with exit code {d}:\n{s}", .{ code, cmd }); |
| ... | @@ -3631,6 +3799,62 @@ fn runOrTest( | ... | @@ -3631,6 +3799,62 @@ fn runOrTest( |
| 3631 | } | 3799 | } |
| 3632 | } | 3800 | } |
| 3633 | 3801 | ||
| 3802 | fn runOrTestHotSwap( | ||
| 3803 | comp: *Compilation, | ||
| 3804 | gpa: Allocator, | ||
| 3805 | test_exec_args: []const ?[]const u8, | ||
| 3806 | self_exe_path: []const u8, | ||
| 3807 | arg_mode: ArgMode, | ||
| 3808 | all_args: []const []const u8, | ||
| 3809 | runtime_args_start: ?usize, | ||
| 3810 | ) !std.ChildProcess.Id { | ||
| 3811 | const exe_emit = comp.bin_file.options.emit.?; | ||
| 3812 | // A naive `directory.join` here will indeed get the correct path to the binary, | ||
| 3813 | // however, in the case of cwd, we actually want `./foo` so that the path can be executed. | ||
| 3814 | const exe_path = try fs.path.join(gpa, &[_][]const u8{ | ||
| 3815 | exe_emit.directory.path orelse ".", exe_emit.sub_path, | ||
| 3816 | }); | ||
| 3817 | defer gpa.free(exe_path); | ||
| 3818 | |||
| 3819 | var argv = std.ArrayList([]const u8).init(gpa); | ||
| 3820 | defer argv.deinit(); | ||
| 3821 | |||
| 3822 | if (test_exec_args.len == 0) { | ||
| 3823 | // when testing pass the zig_exe_path to argv | ||
| 3824 | if (arg_mode == .zig_test) | ||
| 3825 | try argv.appendSlice(&[_][]const u8{ | ||
| 3826 | exe_path, self_exe_path, | ||
| 3827 | }) | ||
| 3828 | // when running just pass the current exe | ||
| 3829 | else | ||
| 3830 | try argv.appendSlice(&[_][]const u8{ | ||
| 3831 | exe_path, | ||
| 3832 | }); | ||
| 3833 | } else { | ||
| 3834 | for (test_exec_args) |arg| { | ||
| 3835 | if (arg) |a| { | ||
| 3836 | try argv.append(a); | ||
| 3837 | } else { | ||
| 3838 | try argv.appendSlice(&[_][]const u8{ | ||
| 3839 | exe_path, self_exe_path, | ||
| 3840 | }); | ||
| 3841 | } | ||
| 3842 | } | ||
| 3843 | } | ||
| 3844 | if (runtime_args_start) |i| { | ||
| 3845 | try argv.appendSlice(all_args[i..]); | ||
| 3846 | } | ||
| 3847 | var child = std.ChildProcess.init(argv.items, gpa); | ||
| 3848 | |||
| 3849 | child.stdin_behavior = .Inherit; | ||
| 3850 | child.stdout_behavior = .Inherit; | ||
| 3851 | child.stderr_behavior = .Inherit; | ||
| 3852 | |||
| 3853 | try child.spawn(); | ||
| 3854 | |||
| 3855 | return child.id; | ||
| 3856 | } | ||
| 3857 | |||
| 3634 | const AfterUpdateHook = union(enum) { | 3858 | const AfterUpdateHook = union(enum) { |
| 3635 | none, | 3859 | none, |
| 3636 | print_emit_bin_dir_path, | 3860 | print_emit_bin_dir_path, |
| ... | @@ -3638,24 +3862,30 @@ const AfterUpdateHook = union(enum) { | ... | @@ -3638,24 +3862,30 @@ const AfterUpdateHook = union(enum) { |
| 3638 | }; | 3862 | }; |
| 3639 | 3863 | ||
| 3640 | fn updateModule(gpa: Allocator, comp: *Compilation, hook: AfterUpdateHook) !void { | 3864 | fn updateModule(gpa: Allocator, comp: *Compilation, hook: AfterUpdateHook) !void { |
| 3641 | try comp.update(); | 3865 | { |
| 3866 | // If the terminal is dumb, we dont want to show the user all the output. | ||
| 3867 | var progress: std.Progress = .{ .dont_print_on_dumb = true }; | ||
| 3868 | const main_progress_node = progress.start("", 0); | ||
| 3869 | defer main_progress_node.end(); | ||
| 3870 | switch (comp.color) { | ||
| 3871 | .off => { | ||
| 3872 | progress.terminal = null; | ||
| 3873 | }, | ||
| 3874 | .on => { | ||
| 3875 | progress.terminal = std.io.getStdErr(); | ||
| 3876 | progress.supports_ansi_escape_codes = true; | ||
| 3877 | }, | ||
| 3878 | .auto => {}, | ||
| 3879 | } | ||
| 3880 | |||
| 3881 | try comp.update(main_progress_node); | ||
| 3882 | } | ||
| 3642 | 3883 | ||
| 3643 | var errors = try comp.getAllErrorsAlloc(); | 3884 | var errors = try comp.getAllErrorsAlloc(); |
| 3644 | defer errors.deinit(comp.gpa); | 3885 | defer errors.deinit(comp.gpa); |
| 3645 | 3886 | ||
| 3646 | if (errors.list.len != 0) { | 3887 | if (errors.errorMessageCount() > 0) { |
| 3647 | const ttyconf: std.debug.TTY.Config = switch (comp.color) { | 3888 | errors.renderToStdErr(renderOptions(comp.color)); |
| 3648 | .auto => std.debug.detectTTYConfig(std.io.getStdErr()), | ||
| 3649 | .on => .escape_codes, | ||
| 3650 | .off => .no_color, | ||
| 3651 | }; | ||
| 3652 | for (errors.list) |full_err_msg| { | ||
| 3653 | full_err_msg.renderToStdErr(ttyconf); | ||
| 3654 | } | ||
| 3655 | const log_text = comp.getCompileLogOutput(); | ||
| 3656 | if (log_text.len != 0) { | ||
| 3657 | std.debug.print("\nCompile Log Output:\n{s}", .{log_text}); | ||
| 3658 | } | ||
| 3659 | return error.SemanticAnalyzeFail; | 3889 | return error.SemanticAnalyzeFail; |
| 3660 | } else switch (hook) { | 3890 | } else switch (hook) { |
| 3661 | .none => {}, | 3891 | .none => {}, |
| ... | @@ -3697,7 +3927,12 @@ fn updateModule(gpa: Allocator, comp: *Compilation, hook: AfterUpdateHook) !void | ... | @@ -3697,7 +3927,12 @@ fn updateModule(gpa: Allocator, comp: *Compilation, hook: AfterUpdateHook) !void |
| 3697 | } | 3927 | } |
| 3698 | } | 3928 | } |
| 3699 | 3929 | ||
| 3700 | fn cmdTranslateC(comp: *Compilation, arena: Allocator, enable_cache: bool) !void { | 3930 | const TranslateCOutput = struct { |
| 3931 | path: []const u8, | ||
| 3932 | cache_hit: bool, | ||
| 3933 | }; | ||
| 3934 | |||
| 3935 | fn cmdTranslateC(comp: *Compilation, arena: Allocator, fancy_output: ?*TranslateCOutput) !void { | ||
| 3701 | if (!build_options.have_llvm) | 3936 | if (!build_options.have_llvm) |
| 3702 | fatal("cannot translate-c: compiler built without LLVM extensions", .{}); | 3937 | fatal("cannot translate-c: compiler built without LLVM extensions", .{}); |
| 3703 | 3938 | ||
| ... | @@ -3708,14 +3943,16 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, enable_cache: bool) !void | ... | @@ -3708,14 +3943,16 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, enable_cache: bool) !void |
| 3708 | 3943 | ||
| 3709 | var man: Cache.Manifest = comp.obtainCObjectCacheManifest(); | 3944 | var man: Cache.Manifest = comp.obtainCObjectCacheManifest(); |
| 3710 | man.want_shared_lock = false; | 3945 | man.want_shared_lock = false; |
| 3711 | defer if (enable_cache) man.deinit(); | 3946 | defer man.deinit(); |
| 3712 | 3947 | ||
| 3713 | man.hash.add(@as(u16, 0xb945)); // Random number to distinguish translate-c from compiling C objects | 3948 | man.hash.add(@as(u16, 0xb945)); // Random number to distinguish translate-c from compiling C objects |
| 3714 | Compilation.cache_helpers.hashCSource(&man, c_source_file) catch |err| { | 3949 | Compilation.cache_helpers.hashCSource(&man, c_source_file) catch |err| { |
| 3715 | fatal("unable to process '{s}': {s}", .{ c_source_file.src_path, @errorName(err) }); | 3950 | fatal("unable to process '{s}': {s}", .{ c_source_file.src_path, @errorName(err) }); |
| 3716 | }; | 3951 | }; |
| 3717 | 3952 | ||
| 3953 | if (fancy_output) |p| p.cache_hit = true; | ||
| 3718 | const digest = if (try man.hit()) man.final() else digest: { | 3954 | const digest = if (try man.hit()) man.final() else digest: { |
| 3955 | if (fancy_output) |p| p.cache_hit = false; | ||
| 3719 | var argv = std.ArrayList([]const u8).init(arena); | 3956 | var argv = std.ArrayList([]const u8).init(arena); |
| 3720 | try argv.append(""); // argv[0] is program name, actual args start at [1] | 3957 | try argv.append(""); // argv[0] is program name, actual args start at [1] |
| 3721 | 3958 | ||
| ... | @@ -3766,6 +4003,7 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, enable_cache: bool) !void | ... | @@ -3766,6 +4003,7 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, enable_cache: bool) !void |
| 3766 | error.OutOfMemory => return error.OutOfMemory, | 4003 | error.OutOfMemory => return error.OutOfMemory, |
| 3767 | error.ASTUnitFailure => fatal("clang API returned errors but due to a clang bug, it is not exposing the errors for zig to see. For more details: https://github.com/ziglang/zig/issues/4455", .{}), | 4004 | error.ASTUnitFailure => fatal("clang API returned errors but due to a clang bug, it is not exposing the errors for zig to see. For more details: https://github.com/ziglang/zig/issues/4455", .{}), |
| 3768 | error.SemanticAnalyzeFail => { | 4005 | error.SemanticAnalyzeFail => { |
| 4006 | // TODO convert these to zig errors | ||
| 3769 | for (clang_errors) |clang_err| { | 4007 | for (clang_errors) |clang_err| { |
| 3770 | std.debug.print("{s}:{d}:{d}: {s}\n", .{ | 4008 | std.debug.print("{s}:{d}:{d}: {s}\n", .{ |
| 3771 | if (clang_err.filename_ptr) |p| p[0..clang_err.filename_len] else "(no file)", | 4009 | if (clang_err.filename_ptr) |p| p[0..clang_err.filename_len] else "(no file)", |
| ... | @@ -3810,12 +4048,11 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, enable_cache: bool) !void | ... | @@ -3810,12 +4048,11 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, enable_cache: bool) !void |
| 3810 | break :digest digest; | 4048 | break :digest digest; |
| 3811 | }; | 4049 | }; |
| 3812 | 4050 | ||
| 3813 | if (enable_cache) { | 4051 | if (fancy_output) |p| { |
| 3814 | const full_zig_path = try comp.local_cache_directory.join(arena, &[_][]const u8{ | 4052 | const full_zig_path = try comp.local_cache_directory.join(arena, &[_][]const u8{ |
| 3815 | "o", &digest, translated_zig_basename, | 4053 | "o", &digest, translated_zig_basename, |
| 3816 | }); | 4054 | }); |
| 3817 | try io.getStdOut().writer().print("{s}\n", .{full_zig_path}); | 4055 | p.path = full_zig_path; |
| 3818 | return cleanExit(); | ||
| 3819 | } else { | 4056 | } else { |
| 3820 | const out_zig_path = try fs.path.join(arena, &[_][]const u8{ "o", &digest, translated_zig_basename }); | 4057 | const out_zig_path = try fs.path.join(arena, &[_][]const u8{ "o", &digest, translated_zig_basename }); |
| 3821 | const zig_file = comp.local_cache_directory.handle.openFile(out_zig_path, .{}) catch |err| { | 4058 | const zig_file = comp.local_cache_directory.handle.openFile(out_zig_path, .{}) catch |err| { |
| ... | @@ -4009,6 +4246,8 @@ pub const usage_build = | ... | @@ -4009,6 +4246,8 @@ pub const usage_build = |
| 4009 | \\Options: | 4246 | \\Options: |
| 4010 | \\ -freference-trace[=num] How many lines of reference trace should be shown per compile error | 4247 | \\ -freference-trace[=num] How many lines of reference trace should be shown per compile error |
| 4011 | \\ -fno-reference-trace Disable reference trace | 4248 | \\ -fno-reference-trace Disable reference trace |
| 4249 | \\ -fsummary Print the build summary, even on success | ||
| 4250 | \\ -fno-summary Omit the build summary, even on failure | ||
| 4012 | \\ --build-file [file] Override path to build.zig | 4251 | \\ --build-file [file] Override path to build.zig |
| 4013 | \\ --cache-dir [path] Override path to local Zig cache directory | 4252 | \\ --cache-dir [path] Override path to local Zig cache directory |
| 4014 | \\ --global-cache-dir [path] Override path to global Zig cache directory | 4253 | \\ --global-cache-dir [path] Override path to global Zig cache directory |
| ... | @@ -4021,7 +4260,6 @@ pub const usage_build = | ... | @@ -4021,7 +4260,6 @@ pub const usage_build = |
| 4021 | 4260 | ||
| 4022 | pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { | 4261 | pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { |
| 4023 | var color: Color = .auto; | 4262 | var color: Color = .auto; |
| 4024 | var prominent_compile_errors: bool = false; | ||
| 4025 | 4263 | ||
| 4026 | // We want to release all the locks before executing the child process, so we make a nice | 4264 | // We want to release all the locks before executing the child process, so we make a nice |
| 4027 | // big block here to ensure the cleanup gets run when we extract out our argv. | 4265 | // big block here to ensure the cleanup gets run when we extract out our argv. |
| ... | @@ -4082,8 +4320,6 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi | ... | @@ -4082,8 +4320,6 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi |
| 4082 | i += 1; | 4320 | i += 1; |
| 4083 | override_global_cache_dir = args[i]; | 4321 | override_global_cache_dir = args[i]; |
| 4084 | continue; | 4322 | continue; |
| 4085 | } else if (mem.eql(u8, arg, "--prominent-compile-errors")) { | ||
| 4086 | prominent_compile_errors = true; | ||
| 4087 | } else if (mem.eql(u8, arg, "-freference-trace")) { | 4323 | } else if (mem.eql(u8, arg, "-freference-trace")) { |
| 4088 | try child_argv.append(arg); | 4324 | try child_argv.append(arg); |
| 4089 | reference_trace = 256; | 4325 | reference_trace = 256; |
| ... | @@ -4201,7 +4437,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi | ... | @@ -4201,7 +4437,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi |
| 4201 | .basename = exe_basename, | 4437 | .basename = exe_basename, |
| 4202 | }; | 4438 | }; |
| 4203 | var thread_pool: ThreadPool = undefined; | 4439 | var thread_pool: ThreadPool = undefined; |
| 4204 | try thread_pool.init(gpa); | 4440 | try thread_pool.init(.{ .allocator = gpa }); |
| 4205 | defer thread_pool.deinit(); | 4441 | defer thread_pool.deinit(); |
| 4206 | 4442 | ||
| 4207 | var cleanup_build_runner_dir: ?fs.Dir = null; | 4443 | var cleanup_build_runner_dir: ?fs.Dir = null; |
| ... | @@ -4251,9 +4487,13 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi | ... | @@ -4251,9 +4487,13 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi |
| 4251 | var all_modules: Package.AllModules = .{}; | 4487 | var all_modules: Package.AllModules = .{}; |
| 4252 | defer all_modules.deinit(gpa); | 4488 | defer all_modules.deinit(gpa); |
| 4253 | 4489 | ||
| 4490 | var wip_errors: std.zig.ErrorBundle.Wip = undefined; | ||
| 4491 | try wip_errors.init(gpa); | ||
| 4492 | defer wip_errors.deinit(); | ||
| 4493 | |||
| 4254 | // Here we borrow main package's table and will replace it with a fresh | 4494 | // Here we borrow main package's table and will replace it with a fresh |
| 4255 | // one after this process completes. | 4495 | // one after this process completes. |
| 4256 | build_pkg.fetchAndAddDependencies( | 4496 | const fetch_result = build_pkg.fetchAndAddDependencies( |
| 4257 | &main_pkg, | 4497 | &main_pkg, |
| 4258 | arena, | 4498 | arena, |
| 4259 | &thread_pool, | 4499 | &thread_pool, |
| ... | @@ -4264,12 +4504,16 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi | ... | @@ -4264,12 +4504,16 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi |
| 4264 | &dependencies_source, | 4504 | &dependencies_source, |
| 4265 | &build_roots_source, | 4505 | &build_roots_source, |
| 4266 | "", | 4506 | "", |
| 4267 | color, | 4507 | &wip_errors, |
| 4268 | &all_modules, | 4508 | &all_modules, |
| 4269 | ) catch |err| switch (err) { | 4509 | ); |
| 4270 | error.PackageFetchFailed => process.exit(1), | 4510 | if (wip_errors.root_list.items.len > 0) { |
| 4271 | else => |e| return e, | 4511 | var errors = try wip_errors.toOwnedBundle(""); |
| 4272 | }; | 4512 | defer errors.deinit(gpa); |
| 4513 | errors.renderToStdErr(renderOptions(color)); | ||
| 4514 | process.exit(1); | ||
| 4515 | } | ||
| 4516 | try fetch_result; | ||
| 4273 | 4517 | ||
| 4274 | try dependencies_source.appendSlice("};\npub const build_root = struct {\n"); | 4518 | try dependencies_source.appendSlice("};\npub const build_root = struct {\n"); |
| 4275 | try dependencies_source.appendSlice(build_roots_source.items); | 4519 | try dependencies_source.appendSlice(build_roots_source.items); |
| ... | @@ -4312,7 +4556,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi | ... | @@ -4312,7 +4556,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi |
| 4312 | defer comp.destroy(); | 4556 | defer comp.destroy(); |
| 4313 | 4557 | ||
| 4314 | updateModule(gpa, comp, .none) catch |err| switch (err) { | 4558 | updateModule(gpa, comp, .none) catch |err| switch (err) { |
| 4315 | error.SemanticAnalyzeFail => process.exit(1), | 4559 | error.SemanticAnalyzeFail => process.exit(2), |
| 4316 | else => |e| return e, | 4560 | else => |e| return e, |
| 4317 | }; | 4561 | }; |
| 4318 | try comp.makeBinFileExecutable(); | 4562 | try comp.makeBinFileExecutable(); |
| ... | @@ -4336,13 +4580,13 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi | ... | @@ -4336,13 +4580,13 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi |
| 4336 | switch (term) { | 4580 | switch (term) { |
| 4337 | .Exited => |code| { | 4581 | .Exited => |code| { |
| 4338 | if (code == 0) return cleanExit(); | 4582 | if (code == 0) return cleanExit(); |
| 4583 | // Indicates that the build runner has reported compile errors | ||
| 4584 | // and this parent process does not need to report any further | ||
| 4585 | // diagnostics. | ||
| 4586 | if (code == 2) process.exit(2); | ||
| 4339 | 4587 | ||
| 4340 | if (prominent_compile_errors) { | 4588 | const cmd = try std.mem.join(arena, " ", child_argv); |
| 4341 | fatal("the build command failed with exit code {d}", .{code}); | 4589 | fatal("the following build command failed with exit code {d}:\n{s}", .{ code, cmd }); |
| 4342 | } else { | ||
| 4343 | const cmd = try std.mem.join(arena, " ", child_argv); | ||
| 4344 | fatal("the following build command failed with exit code {d}:\n{s}", .{ code, cmd }); | ||
| 4345 | } | ||
| 4346 | }, | 4590 | }, |
| 4347 | else => { | 4591 | else => { |
| 4348 | const cmd = try std.mem.join(arena, " ", child_argv); | 4592 | const cmd = try std.mem.join(arena, " ", child_argv); |
| ... | @@ -4356,7 +4600,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi | ... | @@ -4356,7 +4600,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi |
| 4356 | } | 4600 | } |
| 4357 | 4601 | ||
| 4358 | fn readSourceFileToEndAlloc( | 4602 | fn readSourceFileToEndAlloc( |
| 4359 | allocator: mem.Allocator, | 4603 | allocator: Allocator, |
| 4360 | input: *const fs.File, | 4604 | input: *const fs.File, |
| 4361 | size_hint: ?usize, | 4605 | size_hint: ?usize, |
| 4362 | ) ![:0]u8 { | 4606 | ) ![:0]u8 { |
| ... | @@ -4500,12 +4744,7 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void | ... | @@ -4500,12 +4744,7 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void |
| 4500 | }; | 4744 | }; |
| 4501 | defer tree.deinit(gpa); | 4745 | defer tree.deinit(gpa); |
| 4502 | 4746 | ||
| 4503 | try printErrsMsgToStdErr(gpa, arena, tree, "<stdin>", color); | ||
| 4504 | var has_ast_error = false; | ||
| 4505 | if (check_ast_flag) { | 4747 | if (check_ast_flag) { |
| 4506 | const Module = @import("Module.zig"); | ||
| 4507 | const AstGen = @import("AstGen.zig"); | ||
| 4508 | |||
| 4509 | var file: Module.File = .{ | 4748 | var file: Module.File = .{ |
| 4510 | .status = .never_loaded, | 4749 | .status = .never_loaded, |
| 4511 | .source_loaded = true, | 4750 | .source_loaded = true, |
| ... | @@ -4528,25 +4767,18 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void | ... | @@ -4528,25 +4767,18 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void |
| 4528 | defer file.zir.deinit(gpa); | 4767 | defer file.zir.deinit(gpa); |
| 4529 | 4768 | ||
| 4530 | if (file.zir.hasCompileErrors()) { | 4769 | if (file.zir.hasCompileErrors()) { |
| 4531 | var arena_instance = std.heap.ArenaAllocator.init(gpa); | 4770 | var wip_errors: std.zig.ErrorBundle.Wip = undefined; |
| 4532 | defer arena_instance.deinit(); | 4771 | try wip_errors.init(gpa); |
| 4533 | var errors = std.ArrayList(Compilation.AllErrors.Message).init(gpa); | 4772 | defer wip_errors.deinit(); |
| 4534 | defer errors.deinit(); | 4773 | try Compilation.addZirErrorMessages(&wip_errors, &file); |
| 4535 | 4774 | var error_bundle = try wip_errors.toOwnedBundle(""); | |
| 4536 | try Compilation.AllErrors.addZir(arena_instance.allocator(), &errors, &file); | 4775 | defer error_bundle.deinit(gpa); |
| 4537 | const ttyconf: std.debug.TTY.Config = switch (color) { | 4776 | error_bundle.renderToStdErr(renderOptions(color)); |
| 4538 | .auto => std.debug.detectTTYConfig(std.io.getStdErr()), | 4777 | process.exit(2); |
| 4539 | .on => .escape_codes, | ||
| 4540 | .off => .no_color, | ||
| 4541 | }; | ||
| 4542 | for (errors.items) |full_err_msg| { | ||
| 4543 | full_err_msg.renderToStdErr(ttyconf); | ||
| 4544 | } | ||
| 4545 | has_ast_error = true; | ||
| 4546 | } | 4778 | } |
| 4547 | } | 4779 | } else if (tree.errors.len != 0) { |
| 4548 | if (tree.errors.len != 0 or has_ast_error) { | 4780 | try printAstErrorsToStderr(gpa, tree, "<stdin>", color); |
| 4549 | process.exit(1); | 4781 | process.exit(2); |
| 4550 | } | 4782 | } |
| 4551 | const formatted = try tree.render(gpa); | 4783 | const formatted = try tree.render(gpa); |
| 4552 | defer gpa.free(formatted); | 4784 | defer gpa.free(formatted); |
| ... | @@ -4688,12 +4920,13 @@ fn fmtPathFile( | ... | @@ -4688,12 +4920,13 @@ fn fmtPathFile( |
| 4688 | if (stat.kind == .Directory) | 4920 | if (stat.kind == .Directory) |
| 4689 | return error.IsDir; | 4921 | return error.IsDir; |
| 4690 | 4922 | ||
| 4923 | const gpa = fmt.gpa; | ||
| 4691 | const source_code = try readSourceFileToEndAlloc( | 4924 | const source_code = try readSourceFileToEndAlloc( |
| 4692 | fmt.gpa, | 4925 | gpa, |
| 4693 | &source_file, | 4926 | &source_file, |
| 4694 | std.math.cast(usize, stat.size) orelse return error.FileTooBig, | 4927 | std.math.cast(usize, stat.size) orelse return error.FileTooBig, |
| 4695 | ); | 4928 | ); |
| 4696 | defer fmt.gpa.free(source_code); | 4929 | defer gpa.free(source_code); |
| 4697 | 4930 | ||
| 4698 | source_file.close(); | 4931 | source_file.close(); |
| 4699 | file_closed = true; | 4932 | file_closed = true; |
| ... | @@ -4701,19 +4934,16 @@ fn fmtPathFile( | ... | @@ -4701,19 +4934,16 @@ fn fmtPathFile( |
| 4701 | // Add to set after no longer possible to get error.IsDir. | 4934 | // Add to set after no longer possible to get error.IsDir. |
| 4702 | if (try fmt.seen.fetchPut(stat.inode, {})) |_| return; | 4935 | if (try fmt.seen.fetchPut(stat.inode, {})) |_| return; |
| 4703 | 4936 | ||
| 4704 | var tree = try Ast.parse(fmt.gpa, source_code, .zig); | 4937 | var tree = try Ast.parse(gpa, source_code, .zig); |
| 4705 | defer tree.deinit(fmt.gpa); | 4938 | defer tree.deinit(gpa); |
| 4706 | 4939 | ||
| 4707 | try printErrsMsgToStdErr(fmt.gpa, fmt.arena, tree, file_path, fmt.color); | ||
| 4708 | if (tree.errors.len != 0) { | 4940 | if (tree.errors.len != 0) { |
| 4941 | try printAstErrorsToStderr(gpa, tree, file_path, fmt.color); | ||
| 4709 | fmt.any_error = true; | 4942 | fmt.any_error = true; |
| 4710 | return; | 4943 | return; |
| 4711 | } | 4944 | } |
| 4712 | 4945 | ||
| 4713 | if (fmt.check_ast) { | 4946 | if (fmt.check_ast) { |
| 4714 | const Module = @import("Module.zig"); | ||
| 4715 | const AstGen = @import("AstGen.zig"); | ||
| 4716 | |||
| 4717 | var file: Module.File = .{ | 4947 | var file: Module.File = .{ |
| 4718 | .status = .never_loaded, | 4948 | .status = .never_loaded, |
| 4719 | .source_loaded = true, | 4949 | .source_loaded = true, |
| ... | @@ -4732,31 +4962,24 @@ fn fmtPathFile( | ... | @@ -4732,31 +4962,24 @@ fn fmtPathFile( |
| 4732 | .root_decl = .none, | 4962 | .root_decl = .none, |
| 4733 | }; | 4963 | }; |
| 4734 | 4964 | ||
| 4735 | file.pkg = try Package.create(fmt.gpa, null, file.sub_file_path); | 4965 | file.pkg = try Package.create(gpa, null, file.sub_file_path); |
| 4736 | defer file.pkg.destroy(fmt.gpa); | 4966 | defer file.pkg.destroy(gpa); |
| 4737 | 4967 | ||
| 4738 | if (stat.size > max_src_size) | 4968 | if (stat.size > max_src_size) |
| 4739 | return error.FileTooBig; | 4969 | return error.FileTooBig; |
| 4740 | 4970 | ||
| 4741 | file.zir = try AstGen.generate(fmt.gpa, file.tree); | 4971 | file.zir = try AstGen.generate(gpa, file.tree); |
| 4742 | file.zir_loaded = true; | 4972 | file.zir_loaded = true; |
| 4743 | defer file.zir.deinit(fmt.gpa); | 4973 | defer file.zir.deinit(gpa); |
| 4744 | 4974 | ||
| 4745 | if (file.zir.hasCompileErrors()) { | 4975 | if (file.zir.hasCompileErrors()) { |
| 4746 | var arena_instance = std.heap.ArenaAllocator.init(fmt.gpa); | 4976 | var wip_errors: std.zig.ErrorBundle.Wip = undefined; |
| 4747 | defer arena_instance.deinit(); | 4977 | try wip_errors.init(gpa); |
| 4748 | var errors = std.ArrayList(Compilation.AllErrors.Message).init(fmt.gpa); | 4978 | defer wip_errors.deinit(); |
| 4749 | defer errors.deinit(); | 4979 | try Compilation.addZirErrorMessages(&wip_errors, &file); |
| 4750 | 4980 | var error_bundle = try wip_errors.toOwnedBundle(""); | |
| 4751 | try Compilation.AllErrors.addZir(arena_instance.allocator(), &errors, &file); | 4981 | defer error_bundle.deinit(gpa); |
| 4752 | const ttyconf: std.debug.TTY.Config = switch (fmt.color) { | 4982 | error_bundle.renderToStdErr(renderOptions(fmt.color)); |
| 4753 | .auto => std.debug.detectTTYConfig(std.io.getStdErr()), | ||
| 4754 | .on => .escape_codes, | ||
| 4755 | .off => .no_color, | ||
| 4756 | }; | ||
| 4757 | for (errors.items) |full_err_msg| { | ||
| 4758 | full_err_msg.renderToStdErr(ttyconf); | ||
| 4759 | } | ||
| 4760 | fmt.any_error = true; | 4983 | fmt.any_error = true; |
| 4761 | } | 4984 | } |
| 4762 | } | 4985 | } |
| ... | @@ -4784,100 +5007,50 @@ fn fmtPathFile( | ... | @@ -4784,100 +5007,50 @@ fn fmtPathFile( |
| 4784 | } | 5007 | } |
| 4785 | } | 5008 | } |
| 4786 | 5009 | ||
| 4787 | pub fn printErrsMsgToStdErr( | 5010 | fn printAstErrorsToStderr(gpa: Allocator, tree: Ast, path: []const u8, color: Color) !void { |
| 4788 | gpa: mem.Allocator, | 5011 | var wip_errors: std.zig.ErrorBundle.Wip = undefined; |
| 4789 | arena: mem.Allocator, | 5012 | try wip_errors.init(gpa); |
| 5013 | defer wip_errors.deinit(); | ||
| 5014 | |||
| 5015 | try putAstErrorsIntoBundle(gpa, tree, path, &wip_errors); | ||
| 5016 | |||
| 5017 | var error_bundle = try wip_errors.toOwnedBundle(""); | ||
| 5018 | defer error_bundle.deinit(gpa); | ||
| 5019 | error_bundle.renderToStdErr(renderOptions(color)); | ||
| 5020 | } | ||
| 5021 | |||
| 5022 | pub fn putAstErrorsIntoBundle( | ||
| 5023 | gpa: Allocator, | ||
| 4790 | tree: Ast, | 5024 | tree: Ast, |
| 4791 | path: []const u8, | 5025 | path: []const u8, |
| 4792 | color: Color, | 5026 | wip_errors: *std.zig.ErrorBundle.Wip, |
| 4793 | ) !void { | 5027 | ) !void { |
| 4794 | const parse_errors: []const Ast.Error = tree.errors; | 5028 | var file: Module.File = .{ |
| 4795 | var i: usize = 0; | 5029 | .status = .never_loaded, |
| 4796 | while (i < parse_errors.len) : (i += 1) { | 5030 | .source_loaded = true, |
| 4797 | const parse_error = parse_errors[i]; | 5031 | .zir_loaded = false, |
| 4798 | const lok_token = parse_error.token; | 5032 | .sub_file_path = path, |
| 4799 | const token_tags = tree.tokens.items(.tag); | 5033 | .source = tree.source, |
| 4800 | const start_loc = tree.tokenLocation(0, lok_token); | 5034 | .stat = .{ |
| 4801 | const source_line = tree.source[start_loc.line_start..start_loc.line_end]; | 5035 | .size = 0, |
| 4802 | 5036 | .inode = 0, | |
| 4803 | var text_buf = std.ArrayList(u8).init(gpa); | 5037 | .mtime = 0, |
| 4804 | defer text_buf.deinit(); | 5038 | }, |
| 4805 | const writer = text_buf.writer(); | 5039 | .tree = tree, |
| 4806 | try tree.renderError(parse_error, writer); | 5040 | .tree_loaded = true, |
| 4807 | const text = try arena.dupe(u8, text_buf.items); | 5041 | .zir = undefined, |
| 4808 | 5042 | .pkg = undefined, | |
| 4809 | var notes_buffer: [2]Compilation.AllErrors.Message = undefined; | 5043 | .root_decl = .none, |
| 4810 | var notes_len: usize = 0; | 5044 | }; |
| 4811 | |||
| 4812 | if (token_tags[parse_error.token + @boolToInt(parse_error.token_is_prev)] == .invalid) { | ||
| 4813 | const bad_off = @intCast(u32, tree.tokenSlice(parse_error.token + @boolToInt(parse_error.token_is_prev)).len); | ||
| 4814 | const byte_offset = @intCast(u32, start_loc.line_start) + @intCast(u32, start_loc.column) + bad_off; | ||
| 4815 | notes_buffer[notes_len] = .{ | ||
| 4816 | .src = .{ | ||
| 4817 | .src_path = path, | ||
| 4818 | .msg = try std.fmt.allocPrint(arena, "invalid byte: '{'}'", .{ | ||
| 4819 | std.zig.fmtEscapes(tree.source[byte_offset..][0..1]), | ||
| 4820 | }), | ||
| 4821 | .span = .{ .start = byte_offset, .end = byte_offset + 1, .main = byte_offset }, | ||
| 4822 | .line = @intCast(u32, start_loc.line), | ||
| 4823 | .column = @intCast(u32, start_loc.column) + bad_off, | ||
| 4824 | .source_line = source_line, | ||
| 4825 | }, | ||
| 4826 | }; | ||
| 4827 | notes_len += 1; | ||
| 4828 | } | ||
| 4829 | |||
| 4830 | for (parse_errors[i + 1 ..]) |note| { | ||
| 4831 | if (!note.is_note) break; | ||
| 4832 | |||
| 4833 | text_buf.items.len = 0; | ||
| 4834 | try tree.renderError(note, writer); | ||
| 4835 | const note_loc = tree.tokenLocation(0, note.token); | ||
| 4836 | const byte_offset = @intCast(u32, note_loc.line_start); | ||
| 4837 | notes_buffer[notes_len] = .{ | ||
| 4838 | .src = .{ | ||
| 4839 | .src_path = path, | ||
| 4840 | .msg = try arena.dupe(u8, text_buf.items), | ||
| 4841 | .span = .{ | ||
| 4842 | .start = byte_offset, | ||
| 4843 | .end = byte_offset + @intCast(u32, tree.tokenSlice(note.token).len), | ||
| 4844 | .main = byte_offset, | ||
| 4845 | }, | ||
| 4846 | .line = @intCast(u32, note_loc.line), | ||
| 4847 | .column = @intCast(u32, note_loc.column), | ||
| 4848 | .source_line = tree.source[note_loc.line_start..note_loc.line_end], | ||
| 4849 | }, | ||
| 4850 | }; | ||
| 4851 | i += 1; | ||
| 4852 | notes_len += 1; | ||
| 4853 | } | ||
| 4854 | 5045 | ||
| 4855 | const extra_offset = tree.errorOffset(parse_error); | 5046 | file.pkg = try Package.create(gpa, null, path); |
| 4856 | const byte_offset = @intCast(u32, start_loc.line_start) + extra_offset; | 5047 | defer file.pkg.destroy(gpa); |
| 4857 | const message: Compilation.AllErrors.Message = .{ | ||
| 4858 | .src = .{ | ||
| 4859 | .src_path = path, | ||
| 4860 | .msg = text, | ||
| 4861 | .span = .{ | ||
| 4862 | .start = byte_offset, | ||
| 4863 | .end = byte_offset + @intCast(u32, tree.tokenSlice(lok_token).len), | ||
| 4864 | .main = byte_offset, | ||
| 4865 | }, | ||
| 4866 | .line = @intCast(u32, start_loc.line), | ||
| 4867 | .column = @intCast(u32, start_loc.column) + extra_offset, | ||
| 4868 | .source_line = source_line, | ||
| 4869 | .notes = notes_buffer[0..notes_len], | ||
| 4870 | }, | ||
| 4871 | }; | ||
| 4872 | 5048 | ||
| 4873 | const ttyconf: std.debug.TTY.Config = switch (color) { | 5049 | file.zir = try AstGen.generate(gpa, file.tree); |
| 4874 | .auto => std.debug.detectTTYConfig(std.io.getStdErr()), | 5050 | file.zir_loaded = true; |
| 4875 | .on => .escape_codes, | 5051 | defer file.zir.deinit(gpa); |
| 4876 | .off => .no_color, | ||
| 4877 | }; | ||
| 4878 | 5052 | ||
| 4879 | message.renderToStdErr(ttyconf); | 5053 | try Compilation.addZirErrorMessages(wip_errors, &file); |
| 4880 | } | ||
| 4881 | } | 5054 | } |
| 4882 | 5055 | ||
| 4883 | pub const info_zen = | 5056 | pub const info_zen = |
| ... | @@ -5325,19 +5498,6 @@ fn detectNativeTargetInfo(cross_target: std.zig.CrossTarget) !std.zig.system.Nat | ... | @@ -5325,19 +5498,6 @@ fn detectNativeTargetInfo(cross_target: std.zig.CrossTarget) !std.zig.system.Nat |
| 5325 | return std.zig.system.NativeTargetInfo.detect(cross_target); | 5498 | return std.zig.system.NativeTargetInfo.detect(cross_target); |
| 5326 | } | 5499 | } |
| 5327 | 5500 | ||
| 5328 | /// Indicate that we are now terminating with a successful exit code. | ||
| 5329 | /// In debug builds, this is a no-op, so that the calling code's | ||
| 5330 | /// cleanup mechanisms are tested and so that external tools that | ||
| 5331 | /// check for resource leaks can be accurate. In release builds, this | ||
| 5332 | /// calls exit(0), and does not return. | ||
| 5333 | pub fn cleanExit() void { | ||
| 5334 | if (builtin.mode == .Debug) { | ||
| 5335 | return; | ||
| 5336 | } else { | ||
| 5337 | process.exit(0); | ||
| 5338 | } | ||
| 5339 | } | ||
| 5340 | |||
| 5341 | const usage_ast_check = | 5501 | const usage_ast_check = |
| 5342 | \\Usage: zig ast-check [file] | 5502 | \\Usage: zig ast-check [file] |
| 5343 | \\ | 5503 | \\ |
| ... | @@ -5360,8 +5520,6 @@ pub fn cmdAstCheck( | ... | @@ -5360,8 +5520,6 @@ pub fn cmdAstCheck( |
| 5360 | arena: Allocator, | 5520 | arena: Allocator, |
| 5361 | args: []const []const u8, | 5521 | args: []const []const u8, |
| 5362 | ) !void { | 5522 | ) !void { |
| 5363 | const Module = @import("Module.zig"); | ||
| 5364 | const AstGen = @import("AstGen.zig"); | ||
| 5365 | const Zir = @import("Zir.zig"); | 5523 | const Zir = @import("Zir.zig"); |
| 5366 | 5524 | ||
| 5367 | var color: Color = .auto; | 5525 | var color: Color = .auto; |
| ... | @@ -5451,26 +5609,18 @@ pub fn cmdAstCheck( | ... | @@ -5451,26 +5609,18 @@ pub fn cmdAstCheck( |
| 5451 | file.tree_loaded = true; | 5609 | file.tree_loaded = true; |
| 5452 | defer file.tree.deinit(gpa); | 5610 | defer file.tree.deinit(gpa); |
| 5453 | 5611 | ||
| 5454 | try printErrsMsgToStdErr(gpa, arena, file.tree, file.sub_file_path, color); | ||
| 5455 | if (file.tree.errors.len != 0) { | ||
| 5456 | process.exit(1); | ||
| 5457 | } | ||
| 5458 | |||
| 5459 | file.zir = try AstGen.generate(gpa, file.tree); | 5612 | file.zir = try AstGen.generate(gpa, file.tree); |
| 5460 | file.zir_loaded = true; | 5613 | file.zir_loaded = true; |
| 5461 | defer file.zir.deinit(gpa); | 5614 | defer file.zir.deinit(gpa); |
| 5462 | 5615 | ||
| 5463 | if (file.zir.hasCompileErrors()) { | 5616 | if (file.zir.hasCompileErrors()) { |
| 5464 | var errors = std.ArrayList(Compilation.AllErrors.Message).init(arena); | 5617 | var wip_errors: std.zig.ErrorBundle.Wip = undefined; |
| 5465 | try Compilation.AllErrors.addZir(arena, &errors, &file); | 5618 | try wip_errors.init(gpa); |
| 5466 | const ttyconf: std.debug.TTY.Config = switch (color) { | 5619 | defer wip_errors.deinit(); |
| 5467 | .auto => std.debug.detectTTYConfig(std.io.getStdErr()), | 5620 | try Compilation.addZirErrorMessages(&wip_errors, &file); |
| 5468 | .on => .escape_codes, | 5621 | var error_bundle = try wip_errors.toOwnedBundle(""); |
| 5469 | .off => .no_color, | 5622 | defer error_bundle.deinit(gpa); |
| 5470 | }; | 5623 | error_bundle.renderToStdErr(renderOptions(color)); |
| 5471 | for (errors.items) |full_err_msg| { | ||
| 5472 | full_err_msg.renderToStdErr(ttyconf); | ||
| 5473 | } | ||
| 5474 | process.exit(1); | 5624 | process.exit(1); |
| 5475 | } | 5625 | } |
| 5476 | 5626 | ||
| ... | @@ -5528,8 +5678,7 @@ pub fn cmdChangelist( | ... | @@ -5528,8 +5678,7 @@ pub fn cmdChangelist( |
| 5528 | arena: Allocator, | 5678 | arena: Allocator, |
| 5529 | args: []const []const u8, | 5679 | args: []const []const u8, |
| 5530 | ) !void { | 5680 | ) !void { |
| 5531 | const Module = @import("Module.zig"); | 5681 | const color: Color = .auto; |
| 5532 | const AstGen = @import("AstGen.zig"); | ||
| 5533 | const Zir = @import("Zir.zig"); | 5682 | const Zir = @import("Zir.zig"); |
| 5534 | 5683 | ||
| 5535 | const old_source_file = args[0]; | 5684 | const old_source_file = args[0]; |
| ... | @@ -5577,22 +5726,18 @@ pub fn cmdChangelist( | ... | @@ -5577,22 +5726,18 @@ pub fn cmdChangelist( |
| 5577 | file.tree_loaded = true; | 5726 | file.tree_loaded = true; |
| 5578 | defer file.tree.deinit(gpa); | 5727 | defer file.tree.deinit(gpa); |
| 5579 | 5728 | ||
| 5580 | try printErrsMsgToStdErr(gpa, arena, file.tree, old_source_file, .auto); | ||
| 5581 | if (file.tree.errors.len != 0) { | ||
| 5582 | process.exit(1); | ||
| 5583 | } | ||
| 5584 | |||
| 5585 | file.zir = try AstGen.generate(gpa, file.tree); | 5729 | file.zir = try AstGen.generate(gpa, file.tree); |
| 5586 | file.zir_loaded = true; | 5730 | file.zir_loaded = true; |
| 5587 | defer file.zir.deinit(gpa); | 5731 | defer file.zir.deinit(gpa); |
| 5588 | 5732 | ||
| 5589 | if (file.zir.hasCompileErrors()) { | 5733 | if (file.zir.hasCompileErrors()) { |
| 5590 | var errors = std.ArrayList(Compilation.AllErrors.Message).init(arena); | 5734 | var wip_errors: std.zig.ErrorBundle.Wip = undefined; |
| 5591 | try Compilation.AllErrors.addZir(arena, &errors, &file); | 5735 | try wip_errors.init(gpa); |
| 5592 | const ttyconf = std.debug.detectTTYConfig(std.io.getStdErr()); | 5736 | defer wip_errors.deinit(); |
| 5593 | for (errors.items) |full_err_msg| { | 5737 | try Compilation.addZirErrorMessages(&wip_errors, &file); |
| 5594 | full_err_msg.renderToStdErr(ttyconf); | 5738 | var error_bundle = try wip_errors.toOwnedBundle(""); |
| 5595 | } | 5739 | defer error_bundle.deinit(gpa); |
| 5740 | error_bundle.renderToStdErr(renderOptions(color)); | ||
| 5596 | process.exit(1); | 5741 | process.exit(1); |
| 5597 | } | 5742 | } |
| 5598 | 5743 | ||
| ... | @@ -5614,11 +5759,6 @@ pub fn cmdChangelist( | ... | @@ -5614,11 +5759,6 @@ pub fn cmdChangelist( |
| 5614 | var new_tree = try Ast.parse(gpa, new_source, .zig); | 5759 | var new_tree = try Ast.parse(gpa, new_source, .zig); |
| 5615 | defer new_tree.deinit(gpa); | 5760 | defer new_tree.deinit(gpa); |
| 5616 | 5761 | ||
| 5617 | try printErrsMsgToStdErr(gpa, arena, new_tree, new_source_file, .auto); | ||
| 5618 | if (new_tree.errors.len != 0) { | ||
| 5619 | process.exit(1); | ||
| 5620 | } | ||
| 5621 | |||
| 5622 | var old_zir = file.zir; | 5762 | var old_zir = file.zir; |
| 5623 | defer old_zir.deinit(gpa); | 5763 | defer old_zir.deinit(gpa); |
| 5624 | file.zir_loaded = false; | 5764 | file.zir_loaded = false; |
| ... | @@ -5626,12 +5766,13 @@ pub fn cmdChangelist( | ... | @@ -5626,12 +5766,13 @@ pub fn cmdChangelist( |
| 5626 | file.zir_loaded = true; | 5766 | file.zir_loaded = true; |
| 5627 | 5767 | ||
| 5628 | if (file.zir.hasCompileErrors()) { | 5768 | if (file.zir.hasCompileErrors()) { |
| 5629 | var errors = std.ArrayList(Compilation.AllErrors.Message).init(arena); | 5769 | var wip_errors: std.zig.ErrorBundle.Wip = undefined; |
| 5630 | try Compilation.AllErrors.addZir(arena, &errors, &file); | 5770 | try wip_errors.init(gpa); |
| 5631 | const ttyconf = std.debug.detectTTYConfig(std.io.getStdErr()); | 5771 | defer wip_errors.deinit(); |
| 5632 | for (errors.items) |full_err_msg| { | 5772 | try Compilation.addZirErrorMessages(&wip_errors, &file); |
| 5633 | full_err_msg.renderToStdErr(ttyconf); | 5773 | var error_bundle = try wip_errors.toOwnedBundle(""); |
| 5634 | } | 5774 | defer error_bundle.deinit(gpa); |
| 5775 | error_bundle.renderToStdErr(renderOptions(color)); | ||
| 5635 | process.exit(1); | 5776 | process.exit(1); |
| 5636 | } | 5777 | } |
| 5637 | 5778 | ||
| ... | @@ -5892,3 +6033,20 @@ const ClangSearchSanitizer = struct { | ... | @@ -5892,3 +6033,20 @@ const ClangSearchSanitizer = struct { |
| 5892 | iframework: bool = false, | 6033 | iframework: bool = false, |
| 5893 | }; | 6034 | }; |
| 5894 | }; | 6035 | }; |
| 6036 | |||
| 6037 | fn get_tty_conf(color: Color) std.debug.TTY.Config { | ||
| 6038 | return switch (color) { | ||
| 6039 | .auto => std.debug.detectTTYConfig(std.io.getStdErr()), | ||
| 6040 | .on => .escape_codes, | ||
| 6041 | .off => .no_color, | ||
| 6042 | }; | ||
| 6043 | } | ||
| 6044 | |||
| 6045 | fn renderOptions(color: Color) std.zig.ErrorBundle.RenderOptions { | ||
| 6046 | const ttyconf = get_tty_conf(color); | ||
| 6047 | return .{ | ||
| 6048 | .ttyconf = ttyconf, | ||
| 6049 | .include_source_line = ttyconf != .no_color, | ||
| 6050 | .include_reference_trace = ttyconf != .no_color, | ||
| 6051 | }; | ||
| 6052 | } |
src/mingw.zig+7-7| ... | @@ -19,7 +19,7 @@ pub const CRTFile = enum { | ... | @@ -19,7 +19,7 @@ pub const CRTFile = enum { |
| 19 | uuid_lib, | 19 | uuid_lib, |
| 20 | }; | 20 | }; |
| 21 | 21 | ||
| 22 | pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { | 22 | pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progress.Node) !void { |
| 23 | if (!build_options.have_llvm) { | 23 | if (!build_options.have_llvm) { |
| 24 | return error.ZigCompilerNotBuiltWithLLVMExtensions; | 24 | return error.ZigCompilerNotBuiltWithLLVMExtensions; |
| 25 | } | 25 | } |
| ... | @@ -41,7 +41,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { | ... | @@ -41,7 +41,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { |
| 41 | //"-D_UNICODE", | 41 | //"-D_UNICODE", |
| 42 | //"-DWPRFLAG=1", | 42 | //"-DWPRFLAG=1", |
| 43 | }); | 43 | }); |
| 44 | return comp.build_crt_file("crt2", .Obj, &[1]Compilation.CSourceFile{ | 44 | return comp.build_crt_file("crt2", .Obj, .@"mingw-w64 crt2.o", prog_node, &.{ |
| 45 | .{ | 45 | .{ |
| 46 | .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ | 46 | .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ |
| 47 | "libc", "mingw", "crt", "crtexe.c", | 47 | "libc", "mingw", "crt", "crtexe.c", |
| ... | @@ -60,7 +60,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { | ... | @@ -60,7 +60,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { |
| 60 | "-U__CRTDLL__", | 60 | "-U__CRTDLL__", |
| 61 | "-D__MSVCRT__", | 61 | "-D__MSVCRT__", |
| 62 | }); | 62 | }); |
| 63 | return comp.build_crt_file("dllcrt2", .Obj, &[1]Compilation.CSourceFile{ | 63 | return comp.build_crt_file("dllcrt2", .Obj, .@"mingw-w64 dllcrt2.o", prog_node, &.{ |
| 64 | .{ | 64 | .{ |
| 65 | .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ | 65 | .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ |
| 66 | "libc", "mingw", "crt", "crtdll.c", | 66 | "libc", "mingw", "crt", "crtdll.c", |
| ... | @@ -100,7 +100,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { | ... | @@ -100,7 +100,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { |
| 100 | .extra_flags = args.items, | 100 | .extra_flags = args.items, |
| 101 | }; | 101 | }; |
| 102 | } | 102 | } |
| 103 | return comp.build_crt_file("mingw32", .Lib, &c_source_files); | 103 | return comp.build_crt_file("mingw32", .Lib, .@"mingw-w64 mingw32.lib", prog_node, &c_source_files); |
| 104 | }, | 104 | }, |
| 105 | 105 | ||
| 106 | .msvcrt_os_lib => { | 106 | .msvcrt_os_lib => { |
| ... | @@ -148,7 +148,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { | ... | @@ -148,7 +148,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { |
| 148 | }; | 148 | }; |
| 149 | } | 149 | } |
| 150 | } | 150 | } |
| 151 | return comp.build_crt_file("msvcrt-os", .Lib, c_source_files.items); | 151 | return comp.build_crt_file("msvcrt-os", .Lib, .@"mingw-w64 msvcrt-os.lib", prog_node, c_source_files.items); |
| 152 | }, | 152 | }, |
| 153 | 153 | ||
| 154 | .mingwex_lib => { | 154 | .mingwex_lib => { |
| ... | @@ -211,7 +211,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { | ... | @@ -211,7 +211,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { |
| 211 | } else { | 211 | } else { |
| 212 | @panic("unsupported arch"); | 212 | @panic("unsupported arch"); |
| 213 | } | 213 | } |
| 214 | return comp.build_crt_file("mingwex", .Lib, c_source_files.items); | 214 | return comp.build_crt_file("mingwex", .Lib, .@"mingw-w64 mingwex.lib", prog_node, c_source_files.items); |
| 215 | }, | 215 | }, |
| 216 | 216 | ||
| 217 | .uuid_lib => { | 217 | .uuid_lib => { |
| ... | @@ -244,7 +244,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { | ... | @@ -244,7 +244,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { |
| 244 | .extra_flags = extra_flags, | 244 | .extra_flags = extra_flags, |
| 245 | }; | 245 | }; |
| 246 | } | 246 | } |
| 247 | return comp.build_crt_file("uuid", .Lib, &c_source_files); | 247 | return comp.build_crt_file("uuid", .Lib, .@"mingw-w64 uuid.lib", prog_node, &c_source_files); |
| 248 | }, | 248 | }, |
| 249 | } | 249 | } |
| 250 | } | 250 | } |
src/musl.zig+8-8| ... | @@ -17,7 +17,7 @@ pub const CRTFile = enum { | ... | @@ -17,7 +17,7 @@ pub const CRTFile = enum { |
| 17 | libc_so, | 17 | libc_so, |
| 18 | }; | 18 | }; |
| 19 | 19 | ||
| 20 | pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { | 20 | pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progress.Node) !void { |
| 21 | if (!build_options.have_llvm) { | 21 | if (!build_options.have_llvm) { |
| 22 | return error.ZigCompilerNotBuiltWithLLVMExtensions; | 22 | return error.ZigCompilerNotBuiltWithLLVMExtensions; |
| 23 | } | 23 | } |
| ... | @@ -33,7 +33,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { | ... | @@ -33,7 +33,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { |
| 33 | try args.appendSlice(&[_][]const u8{ | 33 | try args.appendSlice(&[_][]const u8{ |
| 34 | "-Qunused-arguments", | 34 | "-Qunused-arguments", |
| 35 | }); | 35 | }); |
| 36 | return comp.build_crt_file("crti", .Obj, &[1]Compilation.CSourceFile{ | 36 | return comp.build_crt_file("crti", .Obj, .@"musl crti.o", prog_node, &.{ |
| 37 | .{ | 37 | .{ |
| 38 | .src_path = try start_asm_path(comp, arena, "crti.s"), | 38 | .src_path = try start_asm_path(comp, arena, "crti.s"), |
| 39 | .extra_flags = args.items, | 39 | .extra_flags = args.items, |
| ... | @@ -46,7 +46,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { | ... | @@ -46,7 +46,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { |
| 46 | try args.appendSlice(&[_][]const u8{ | 46 | try args.appendSlice(&[_][]const u8{ |
| 47 | "-Qunused-arguments", | 47 | "-Qunused-arguments", |
| 48 | }); | 48 | }); |
| 49 | return comp.build_crt_file("crtn", .Obj, &[1]Compilation.CSourceFile{ | 49 | return comp.build_crt_file("crtn", .Obj, .@"musl crtn.o", prog_node, &.{ |
| 50 | .{ | 50 | .{ |
| 51 | .src_path = try start_asm_path(comp, arena, "crtn.s"), | 51 | .src_path = try start_asm_path(comp, arena, "crtn.s"), |
| 52 | .extra_flags = args.items, | 52 | .extra_flags = args.items, |
| ... | @@ -60,7 +60,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { | ... | @@ -60,7 +60,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { |
| 60 | "-fno-stack-protector", | 60 | "-fno-stack-protector", |
| 61 | "-DCRT", | 61 | "-DCRT", |
| 62 | }); | 62 | }); |
| 63 | return comp.build_crt_file("crt1", .Obj, &[1]Compilation.CSourceFile{ | 63 | return comp.build_crt_file("crt1", .Obj, .@"musl crt1.o", prog_node, &.{ |
| 64 | .{ | 64 | .{ |
| 65 | .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ | 65 | .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ |
| 66 | "libc", "musl", "crt", "crt1.c", | 66 | "libc", "musl", "crt", "crt1.c", |
| ... | @@ -77,7 +77,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { | ... | @@ -77,7 +77,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { |
| 77 | "-fno-stack-protector", | 77 | "-fno-stack-protector", |
| 78 | "-DCRT", | 78 | "-DCRT", |
| 79 | }); | 79 | }); |
| 80 | return comp.build_crt_file("rcrt1", .Obj, &[1]Compilation.CSourceFile{ | 80 | return comp.build_crt_file("rcrt1", .Obj, .@"musl rcrt1.o", prog_node, &.{ |
| 81 | .{ | 81 | .{ |
| 82 | .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ | 82 | .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ |
| 83 | "libc", "musl", "crt", "rcrt1.c", | 83 | "libc", "musl", "crt", "rcrt1.c", |
| ... | @@ -94,7 +94,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { | ... | @@ -94,7 +94,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { |
| 94 | "-fno-stack-protector", | 94 | "-fno-stack-protector", |
| 95 | "-DCRT", | 95 | "-DCRT", |
| 96 | }); | 96 | }); |
| 97 | return comp.build_crt_file("Scrt1", .Obj, &[1]Compilation.CSourceFile{ | 97 | return comp.build_crt_file("Scrt1", .Obj, .@"musl Scrt1.o", prog_node, &.{ |
| 98 | .{ | 98 | .{ |
| 99 | .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ | 99 | .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ |
| 100 | "libc", "musl", "crt", "Scrt1.c", | 100 | "libc", "musl", "crt", "Scrt1.c", |
| ... | @@ -187,7 +187,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { | ... | @@ -187,7 +187,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { |
| 187 | .extra_flags = args.items, | 187 | .extra_flags = args.items, |
| 188 | }; | 188 | }; |
| 189 | } | 189 | } |
| 190 | return comp.build_crt_file("c", .Lib, c_source_files.items); | 190 | return comp.build_crt_file("c", .Lib, .@"musl libc.a", prog_node, c_source_files.items); |
| 191 | }, | 191 | }, |
| 192 | .libc_so => { | 192 | .libc_so => { |
| 193 | const target = comp.getTarget(); | 193 | const target = comp.getTarget(); |
| ... | @@ -241,7 +241,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { | ... | @@ -241,7 +241,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { |
| 241 | }); | 241 | }); |
| 242 | defer sub_compilation.destroy(); | 242 | defer sub_compilation.destroy(); |
| 243 | 243 | ||
| 244 | try sub_compilation.updateSubCompilation(); | 244 | try comp.updateSubCompilation(sub_compilation, .@"musl libc.so", prog_node); |
| 245 | 245 | ||
| 246 | try comp.crt_files.ensureUnusedCapacity(comp.gpa, 1); | 246 | try comp.crt_files.ensureUnusedCapacity(comp.gpa, 1); |
| 247 | 247 |
src/objcopy.zig+45-5| ... | @@ -4,22 +4,25 @@ const fs = std.fs; | ... | @@ -4,22 +4,25 @@ const fs = std.fs; |
| 4 | const elf = std.elf; | 4 | const elf = std.elf; |
| 5 | const Allocator = std.mem.Allocator; | 5 | const Allocator = std.mem.Allocator; |
| 6 | const File = std.fs.File; | 6 | const File = std.fs.File; |
| 7 | const assert = std.debug.assert; | ||
| 8 | |||
| 7 | const main = @import("main.zig"); | 9 | const main = @import("main.zig"); |
| 8 | const fatal = main.fatal; | 10 | const fatal = main.fatal; |
| 9 | const cleanExit = main.cleanExit; | 11 | const Server = std.zig.Server; |
| 12 | const build_options = @import("build_options"); | ||
| 10 | 13 | ||
| 11 | pub fn cmdObjCopy( | 14 | pub fn cmdObjCopy( |
| 12 | gpa: Allocator, | 15 | gpa: Allocator, |
| 13 | arena: Allocator, | 16 | arena: Allocator, |
| 14 | args: []const []const u8, | 17 | args: []const []const u8, |
| 15 | ) !void { | 18 | ) !void { |
| 16 | _ = gpa; | ||
| 17 | var i: usize = 0; | 19 | var i: usize = 0; |
| 18 | var opt_out_fmt: ?std.Target.ObjectFormat = null; | 20 | var opt_out_fmt: ?std.Target.ObjectFormat = null; |
| 19 | var opt_input: ?[]const u8 = null; | 21 | var opt_input: ?[]const u8 = null; |
| 20 | var opt_output: ?[]const u8 = null; | 22 | var opt_output: ?[]const u8 = null; |
| 21 | var only_section: ?[]const u8 = null; | 23 | var only_section: ?[]const u8 = null; |
| 22 | var pad_to: ?u64 = null; | 24 | var pad_to: ?u64 = null; |
| 25 | var listen = false; | ||
| 23 | while (i < args.len) : (i += 1) { | 26 | while (i < args.len) : (i += 1) { |
| 24 | const arg = args[i]; | 27 | const arg = args[i]; |
| 25 | if (!mem.startsWith(u8, arg, "-")) { | 28 | if (!mem.startsWith(u8, arg, "-")) { |
| ... | @@ -54,6 +57,8 @@ pub fn cmdObjCopy( | ... | @@ -54,6 +57,8 @@ pub fn cmdObjCopy( |
| 54 | i += 1; | 57 | i += 1; |
| 55 | if (i >= args.len) fatal("expected another argument after '{s}'", .{arg}); | 58 | if (i >= args.len) fatal("expected another argument after '{s}'", .{arg}); |
| 56 | only_section = args[i]; | 59 | only_section = args[i]; |
| 60 | } else if (mem.eql(u8, arg, "--listen=-")) { | ||
| 61 | listen = true; | ||
| 57 | } else if (mem.startsWith(u8, arg, "--only-section=")) { | 62 | } else if (mem.startsWith(u8, arg, "--only-section=")) { |
| 58 | only_section = arg["--output-target=".len..]; | 63 | only_section = arg["--output-target=".len..]; |
| 59 | } else if (mem.eql(u8, arg, "--pad-to")) { | 64 | } else if (mem.eql(u8, arg, "--pad-to")) { |
| ... | @@ -102,10 +107,45 @@ pub fn cmdObjCopy( | ... | @@ -102,10 +107,45 @@ pub fn cmdObjCopy( |
| 102 | .only_section = only_section, | 107 | .only_section = only_section, |
| 103 | .pad_to = pad_to, | 108 | .pad_to = pad_to, |
| 104 | }); | 109 | }); |
| 105 | return cleanExit(); | ||
| 106 | }, | 110 | }, |
| 107 | else => fatal("unsupported output object format: {s}", .{@tagName(out_fmt)}), | 111 | else => fatal("unsupported output object format: {s}", .{@tagName(out_fmt)}), |
| 108 | } | 112 | } |
| 113 | |||
| 114 | if (listen) { | ||
| 115 | var server = try Server.init(.{ | ||
| 116 | .gpa = gpa, | ||
| 117 | .in = std.io.getStdIn(), | ||
| 118 | .out = std.io.getStdOut(), | ||
| 119 | .zig_version = build_options.version, | ||
| 120 | }); | ||
| 121 | defer server.deinit(); | ||
| 122 | |||
| 123 | var seen_update = false; | ||
| 124 | while (true) { | ||
| 125 | const hdr = try server.receiveMessage(); | ||
| 126 | switch (hdr.tag) { | ||
| 127 | .exit => { | ||
| 128 | return std.process.cleanExit(); | ||
| 129 | }, | ||
| 130 | .update => { | ||
| 131 | if (seen_update) { | ||
| 132 | std.debug.print("zig objcopy only supports 1 update for now\n", .{}); | ||
| 133 | std.process.exit(1); | ||
| 134 | } | ||
| 135 | seen_update = true; | ||
| 136 | |||
| 137 | try server.serveEmitBinPath(output, .{ | ||
| 138 | .flags = .{ .cache_hit = false }, | ||
| 139 | }); | ||
| 140 | }, | ||
| 141 | else => { | ||
| 142 | std.debug.print("unsupported message: {s}", .{@tagName(hdr.tag)}); | ||
| 143 | std.process.exit(1); | ||
| 144 | }, | ||
| 145 | } | ||
| 146 | } | ||
| 147 | } | ||
| 148 | return std.process.cleanExit(); | ||
| 109 | } | 149 | } |
| 110 | 150 | ||
| 111 | const usage = | 151 | const usage = |
| ... | @@ -417,7 +457,7 @@ const HexWriter = struct { | ... | @@ -417,7 +457,7 @@ const HexWriter = struct { |
| 417 | } | 457 | } |
| 418 | 458 | ||
| 419 | fn Address(address: u32) Record { | 459 | fn Address(address: u32) Record { |
| 420 | std.debug.assert(address > 0xFFFF); | 460 | assert(address > 0xFFFF); |
| 421 | const segment = @intCast(u16, address / 0x10000); | 461 | const segment = @intCast(u16, address / 0x10000); |
| 422 | if (address > 0xFFFFF) { | 462 | if (address > 0xFFFFF) { |
| 423 | return Record{ | 463 | return Record{ |
| ... | @@ -460,7 +500,7 @@ const HexWriter = struct { | ... | @@ -460,7 +500,7 @@ const HexWriter = struct { |
| 460 | const BUFSIZE = 1 + (1 + 2 + 1 + MAX_PAYLOAD_LEN + 1) * 2 + linesep.len; | 500 | const BUFSIZE = 1 + (1 + 2 + 1 + MAX_PAYLOAD_LEN + 1) * 2 + linesep.len; |
| 461 | var outbuf: [BUFSIZE]u8 = undefined; | 501 | var outbuf: [BUFSIZE]u8 = undefined; |
| 462 | const payload_bytes = self.getPayloadBytes(); | 502 | const payload_bytes = self.getPayloadBytes(); |
| 463 | std.debug.assert(payload_bytes.len <= MAX_PAYLOAD_LEN); | 503 | assert(payload_bytes.len <= MAX_PAYLOAD_LEN); |
| 464 | 504 | ||
| 465 | const line = try std.fmt.bufPrint(&outbuf, ":{0X:0>2}{1X:0>4}{2X:0>2}{3s}{4X:0>2}" ++ linesep, .{ | 505 | const line = try std.fmt.bufPrint(&outbuf, ":{0X:0>2}{1X:0>4}{2X:0>2}{3s}{4X:0>2}" ++ linesep, .{ |
| 466 | @intCast(u8, payload_bytes.len), | 506 | @intCast(u8, payload_bytes.len), |
src/test.zig deleted-1984| ... | @@ -1,1984 +0,0 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const builtin = @import("builtin"); | ||
| 3 | const Allocator = std.mem.Allocator; | ||
| 4 | const CrossTarget = std.zig.CrossTarget; | ||
| 5 | const print = std.debug.print; | ||
| 6 | const assert = std.debug.assert; | ||
| 7 | |||
| 8 | const link = @import("link.zig"); | ||
| 9 | const Compilation = @import("Compilation.zig"); | ||
| 10 | const Package = @import("Package.zig"); | ||
| 11 | const introspect = @import("introspect.zig"); | ||
| 12 | const build_options = @import("build_options"); | ||
| 13 | const ThreadPool = @import("ThreadPool.zig"); | ||
| 14 | const WaitGroup = @import("WaitGroup.zig"); | ||
| 15 | const zig_h = link.File.C.zig_h; | ||
| 16 | |||
| 17 | const enable_qemu: bool = build_options.enable_qemu; | ||
| 18 | const enable_wine: bool = build_options.enable_wine; | ||
| 19 | const enable_wasmtime: bool = build_options.enable_wasmtime; | ||
| 20 | const enable_darling: bool = build_options.enable_darling; | ||
| 21 | const enable_rosetta: bool = build_options.enable_rosetta; | ||
| 22 | const glibc_runtimes_dir: ?[]const u8 = build_options.glibc_runtimes_dir; | ||
| 23 | const skip_stage1 = true; | ||
| 24 | |||
| 25 | const hr = "=" ** 80; | ||
| 26 | |||
| 27 | test { | ||
| 28 | const use_gpa = build_options.force_gpa or !builtin.link_libc; | ||
| 29 | const gpa = gpa: { | ||
| 30 | if (use_gpa) { | ||
| 31 | break :gpa std.testing.allocator; | ||
| 32 | } | ||
| 33 | // We would prefer to use raw libc allocator here, but cannot | ||
| 34 | // use it if it won't support the alignment we need. | ||
| 35 | if (@alignOf(std.c.max_align_t) < @alignOf(i128)) { | ||
| 36 | break :gpa std.heap.c_allocator; | ||
| 37 | } | ||
| 38 | break :gpa std.heap.raw_c_allocator; | ||
| 39 | }; | ||
| 40 | |||
| 41 | var arena_allocator = std.heap.ArenaAllocator.init(gpa); | ||
| 42 | defer arena_allocator.deinit(); | ||
| 43 | const arena = arena_allocator.allocator(); | ||
| 44 | |||
| 45 | var ctx = TestContext.init(gpa, arena); | ||
| 46 | defer ctx.deinit(); | ||
| 47 | |||
| 48 | { | ||
| 49 | const dir_path = try std.fs.path.join(arena, &.{ | ||
| 50 | std.fs.path.dirname(@src().file).?, "..", "test", "cases", | ||
| 51 | }); | ||
| 52 | |||
| 53 | var dir = try std.fs.cwd().openIterableDir(dir_path, .{}); | ||
| 54 | defer dir.close(); | ||
| 55 | |||
| 56 | ctx.addTestCasesFromDir(dir); | ||
| 57 | } | ||
| 58 | |||
| 59 | try @import("../test/cases.zig").addCases(&ctx); | ||
| 60 | |||
| 61 | try ctx.run(); | ||
| 62 | } | ||
| 63 | |||
| 64 | const ErrorMsg = union(enum) { | ||
| 65 | src: struct { | ||
| 66 | src_path: []const u8, | ||
| 67 | msg: []const u8, | ||
| 68 | // maxint means match anything | ||
| 69 | // this is a workaround for stage1 compiler bug I ran into when making it ?u32 | ||
| 70 | line: u32, | ||
| 71 | // maxint means match anything | ||
| 72 | // this is a workaround for stage1 compiler bug I ran into when making it ?u32 | ||
| 73 | column: u32, | ||
| 74 | kind: Kind, | ||
| 75 | count: u32, | ||
| 76 | }, | ||
| 77 | plain: struct { | ||
| 78 | msg: []const u8, | ||
| 79 | kind: Kind, | ||
| 80 | count: u32, | ||
| 81 | }, | ||
| 82 | |||
| 83 | const Kind = enum { | ||
| 84 | @"error", | ||
| 85 | note, | ||
| 86 | }; | ||
| 87 | |||
| 88 | fn init(other: Compilation.AllErrors.Message, kind: Kind) ErrorMsg { | ||
| 89 | switch (other) { | ||
| 90 | .src => |src| return .{ | ||
| 91 | .src = .{ | ||
| 92 | .src_path = src.src_path, | ||
| 93 | .msg = src.msg, | ||
| 94 | .line = @intCast(u32, src.line), | ||
| 95 | .column = @intCast(u32, src.column), | ||
| 96 | .kind = kind, | ||
| 97 | .count = src.count, | ||
| 98 | }, | ||
| 99 | }, | ||
| 100 | .plain => |plain| return .{ | ||
| 101 | .plain = .{ | ||
| 102 | .msg = plain.msg, | ||
| 103 | .kind = kind, | ||
| 104 | .count = plain.count, | ||
| 105 | }, | ||
| 106 | }, | ||
| 107 | } | ||
| 108 | } | ||
| 109 | |||
| 110 | pub fn format( | ||
| 111 | self: ErrorMsg, | ||
| 112 | comptime fmt: []const u8, | ||
| 113 | options: std.fmt.FormatOptions, | ||
| 114 | writer: anytype, | ||
| 115 | ) !void { | ||
| 116 | _ = fmt; | ||
| 117 | _ = options; | ||
| 118 | switch (self) { | ||
| 119 | .src => |src| { | ||
| 120 | if (!std.mem.eql(u8, src.src_path, "?") or | ||
| 121 | src.line != std.math.maxInt(u32) or | ||
| 122 | src.column != std.math.maxInt(u32)) | ||
| 123 | { | ||
| 124 | try writer.print("{s}:", .{src.src_path}); | ||
| 125 | if (src.line != std.math.maxInt(u32)) { | ||
| 126 | try writer.print("{d}:", .{src.line + 1}); | ||
| 127 | } else { | ||
| 128 | try writer.writeAll("?:"); | ||
| 129 | } | ||
| 130 | if (src.column != std.math.maxInt(u32)) { | ||
| 131 | try writer.print("{d}: ", .{src.column + 1}); | ||
| 132 | } else { | ||
| 133 | try writer.writeAll("?: "); | ||
| 134 | } | ||
| 135 | } | ||
| 136 | try writer.print("{s}: {s}", .{ @tagName(src.kind), src.msg }); | ||
| 137 | if (src.count != 1) { | ||
| 138 | try writer.print(" ({d} times)", .{src.count}); | ||
| 139 | } | ||
| 140 | }, | ||
| 141 | .plain => |plain| { | ||
| 142 | try writer.print("{s}: {s}", .{ @tagName(plain.kind), plain.msg }); | ||
| 143 | if (plain.count != 1) { | ||
| 144 | try writer.print(" ({d} times)", .{plain.count}); | ||
| 145 | } | ||
| 146 | }, | ||
| 147 | } | ||
| 148 | } | ||
| 149 | }; | ||
| 150 | |||
| 151 | /// Default config values for known test manifest key-value pairings. | ||
| 152 | /// Currently handled defaults are: | ||
| 153 | /// * backend | ||
| 154 | /// * target | ||
| 155 | /// * output_mode | ||
| 156 | /// * is_test | ||
| 157 | const TestManifestConfigDefaults = struct { | ||
| 158 | /// Asserts if the key doesn't exist - yep, it's an oversight alright. | ||
| 159 | fn get(@"type": TestManifest.Type, key: []const u8) []const u8 { | ||
| 160 | if (std.mem.eql(u8, key, "backend")) { | ||
| 161 | return "stage2"; | ||
| 162 | } else if (std.mem.eql(u8, key, "target")) { | ||
| 163 | comptime { | ||
| 164 | var defaults: []const u8 = ""; | ||
| 165 | // TODO should we only return "mainstream" targets by default here? | ||
| 166 | // TODO we should also specify ABIs explicitly as the backends are | ||
| 167 | // getting more and more complete | ||
| 168 | // Linux | ||
| 169 | inline for (&[_][]const u8{ "x86_64", "arm", "aarch64" }) |arch| { | ||
| 170 | defaults = defaults ++ arch ++ "-linux" ++ ","; | ||
| 171 | } | ||
| 172 | // macOS | ||
| 173 | inline for (&[_][]const u8{ "x86_64", "aarch64" }) |arch| { | ||
| 174 | defaults = defaults ++ arch ++ "-macos" ++ ","; | ||
| 175 | } | ||
| 176 | // Windows | ||
| 177 | defaults = defaults ++ "x86_64-windows" ++ ","; | ||
| 178 | // Wasm | ||
| 179 | defaults = defaults ++ "wasm32-wasi"; | ||
| 180 | return defaults; | ||
| 181 | } | ||
| 182 | } else if (std.mem.eql(u8, key, "output_mode")) { | ||
| 183 | return switch (@"type") { | ||
| 184 | .@"error" => "Obj", | ||
| 185 | .run => "Exe", | ||
| 186 | .cli => @panic("TODO test harness for CLI tests"), | ||
| 187 | }; | ||
| 188 | } else if (std.mem.eql(u8, key, "is_test")) { | ||
| 189 | return "0"; | ||
| 190 | } else unreachable; | ||
| 191 | } | ||
| 192 | }; | ||
| 193 | |||
| 194 | /// Manifest syntax example: | ||
| 195 | /// (see https://github.com/ziglang/zig/issues/11288) | ||
| 196 | /// | ||
| 197 | /// error | ||
| 198 | /// backend=stage1,stage2 | ||
| 199 | /// output_mode=exe | ||
| 200 | /// | ||
| 201 | /// :3:19: error: foo | ||
| 202 | /// | ||
| 203 | /// run | ||
| 204 | /// target=x86_64-linux,aarch64-macos | ||
| 205 | /// | ||
| 206 | /// I am expected stdout! Hello! | ||
| 207 | /// | ||
| 208 | /// cli | ||
| 209 | /// | ||
| 210 | /// build test | ||
| 211 | const TestManifest = struct { | ||
| 212 | type: Type, | ||
| 213 | config_map: std.StringHashMap([]const u8), | ||
| 214 | trailing_bytes: []const u8 = "", | ||
| 215 | |||
| 216 | const Type = enum { | ||
| 217 | @"error", | ||
| 218 | run, | ||
| 219 | cli, | ||
| 220 | }; | ||
| 221 | |||
| 222 | const TrailingIterator = struct { | ||
| 223 | inner: std.mem.TokenIterator(u8), | ||
| 224 | |||
| 225 | fn next(self: *TrailingIterator) ?[]const u8 { | ||
| 226 | const next_inner = self.inner.next() orelse return null; | ||
| 227 | return std.mem.trim(u8, next_inner[2..], " \t"); | ||
| 228 | } | ||
| 229 | }; | ||
| 230 | |||
| 231 | fn ConfigValueIterator(comptime T: type) type { | ||
| 232 | return struct { | ||
| 233 | inner: std.mem.SplitIterator(u8), | ||
| 234 | |||
| 235 | fn next(self: *@This()) !?T { | ||
| 236 | const next_raw = self.inner.next() orelse return null; | ||
| 237 | const parseFn = getDefaultParser(T); | ||
| 238 | return try parseFn(next_raw); | ||
| 239 | } | ||
| 240 | }; | ||
| 241 | } | ||
| 242 | |||
| 243 | fn parse(arena: Allocator, bytes: []const u8) !TestManifest { | ||
| 244 | // The manifest is the last contiguous block of comments in the file | ||
| 245 | // We scan for the beginning by searching backward for the first non-empty line that does not start with "//" | ||
| 246 | var start: ?usize = null; | ||
| 247 | var end: usize = bytes.len; | ||
| 248 | if (bytes.len > 0) { | ||
| 249 | var cursor: usize = bytes.len - 1; | ||
| 250 | while (true) { | ||
| 251 | // Move to beginning of line | ||
| 252 | while (cursor > 0 and bytes[cursor - 1] != '\n') cursor -= 1; | ||
| 253 | |||
| 254 | if (std.mem.startsWith(u8, bytes[cursor..], "//")) { | ||
| 255 | start = cursor; // Contiguous comment line, include in manifest | ||
| 256 | } else { | ||
| 257 | if (start != null) break; // Encountered non-comment line, end of manifest | ||
| 258 | |||
| 259 | // We ignore all-whitespace lines following the comment block, but anything else | ||
| 260 | // means that there is no manifest present. | ||
| 261 | if (std.mem.trim(u8, bytes[cursor..end], " \r\n\t").len == 0) { | ||
| 262 | end = cursor; | ||
| 263 | } else break; // If it's not whitespace, there is no manifest | ||
| 264 | } | ||
| 265 | |||
| 266 | // Move to previous line | ||
| 267 | if (cursor != 0) cursor -= 1 else break; | ||
| 268 | } | ||
| 269 | } | ||
| 270 | |||
| 271 | const actual_start = start orelse return error.MissingTestManifest; | ||
| 272 | const manifest_bytes = bytes[actual_start..end]; | ||
| 273 | |||
| 274 | var it = std.mem.tokenize(u8, manifest_bytes, "\r\n"); | ||
| 275 | |||
| 276 | // First line is the test type | ||
| 277 | const tt: Type = blk: { | ||
| 278 | const line = it.next() orelse return error.MissingTestCaseType; | ||
| 279 | const raw = std.mem.trim(u8, line[2..], " \t"); | ||
| 280 | if (std.mem.eql(u8, raw, "error")) { | ||
| 281 | break :blk .@"error"; | ||
| 282 | } else if (std.mem.eql(u8, raw, "run")) { | ||
| 283 | break :blk .run; | ||
| 284 | } else if (std.mem.eql(u8, raw, "cli")) { | ||
| 285 | break :blk .cli; | ||
| 286 | } else { | ||
| 287 | std.log.warn("unknown test case type requested: {s}", .{raw}); | ||
| 288 | return error.UnknownTestCaseType; | ||
| 289 | } | ||
| 290 | }; | ||
| 291 | |||
| 292 | var manifest: TestManifest = .{ | ||
| 293 | .type = tt, | ||
| 294 | .config_map = std.StringHashMap([]const u8).init(arena), | ||
| 295 | }; | ||
| 296 | |||
| 297 | // Any subsequent line until a blank comment line is key=value(s) pair | ||
| 298 | while (it.next()) |line| { | ||
| 299 | const trimmed = std.mem.trim(u8, line[2..], " \t"); | ||
| 300 | if (trimmed.len == 0) break; | ||
| 301 | |||
| 302 | // Parse key=value(s) | ||
| 303 | var kv_it = std.mem.split(u8, trimmed, "="); | ||
| 304 | const key = kv_it.first(); | ||
| 305 | try manifest.config_map.putNoClobber(key, kv_it.next() orelse return error.MissingValuesForConfig); | ||
| 306 | } | ||
| 307 | |||
| 308 | // Finally, trailing is expected output | ||
| 309 | manifest.trailing_bytes = manifest_bytes[it.index..]; | ||
| 310 | |||
| 311 | return manifest; | ||
| 312 | } | ||
| 313 | |||
| 314 | fn getConfigForKey( | ||
| 315 | self: TestManifest, | ||
| 316 | key: []const u8, | ||
| 317 | comptime T: type, | ||
| 318 | ) ConfigValueIterator(T) { | ||
| 319 | const bytes = self.config_map.get(key) orelse TestManifestConfigDefaults.get(self.type, key); | ||
| 320 | return ConfigValueIterator(T){ | ||
| 321 | .inner = std.mem.split(u8, bytes, ","), | ||
| 322 | }; | ||
| 323 | } | ||
| 324 | |||
| 325 | fn getConfigForKeyAlloc( | ||
| 326 | self: TestManifest, | ||
| 327 | allocator: Allocator, | ||
| 328 | key: []const u8, | ||
| 329 | comptime T: type, | ||
| 330 | ) ![]const T { | ||
| 331 | var out = std.ArrayList(T).init(allocator); | ||
| 332 | defer out.deinit(); | ||
| 333 | var it = self.getConfigForKey(key, T); | ||
| 334 | while (try it.next()) |item| { | ||
| 335 | try out.append(item); | ||
| 336 | } | ||
| 337 | return try out.toOwnedSlice(); | ||
| 338 | } | ||
| 339 | |||
| 340 | fn getConfigForKeyAssertSingle(self: TestManifest, key: []const u8, comptime T: type) !T { | ||
| 341 | var it = self.getConfigForKey(key, T); | ||
| 342 | const res = (try it.next()) orelse unreachable; | ||
| 343 | assert((try it.next()) == null); | ||
| 344 | return res; | ||
| 345 | } | ||
| 346 | |||
| 347 | fn trailing(self: TestManifest) TrailingIterator { | ||
| 348 | return .{ | ||
| 349 | .inner = std.mem.tokenize(u8, self.trailing_bytes, "\r\n"), | ||
| 350 | }; | ||
| 351 | } | ||
| 352 | |||
| 353 | fn trailingAlloc(self: TestManifest, allocator: Allocator) error{OutOfMemory}![]const []const u8 { | ||
| 354 | var out = std.ArrayList([]const u8).init(allocator); | ||
| 355 | defer out.deinit(); | ||
| 356 | var it = self.trailing(); | ||
| 357 | while (it.next()) |line| { | ||
| 358 | try out.append(line); | ||
| 359 | } | ||
| 360 | return try out.toOwnedSlice(); | ||
| 361 | } | ||
| 362 | |||
| 363 | fn ParseFn(comptime T: type) type { | ||
| 364 | return fn ([]const u8) anyerror!T; | ||
| 365 | } | ||
| 366 | |||
| 367 | fn getDefaultParser(comptime T: type) ParseFn(T) { | ||
| 368 | if (T == CrossTarget) return struct { | ||
| 369 | fn parse(str: []const u8) anyerror!T { | ||
| 370 | var opts = CrossTarget.ParseOptions{ | ||
| 371 | .arch_os_abi = str, | ||
| 372 | }; | ||
| 373 | return try CrossTarget.parse(opts); | ||
| 374 | } | ||
| 375 | }.parse; | ||
| 376 | |||
| 377 | switch (@typeInfo(T)) { | ||
| 378 | .Int => return struct { | ||
| 379 | fn parse(str: []const u8) anyerror!T { | ||
| 380 | return try std.fmt.parseInt(T, str, 0); | ||
| 381 | } | ||
| 382 | }.parse, | ||
| 383 | .Bool => return struct { | ||
| 384 | fn parse(str: []const u8) anyerror!T { | ||
| 385 | const as_int = try std.fmt.parseInt(u1, str, 0); | ||
| 386 | return as_int > 0; | ||
| 387 | } | ||
| 388 | }.parse, | ||
| 389 | .Enum => return struct { | ||
| 390 | fn parse(str: []const u8) anyerror!T { | ||
| 391 | return std.meta.stringToEnum(T, str) orelse { | ||
| 392 | std.log.err("unknown enum variant for {s}: {s}", .{ @typeName(T), str }); | ||
| 393 | return error.UnknownEnumVariant; | ||
| 394 | }; | ||
| 395 | } | ||
| 396 | }.parse, | ||
| 397 | .Struct => @compileError("no default parser for " ++ @typeName(T)), | ||
| 398 | else => @compileError("no default parser for " ++ @typeName(T)), | ||
| 399 | } | ||
| 400 | } | ||
| 401 | }; | ||
| 402 | |||
| 403 | const TestStrategy = enum { | ||
| 404 | /// Execute tests as independent compilations, unless they are explicitly | ||
| 405 | /// incremental ("foo.0.zig", "foo.1.zig", etc.) | ||
| 406 | independent, | ||
| 407 | /// Execute all tests as incremental updates to a single compilation. Explicitly | ||
| 408 | /// incremental tests ("foo.0.zig", "foo.1.zig", etc.) still execute in order | ||
| 409 | incremental, | ||
| 410 | }; | ||
| 411 | |||
| 412 | /// Iterates a set of filenames extracting batches that are either incremental | ||
| 413 | /// ("foo.0.zig", "foo.1.zig", etc.) or independent ("foo.zig", "bar.zig", etc.). | ||
| 414 | /// Assumes filenames are sorted. | ||
| 415 | const TestIterator = struct { | ||
| 416 | start: usize = 0, | ||
| 417 | end: usize = 0, | ||
| 418 | filenames: []const []const u8, | ||
| 419 | /// reset on each call to `next` | ||
| 420 | index: usize = 0, | ||
| 421 | |||
| 422 | const Error = error{InvalidIncrementalTestIndex}; | ||
| 423 | |||
| 424 | fn next(it: *TestIterator) Error!?[]const []const u8 { | ||
| 425 | try it.nextInner(); | ||
| 426 | if (it.start == it.end) return null; | ||
| 427 | return it.filenames[it.start..it.end]; | ||
| 428 | } | ||
| 429 | |||
| 430 | fn nextInner(it: *TestIterator) Error!void { | ||
| 431 | it.start = it.end; | ||
| 432 | if (it.end == it.filenames.len) return; | ||
| 433 | if (it.end + 1 == it.filenames.len) { | ||
| 434 | it.end += 1; | ||
| 435 | return; | ||
| 436 | } | ||
| 437 | |||
| 438 | const remaining = it.filenames[it.end..]; | ||
| 439 | it.index = 0; | ||
| 440 | while (it.index < remaining.len - 1) : (it.index += 1) { | ||
| 441 | // First, check if this file is part of an incremental update sequence | ||
| 442 | // Split filename into "<base_name>.<index>.<file_ext>" | ||
| 443 | const prev_parts = getTestFileNameParts(remaining[it.index]); | ||
| 444 | const new_parts = getTestFileNameParts(remaining[it.index + 1]); | ||
| 445 | |||
| 446 | // If base_name and file_ext match, these files are in the same test sequence | ||
| 447 | // and the new one should be the incremented version of the previous test | ||
| 448 | if (std.mem.eql(u8, prev_parts.base_name, new_parts.base_name) and | ||
| 449 | std.mem.eql(u8, prev_parts.file_ext, new_parts.file_ext)) | ||
| 450 | { | ||
| 451 | // This is "foo.X.zig" followed by "foo.Y.zig". Make sure that X = Y + 1 | ||
| 452 | if (prev_parts.test_index == null) | ||
| 453 | return error.InvalidIncrementalTestIndex; | ||
| 454 | if (new_parts.test_index == null) | ||
| 455 | return error.InvalidIncrementalTestIndex; | ||
| 456 | if (new_parts.test_index.? != prev_parts.test_index.? + 1) | ||
| 457 | return error.InvalidIncrementalTestIndex; | ||
| 458 | } else { | ||
| 459 | // This is not the same test sequence, so the new file must be the first file | ||
| 460 | // in a new sequence ("*.0.zig") or an independent test file ("*.zig") | ||
| 461 | if (new_parts.test_index != null and new_parts.test_index.? != 0) | ||
| 462 | return error.InvalidIncrementalTestIndex; | ||
| 463 | |||
| 464 | it.end += it.index + 1; | ||
| 465 | break; | ||
| 466 | } | ||
| 467 | } else { | ||
| 468 | it.end += remaining.len; | ||
| 469 | } | ||
| 470 | } | ||
| 471 | |||
| 472 | /// In the event of an `error.InvalidIncrementalTestIndex`, this function can | ||
| 473 | /// be used to find the current filename that was being processed. | ||
| 474 | /// Asserts the iterator hasn't reached the end. | ||
| 475 | fn currentFilename(it: TestIterator) []const u8 { | ||
| 476 | assert(it.end != it.filenames.len); | ||
| 477 | const remaining = it.filenames[it.end..]; | ||
| 478 | return remaining[it.index + 1]; | ||
| 479 | } | ||
| 480 | }; | ||
| 481 | |||
| 482 | /// For a filename in the format "<filename>.X.<ext>" or "<filename>.<ext>", returns | ||
| 483 | /// "<filename>", "<ext>" and X parsed as a decimal number. If X is not present, or | ||
| 484 | /// cannot be parsed as a decimal number, it is treated as part of <filename> | ||
| 485 | fn getTestFileNameParts(name: []const u8) struct { | ||
| 486 | base_name: []const u8, | ||
| 487 | file_ext: []const u8, | ||
| 488 | test_index: ?usize, | ||
| 489 | } { | ||
| 490 | const file_ext = std.fs.path.extension(name); | ||
| 491 | const trimmed = name[0 .. name.len - file_ext.len]; // Trim off ".<ext>" | ||
| 492 | const maybe_index = std.fs.path.extension(trimmed); // Extract ".X" | ||
| 493 | |||
| 494 | // Attempt to parse index | ||
| 495 | const index: ?usize = if (maybe_index.len > 0) | ||
| 496 | std.fmt.parseInt(usize, maybe_index[1..], 10) catch null | ||
| 497 | else | ||
| 498 | null; | ||
| 499 | |||
| 500 | // Adjust "<filename>" extent based on parsing success | ||
| 501 | const base_name_end = trimmed.len - if (index != null) maybe_index.len else 0; | ||
| 502 | return .{ | ||
| 503 | .base_name = name[0..base_name_end], | ||
| 504 | .file_ext = if (file_ext.len > 0) file_ext[1..] else file_ext, | ||
| 505 | .test_index = index, | ||
| 506 | }; | ||
| 507 | } | ||
| 508 | |||
| 509 | /// Sort test filenames in-place, so that incremental test cases ("foo.0.zig", | ||
| 510 | /// "foo.1.zig", etc.) are contiguous and appear in numerical order. | ||
| 511 | fn sortTestFilenames(filenames: [][]const u8) void { | ||
| 512 | const Context = struct { | ||
| 513 | pub fn lessThan(_: @This(), a: []const u8, b: []const u8) bool { | ||
| 514 | const a_parts = getTestFileNameParts(a); | ||
| 515 | const b_parts = getTestFileNameParts(b); | ||
| 516 | |||
| 517 | // Sort "<base_name>.X.<file_ext>" based on "<base_name>" and "<file_ext>" first | ||
| 518 | return switch (std.mem.order(u8, a_parts.base_name, b_parts.base_name)) { | ||
| 519 | .lt => true, | ||
| 520 | .gt => false, | ||
| 521 | .eq => switch (std.mem.order(u8, a_parts.file_ext, b_parts.file_ext)) { | ||
| 522 | .lt => true, | ||
| 523 | .gt => false, | ||
| 524 | .eq => { | ||
| 525 | // a and b differ only in their ".X" part | ||
| 526 | |||
| 527 | // Sort "<base_name>.<file_ext>" before any "<base_name>.X.<file_ext>" | ||
| 528 | if (a_parts.test_index) |a_index| { | ||
| 529 | if (b_parts.test_index) |b_index| { | ||
| 530 | // Make sure that incremental tests appear in linear order | ||
| 531 | return a_index < b_index; | ||
| 532 | } else { | ||
| 533 | return false; | ||
| 534 | } | ||
| 535 | } else { | ||
| 536 | return b_parts.test_index != null; | ||
| 537 | } | ||
| 538 | }, | ||
| 539 | }, | ||
| 540 | }; | ||
| 541 | } | ||
| 542 | }; | ||
| 543 | std.sort.sort([]const u8, filenames, Context{}, Context.lessThan); | ||
| 544 | } | ||
| 545 | |||
| 546 | pub const TestContext = struct { | ||
| 547 | gpa: Allocator, | ||
| 548 | arena: Allocator, | ||
| 549 | cases: std.ArrayList(Case), | ||
| 550 | |||
| 551 | pub const Update = struct { | ||
| 552 | /// The input to the current update. We simulate an incremental update | ||
| 553 | /// with the file's contents changed to this value each update. | ||
| 554 | /// | ||
| 555 | /// This value can change entirely between updates, which would be akin | ||
| 556 | /// to deleting the source file and creating a new one from scratch; or | ||
| 557 | /// you can keep it mostly consistent, with small changes, testing the | ||
| 558 | /// effects of the incremental compilation. | ||
| 559 | src: [:0]const u8, | ||
| 560 | name: []const u8, | ||
| 561 | case: union(enum) { | ||
| 562 | /// Check the main binary output file against an expected set of bytes. | ||
| 563 | /// This is most useful with, for example, `-ofmt=c`. | ||
| 564 | CompareObjectFile: []const u8, | ||
| 565 | /// An error update attempts to compile bad code, and ensures that it | ||
| 566 | /// fails to compile, and for the expected reasons. | ||
| 567 | /// A slice containing the expected errors *in sequential order*. | ||
| 568 | Error: []const ErrorMsg, | ||
| 569 | /// An execution update compiles and runs the input, testing the | ||
| 570 | /// stdout against the expected results | ||
| 571 | /// This is a slice containing the expected message. | ||
| 572 | Execution: []const u8, | ||
| 573 | /// A header update compiles the input with the equivalent of | ||
| 574 | /// `-femit-h` and tests the produced header against the | ||
| 575 | /// expected result | ||
| 576 | Header: []const u8, | ||
| 577 | }, | ||
| 578 | }; | ||
| 579 | |||
| 580 | pub const File = struct { | ||
| 581 | /// Contents of the importable file. Doesn't yet support incremental updates. | ||
| 582 | src: [:0]const u8, | ||
| 583 | path: []const u8, | ||
| 584 | }; | ||
| 585 | |||
| 586 | pub const DepModule = struct { | ||
| 587 | name: []const u8, | ||
| 588 | path: []const u8, | ||
| 589 | }; | ||
| 590 | |||
| 591 | pub const Backend = enum { | ||
| 592 | stage1, | ||
| 593 | stage2, | ||
| 594 | llvm, | ||
| 595 | }; | ||
| 596 | |||
| 597 | /// A `Case` consists of a list of `Update`. The same `Compilation` is used for each | ||
| 598 | /// update, so each update's source is treated as a single file being | ||
| 599 | /// updated by the test harness and incrementally compiled. | ||
| 600 | pub const Case = struct { | ||
| 601 | /// The name of the test case. This is shown if a test fails, and | ||
| 602 | /// otherwise ignored. | ||
| 603 | name: []const u8, | ||
| 604 | /// The platform the test targets. For non-native platforms, an emulator | ||
| 605 | /// such as QEMU is required for tests to complete. | ||
| 606 | target: CrossTarget, | ||
| 607 | /// In order to be able to run e.g. Execution updates, this must be set | ||
| 608 | /// to Executable. | ||
| 609 | output_mode: std.builtin.OutputMode, | ||
| 610 | optimize_mode: std.builtin.Mode = .Debug, | ||
| 611 | updates: std.ArrayList(Update), | ||
| 612 | emit_h: bool = false, | ||
| 613 | is_test: bool = false, | ||
| 614 | expect_exact: bool = false, | ||
| 615 | backend: Backend = .stage2, | ||
| 616 | link_libc: bool = false, | ||
| 617 | |||
| 618 | files: std.ArrayList(File), | ||
| 619 | deps: std.ArrayList(DepModule), | ||
| 620 | |||
| 621 | result: anyerror!void = {}, | ||
| 622 | |||
| 623 | pub fn addSourceFile(case: *Case, name: []const u8, src: [:0]const u8) void { | ||
| 624 | case.files.append(.{ .path = name, .src = src }) catch @panic("out of memory"); | ||
| 625 | } | ||
| 626 | |||
| 627 | pub fn addDepModule(case: *Case, name: []const u8, path: []const u8) void { | ||
| 628 | case.deps.append(.{ | ||
| 629 | .name = name, | ||
| 630 | .path = path, | ||
| 631 | }) catch @panic("out of memory"); | ||
| 632 | } | ||
| 633 | |||
| 634 | /// Adds a subcase in which the module is updated with `src`, and a C | ||
| 635 | /// header is generated. | ||
| 636 | pub fn addHeader(self: *Case, src: [:0]const u8, result: [:0]const u8) void { | ||
| 637 | self.emit_h = true; | ||
| 638 | self.updates.append(.{ | ||
| 639 | .src = src, | ||
| 640 | .name = "update", | ||
| 641 | .case = .{ .Header = result }, | ||
| 642 | }) catch @panic("out of memory"); | ||
| 643 | } | ||
| 644 | |||
| 645 | /// Adds a subcase in which the module is updated with `src`, compiled, | ||
| 646 | /// run, and the output is tested against `result`. | ||
| 647 | pub fn addCompareOutput(self: *Case, src: [:0]const u8, result: []const u8) void { | ||
| 648 | self.updates.append(.{ | ||
| 649 | .src = src, | ||
| 650 | .name = "update", | ||
| 651 | .case = .{ .Execution = result }, | ||
| 652 | }) catch @panic("out of memory"); | ||
| 653 | } | ||
| 654 | |||
| 655 | /// Adds a subcase in which the module is updated with `src`, compiled, | ||
| 656 | /// and the object file data is compared against `result`. | ||
| 657 | pub fn addCompareObjectFile(self: *Case, src: [:0]const u8, result: []const u8) void { | ||
| 658 | self.updates.append(.{ | ||
| 659 | .src = src, | ||
| 660 | .name = "update", | ||
| 661 | .case = .{ .CompareObjectFile = result }, | ||
| 662 | }) catch @panic("out of memory"); | ||
| 663 | } | ||
| 664 | |||
| 665 | pub fn addError(self: *Case, src: [:0]const u8, errors: []const []const u8) void { | ||
| 666 | return self.addErrorNamed("update", src, errors); | ||
| 667 | } | ||
| 668 | |||
| 669 | /// Adds a subcase in which the module is updated with `src`, which | ||
| 670 | /// should contain invalid input, and ensures that compilation fails | ||
| 671 | /// for the expected reasons, given in sequential order in `errors` in | ||
| 672 | /// the form `:line:column: error: message`. | ||
| 673 | pub fn addErrorNamed( | ||
| 674 | self: *Case, | ||
| 675 | name: []const u8, | ||
| 676 | src: [:0]const u8, | ||
| 677 | errors: []const []const u8, | ||
| 678 | ) void { | ||
| 679 | var array = self.updates.allocator.alloc(ErrorMsg, errors.len) catch @panic("out of memory"); | ||
| 680 | for (errors, 0..) |err_msg_line, i| { | ||
| 681 | if (std.mem.startsWith(u8, err_msg_line, "error: ")) { | ||
| 682 | array[i] = .{ | ||
| 683 | .plain = .{ | ||
| 684 | .msg = err_msg_line["error: ".len..], | ||
| 685 | .kind = .@"error", | ||
| 686 | .count = 1, | ||
| 687 | }, | ||
| 688 | }; | ||
| 689 | continue; | ||
| 690 | } else if (std.mem.startsWith(u8, err_msg_line, "note: ")) { | ||
| 691 | array[i] = .{ | ||
| 692 | .plain = .{ | ||
| 693 | .msg = err_msg_line["note: ".len..], | ||
| 694 | .kind = .note, | ||
| 695 | .count = 1, | ||
| 696 | }, | ||
| 697 | }; | ||
| 698 | continue; | ||
| 699 | } | ||
| 700 | // example: "file.zig:1:2: error: bad thing happened" | ||
| 701 | var it = std.mem.split(u8, err_msg_line, ":"); | ||
| 702 | const src_path = it.first(); | ||
| 703 | const line_text = it.next() orelse @panic("missing line"); | ||
| 704 | const col_text = it.next() orelse @panic("missing column"); | ||
| 705 | const kind_text = it.next() orelse @panic("missing 'error'/'note'"); | ||
| 706 | var msg = it.rest()[1..]; // skip over the space at end of "error: " | ||
| 707 | |||
| 708 | const line: ?u32 = if (std.mem.eql(u8, line_text, "?")) | ||
| 709 | null | ||
| 710 | else | ||
| 711 | std.fmt.parseInt(u32, line_text, 10) catch @panic("bad line number"); | ||
| 712 | const column: ?u32 = if (std.mem.eql(u8, line_text, "?")) | ||
| 713 | null | ||
| 714 | else | ||
| 715 | std.fmt.parseInt(u32, col_text, 10) catch @panic("bad column number"); | ||
| 716 | const kind: ErrorMsg.Kind = if (std.mem.eql(u8, kind_text, " error")) | ||
| 717 | .@"error" | ||
| 718 | else if (std.mem.eql(u8, kind_text, " note")) | ||
| 719 | .note | ||
| 720 | else | ||
| 721 | @panic("expected 'error'/'note'"); | ||
| 722 | |||
| 723 | const line_0based: u32 = if (line) |n| blk: { | ||
| 724 | if (n == 0) { | ||
| 725 | print("{s}: line must be specified starting at one\n", .{self.name}); | ||
| 726 | return; | ||
| 727 | } | ||
| 728 | break :blk n - 1; | ||
| 729 | } else std.math.maxInt(u32); | ||
| 730 | |||
| 731 | const column_0based: u32 = if (column) |n| blk: { | ||
| 732 | if (n == 0) { | ||
| 733 | print("{s}: line must be specified starting at one\n", .{self.name}); | ||
| 734 | return; | ||
| 735 | } | ||
| 736 | break :blk n - 1; | ||
| 737 | } else std.math.maxInt(u32); | ||
| 738 | |||
| 739 | const suffix = " times)"; | ||
| 740 | const count = if (std.mem.endsWith(u8, msg, suffix)) count: { | ||
| 741 | const lparen = std.mem.lastIndexOfScalar(u8, msg, '(').?; | ||
| 742 | const count = std.fmt.parseInt(u32, msg[lparen + 1 .. msg.len - suffix.len], 10) catch @panic("bad error note count number"); | ||
| 743 | msg = msg[0 .. lparen - 1]; | ||
| 744 | break :count count; | ||
| 745 | } else 1; | ||
| 746 | |||
| 747 | array[i] = .{ | ||
| 748 | .src = .{ | ||
| 749 | .src_path = src_path, | ||
| 750 | .msg = msg, | ||
| 751 | .line = line_0based, | ||
| 752 | .column = column_0based, | ||
| 753 | .kind = kind, | ||
| 754 | .count = count, | ||
| 755 | }, | ||
| 756 | }; | ||
| 757 | } | ||
| 758 | self.updates.append(.{ | ||
| 759 | .src = src, | ||
| 760 | .name = name, | ||
| 761 | .case = .{ .Error = array }, | ||
| 762 | }) catch @panic("out of memory"); | ||
| 763 | } | ||
| 764 | |||
| 765 | /// Adds a subcase in which the module is updated with `src`, and | ||
| 766 | /// asserts that it compiles without issue | ||
| 767 | pub fn compiles(self: *Case, src: [:0]const u8) void { | ||
| 768 | self.addError(src, &[_][]const u8{}); | ||
| 769 | } | ||
| 770 | }; | ||
| 771 | |||
| 772 | pub fn addExe( | ||
| 773 | ctx: *TestContext, | ||
| 774 | name: []const u8, | ||
| 775 | target: CrossTarget, | ||
| 776 | ) *Case { | ||
| 777 | ctx.cases.append(Case{ | ||
| 778 | .name = name, | ||
| 779 | .target = target, | ||
| 780 | .updates = std.ArrayList(Update).init(ctx.cases.allocator), | ||
| 781 | .output_mode = .Exe, | ||
| 782 | .files = std.ArrayList(File).init(ctx.arena), | ||
| 783 | .deps = std.ArrayList(DepModule).init(ctx.arena), | ||
| 784 | }) catch @panic("out of memory"); | ||
| 785 | return &ctx.cases.items[ctx.cases.items.len - 1]; | ||
| 786 | } | ||
| 787 | |||
| 788 | /// Adds a test case for Zig input, producing an executable | ||
| 789 | pub fn exe(ctx: *TestContext, name: []const u8, target: CrossTarget) *Case { | ||
| 790 | return ctx.addExe(name, target); | ||
| 791 | } | ||
| 792 | |||
| 793 | pub fn exeFromCompiledC(ctx: *TestContext, name: []const u8, target: CrossTarget) *Case { | ||
| 794 | const prefixed_name = std.fmt.allocPrint(ctx.arena, "CBE: {s}", .{name}) catch | ||
| 795 | @panic("out of memory"); | ||
| 796 | var target_adjusted = target; | ||
| 797 | target_adjusted.ofmt = std.Target.ObjectFormat.c; | ||
| 798 | ctx.cases.append(Case{ | ||
| 799 | .name = prefixed_name, | ||
| 800 | .target = target_adjusted, | ||
| 801 | .updates = std.ArrayList(Update).init(ctx.cases.allocator), | ||
| 802 | .output_mode = .Exe, | ||
| 803 | .files = std.ArrayList(File).init(ctx.arena), | ||
| 804 | .deps = std.ArrayList(DepModule).init(ctx.arena), | ||
| 805 | .link_libc = true, | ||
| 806 | }) catch @panic("out of memory"); | ||
| 807 | return &ctx.cases.items[ctx.cases.items.len - 1]; | ||
| 808 | } | ||
| 809 | |||
| 810 | /// Adds a test case that uses the LLVM backend to emit an executable. | ||
| 811 | /// Currently this implies linking libc, because only then we can generate a testable executable. | ||
| 812 | pub fn exeUsingLlvmBackend(ctx: *TestContext, name: []const u8, target: CrossTarget) *Case { | ||
| 813 | ctx.cases.append(Case{ | ||
| 814 | .name = name, | ||
| 815 | .target = target, | ||
| 816 | .updates = std.ArrayList(Update).init(ctx.cases.allocator), | ||
| 817 | .output_mode = .Exe, | ||
| 818 | .files = std.ArrayList(File).init(ctx.arena), | ||
| 819 | .deps = std.ArrayList(DepModule).init(ctx.arena), | ||
| 820 | .backend = .llvm, | ||
| 821 | .link_libc = true, | ||
| 822 | }) catch @panic("out of memory"); | ||
| 823 | return &ctx.cases.items[ctx.cases.items.len - 1]; | ||
| 824 | } | ||
| 825 | |||
| 826 | pub fn addObj( | ||
| 827 | ctx: *TestContext, | ||
| 828 | name: []const u8, | ||
| 829 | target: CrossTarget, | ||
| 830 | ) *Case { | ||
| 831 | ctx.cases.append(Case{ | ||
| 832 | .name = name, | ||
| 833 | .target = target, | ||
| 834 | .updates = std.ArrayList(Update).init(ctx.cases.allocator), | ||
| 835 | .output_mode = .Obj, | ||
| 836 | .files = std.ArrayList(File).init(ctx.arena), | ||
| 837 | .deps = std.ArrayList(DepModule).init(ctx.arena), | ||
| 838 | }) catch @panic("out of memory"); | ||
| 839 | return &ctx.cases.items[ctx.cases.items.len - 1]; | ||
| 840 | } | ||
| 841 | |||
| 842 | pub fn addTest( | ||
| 843 | ctx: *TestContext, | ||
| 844 | name: []const u8, | ||
| 845 | target: CrossTarget, | ||
| 846 | ) *Case { | ||
| 847 | ctx.cases.append(Case{ | ||
| 848 | .name = name, | ||
| 849 | .target = target, | ||
| 850 | .updates = std.ArrayList(Update).init(ctx.cases.allocator), | ||
| 851 | .output_mode = .Exe, | ||
| 852 | .is_test = true, | ||
| 853 | .files = std.ArrayList(File).init(ctx.arena), | ||
| 854 | .deps = std.ArrayList(DepModule).init(ctx.arena), | ||
| 855 | }) catch @panic("out of memory"); | ||
| 856 | return &ctx.cases.items[ctx.cases.items.len - 1]; | ||
| 857 | } | ||
| 858 | |||
| 859 | /// Adds a test case for Zig input, producing an object file. | ||
| 860 | pub fn obj(ctx: *TestContext, name: []const u8, target: CrossTarget) *Case { | ||
| 861 | return ctx.addObj(name, target); | ||
| 862 | } | ||
| 863 | |||
| 864 | /// Adds a test case for ZIR input, producing an object file. | ||
| 865 | pub fn objZIR(ctx: *TestContext, name: []const u8, target: CrossTarget) *Case { | ||
| 866 | return ctx.addObj(name, target, .ZIR); | ||
| 867 | } | ||
| 868 | |||
| 869 | /// Adds a test case for Zig or ZIR input, producing C code. | ||
| 870 | pub fn addC(ctx: *TestContext, name: []const u8, target: CrossTarget) *Case { | ||
| 871 | var target_adjusted = target; | ||
| 872 | target_adjusted.ofmt = std.Target.ObjectFormat.c; | ||
| 873 | ctx.cases.append(Case{ | ||
| 874 | .name = name, | ||
| 875 | .target = target_adjusted, | ||
| 876 | .updates = std.ArrayList(Update).init(ctx.cases.allocator), | ||
| 877 | .output_mode = .Obj, | ||
| 878 | .files = std.ArrayList(File).init(ctx.arena), | ||
| 879 | .deps = std.ArrayList(DepModule).init(ctx.arena), | ||
| 880 | }) catch @panic("out of memory"); | ||
| 881 | return &ctx.cases.items[ctx.cases.items.len - 1]; | ||
| 882 | } | ||
| 883 | |||
| 884 | pub fn c(ctx: *TestContext, name: []const u8, target: CrossTarget, src: [:0]const u8, comptime out: [:0]const u8) void { | ||
| 885 | ctx.addC(name, target).addCompareObjectFile(src, zig_h ++ out); | ||
| 886 | } | ||
| 887 | |||
| 888 | pub fn h(ctx: *TestContext, name: []const u8, target: CrossTarget, src: [:0]const u8, comptime out: [:0]const u8) void { | ||
| 889 | ctx.addC(name, target).addHeader(src, zig_h ++ out); | ||
| 890 | } | ||
| 891 | |||
| 892 | pub fn objErrStage1( | ||
| 893 | ctx: *TestContext, | ||
| 894 | name: []const u8, | ||
| 895 | src: [:0]const u8, | ||
| 896 | expected_errors: []const []const u8, | ||
| 897 | ) void { | ||
| 898 | const case = ctx.addObj(name, .{}); | ||
| 899 | case.backend = .stage1; | ||
| 900 | case.addError(src, expected_errors); | ||
| 901 | } | ||
| 902 | |||
| 903 | pub fn testErrStage1( | ||
| 904 | ctx: *TestContext, | ||
| 905 | name: []const u8, | ||
| 906 | src: [:0]const u8, | ||
| 907 | expected_errors: []const []const u8, | ||
| 908 | ) void { | ||
| 909 | const case = ctx.addTest(name, .{}); | ||
| 910 | case.backend = .stage1; | ||
| 911 | case.addError(src, expected_errors); | ||
| 912 | } | ||
| 913 | |||
| 914 | pub fn exeErrStage1( | ||
| 915 | ctx: *TestContext, | ||
| 916 | name: []const u8, | ||
| 917 | src: [:0]const u8, | ||
| 918 | expected_errors: []const []const u8, | ||
| 919 | ) void { | ||
| 920 | const case = ctx.addExe(name, .{}); | ||
| 921 | case.backend = .stage1; | ||
| 922 | case.addError(src, expected_errors); | ||
| 923 | } | ||
| 924 | |||
| 925 | pub fn addCompareOutput( | ||
| 926 | ctx: *TestContext, | ||
| 927 | name: []const u8, | ||
| 928 | src: [:0]const u8, | ||
| 929 | expected_stdout: []const u8, | ||
| 930 | ) void { | ||
| 931 | ctx.addExe(name, .{}).addCompareOutput(src, expected_stdout); | ||
| 932 | } | ||
| 933 | |||
| 934 | /// Adds a test case that compiles the Zig source given in `src`, executes | ||
| 935 | /// it, runs it, and tests the output against `expected_stdout` | ||
| 936 | pub fn compareOutput( | ||
| 937 | ctx: *TestContext, | ||
| 938 | name: []const u8, | ||
| 939 | src: [:0]const u8, | ||
| 940 | expected_stdout: []const u8, | ||
| 941 | ) void { | ||
| 942 | return ctx.addCompareOutput(name, src, expected_stdout); | ||
| 943 | } | ||
| 944 | |||
| 945 | /// Adds a test case that compiles the ZIR source given in `src`, executes | ||
| 946 | /// it, runs it, and tests the output against `expected_stdout` | ||
| 947 | pub fn compareOutputZIR( | ||
| 948 | ctx: *TestContext, | ||
| 949 | name: []const u8, | ||
| 950 | src: [:0]const u8, | ||
| 951 | expected_stdout: []const u8, | ||
| 952 | ) void { | ||
| 953 | ctx.addCompareOutput(name, .ZIR, src, expected_stdout); | ||
| 954 | } | ||
| 955 | |||
| 956 | pub fn addTransform( | ||
| 957 | ctx: *TestContext, | ||
| 958 | name: []const u8, | ||
| 959 | target: CrossTarget, | ||
| 960 | src: [:0]const u8, | ||
| 961 | result: [:0]const u8, | ||
| 962 | ) void { | ||
| 963 | ctx.addObj(name, target).addTransform(src, result); | ||
| 964 | } | ||
| 965 | |||
| 966 | /// Adds a test case that compiles the Zig given in `src` to ZIR and tests | ||
| 967 | /// the ZIR against `result` | ||
| 968 | pub fn transform( | ||
| 969 | ctx: *TestContext, | ||
| 970 | name: []const u8, | ||
| 971 | target: CrossTarget, | ||
| 972 | src: [:0]const u8, | ||
| 973 | result: [:0]const u8, | ||
| 974 | ) void { | ||
| 975 | ctx.addTransform(name, target, src, result); | ||
| 976 | } | ||
| 977 | |||
| 978 | pub fn addError( | ||
| 979 | ctx: *TestContext, | ||
| 980 | name: []const u8, | ||
| 981 | target: CrossTarget, | ||
| 982 | src: [:0]const u8, | ||
| 983 | expected_errors: []const []const u8, | ||
| 984 | ) void { | ||
| 985 | ctx.addObj(name, target).addError(src, expected_errors); | ||
| 986 | } | ||
| 987 | |||
| 988 | /// Adds a test case that ensures that the Zig given in `src` fails to | ||
| 989 | /// compile for the expected reasons, given in sequential order in | ||
| 990 | /// `expected_errors` in the form `:line:column: error: message`. | ||
| 991 | pub fn compileError( | ||
| 992 | ctx: *TestContext, | ||
| 993 | name: []const u8, | ||
| 994 | target: CrossTarget, | ||
| 995 | src: [:0]const u8, | ||
| 996 | expected_errors: []const []const u8, | ||
| 997 | ) void { | ||
| 998 | ctx.addError(name, target, src, expected_errors); | ||
| 999 | } | ||
| 1000 | |||
| 1001 | /// Adds a test case that ensures that the ZIR given in `src` fails to | ||
| 1002 | /// compile for the expected reasons, given in sequential order in | ||
| 1003 | /// `expected_errors` in the form `:line:column: error: message`. | ||
| 1004 | pub fn compileErrorZIR( | ||
| 1005 | ctx: *TestContext, | ||
| 1006 | name: []const u8, | ||
| 1007 | target: CrossTarget, | ||
| 1008 | src: [:0]const u8, | ||
| 1009 | expected_errors: []const []const u8, | ||
| 1010 | ) void { | ||
| 1011 | ctx.addError(name, target, .ZIR, src, expected_errors); | ||
| 1012 | } | ||
| 1013 | |||
| 1014 | pub fn addCompiles( | ||
| 1015 | ctx: *TestContext, | ||
| 1016 | name: []const u8, | ||
| 1017 | target: CrossTarget, | ||
| 1018 | src: [:0]const u8, | ||
| 1019 | ) void { | ||
| 1020 | ctx.addObj(name, target).compiles(src); | ||
| 1021 | } | ||
| 1022 | |||
| 1023 | /// Adds a test case that asserts that the Zig given in `src` compiles | ||
| 1024 | /// without any errors. | ||
| 1025 | pub fn compiles( | ||
| 1026 | ctx: *TestContext, | ||
| 1027 | name: []const u8, | ||
| 1028 | target: CrossTarget, | ||
| 1029 | src: [:0]const u8, | ||
| 1030 | ) void { | ||
| 1031 | ctx.addCompiles(name, target, src); | ||
| 1032 | } | ||
| 1033 | |||
| 1034 | /// Adds a test case that asserts that the ZIR given in `src` compiles | ||
| 1035 | /// without any errors. | ||
| 1036 | pub fn compilesZIR( | ||
| 1037 | ctx: *TestContext, | ||
| 1038 | name: []const u8, | ||
| 1039 | target: CrossTarget, | ||
| 1040 | src: [:0]const u8, | ||
| 1041 | ) void { | ||
| 1042 | ctx.addCompiles(name, target, .ZIR, src); | ||
| 1043 | } | ||
| 1044 | |||
| 1045 | /// Adds a test case that first ensures that the Zig given in `src` fails | ||
| 1046 | /// to compile for the reasons given in sequential order in | ||
| 1047 | /// `expected_errors` in the form `:line:column: error: message`, then | ||
| 1048 | /// asserts that fixing the source (updating with `fixed_src`) isn't broken | ||
| 1049 | /// by incremental compilation. | ||
| 1050 | pub fn incrementalFailure( | ||
| 1051 | ctx: *TestContext, | ||
| 1052 | name: []const u8, | ||
| 1053 | target: CrossTarget, | ||
| 1054 | src: [:0]const u8, | ||
| 1055 | expected_errors: []const []const u8, | ||
| 1056 | fixed_src: [:0]const u8, | ||
| 1057 | ) void { | ||
| 1058 | var case = ctx.addObj(name, target); | ||
| 1059 | case.addError(src, expected_errors); | ||
| 1060 | case.compiles(fixed_src); | ||
| 1061 | } | ||
| 1062 | |||
| 1063 | /// Adds a test case that first ensures that the ZIR given in `src` fails | ||
| 1064 | /// to compile for the reasons given in sequential order in | ||
| 1065 | /// `expected_errors` in the form `:line:column: error: message`, then | ||
| 1066 | /// asserts that fixing the source (updating with `fixed_src`) isn't broken | ||
| 1067 | /// by incremental compilation. | ||
| 1068 | pub fn incrementalFailureZIR( | ||
| 1069 | ctx: *TestContext, | ||
| 1070 | name: []const u8, | ||
| 1071 | target: CrossTarget, | ||
| 1072 | src: [:0]const u8, | ||
| 1073 | expected_errors: []const []const u8, | ||
| 1074 | fixed_src: [:0]const u8, | ||
| 1075 | ) void { | ||
| 1076 | var case = ctx.addObj(name, target, .ZIR); | ||
| 1077 | case.addError(src, expected_errors); | ||
| 1078 | case.compiles(fixed_src); | ||
| 1079 | } | ||
| 1080 | |||
| 1081 | /// Adds a test for each file in the provided directory. | ||
| 1082 | /// Testing strategy (TestStrategy) is inferred automatically from filenames. | ||
| 1083 | /// Recurses nested directories. | ||
| 1084 | /// | ||
| 1085 | /// Each file should include a test manifest as a contiguous block of comments at | ||
| 1086 | /// the end of the file. The first line should be the test type, followed by a set of | ||
| 1087 | /// key-value config values, followed by a blank line, then the expected output. | ||
| 1088 | pub fn addTestCasesFromDir(ctx: *TestContext, dir: std.fs.IterableDir) void { | ||
| 1089 | var current_file: []const u8 = "none"; | ||
| 1090 | ctx.addTestCasesFromDirInner(dir, &current_file) catch |err| { | ||
| 1091 | std.debug.panic("test harness failed to process file '{s}': {s}\n", .{ | ||
| 1092 | current_file, @errorName(err), | ||
| 1093 | }); | ||
| 1094 | }; | ||
| 1095 | } | ||
| 1096 | |||
| 1097 | fn addTestCasesFromDirInner( | ||
| 1098 | ctx: *TestContext, | ||
| 1099 | iterable_dir: std.fs.IterableDir, | ||
| 1100 | /// This is kept up to date with the currently being processed file so | ||
| 1101 | /// that if any errors occur the caller knows it happened during this file. | ||
| 1102 | current_file: *[]const u8, | ||
| 1103 | ) !void { | ||
| 1104 | var it = try iterable_dir.walk(ctx.arena); | ||
| 1105 | var filenames = std.ArrayList([]const u8).init(ctx.arena); | ||
| 1106 | |||
| 1107 | while (try it.next()) |entry| { | ||
| 1108 | if (entry.kind != .File) continue; | ||
| 1109 | |||
| 1110 | // Ignore stuff such as .swp files | ||
| 1111 | switch (Compilation.classifyFileExt(entry.basename)) { | ||
| 1112 | .unknown => continue, | ||
| 1113 | else => {}, | ||
| 1114 | } | ||
| 1115 | try filenames.append(try ctx.arena.dupe(u8, entry.path)); | ||
| 1116 | } | ||
| 1117 | |||
| 1118 | // Sort filenames, so that incremental tests are contiguous and in-order | ||
| 1119 | sortTestFilenames(filenames.items); | ||
| 1120 | |||
| 1121 | var test_it = TestIterator{ .filenames = filenames.items }; | ||
| 1122 | while (test_it.next()) |maybe_batch| { | ||
| 1123 | const batch = maybe_batch orelse break; | ||
| 1124 | const strategy: TestStrategy = if (batch.len > 1) .incremental else .independent; | ||
| 1125 | var cases = std.ArrayList(usize).init(ctx.arena); | ||
| 1126 | |||
| 1127 | for (batch) |filename| { | ||
| 1128 | current_file.* = filename; | ||
| 1129 | |||
| 1130 | const max_file_size = 10 * 1024 * 1024; | ||
| 1131 | const src = try iterable_dir.dir.readFileAllocOptions(ctx.arena, filename, max_file_size, null, 1, 0); | ||
| 1132 | |||
| 1133 | // Parse the manifest | ||
| 1134 | var manifest = try TestManifest.parse(ctx.arena, src); | ||
| 1135 | |||
| 1136 | if (cases.items.len == 0) { | ||
| 1137 | const backends = try manifest.getConfigForKeyAlloc(ctx.arena, "backend", Backend); | ||
| 1138 | const targets = try manifest.getConfigForKeyAlloc(ctx.arena, "target", CrossTarget); | ||
| 1139 | const is_test = try manifest.getConfigForKeyAssertSingle("is_test", bool); | ||
| 1140 | const output_mode = try manifest.getConfigForKeyAssertSingle("output_mode", std.builtin.OutputMode); | ||
| 1141 | |||
| 1142 | const name_prefix = blk: { | ||
| 1143 | const ext_index = std.mem.lastIndexOfScalar(u8, current_file.*, '.') orelse | ||
| 1144 | return error.InvalidFilename; | ||
| 1145 | const index = std.mem.lastIndexOfScalar(u8, current_file.*[0..ext_index], '.') orelse ext_index; | ||
| 1146 | break :blk current_file.*[0..index]; | ||
| 1147 | }; | ||
| 1148 | |||
| 1149 | // Cross-product to get all possible test combinations | ||
| 1150 | for (backends) |backend| { | ||
| 1151 | for (targets) |target| { | ||
| 1152 | const name = try std.fmt.allocPrint(ctx.arena, "{s} ({s}, {s})", .{ | ||
| 1153 | name_prefix, | ||
| 1154 | @tagName(backend), | ||
| 1155 | try target.zigTriple(ctx.arena), | ||
| 1156 | }); | ||
| 1157 | const next = ctx.cases.items.len; | ||
| 1158 | try ctx.cases.append(.{ | ||
| 1159 | .name = name, | ||
| 1160 | .target = target, | ||
| 1161 | .backend = backend, | ||
| 1162 | .updates = std.ArrayList(TestContext.Update).init(ctx.cases.allocator), | ||
| 1163 | .is_test = is_test, | ||
| 1164 | .output_mode = output_mode, | ||
| 1165 | .link_libc = backend == .llvm, | ||
| 1166 | .files = std.ArrayList(TestContext.File).init(ctx.cases.allocator), | ||
| 1167 | .deps = std.ArrayList(DepModule).init(ctx.cases.allocator), | ||
| 1168 | }); | ||
| 1169 | try cases.append(next); | ||
| 1170 | } | ||
| 1171 | } | ||
| 1172 | } | ||
| 1173 | |||
| 1174 | for (cases.items) |case_index| { | ||
| 1175 | const case = &ctx.cases.items[case_index]; | ||
| 1176 | switch (manifest.type) { | ||
| 1177 | .@"error" => { | ||
| 1178 | const errors = try manifest.trailingAlloc(ctx.arena); | ||
| 1179 | switch (strategy) { | ||
| 1180 | .independent => { | ||
| 1181 | case.addError(src, errors); | ||
| 1182 | }, | ||
| 1183 | .incremental => { | ||
| 1184 | case.addErrorNamed("update", src, errors); | ||
| 1185 | }, | ||
| 1186 | } | ||
| 1187 | }, | ||
| 1188 | .run => { | ||
| 1189 | var output = std.ArrayList(u8).init(ctx.arena); | ||
| 1190 | var trailing_it = manifest.trailing(); | ||
| 1191 | while (trailing_it.next()) |line| { | ||
| 1192 | try output.appendSlice(line); | ||
| 1193 | try output.append('\n'); | ||
| 1194 | } | ||
| 1195 | if (output.items.len > 0) { | ||
| 1196 | try output.resize(output.items.len - 1); | ||
| 1197 | } | ||
| 1198 | case.addCompareOutput(src, try output.toOwnedSlice()); | ||
| 1199 | }, | ||
| 1200 | .cli => @panic("TODO cli tests"), | ||
| 1201 | } | ||
| 1202 | } | ||
| 1203 | } | ||
| 1204 | } else |err| { | ||
| 1205 | // make sure the current file is set to the file that produced an error | ||
| 1206 | current_file.* = test_it.currentFilename(); | ||
| 1207 | return err; | ||
| 1208 | } | ||
| 1209 | } | ||
| 1210 | |||
| 1211 | fn init(gpa: Allocator, arena: Allocator) TestContext { | ||
| 1212 | return .{ | ||
| 1213 | .gpa = gpa, | ||
| 1214 | .cases = std.ArrayList(Case).init(gpa), | ||
| 1215 | .arena = arena, | ||
| 1216 | }; | ||
| 1217 | } | ||
| 1218 | |||
| 1219 | fn deinit(self: *TestContext) void { | ||
| 1220 | for (self.cases.items) |case| { | ||
| 1221 | for (case.updates.items) |u| { | ||
| 1222 | if (u.case == .Error) { | ||
| 1223 | case.updates.allocator.free(u.case.Error); | ||
| 1224 | } | ||
| 1225 | } | ||
| 1226 | case.updates.deinit(); | ||
| 1227 | } | ||
| 1228 | self.cases.deinit(); | ||
| 1229 | self.* = undefined; | ||
| 1230 | } | ||
| 1231 | |||
| 1232 | fn run(self: *TestContext) !void { | ||
| 1233 | const host = try std.zig.system.NativeTargetInfo.detect(.{}); | ||
| 1234 | const zig_exe_path = try std.process.getEnvVarOwned(self.arena, "ZIG_EXE"); | ||
| 1235 | |||
| 1236 | var progress = std.Progress{}; | ||
| 1237 | const root_node = progress.start("compiler", self.cases.items.len); | ||
| 1238 | defer root_node.end(); | ||
| 1239 | |||
| 1240 | var zig_lib_directory = try introspect.findZigLibDir(self.gpa); | ||
| 1241 | defer zig_lib_directory.handle.close(); | ||
| 1242 | defer self.gpa.free(zig_lib_directory.path.?); | ||
| 1243 | |||
| 1244 | var aux_thread_pool: ThreadPool = undefined; | ||
| 1245 | try aux_thread_pool.init(self.gpa); | ||
| 1246 | defer aux_thread_pool.deinit(); | ||
| 1247 | |||
| 1248 | // Use the same global cache dir for all the tests, such that we for example don't have to | ||
| 1249 | // rebuild musl libc for every case (when LLVM backend is enabled). | ||
| 1250 | var global_tmp = std.testing.tmpDir(.{}); | ||
| 1251 | defer global_tmp.cleanup(); | ||
| 1252 | |||
| 1253 | var cache_dir = try global_tmp.dir.makeOpenPath("zig-cache", .{}); | ||
| 1254 | defer cache_dir.close(); | ||
| 1255 | const tmp_dir_path = try std.fs.path.join(self.gpa, &[_][]const u8{ ".", "zig-cache", "tmp", &global_tmp.sub_path }); | ||
| 1256 | defer self.gpa.free(tmp_dir_path); | ||
| 1257 | |||
| 1258 | const global_cache_directory: Compilation.Directory = .{ | ||
| 1259 | .handle = cache_dir, | ||
| 1260 | .path = try std.fs.path.join(self.gpa, &[_][]const u8{ tmp_dir_path, "zig-cache" }), | ||
| 1261 | }; | ||
| 1262 | defer self.gpa.free(global_cache_directory.path.?); | ||
| 1263 | |||
| 1264 | { | ||
| 1265 | for (self.cases.items) |*case| { | ||
| 1266 | if (build_options.skip_non_native) { | ||
| 1267 | if (case.target.getCpuArch() != builtin.cpu.arch) | ||
| 1268 | continue; | ||
| 1269 | if (case.target.getObjectFormat() != builtin.object_format) | ||
| 1270 | continue; | ||
| 1271 | } | ||
| 1272 | |||
| 1273 | // Skip tests that require LLVM backend when it is not available | ||
| 1274 | if (!build_options.have_llvm and case.backend == .llvm) | ||
| 1275 | continue; | ||
| 1276 | |||
| 1277 | if (skip_stage1 and case.backend == .stage1) | ||
| 1278 | continue; | ||
| 1279 | |||
| 1280 | if (build_options.test_filter) |test_filter| { | ||
| 1281 | if (std.mem.indexOf(u8, case.name, test_filter) == null) continue; | ||
| 1282 | } | ||
| 1283 | |||
| 1284 | var prg_node = root_node.start(case.name, case.updates.items.len); | ||
| 1285 | prg_node.activate(); | ||
| 1286 | defer prg_node.end(); | ||
| 1287 | |||
| 1288 | case.result = runOneCase( | ||
| 1289 | self.gpa, | ||
| 1290 | &prg_node, | ||
| 1291 | case.*, | ||
| 1292 | zig_lib_directory, | ||
| 1293 | zig_exe_path, | ||
| 1294 | &aux_thread_pool, | ||
| 1295 | global_cache_directory, | ||
| 1296 | host, | ||
| 1297 | ); | ||
| 1298 | } | ||
| 1299 | } | ||
| 1300 | |||
| 1301 | var fail_count: usize = 0; | ||
| 1302 | for (self.cases.items) |*case| { | ||
| 1303 | case.result catch |err| { | ||
| 1304 | fail_count += 1; | ||
| 1305 | print("{s} failed: {s}\n", .{ case.name, @errorName(err) }); | ||
| 1306 | }; | ||
| 1307 | } | ||
| 1308 | |||
| 1309 | if (fail_count != 0) { | ||
| 1310 | print("{d} tests failed\n", .{fail_count}); | ||
| 1311 | return error.TestFailed; | ||
| 1312 | } | ||
| 1313 | } | ||
| 1314 | |||
| 1315 | fn runOneCase( | ||
| 1316 | allocator: Allocator, | ||
| 1317 | root_node: *std.Progress.Node, | ||
| 1318 | case: Case, | ||
| 1319 | zig_lib_directory: Compilation.Directory, | ||
| 1320 | zig_exe_path: []const u8, | ||
| 1321 | thread_pool: *ThreadPool, | ||
| 1322 | global_cache_directory: Compilation.Directory, | ||
| 1323 | host: std.zig.system.NativeTargetInfo, | ||
| 1324 | ) !void { | ||
| 1325 | const target_info = try std.zig.system.NativeTargetInfo.detect(case.target); | ||
| 1326 | const target = target_info.target; | ||
| 1327 | |||
| 1328 | var arena_allocator = std.heap.ArenaAllocator.init(allocator); | ||
| 1329 | defer arena_allocator.deinit(); | ||
| 1330 | const arena = arena_allocator.allocator(); | ||
| 1331 | |||
| 1332 | var tmp = std.testing.tmpDir(.{}); | ||
| 1333 | defer tmp.cleanup(); | ||
| 1334 | |||
| 1335 | var cache_dir = try tmp.dir.makeOpenPath("zig-cache", .{}); | ||
| 1336 | defer cache_dir.close(); | ||
| 1337 | |||
| 1338 | const tmp_dir_path = try std.fs.path.join( | ||
| 1339 | arena, | ||
| 1340 | &[_][]const u8{ ".", "zig-cache", "tmp", &tmp.sub_path }, | ||
| 1341 | ); | ||
| 1342 | const tmp_dir_path_plus_slash = try std.fmt.allocPrint( | ||
| 1343 | arena, | ||
| 1344 | "{s}" ++ std.fs.path.sep_str, | ||
| 1345 | .{tmp_dir_path}, | ||
| 1346 | ); | ||
| 1347 | const local_cache_path = try std.fs.path.join( | ||
| 1348 | arena, | ||
| 1349 | &[_][]const u8{ tmp_dir_path, "zig-cache" }, | ||
| 1350 | ); | ||
| 1351 | |||
| 1352 | for (case.files.items) |file| { | ||
| 1353 | try tmp.dir.writeFile(file.path, file.src); | ||
| 1354 | } | ||
| 1355 | |||
| 1356 | if (case.backend == .stage1) { | ||
| 1357 | // stage1 backend has limitations: | ||
| 1358 | // * leaks memory | ||
| 1359 | // * calls exit() when a compile error happens | ||
| 1360 | // * cannot handle updates | ||
| 1361 | // because of this we must spawn a child process rather than | ||
| 1362 | // using Compilation directly. | ||
| 1363 | |||
| 1364 | if (!std.process.can_spawn) { | ||
| 1365 | print("Unable to spawn child processes on {s}, skipping test.\n", .{@tagName(builtin.os.tag)}); | ||
| 1366 | return; // Pass test. | ||
| 1367 | } | ||
| 1368 | |||
| 1369 | assert(case.updates.items.len == 1); | ||
| 1370 | const update = case.updates.items[0]; | ||
| 1371 | try tmp.dir.writeFile(tmp_src_path, update.src); | ||
| 1372 | |||
| 1373 | var zig_args = std.ArrayList([]const u8).init(arena); | ||
| 1374 | try zig_args.append(zig_exe_path); | ||
| 1375 | |||
| 1376 | if (case.is_test) { | ||
| 1377 | try zig_args.append("test"); | ||
| 1378 | } else if (update.case == .Execution) { | ||
| 1379 | try zig_args.append("run"); | ||
| 1380 | } else switch (case.output_mode) { | ||
| 1381 | .Obj => try zig_args.append("build-obj"), | ||
| 1382 | .Exe => try zig_args.append("build-exe"), | ||
| 1383 | .Lib => try zig_args.append("build-lib"), | ||
| 1384 | } | ||
| 1385 | |||
| 1386 | try zig_args.append(try std.fs.path.join(arena, &.{ tmp_dir_path, tmp_src_path })); | ||
| 1387 | |||
| 1388 | try zig_args.append("--name"); | ||
| 1389 | try zig_args.append("test"); | ||
| 1390 | |||
| 1391 | try zig_args.append("--cache-dir"); | ||
| 1392 | try zig_args.append(local_cache_path); | ||
| 1393 | |||
| 1394 | try zig_args.append("--global-cache-dir"); | ||
| 1395 | try zig_args.append(global_cache_directory.path orelse "."); | ||
| 1396 | |||
| 1397 | if (!case.target.isNative()) { | ||
| 1398 | try zig_args.append("-target"); | ||
| 1399 | try zig_args.append(try target.zigTriple(arena)); | ||
| 1400 | } | ||
| 1401 | |||
| 1402 | try zig_args.append("-O"); | ||
| 1403 | try zig_args.append(@tagName(case.optimize_mode)); | ||
| 1404 | |||
| 1405 | // Prevent sub-process progress bar from interfering with the | ||
| 1406 | // one in this parent process. | ||
| 1407 | try zig_args.append("--color"); | ||
| 1408 | try zig_args.append("off"); | ||
| 1409 | |||
| 1410 | const result = try std.ChildProcess.exec(.{ | ||
| 1411 | .allocator = arena, | ||
| 1412 | .argv = zig_args.items, | ||
| 1413 | }); | ||
| 1414 | switch (update.case) { | ||
| 1415 | .Error => |case_error_list| { | ||
| 1416 | switch (result.term) { | ||
| 1417 | .Exited => |code| { | ||
| 1418 | if (code == 0) { | ||
| 1419 | dumpArgs(zig_args.items); | ||
| 1420 | return error.CompilationIncorrectlySucceeded; | ||
| 1421 | } | ||
| 1422 | }, | ||
| 1423 | else => { | ||
| 1424 | std.debug.print("{s}", .{result.stderr}); | ||
| 1425 | dumpArgs(zig_args.items); | ||
| 1426 | return error.CompilationCrashed; | ||
| 1427 | }, | ||
| 1428 | } | ||
| 1429 | var ok = true; | ||
| 1430 | if (case.expect_exact) { | ||
| 1431 | var err_iter = std.mem.split(u8, result.stderr, "\n"); | ||
| 1432 | var i: usize = 0; | ||
| 1433 | ok = while (err_iter.next()) |line| : (i += 1) { | ||
| 1434 | if (i >= case_error_list.len) break false; | ||
| 1435 | const expected = try std.mem.replaceOwned( | ||
| 1436 | u8, | ||
| 1437 | arena, | ||
| 1438 | try std.fmt.allocPrint(arena, "{s}", .{case_error_list[i]}), | ||
| 1439 | "${DIR}", | ||
| 1440 | tmp_dir_path_plus_slash, | ||
| 1441 | ); | ||
| 1442 | |||
| 1443 | if (std.mem.indexOf(u8, line, expected) == null) break false; | ||
| 1444 | continue; | ||
| 1445 | } else true; | ||
| 1446 | |||
| 1447 | ok = ok and i == case_error_list.len; | ||
| 1448 | |||
| 1449 | if (!ok) { | ||
| 1450 | print("\n======== Expected these compile errors: ========\n", .{}); | ||
| 1451 | for (case_error_list) |msg| { | ||
| 1452 | const expected = try std.fmt.allocPrint(arena, "{s}", .{msg}); | ||
| 1453 | print("{s}\n", .{expected}); | ||
| 1454 | } | ||
| 1455 | } | ||
| 1456 | } else { | ||
| 1457 | for (case_error_list) |msg| { | ||
| 1458 | const expected = try std.mem.replaceOwned( | ||
| 1459 | u8, | ||
| 1460 | arena, | ||
| 1461 | try std.fmt.allocPrint(arena, "{s}", .{msg}), | ||
| 1462 | "${DIR}", | ||
| 1463 | tmp_dir_path_plus_slash, | ||
| 1464 | ); | ||
| 1465 | if (std.mem.indexOf(u8, result.stderr, expected) == null) { | ||
| 1466 | print( | ||
| 1467 | \\ | ||
| 1468 | \\=========== Expected compile error: ============ | ||
| 1469 | \\{s} | ||
| 1470 | \\ | ||
| 1471 | , .{expected}); | ||
| 1472 | ok = false; | ||
| 1473 | break; | ||
| 1474 | } | ||
| 1475 | } | ||
| 1476 | } | ||
| 1477 | |||
| 1478 | if (!ok) { | ||
| 1479 | print( | ||
| 1480 | \\================= Full output: ================= | ||
| 1481 | \\{s} | ||
| 1482 | \\================================================ | ||
| 1483 | \\ | ||
| 1484 | , .{result.stderr}); | ||
| 1485 | return error.TestFailed; | ||
| 1486 | } | ||
| 1487 | }, | ||
| 1488 | .CompareObjectFile => @panic("TODO implement in the test harness"), | ||
| 1489 | .Execution => |expected_stdout| { | ||
| 1490 | switch (result.term) { | ||
| 1491 | .Exited => |code| { | ||
| 1492 | if (code != 0) { | ||
| 1493 | std.debug.print("{s}", .{result.stderr}); | ||
| 1494 | dumpArgs(zig_args.items); | ||
| 1495 | return error.CompilationFailed; | ||
| 1496 | } | ||
| 1497 | }, | ||
| 1498 | else => { | ||
| 1499 | std.debug.print("{s}", .{result.stderr}); | ||
| 1500 | dumpArgs(zig_args.items); | ||
| 1501 | return error.CompilationCrashed; | ||
| 1502 | }, | ||
| 1503 | } | ||
| 1504 | try std.testing.expectEqualStrings("", result.stderr); | ||
| 1505 | try std.testing.expectEqualStrings(expected_stdout, result.stdout); | ||
| 1506 | }, | ||
| 1507 | .Header => @panic("TODO implement in the test harness"), | ||
| 1508 | } | ||
| 1509 | return; | ||
| 1510 | } | ||
| 1511 | |||
| 1512 | const zig_cache_directory: Compilation.Directory = .{ | ||
| 1513 | .handle = cache_dir, | ||
| 1514 | .path = local_cache_path, | ||
| 1515 | }; | ||
| 1516 | |||
| 1517 | var main_pkg: Package = .{ | ||
| 1518 | .root_src_directory = .{ .path = tmp_dir_path, .handle = tmp.dir }, | ||
| 1519 | .root_src_path = tmp_src_path, | ||
| 1520 | }; | ||
| 1521 | defer { | ||
| 1522 | var it = main_pkg.table.iterator(); | ||
| 1523 | while (it.next()) |kv| { | ||
| 1524 | allocator.free(kv.key_ptr.*); | ||
| 1525 | kv.value_ptr.*.destroy(allocator); | ||
| 1526 | } | ||
| 1527 | main_pkg.table.deinit(allocator); | ||
| 1528 | } | ||
| 1529 | |||
| 1530 | for (case.deps.items) |dep| { | ||
| 1531 | var pkg = try Package.create( | ||
| 1532 | allocator, | ||
| 1533 | tmp_dir_path, | ||
| 1534 | dep.path, | ||
| 1535 | ); | ||
| 1536 | errdefer pkg.destroy(allocator); | ||
| 1537 | try main_pkg.add(allocator, dep.name, pkg); | ||
| 1538 | } | ||
| 1539 | |||
| 1540 | const bin_name = try std.zig.binNameAlloc(arena, .{ | ||
| 1541 | .root_name = "test_case", | ||
| 1542 | .target = target, | ||
| 1543 | .output_mode = case.output_mode, | ||
| 1544 | }); | ||
| 1545 | |||
| 1546 | const emit_directory: Compilation.Directory = .{ | ||
| 1547 | .path = tmp_dir_path, | ||
| 1548 | .handle = tmp.dir, | ||
| 1549 | }; | ||
| 1550 | const emit_bin: Compilation.EmitLoc = .{ | ||
| 1551 | .directory = emit_directory, | ||
| 1552 | .basename = bin_name, | ||
| 1553 | }; | ||
| 1554 | const emit_h: ?Compilation.EmitLoc = if (case.emit_h) .{ | ||
| 1555 | .directory = emit_directory, | ||
| 1556 | .basename = "test_case.h", | ||
| 1557 | } else null; | ||
| 1558 | const use_llvm: bool = switch (case.backend) { | ||
| 1559 | .llvm => true, | ||
| 1560 | else => false, | ||
| 1561 | }; | ||
| 1562 | const comp = try Compilation.create(allocator, .{ | ||
| 1563 | .local_cache_directory = zig_cache_directory, | ||
| 1564 | .global_cache_directory = global_cache_directory, | ||
| 1565 | .zig_lib_directory = zig_lib_directory, | ||
| 1566 | .thread_pool = thread_pool, | ||
| 1567 | .root_name = "test_case", | ||
| 1568 | .target = target, | ||
| 1569 | // TODO: support tests for object file building, and library builds | ||
| 1570 | // and linking. This will require a rework to support multi-file | ||
| 1571 | // tests. | ||
| 1572 | .output_mode = case.output_mode, | ||
| 1573 | .is_test = case.is_test, | ||
| 1574 | .optimize_mode = case.optimize_mode, | ||
| 1575 | .emit_bin = emit_bin, | ||
| 1576 | .emit_h = emit_h, | ||
| 1577 | .main_pkg = &main_pkg, | ||
| 1578 | .keep_source_files_loaded = true, | ||
| 1579 | .is_native_os = case.target.isNativeOs(), | ||
| 1580 | .is_native_abi = case.target.isNativeAbi(), | ||
| 1581 | .dynamic_linker = target_info.dynamic_linker.get(), | ||
| 1582 | .link_libc = case.link_libc, | ||
| 1583 | .use_llvm = use_llvm, | ||
| 1584 | .self_exe_path = zig_exe_path, | ||
| 1585 | // TODO instead of turning off color, pass in a std.Progress.Node | ||
| 1586 | .color = .off, | ||
| 1587 | .reference_trace = 0, | ||
| 1588 | // TODO: force self-hosted linkers with stage2 backend to avoid LLD creeping in | ||
| 1589 | // until the auto-select mechanism deems them worthy | ||
| 1590 | .use_lld = switch (case.backend) { | ||
| 1591 | .stage2 => false, | ||
| 1592 | else => null, | ||
| 1593 | }, | ||
| 1594 | }); | ||
| 1595 | defer comp.destroy(); | ||
| 1596 | |||
| 1597 | update: for (case.updates.items, 0..) |update, update_index| { | ||
| 1598 | var update_node = root_node.start(update.name, 3); | ||
| 1599 | update_node.activate(); | ||
| 1600 | defer update_node.end(); | ||
| 1601 | |||
| 1602 | var sync_node = update_node.start("write", 0); | ||
| 1603 | sync_node.activate(); | ||
| 1604 | try tmp.dir.writeFile(tmp_src_path, update.src); | ||
| 1605 | sync_node.end(); | ||
| 1606 | |||
| 1607 | var module_node = update_node.start("parse/analysis/codegen", 0); | ||
| 1608 | module_node.activate(); | ||
| 1609 | module_node.context.refresh(); | ||
| 1610 | try comp.makeBinFileWritable(); | ||
| 1611 | try comp.update(); | ||
| 1612 | module_node.end(); | ||
| 1613 | |||
| 1614 | if (update.case != .Error) { | ||
| 1615 | var all_errors = try comp.getAllErrorsAlloc(); | ||
| 1616 | defer all_errors.deinit(allocator); | ||
| 1617 | if (all_errors.list.len != 0) { | ||
| 1618 | print( | ||
| 1619 | "\nCase '{s}': unexpected errors at update_index={d}:\n{s}\n", | ||
| 1620 | .{ case.name, update_index, hr }, | ||
| 1621 | ); | ||
| 1622 | for (all_errors.list) |err_msg| { | ||
| 1623 | switch (err_msg) { | ||
| 1624 | .src => |src| { | ||
| 1625 | print("{s}:{d}:{d}: error: {s}\n{s}\n", .{ | ||
| 1626 | src.src_path, src.line + 1, src.column + 1, src.msg, hr, | ||
| 1627 | }); | ||
| 1628 | }, | ||
| 1629 | .plain => |plain| { | ||
| 1630 | print("error: {s}\n{s}\n", .{ plain.msg, hr }); | ||
| 1631 | }, | ||
| 1632 | } | ||
| 1633 | } | ||
| 1634 | // TODO print generated C code | ||
| 1635 | return error.UnexpectedCompileErrors; | ||
| 1636 | } | ||
| 1637 | } | ||
| 1638 | |||
| 1639 | switch (update.case) { | ||
| 1640 | .Header => |expected_output| { | ||
| 1641 | var file = try tmp.dir.openFile("test_case.h", .{ .mode = .read_only }); | ||
| 1642 | defer file.close(); | ||
| 1643 | const out = try file.reader().readAllAlloc(arena, 5 * 1024 * 1024); | ||
| 1644 | |||
| 1645 | try std.testing.expectEqualStrings(expected_output, out); | ||
| 1646 | }, | ||
| 1647 | .CompareObjectFile => |expected_output| { | ||
| 1648 | var file = try tmp.dir.openFile(bin_name, .{ .mode = .read_only }); | ||
| 1649 | defer file.close(); | ||
| 1650 | const out = try file.reader().readAllAlloc(arena, 5 * 1024 * 1024); | ||
| 1651 | |||
| 1652 | try std.testing.expectEqualStrings(expected_output, out); | ||
| 1653 | }, | ||
| 1654 | .Error => |case_error_list| { | ||
| 1655 | var test_node = update_node.start("assert", 0); | ||
| 1656 | test_node.activate(); | ||
| 1657 | defer test_node.end(); | ||
| 1658 | |||
| 1659 | const handled_errors = try arena.alloc(bool, case_error_list.len); | ||
| 1660 | std.mem.set(bool, handled_errors, false); | ||
| 1661 | |||
| 1662 | var actual_errors = try comp.getAllErrorsAlloc(); | ||
| 1663 | defer actual_errors.deinit(allocator); | ||
| 1664 | |||
| 1665 | var any_failed = false; | ||
| 1666 | var notes_to_check = std.ArrayList(*const Compilation.AllErrors.Message).init(allocator); | ||
| 1667 | defer notes_to_check.deinit(); | ||
| 1668 | |||
| 1669 | for (actual_errors.list) |actual_error| { | ||
| 1670 | for (case_error_list, 0..) |case_msg, i| { | ||
| 1671 | if (handled_errors[i]) continue; | ||
| 1672 | |||
| 1673 | const ex_tag: std.meta.Tag(@TypeOf(case_msg)) = case_msg; | ||
| 1674 | switch (actual_error) { | ||
| 1675 | .src => |actual_msg| { | ||
| 1676 | for (actual_msg.notes) |*note| { | ||
| 1677 | try notes_to_check.append(note); | ||
| 1678 | } | ||
| 1679 | |||
| 1680 | if (ex_tag != .src) continue; | ||
| 1681 | |||
| 1682 | const src_path_ok = case_msg.src.src_path.len == 0 or | ||
| 1683 | std.mem.eql(u8, case_msg.src.src_path, actual_msg.src_path); | ||
| 1684 | |||
| 1685 | const expected_msg = try std.mem.replaceOwned( | ||
| 1686 | u8, | ||
| 1687 | arena, | ||
| 1688 | case_msg.src.msg, | ||
| 1689 | "${DIR}", | ||
| 1690 | tmp_dir_path_plus_slash, | ||
| 1691 | ); | ||
| 1692 | |||
| 1693 | var buf: [1024]u8 = undefined; | ||
| 1694 | const rendered_msg = blk: { | ||
| 1695 | var msg: Compilation.AllErrors.Message = actual_error; | ||
| 1696 | msg.src.src_path = case_msg.src.src_path; | ||
| 1697 | msg.src.notes = &.{}; | ||
| 1698 | msg.src.source_line = null; | ||
| 1699 | var fib = std.io.fixedBufferStream(&buf); | ||
| 1700 | try msg.renderToWriter(.no_color, fib.writer(), "error", .Red, 0); | ||
| 1701 | var it = std.mem.split(u8, fib.getWritten(), "error: "); | ||
| 1702 | _ = it.first(); | ||
| 1703 | const rendered = it.rest(); | ||
| 1704 | break :blk rendered[0 .. rendered.len - 1]; // trim final newline | ||
| 1705 | }; | ||
| 1706 | |||
| 1707 | if (src_path_ok and | ||
| 1708 | (case_msg.src.line == std.math.maxInt(u32) or | ||
| 1709 | actual_msg.line == case_msg.src.line) and | ||
| 1710 | (case_msg.src.column == std.math.maxInt(u32) or | ||
| 1711 | actual_msg.column == case_msg.src.column) and | ||
| 1712 | std.mem.eql(u8, expected_msg, rendered_msg) and | ||
| 1713 | case_msg.src.kind == .@"error" and | ||
| 1714 | actual_msg.count == case_msg.src.count) | ||
| 1715 | { | ||
| 1716 | handled_errors[i] = true; | ||
| 1717 | break; | ||
| 1718 | } | ||
| 1719 | }, | ||
| 1720 | .plain => |plain| { | ||
| 1721 | if (ex_tag != .plain) continue; | ||
| 1722 | |||
| 1723 | if (std.mem.eql(u8, case_msg.plain.msg, plain.msg) and | ||
| 1724 | case_msg.plain.kind == .@"error" and | ||
| 1725 | case_msg.plain.count == plain.count) | ||
| 1726 | { | ||
| 1727 | handled_errors[i] = true; | ||
| 1728 | break; | ||
| 1729 | } | ||
| 1730 | }, | ||
| 1731 | } | ||
| 1732 | } else { | ||
| 1733 | print( | ||
| 1734 | "\nUnexpected error:\n{s}\n{}\n{s}", | ||
| 1735 | .{ hr, ErrorMsg.init(actual_error, .@"error"), hr }, | ||
| 1736 | ); | ||
| 1737 | any_failed = true; | ||
| 1738 | } | ||
| 1739 | } | ||
| 1740 | while (notes_to_check.popOrNull()) |note| { | ||
| 1741 | for (case_error_list, 0..) |case_msg, i| { | ||
| 1742 | const ex_tag: std.meta.Tag(@TypeOf(case_msg)) = case_msg; | ||
| 1743 | switch (note.*) { | ||
| 1744 | .src => |actual_msg| { | ||
| 1745 | for (actual_msg.notes) |*sub_note| { | ||
| 1746 | try notes_to_check.append(sub_note); | ||
| 1747 | } | ||
| 1748 | if (ex_tag != .src) continue; | ||
| 1749 | |||
| 1750 | const expected_msg = try std.mem.replaceOwned( | ||
| 1751 | u8, | ||
| 1752 | arena, | ||
| 1753 | case_msg.src.msg, | ||
| 1754 | "${DIR}", | ||
| 1755 | tmp_dir_path_plus_slash, | ||
| 1756 | ); | ||
| 1757 | |||
| 1758 | if ((case_msg.src.line == std.math.maxInt(u32) or | ||
| 1759 | actual_msg.line == case_msg.src.line) and | ||
| 1760 | (case_msg.src.column == std.math.maxInt(u32) or | ||
| 1761 | actual_msg.column == case_msg.src.column) and | ||
| 1762 | std.mem.eql(u8, expected_msg, actual_msg.msg) and | ||
| 1763 | case_msg.src.kind == .note and | ||
| 1764 | actual_msg.count == case_msg.src.count) | ||
| 1765 | { | ||
| 1766 | handled_errors[i] = true; | ||
| 1767 | break; | ||
| 1768 | } | ||
| 1769 | }, | ||
| 1770 | .plain => |plain| { | ||
| 1771 | if (ex_tag != .plain) continue; | ||
| 1772 | |||
| 1773 | if (std.mem.eql(u8, case_msg.plain.msg, plain.msg) and | ||
| 1774 | case_msg.plain.kind == .note and | ||
| 1775 | case_msg.plain.count == plain.count) | ||
| 1776 | { | ||
| 1777 | handled_errors[i] = true; | ||
| 1778 | break; | ||
| 1779 | } | ||
| 1780 | }, | ||
| 1781 | } | ||
| 1782 | } else { | ||
| 1783 | print( | ||
| 1784 | "\nUnexpected note:\n{s}\n{}\n{s}", | ||
| 1785 | .{ hr, ErrorMsg.init(note.*, .note), hr }, | ||
| 1786 | ); | ||
| 1787 | any_failed = true; | ||
| 1788 | } | ||
| 1789 | } | ||
| 1790 | |||
| 1791 | for (handled_errors, 0..) |handled, i| { | ||
| 1792 | if (!handled) { | ||
| 1793 | print( | ||
| 1794 | "\nExpected error not found:\n{s}\n{}\n{s}", | ||
| 1795 | .{ hr, case_error_list[i], hr }, | ||
| 1796 | ); | ||
| 1797 | any_failed = true; | ||
| 1798 | } | ||
| 1799 | } | ||
| 1800 | |||
| 1801 | if (any_failed) { | ||
| 1802 | print("\nupdate_index={d}\n", .{update_index}); | ||
| 1803 | return error.WrongCompileErrors; | ||
| 1804 | } | ||
| 1805 | }, | ||
| 1806 | .Execution => |expected_stdout| { | ||
| 1807 | if (!std.process.can_spawn) { | ||
| 1808 | print("Unable to spawn child processes on {s}, skipping test.\n", .{@tagName(builtin.os.tag)}); | ||
| 1809 | continue :update; // Pass test. | ||
| 1810 | } | ||
| 1811 | |||
| 1812 | update_node.setEstimatedTotalItems(4); | ||
| 1813 | |||
| 1814 | var argv = std.ArrayList([]const u8).init(allocator); | ||
| 1815 | defer argv.deinit(); | ||
| 1816 | |||
| 1817 | var exec_result = x: { | ||
| 1818 | var exec_node = update_node.start("execute", 0); | ||
| 1819 | exec_node.activate(); | ||
| 1820 | defer exec_node.end(); | ||
| 1821 | |||
| 1822 | // We go out of our way here to use the unique temporary directory name in | ||
| 1823 | // the exe_path so that it makes its way into the cache hash, avoiding | ||
| 1824 | // cache collisions from multiple threads doing `zig run` at the same time | ||
| 1825 | // on the same test_case.c input filename. | ||
| 1826 | const ss = std.fs.path.sep_str; | ||
| 1827 | const exe_path = try std.fmt.allocPrint( | ||
| 1828 | arena, | ||
| 1829 | ".." ++ ss ++ "{s}" ++ ss ++ "{s}", | ||
| 1830 | .{ &tmp.sub_path, bin_name }, | ||
| 1831 | ); | ||
| 1832 | if (case.target.ofmt != null and case.target.ofmt.? == .c) { | ||
| 1833 | if (host.getExternalExecutor(target_info, .{ .link_libc = true }) != .native) { | ||
| 1834 | // We wouldn't be able to run the compiled C code. | ||
| 1835 | continue :update; // Pass test. | ||
| 1836 | } | ||
| 1837 | try argv.appendSlice(&[_][]const u8{ | ||
| 1838 | zig_exe_path, | ||
| 1839 | "run", | ||
| 1840 | "-cflags", | ||
| 1841 | "-std=c99", | ||
| 1842 | "-pedantic", | ||
| 1843 | "-Werror", | ||
| 1844 | "-Wno-incompatible-library-redeclaration", // https://github.com/ziglang/zig/issues/875 | ||
| 1845 | "--", | ||
| 1846 | "-lc", | ||
| 1847 | exe_path, | ||
| 1848 | }); | ||
| 1849 | if (zig_lib_directory.path) |p| { | ||
| 1850 | try argv.appendSlice(&.{ "-I", p }); | ||
| 1851 | } | ||
| 1852 | } else switch (host.getExternalExecutor(target_info, .{ .link_libc = case.link_libc })) { | ||
| 1853 | .native => { | ||
| 1854 | if (case.backend == .stage2 and case.target.getCpuArch() == .arm) { | ||
| 1855 | // https://github.com/ziglang/zig/issues/13623 | ||
| 1856 | continue :update; // Pass test. | ||
| 1857 | } | ||
| 1858 | try argv.append(exe_path); | ||
| 1859 | }, | ||
| 1860 | .bad_dl, .bad_os_or_cpu => continue :update, // Pass test. | ||
| 1861 | |||
| 1862 | .rosetta => if (enable_rosetta) { | ||
| 1863 | try argv.append(exe_path); | ||
| 1864 | } else { | ||
| 1865 | continue :update; // Rosetta not available, pass test. | ||
| 1866 | }, | ||
| 1867 | |||
| 1868 | .qemu => |qemu_bin_name| if (enable_qemu) { | ||
| 1869 | const need_cross_glibc = target.isGnuLibC() and case.link_libc; | ||
| 1870 | const glibc_dir_arg: ?[]const u8 = if (need_cross_glibc) | ||
| 1871 | glibc_runtimes_dir orelse continue :update // glibc dir not available; pass test | ||
| 1872 | else | ||
| 1873 | null; | ||
| 1874 | try argv.append(qemu_bin_name); | ||
| 1875 | if (glibc_dir_arg) |dir| { | ||
| 1876 | const linux_triple = try target.linuxTriple(arena); | ||
| 1877 | const full_dir = try std.fs.path.join(arena, &[_][]const u8{ | ||
| 1878 | dir, | ||
| 1879 | linux_triple, | ||
| 1880 | }); | ||
| 1881 | |||
| 1882 | try argv.append("-L"); | ||
| 1883 | try argv.append(full_dir); | ||
| 1884 | } | ||
| 1885 | try argv.append(exe_path); | ||
| 1886 | } else { | ||
| 1887 | continue :update; // QEMU not available; pass test. | ||
| 1888 | }, | ||
| 1889 | |||
| 1890 | .wine => |wine_bin_name| if (enable_wine) { | ||
| 1891 | try argv.append(wine_bin_name); | ||
| 1892 | try argv.append(exe_path); | ||
| 1893 | } else { | ||
| 1894 | continue :update; // Wine not available; pass test. | ||
| 1895 | }, | ||
| 1896 | |||
| 1897 | .wasmtime => |wasmtime_bin_name| if (enable_wasmtime) { | ||
| 1898 | try argv.append(wasmtime_bin_name); | ||
| 1899 | try argv.append("--dir=."); | ||
| 1900 | try argv.append(exe_path); | ||
| 1901 | } else { | ||
| 1902 | continue :update; // wasmtime not available; pass test. | ||
| 1903 | }, | ||
| 1904 | |||
| 1905 | .darling => |darling_bin_name| if (enable_darling) { | ||
| 1906 | try argv.append(darling_bin_name); | ||
| 1907 | // Since we use relative to cwd here, we invoke darling with | ||
| 1908 | // "shell" subcommand. | ||
| 1909 | try argv.append("shell"); | ||
| 1910 | try argv.append(exe_path); | ||
| 1911 | } else { | ||
| 1912 | continue :update; // Darling not available; pass test. | ||
| 1913 | }, | ||
| 1914 | } | ||
| 1915 | |||
| 1916 | try comp.makeBinFileExecutable(); | ||
| 1917 | |||
| 1918 | while (true) { | ||
| 1919 | break :x std.ChildProcess.exec(.{ | ||
| 1920 | .allocator = allocator, | ||
| 1921 | .argv = argv.items, | ||
| 1922 | .cwd_dir = tmp.dir, | ||
| 1923 | .cwd = tmp_dir_path, | ||
| 1924 | }) catch |err| switch (err) { | ||
| 1925 | error.FileBusy => { | ||
| 1926 | // There is a fundamental design flaw in Unix systems with how | ||
| 1927 | // ETXTBSY interacts with fork+exec. | ||
| 1928 | // https://github.com/golang/go/issues/22315 | ||
| 1929 | // https://bugs.openjdk.org/browse/JDK-8068370 | ||
| 1930 | // Unfortunately, this could be a real error, but we can't | ||
| 1931 | // tell the difference here. | ||
| 1932 | continue; | ||
| 1933 | }, | ||
| 1934 | else => { | ||
| 1935 | print("\n{s}.{d} The following command failed with {s}:\n", .{ | ||
| 1936 | case.name, update_index, @errorName(err), | ||
| 1937 | }); | ||
| 1938 | dumpArgs(argv.items); | ||
| 1939 | return error.ChildProcessExecution; | ||
| 1940 | }, | ||
| 1941 | }; | ||
| 1942 | } | ||
| 1943 | }; | ||
| 1944 | var test_node = update_node.start("test", 0); | ||
| 1945 | test_node.activate(); | ||
| 1946 | defer test_node.end(); | ||
| 1947 | defer allocator.free(exec_result.stdout); | ||
| 1948 | defer allocator.free(exec_result.stderr); | ||
| 1949 | switch (exec_result.term) { | ||
| 1950 | .Exited => |code| { | ||
| 1951 | if (code != 0) { | ||
| 1952 | print("\n{s}\n{s}: execution exited with code {d}:\n", .{ | ||
| 1953 | exec_result.stderr, case.name, code, | ||
| 1954 | }); | ||
| 1955 | dumpArgs(argv.items); | ||
| 1956 | return error.ChildProcessExecution; | ||
| 1957 | } | ||
| 1958 | }, | ||
| 1959 | else => { | ||
| 1960 | print("\n{s}\n{s}: execution crashed:\n", .{ | ||
| 1961 | exec_result.stderr, case.name, | ||
| 1962 | }); | ||
| 1963 | dumpArgs(argv.items); | ||
| 1964 | return error.ChildProcessExecution; | ||
| 1965 | }, | ||
| 1966 | } | ||
| 1967 | try std.testing.expectEqualStrings(expected_stdout, exec_result.stdout); | ||
| 1968 | // We allow stderr to have garbage in it because wasmtime prints a | ||
| 1969 | // warning about --invoke even though we don't pass it. | ||
| 1970 | //std.testing.expectEqualStrings("", exec_result.stderr); | ||
| 1971 | }, | ||
| 1972 | } | ||
| 1973 | } | ||
| 1974 | } | ||
| 1975 | }; | ||
| 1976 | |||
| 1977 | fn dumpArgs(argv: []const []const u8) void { | ||
| 1978 | for (argv) |arg| { | ||
| 1979 | print("{s} ", .{arg}); | ||
| 1980 | } | ||
| 1981 | print("\n", .{}); | ||
| 1982 | } | ||
| 1983 | |||
| 1984 | const tmp_src_path = "tmp.zig"; | ||
src/wasi_libc.zig+8-8| ... | @@ -59,7 +59,7 @@ pub fn execModelCrtFileFullName(wasi_exec_model: std.builtin.WasiExecModel) []co | ... | @@ -59,7 +59,7 @@ pub fn execModelCrtFileFullName(wasi_exec_model: std.builtin.WasiExecModel) []co |
| 59 | }; | 59 | }; |
| 60 | } | 60 | } |
| 61 | 61 | ||
| 62 | pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { | 62 | pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progress.Node) !void { |
| 63 | if (!build_options.have_llvm) { | 63 | if (!build_options.have_llvm) { |
| 64 | return error.ZigCompilerNotBuiltWithLLVMExtensions; | 64 | return error.ZigCompilerNotBuiltWithLLVMExtensions; |
| 65 | } | 65 | } |
| ... | @@ -74,7 +74,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { | ... | @@ -74,7 +74,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { |
| 74 | var args = std.ArrayList([]const u8).init(arena); | 74 | var args = std.ArrayList([]const u8).init(arena); |
| 75 | try addCCArgs(comp, arena, &args, false); | 75 | try addCCArgs(comp, arena, &args, false); |
| 76 | try addLibcBottomHalfIncludes(comp, arena, &args); | 76 | try addLibcBottomHalfIncludes(comp, arena, &args); |
| 77 | return comp.build_crt_file("crt1-reactor", .Obj, &[1]Compilation.CSourceFile{ | 77 | return comp.build_crt_file("crt1-reactor", .Obj, .@"wasi crt1-reactor.o", prog_node, &.{ |
| 78 | .{ | 78 | .{ |
| 79 | .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ | 79 | .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ |
| 80 | "libc", try sanitize(arena, crt1_reactor_src_file), | 80 | "libc", try sanitize(arena, crt1_reactor_src_file), |
| ... | @@ -87,7 +87,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { | ... | @@ -87,7 +87,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { |
| 87 | var args = std.ArrayList([]const u8).init(arena); | 87 | var args = std.ArrayList([]const u8).init(arena); |
| 88 | try addCCArgs(comp, arena, &args, false); | 88 | try addCCArgs(comp, arena, &args, false); |
| 89 | try addLibcBottomHalfIncludes(comp, arena, &args); | 89 | try addLibcBottomHalfIncludes(comp, arena, &args); |
| 90 | return comp.build_crt_file("crt1-command", .Obj, &[1]Compilation.CSourceFile{ | 90 | return comp.build_crt_file("crt1-command", .Obj, .@"wasi crt1-command.o", prog_node, &.{ |
| 91 | .{ | 91 | .{ |
| 92 | .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ | 92 | .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ |
| 93 | "libc", try sanitize(arena, crt1_command_src_file), | 93 | "libc", try sanitize(arena, crt1_command_src_file), |
| ... | @@ -145,7 +145,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { | ... | @@ -145,7 +145,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { |
| 145 | } | 145 | } |
| 146 | } | 146 | } |
| 147 | 147 | ||
| 148 | try comp.build_crt_file("c", .Lib, libc_sources.items); | 148 | try comp.build_crt_file("c", .Lib, .@"wasi libc.a", prog_node, libc_sources.items); |
| 149 | }, | 149 | }, |
| 150 | .libwasi_emulated_process_clocks_a => { | 150 | .libwasi_emulated_process_clocks_a => { |
| 151 | var args = std.ArrayList([]const u8).init(arena); | 151 | var args = std.ArrayList([]const u8).init(arena); |
| ... | @@ -161,7 +161,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { | ... | @@ -161,7 +161,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { |
| 161 | .extra_flags = args.items, | 161 | .extra_flags = args.items, |
| 162 | }); | 162 | }); |
| 163 | } | 163 | } |
| 164 | try comp.build_crt_file("wasi-emulated-process-clocks", .Lib, emu_clocks_sources.items); | 164 | try comp.build_crt_file("wasi-emulated-process-clocks", .Lib, .@"libwasi-emulated-process-clocks.a", prog_node, emu_clocks_sources.items); |
| 165 | }, | 165 | }, |
| 166 | .libwasi_emulated_getpid_a => { | 166 | .libwasi_emulated_getpid_a => { |
| 167 | var args = std.ArrayList([]const u8).init(arena); | 167 | var args = std.ArrayList([]const u8).init(arena); |
| ... | @@ -177,7 +177,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { | ... | @@ -177,7 +177,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { |
| 177 | .extra_flags = args.items, | 177 | .extra_flags = args.items, |
| 178 | }); | 178 | }); |
| 179 | } | 179 | } |
| 180 | try comp.build_crt_file("wasi-emulated-getpid", .Lib, emu_getpid_sources.items); | 180 | try comp.build_crt_file("wasi-emulated-getpid", .Lib, .@"libwasi-emulated-getpid.a", prog_node, emu_getpid_sources.items); |
| 181 | }, | 181 | }, |
| 182 | .libwasi_emulated_mman_a => { | 182 | .libwasi_emulated_mman_a => { |
| 183 | var args = std.ArrayList([]const u8).init(arena); | 183 | var args = std.ArrayList([]const u8).init(arena); |
| ... | @@ -193,7 +193,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { | ... | @@ -193,7 +193,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { |
| 193 | .extra_flags = args.items, | 193 | .extra_flags = args.items, |
| 194 | }); | 194 | }); |
| 195 | } | 195 | } |
| 196 | try comp.build_crt_file("wasi-emulated-mman", .Lib, emu_mman_sources.items); | 196 | try comp.build_crt_file("wasi-emulated-mman", .Lib, .@"libwasi-emulated-mman.a", prog_node, emu_mman_sources.items); |
| 197 | }, | 197 | }, |
| 198 | .libwasi_emulated_signal_a => { | 198 | .libwasi_emulated_signal_a => { |
| 199 | var emu_signal_sources = std.ArrayList(Compilation.CSourceFile).init(arena); | 199 | var emu_signal_sources = std.ArrayList(Compilation.CSourceFile).init(arena); |
| ... | @@ -228,7 +228,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { | ... | @@ -228,7 +228,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { |
| 228 | } | 228 | } |
| 229 | } | 229 | } |
| 230 | 230 | ||
| 231 | try comp.build_crt_file("wasi-emulated-signal", .Lib, emu_signal_sources.items); | 231 | try comp.build_crt_file("wasi-emulated-signal", .Lib, .@"libwasi-emulated-signal.a", prog_node, emu_signal_sources.items); |
| 232 | }, | 232 | }, |
| 233 | } | 233 | } |
| 234 | } | 234 | } |
test/behavior/array.zig+1| ... | @@ -84,6 +84,7 @@ test "array concat with tuple" { | ... | @@ -84,6 +84,7 @@ test "array concat with tuple" { |
| 84 | } | 84 | } |
| 85 | 85 | ||
| 86 | test "array init with concat" { | 86 | test "array init with concat" { |
| 87 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | ||
| 87 | const a = 'a'; | 88 | const a = 'a'; |
| 88 | var i: [4]u8 = [2]u8{ a, 'b' } ++ [2]u8{ 'c', 'd' }; | 89 | var i: [4]u8 = [2]u8{ a, 'b' } ++ [2]u8{ 'c', 'd' }; |
| 89 | try expect(std.mem.eql(u8, &i, "abcd")); | 90 | try expect(std.mem.eql(u8, &i, "abcd")); |
test/behavior/ptrcast.zig+2| ... | @@ -170,6 +170,7 @@ test "lower reinterpreted comptime field ptr" { | ... | @@ -170,6 +170,7 @@ test "lower reinterpreted comptime field ptr" { |
| 170 | 170 | ||
| 171 | test "reinterpret struct field at comptime" { | 171 | test "reinterpret struct field at comptime" { |
| 172 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 172 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 173 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | ||
| 173 | 174 | ||
| 174 | const numNative = comptime Bytes.init(0x12345678); | 175 | const numNative = comptime Bytes.init(0x12345678); |
| 175 | if (native_endian != .Little) { | 176 | if (native_endian != .Little) { |
| ... | @@ -232,6 +233,7 @@ test "ptrcast of const integer has the correct object size" { | ... | @@ -232,6 +233,7 @@ test "ptrcast of const integer has the correct object size" { |
| 232 | test "implicit optional pointer to optional anyopaque pointer" { | 233 | test "implicit optional pointer to optional anyopaque pointer" { |
| 233 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 234 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 234 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 235 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 236 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | ||
| 235 | 237 | ||
| 236 | var buf: [4]u8 = "aoeu".*; | 238 | var buf: [4]u8 = "aoeu".*; |
| 237 | var x: ?[*]u8 = &buf; | 239 | var x: ?[*]u8 = &buf; |
test/behavior/slice.zig+1| ... | @@ -227,6 +227,7 @@ fn sliceFromLenToLen(a_slice: []u8, start: usize, end: usize) []u8 { | ... | @@ -227,6 +227,7 @@ fn sliceFromLenToLen(a_slice: []u8, start: usize, end: usize) []u8 { |
| 227 | 227 | ||
| 228 | test "C pointer" { | 228 | test "C pointer" { |
| 229 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 229 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 230 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | ||
| 230 | 231 | ||
| 231 | var buf: [*c]const u8 = "kjdhfkjdhfdkjhfkfjhdfkjdhfkdjhfdkjhf"; | 232 | var buf: [*c]const u8 = "kjdhfkjdhfdkjhfkfjhdfkjdhfkdjhfdkjhf"; |
| 232 | var len: u32 = 10; | 233 | var len: u32 = 10; |
test/cases.zig+5-5| ... | @@ -1,8 +1,8 @@ | ... | @@ -1,8 +1,8 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const TestContext = @import("../src/test.zig").TestContext; | 2 | const Cases = @import("src/Cases.zig"); |
| 3 | 3 | ||
| 4 | pub fn addCases(ctx: *TestContext) !void { | 4 | pub fn addCases(cases: *Cases) !void { |
| 5 | try @import("compile_errors.zig").addCases(ctx); | 5 | try @import("compile_errors.zig").addCases(cases); |
| 6 | try @import("stage2/cbe.zig").addCases(ctx); | 6 | try @import("cbe.zig").addCases(cases); |
| 7 | try @import("stage2/nvptx.zig").addCases(ctx); | 7 | try @import("nvptx.zig").addCases(cases); |
| 8 | } | 8 | } |
test/cases/compile_errors/access_inactive_union_field_comptime.zig+1| ... | @@ -21,3 +21,4 @@ pub export fn entry1() void { | ... | @@ -21,3 +21,4 @@ pub export fn entry1() void { |
| 21 | // :9:15: error: access of union field 'a' while field 'b' is active | 21 | // :9:15: error: access of union field 'a' while field 'b' is active |
| 22 | // :2:21: note: union declared here | 22 | // :2:21: note: union declared here |
| 23 | // :14:16: error: access of union field 'a' while field 'b' is active | 23 | // :14:16: error: access of union field 'a' while field 'b' is active |
| 24 | // :2:21: note: union declared here |
test/cases/compile_errors/bad_import.zig+1-1| ... | @@ -4,4 +4,4 @@ const bogus = @import("bogus-does-not-exist.zig",); | ... | @@ -4,4 +4,4 @@ const bogus = @import("bogus-does-not-exist.zig",); |
| 4 | // backend=stage2 | 4 | // backend=stage2 |
| 5 | // target=native | 5 | // target=native |
| 6 | // | 6 | // |
| 7 | // :1:23: error: unable to load '${DIR}bogus-does-not-exist.zig': FileNotFound | 7 | // bogus-does-not-exist.zig': FileNotFound |
test/cases/compile_errors/compileLog_of_tagged_enum_doesnt_crash_the_compiler.zig+4| ... | @@ -15,3 +15,7 @@ pub export fn entry() void { | ... | @@ -15,3 +15,7 @@ pub export fn entry() void { |
| 15 | // target=native | 15 | // target=native |
| 16 | // | 16 | // |
| 17 | // :6:5: error: found compile log statement | 17 | // :6:5: error: found compile log statement |
| 18 | // | ||
| 19 | // Compile Log Output: | ||
| 20 | // @as(tmp.Bar, .{ .X = 123 }) | ||
| 21 | // @as(tmp.Bar, [runtime value]) |
test/cases/compile_errors/compile_log.zig+9| ... | @@ -17,3 +17,12 @@ export fn baz() void { | ... | @@ -17,3 +17,12 @@ export fn baz() void { |
| 17 | // | 17 | // |
| 18 | // :5:5: error: found compile log statement | 18 | // :5:5: error: found compile log statement |
| 19 | // :11:5: note: also here | 19 | // :11:5: note: also here |
| 20 | // | ||
| 21 | // Compile Log Output: | ||
| 22 | // @as(*const [5:0]u8, "begin") | ||
| 23 | // @as(*const [1:0]u8, "a"), @as(i32, 12), @as(*const [1:0]u8, "b"), @as([]const u8, "hi") | ||
| 24 | // @as(*const [3:0]u8, "end") | ||
| 25 | // @as(comptime_int, 4) | ||
| 26 | // @as(*const [5:0]u8, "begin") | ||
| 27 | // @as(*const [1:0]u8, "a"), @as(i32, [runtime value]), @as(*const [1:0]u8, "b"), @as([]const u8, [runtime value]) | ||
| 28 | // @as(*const [3:0]u8, "end") |
test/cases/compile_errors/compile_log_a_pointer_to_an_opaque_value.zig+4-1| ... | @@ -1,5 +1,5 @@ | ... | @@ -1,5 +1,5 @@ |
| 1 | export fn entry() void { | 1 | export fn entry() void { |
| 2 | @compileLog(@ptrCast(*const anyopaque, &entry)); | 2 | @compileLog(@as(*align(1) const anyopaque, @ptrCast(*const anyopaque, &entry))); |
| 3 | } | 3 | } |
| 4 | 4 | ||
| 5 | // error | 5 | // error |
| ... | @@ -7,3 +7,6 @@ export fn entry() void { | ... | @@ -7,3 +7,6 @@ export fn entry() void { |
| 7 | // target=native | 7 | // target=native |
| 8 | // | 8 | // |
| 9 | // :2:5: error: found compile log statement | 9 | // :2:5: error: found compile log statement |
| 10 | // | ||
| 11 | // Compile Log Output: | ||
| 12 | // @as(*const anyopaque, (function 'entry')) |
test/cases/compile_errors/compile_log_statement_inside_function_which_must_be_comptime_evaluated.zig+3| ... | @@ -12,3 +12,6 @@ export fn entry() void { | ... | @@ -12,3 +12,6 @@ export fn entry() void { |
| 12 | // target=native | 12 | // target=native |
| 13 | // | 13 | // |
| 14 | // :2:5: error: found compile log statement | 14 | // :2:5: error: found compile log statement |
| 15 | // | ||
| 16 | // Compile Log Output: | ||
| 17 | // @as(*const [3:0]u8, "i32\x00") |
test/cases/compile_errors/compile_log_statement_warning_deduplication_in_generic_fn.zig+5| ... | @@ -13,3 +13,8 @@ fn inner(comptime n: usize) void { | ... | @@ -13,3 +13,8 @@ fn inner(comptime n: usize) void { |
| 13 | // | 13 | // |
| 14 | // :7:39: error: found compile log statement | 14 | // :7:39: error: found compile log statement |
| 15 | // :7:39: note: also here | 15 | // :7:39: note: also here |
| 16 | // | ||
| 17 | // Compile Log Output: | ||
| 18 | // @as(*const [4:0]u8, "!@#$") | ||
| 19 | // @as(*const [4:0]u8, "!@#$") | ||
| 20 | // @as(*const [4:0]u8, "!@#$") |
test/cases/compile_errors/condition_comptime_reason_explained.zig+2| ... | @@ -45,4 +45,6 @@ pub export fn entry2() void { | ... | @@ -45,4 +45,6 @@ pub export fn entry2() void { |
| 45 | // :22:13: error: unable to resolve comptime value | 45 | // :22:13: error: unable to resolve comptime value |
| 46 | // :22:13: note: condition in comptime switch must be comptime-known | 46 | // :22:13: note: condition in comptime switch must be comptime-known |
| 47 | // :21:17: note: expression is evaluated at comptime because the function returns a comptime-only type 'tmp.S' | 47 | // :21:17: note: expression is evaluated at comptime because the function returns a comptime-only type 'tmp.S' |
| 48 | // :2:12: note: struct requires comptime because of this field | ||
| 49 | // :2:12: note: use '*const fn() void' for a function pointer type | ||
| 48 | // :32:19: note: called from here | 50 | // :32:19: note: called from here |
test/cases/compile_errors/directly_embedding_opaque_type_in_struct_and_union.zig+1| ... | @@ -32,6 +32,7 @@ export fn d() void { | ... | @@ -32,6 +32,7 @@ export fn d() void { |
| 32 | // :3:8: error: opaque types have unknown size and therefore cannot be directly embedded in structs | 32 | // :3:8: error: opaque types have unknown size and therefore cannot be directly embedded in structs |
| 33 | // :1:11: note: opaque declared here | 33 | // :1:11: note: opaque declared here |
| 34 | // :7:10: error: opaque types have unknown size and therefore cannot be directly embedded in unions | 34 | // :7:10: error: opaque types have unknown size and therefore cannot be directly embedded in unions |
| 35 | // :1:11: note: opaque declared here | ||
| 35 | // :19:18: error: opaque types have unknown size and therefore cannot be directly embedded in structs | 36 | // :19:18: error: opaque types have unknown size and therefore cannot be directly embedded in structs |
| 36 | // :18:22: note: opaque declared here | 37 | // :18:22: note: opaque declared here |
| 37 | // :24:23: error: opaque types have unknown size and therefore cannot be directly embedded in structs | 38 | // :24:23: error: opaque types have unknown size and therefore cannot be directly embedded in structs |
test/cases/compile_errors/extern_function_with_comptime_parameter.zig+1-1| ... | @@ -12,6 +12,6 @@ comptime { _ = entry2; } | ... | @@ -12,6 +12,6 @@ comptime { _ = entry2; } |
| 12 | // backend=stage2 | 12 | // backend=stage2 |
| 13 | // target=native | 13 | // target=native |
| 14 | // | 14 | // |
| 15 | // :1:15: error: comptime parameters not allowed in function with calling convention 'C' | ||
| 16 | // :5:30: error: comptime parameters not allowed in function with calling convention 'C' | 15 | // :5:30: error: comptime parameters not allowed in function with calling convention 'C' |
| 17 | // :6:30: error: generic parameters not allowed in function with calling convention 'C' | 16 | // :6:30: error: generic parameters not allowed in function with calling convention 'C' |
| 17 | // :1:15: error: comptime parameters not allowed in function with calling convention 'C' |
test/cases/compile_errors/function_parameter_is_opaque.zig+1| ... | @@ -27,4 +27,5 @@ export fn entry4() void { | ... | @@ -27,4 +27,5 @@ export fn entry4() void { |
| 27 | // :1:17: note: opaque declared here | 27 | // :1:17: note: opaque declared here |
| 28 | // :8:28: error: parameter of type '@TypeOf(null)' not allowed | 28 | // :8:28: error: parameter of type '@TypeOf(null)' not allowed |
| 29 | // :12:8: error: parameter of opaque type 'tmp.FooType' not allowed | 29 | // :12:8: error: parameter of opaque type 'tmp.FooType' not allowed |
| 30 | // :1:17: note: opaque declared here | ||
| 30 | // :17:8: error: parameter of type '@TypeOf(null)' not allowed | 31 | // :17:8: error: parameter of type '@TypeOf(null)' not allowed |
test/cases/compile_errors/helpful_return_type_error_message.zig+1-1| ... | @@ -24,9 +24,9 @@ export fn quux() u32 { | ... | @@ -24,9 +24,9 @@ export fn quux() u32 { |
| 24 | // :8:5: error: expected type 'void', found '@typeInfo(@typeInfo(@TypeOf(tmp.bar)).Fn.return_type.?).ErrorUnion.error_set' | 24 | // :8:5: error: expected type 'void', found '@typeInfo(@typeInfo(@TypeOf(tmp.bar)).Fn.return_type.?).ErrorUnion.error_set' |
| 25 | // :7:17: note: function cannot return an error | 25 | // :7:17: note: function cannot return an error |
| 26 | // :11:15: error: expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(tmp.bar)).Fn.return_type.?).ErrorUnion.error_set!u32' | 26 | // :11:15: error: expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(tmp.bar)).Fn.return_type.?).ErrorUnion.error_set!u32' |
| 27 | // :10:17: note: function cannot return an error | ||
| 28 | // :11:15: note: cannot convert error union to payload type | 27 | // :11:15: note: cannot convert error union to payload type |
| 29 | // :11:15: note: consider using 'try', 'catch', or 'if' | 28 | // :11:15: note: consider using 'try', 'catch', or 'if' |
| 29 | // :10:17: note: function cannot return an error | ||
| 30 | // :15:14: error: expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(tmp.bar)).Fn.return_type.?).ErrorUnion.error_set!u32' | 30 | // :15:14: error: expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(tmp.bar)).Fn.return_type.?).ErrorUnion.error_set!u32' |
| 31 | // :15:14: note: cannot convert error union to payload type | 31 | // :15:14: note: cannot convert error union to payload type |
| 32 | // :15:14: note: consider using 'try', 'catch', or 'if' | 32 | // :15:14: note: consider using 'try', 'catch', or 'if' |
test/cases/compile_errors/implicit_semicolon-block_expr.zig+2| ... | @@ -3,6 +3,8 @@ export fn entry() void { | ... | @@ -3,6 +3,8 @@ export fn entry() void { |
| 3 | var good = {}; | 3 | var good = {}; |
| 4 | _ = {} | 4 | _ = {} |
| 5 | var bad = {}; | 5 | var bad = {}; |
| 6 | _ = good; | ||
| 7 | _ = bad; | ||
| 6 | } | 8 | } |
| 7 | 9 | ||
| 8 | // error | 10 | // error |
test/cases/compile_errors/implicit_semicolon-block_statement.zig+2| ... | @@ -3,6 +3,8 @@ export fn entry() void { | ... | @@ -3,6 +3,8 @@ export fn entry() void { |
| 3 | var good = {}; | 3 | var good = {}; |
| 4 | ({}) | 4 | ({}) |
| 5 | var bad = {}; | 5 | var bad = {}; |
| 6 | _ = good; | ||
| 7 | _ = bad; | ||
| 6 | } | 8 | } |
| 7 | 9 | ||
| 8 | // error | 10 | // error |
test/cases/compile_errors/implicit_semicolon-comptime_expression.zig+2| ... | @@ -3,6 +3,8 @@ export fn entry() void { | ... | @@ -3,6 +3,8 @@ export fn entry() void { |
| 3 | var good = {}; | 3 | var good = {}; |
| 4 | _ = comptime {} | 4 | _ = comptime {} |
| 5 | var bad = {}; | 5 | var bad = {}; |
| 6 | _ = good; | ||
| 7 | _ = bad; | ||
| 6 | } | 8 | } |
| 7 | 9 | ||
| 8 | // error | 10 | // error |
test/cases/compile_errors/implicit_semicolon-comptime_statement.zig+2| ... | @@ -3,6 +3,8 @@ export fn entry() void { | ... | @@ -3,6 +3,8 @@ export fn entry() void { |
| 3 | var good = {}; | 3 | var good = {}; |
| 4 | comptime ({}) | 4 | comptime ({}) |
| 5 | var bad = {}; | 5 | var bad = {}; |
| 6 | _ = good; | ||
| 7 | _ = bad; | ||
| 6 | } | 8 | } |
| 7 | 9 | ||
| 8 | // error | 10 | // error |
test/cases/compile_errors/implicit_semicolon-defer.zig+2| ... | @@ -3,6 +3,8 @@ export fn entry() void { | ... | @@ -3,6 +3,8 @@ export fn entry() void { |
| 3 | var good = {}; | 3 | var good = {}; |
| 4 | defer ({}) | 4 | defer ({}) |
| 5 | var bad = {}; | 5 | var bad = {}; |
| 6 | _ = good; | ||
| 7 | _ = bad; | ||
| 6 | } | 8 | } |
| 7 | 9 | ||
| 8 | // error | 10 | // error |
test/cases/compile_errors/implicit_semicolon-for_expression.zig+3| ... | @@ -3,7 +3,10 @@ export fn entry() void { | ... | @@ -3,7 +3,10 @@ export fn entry() void { |
| 3 | var good = {}; | 3 | var good = {}; |
| 4 | _ = for(foo()) |_| {} | 4 | _ = for(foo()) |_| {} |
| 5 | var bad = {}; | 5 | var bad = {}; |
| 6 | _ = good; | ||
| 7 | _ = bad; | ||
| 6 | } | 8 | } |
| 9 | fn foo() void {} | ||
| 7 | 10 | ||
| 8 | // error | 11 | // error |
| 9 | // backend=stage2 | 12 | // backend=stage2 |
test/cases/compile_errors/implicit_semicolon-for_statement.zig+3| ... | @@ -3,7 +3,10 @@ export fn entry() void { | ... | @@ -3,7 +3,10 @@ export fn entry() void { |
| 3 | var good = {}; | 3 | var good = {}; |
| 4 | for(foo()) |_| ({}) | 4 | for(foo()) |_| ({}) |
| 5 | var bad = {}; | 5 | var bad = {}; |
| 6 | _ = good; | ||
| 7 | _ = bad; | ||
| 6 | } | 8 | } |
| 9 | fn foo() void {} | ||
| 7 | 10 | ||
| 8 | // error | 11 | // error |
| 9 | // backend=stage2 | 12 | // backend=stage2 |
test/cases/compile_errors/implicit_semicolon-if-else-if-else_expression.zig+2| ... | @@ -3,6 +3,8 @@ export fn entry() void { | ... | @@ -3,6 +3,8 @@ export fn entry() void { |
| 3 | var good = {}; | 3 | var good = {}; |
| 4 | _ = if(true) {} else if(true) {} else {} | 4 | _ = if(true) {} else if(true) {} else {} |
| 5 | var bad = {}; | 5 | var bad = {}; |
| 6 | _ = good; | ||
| 7 | _ = bad; | ||
| 6 | } | 8 | } |
| 7 | 9 | ||
| 8 | // error | 10 | // error |
test/cases/compile_errors/implicit_semicolon-if-else-if-else_statement.zig+2| ... | @@ -3,6 +3,8 @@ export fn entry() void { | ... | @@ -3,6 +3,8 @@ export fn entry() void { |
| 3 | var good = {}; | 3 | var good = {}; |
| 4 | if(true) ({}) else if(true) ({}) else ({}) | 4 | if(true) ({}) else if(true) ({}) else ({}) |
| 5 | var bad = {}; | 5 | var bad = {}; |
| 6 | _ = good; | ||
| 7 | _ = bad; | ||
| 6 | } | 8 | } |
| 7 | 9 | ||
| 8 | // error | 10 | // error |
test/cases/compile_errors/implicit_semicolon-if-else-if_expression.zig+2| ... | @@ -3,6 +3,8 @@ export fn entry() void { | ... | @@ -3,6 +3,8 @@ export fn entry() void { |
| 3 | var good = {}; | 3 | var good = {}; |
| 4 | _ = if(true) {} else if(true) {} | 4 | _ = if(true) {} else if(true) {} |
| 5 | var bad = {}; | 5 | var bad = {}; |
| 6 | _ = good; | ||
| 7 | _ = bad; | ||
| 6 | } | 8 | } |
| 7 | 9 | ||
| 8 | // error | 10 | // error |
test/cases/compile_errors/implicit_semicolon-if-else-if_statement.zig+2| ... | @@ -3,6 +3,8 @@ export fn entry() void { | ... | @@ -3,6 +3,8 @@ export fn entry() void { |
| 3 | var good = {}; | 3 | var good = {}; |
| 4 | if(true) ({}) else if(true) ({}) | 4 | if(true) ({}) else if(true) ({}) |
| 5 | var bad = {}; | 5 | var bad = {}; |
| 6 | _ = good; | ||
| 7 | _ = bad; | ||
| 6 | } | 8 | } |
| 7 | 9 | ||
| 8 | // error | 10 | // error |
test/cases/compile_errors/implicit_semicolon-if-else_expression.zig+2| ... | @@ -3,6 +3,8 @@ export fn entry() void { | ... | @@ -3,6 +3,8 @@ export fn entry() void { |
| 3 | var good = {}; | 3 | var good = {}; |
| 4 | _ = if(true) {} else {} | 4 | _ = if(true) {} else {} |
| 5 | var bad = {}; | 5 | var bad = {}; |
| 6 | _ = good; | ||
| 7 | _ = bad; | ||
| 6 | } | 8 | } |
| 7 | 9 | ||
| 8 | // error | 10 | // error |
test/cases/compile_errors/implicit_semicolon-if-else_statement.zig+2| ... | @@ -3,6 +3,8 @@ export fn entry() void { | ... | @@ -3,6 +3,8 @@ export fn entry() void { |
| 3 | var good = {}; | 3 | var good = {}; |
| 4 | if(true) ({}) else ({}) | 4 | if(true) ({}) else ({}) |
| 5 | var bad = {}; | 5 | var bad = {}; |
| 6 | _ = good; | ||
| 7 | _ = bad; | ||
| 6 | } | 8 | } |
| 7 | 9 | ||
| 8 | // error | 10 | // error |
test/cases/compile_errors/implicit_semicolon-if_expression.zig+2| ... | @@ -3,6 +3,8 @@ export fn entry() void { | ... | @@ -3,6 +3,8 @@ export fn entry() void { |
| 3 | var good = {}; | 3 | var good = {}; |
| 4 | _ = if(true) {} | 4 | _ = if(true) {} |
| 5 | var bad = {}; | 5 | var bad = {}; |
| 6 | _ = good; | ||
| 7 | _ = bad; | ||
| 6 | } | 8 | } |
| 7 | 9 | ||
| 8 | // error | 10 | // error |
test/cases/compile_errors/implicit_semicolon-if_statement.zig+2| ... | @@ -3,6 +3,8 @@ export fn entry() void { | ... | @@ -3,6 +3,8 @@ export fn entry() void { |
| 3 | var good = {}; | 3 | var good = {}; |
| 4 | if(true) ({}) | 4 | if(true) ({}) |
| 5 | var bad = {}; | 5 | var bad = {}; |
| 6 | _ = good; | ||
| 7 | _ = bad; | ||
| 6 | } | 8 | } |
| 7 | 9 | ||
| 8 | // error | 10 | // error |
test/cases/compile_errors/implicit_semicolon-test_expression.zig+3| ... | @@ -3,7 +3,10 @@ export fn entry() void { | ... | @@ -3,7 +3,10 @@ export fn entry() void { |
| 3 | var good = {}; | 3 | var good = {}; |
| 4 | _ = if (foo()) |_| {} | 4 | _ = if (foo()) |_| {} |
| 5 | var bad = {}; | 5 | var bad = {}; |
| 6 | _ = good; | ||
| 7 | _ = bad; | ||
| 6 | } | 8 | } |
| 9 | fn foo() void {} | ||
| 7 | 10 | ||
| 8 | // error | 11 | // error |
| 9 | // backend=stage2 | 12 | // backend=stage2 |
test/cases/compile_errors/implicit_semicolon-test_statement.zig+3| ... | @@ -3,7 +3,10 @@ export fn entry() void { | ... | @@ -3,7 +3,10 @@ export fn entry() void { |
| 3 | var good = {}; | 3 | var good = {}; |
| 4 | if (foo()) |_| ({}) | 4 | if (foo()) |_| ({}) |
| 5 | var bad = {}; | 5 | var bad = {}; |
| 6 | _ = good; | ||
| 7 | _ = bad; | ||
| 6 | } | 8 | } |
| 9 | fn foo() void {} | ||
| 7 | 10 | ||
| 8 | // error | 11 | // error |
| 9 | // backend=stage2 | 12 | // backend=stage2 |
test/cases/compile_errors/implicit_semicolon-while-continue_expression.zig+2| ... | @@ -3,6 +3,8 @@ export fn entry() void { | ... | @@ -3,6 +3,8 @@ export fn entry() void { |
| 3 | var good = {}; | 3 | var good = {}; |
| 4 | _ = while(true):({}) {} | 4 | _ = while(true):({}) {} |
| 5 | var bad = {}; | 5 | var bad = {}; |
| 6 | _ = good; | ||
| 7 | _ = bad; | ||
| 6 | } | 8 | } |
| 7 | 9 | ||
| 8 | // error | 10 | // error |
test/cases/compile_errors/implicit_semicolon-while-continue_statement.zig+2| ... | @@ -3,6 +3,8 @@ export fn entry() void { | ... | @@ -3,6 +3,8 @@ export fn entry() void { |
| 3 | var good = {}; | 3 | var good = {}; |
| 4 | while(true):({}) ({}) | 4 | while(true):({}) ({}) |
| 5 | var bad = {}; | 5 | var bad = {}; |
| 6 | _ = good; | ||
| 7 | _ = bad; | ||
| 6 | } | 8 | } |
| 7 | 9 | ||
| 8 | // error | 10 | // error |
test/cases/compile_errors/implicit_semicolon-while_expression.zig+2| ... | @@ -3,6 +3,8 @@ export fn entry() void { | ... | @@ -3,6 +3,8 @@ export fn entry() void { |
| 3 | var good = {}; | 3 | var good = {}; |
| 4 | _ = while(true) {} | 4 | _ = while(true) {} |
| 5 | var bad = {}; | 5 | var bad = {}; |
| 6 | _ = good; | ||
| 7 | _ = bad; | ||
| 6 | } | 8 | } |
| 7 | 9 | ||
| 8 | // error | 10 | // error |
test/cases/compile_errors/implicit_semicolon-while_statement.zig+2| ... | @@ -3,6 +3,8 @@ export fn entry() void { | ... | @@ -3,6 +3,8 @@ export fn entry() void { |
| 3 | var good = {}; | 3 | var good = {}; |
| 4 | while(true) 1 | 4 | while(true) 1 |
| 5 | var bad = {}; | 5 | var bad = {}; |
| 6 | _ = good; | ||
| 7 | _ = bad; | ||
| 6 | } | 8 | } |
| 7 | 9 | ||
| 8 | // error | 10 | // error |
test/cases/compile_errors/invalid_member_of_builtin_enum.zig+1-1| ... | @@ -9,4 +9,4 @@ export fn entry() void { | ... | @@ -9,4 +9,4 @@ export fn entry() void { |
| 9 | // target=native | 9 | // target=native |
| 10 | // | 10 | // |
| 11 | // :3:38: error: enum 'builtin.OptimizeMode' has no member named 'x86' | 11 | // :3:38: error: enum 'builtin.OptimizeMode' has no member named 'x86' |
| 12 | // :?:18: note: enum declared here | 12 | // : note: enum declared here |
test/cases/compile_errors/invalid_store_to_comptime_field.zig+1-1| ... | @@ -73,11 +73,11 @@ pub export fn entry8() void { | ... | @@ -73,11 +73,11 @@ pub export fn entry8() void { |
| 73 | // | 73 | // |
| 74 | // :6:19: error: value stored in comptime field does not match the default value of the field | 74 | // :6:19: error: value stored in comptime field does not match the default value of the field |
| 75 | // :14:19: error: value stored in comptime field does not match the default value of the field | 75 | // :14:19: error: value stored in comptime field does not match the default value of the field |
| 76 | // :53:16: error: value stored in comptime field does not match the default value of the field | ||
| 77 | // :19:38: error: value stored in comptime field does not match the default value of the field | 76 | // :19:38: error: value stored in comptime field does not match the default value of the field |
| 78 | // :31:19: error: value stored in comptime field does not match the default value of the field | 77 | // :31:19: error: value stored in comptime field does not match the default value of the field |
| 79 | // :25:29: note: default value set here | 78 | // :25:29: note: default value set here |
| 80 | // :41:16: error: value stored in comptime field does not match the default value of the field | 79 | // :41:16: error: value stored in comptime field does not match the default value of the field |
| 81 | // :45:12: error: value stored in comptime field does not match the default value of the field | 80 | // :45:12: error: value stored in comptime field does not match the default value of the field |
| 81 | // :53:16: error: value stored in comptime field does not match the default value of the field | ||
| 82 | // :66:43: error: value stored in comptime field does not match the default value of the field | 82 | // :66:43: error: value stored in comptime field does not match the default value of the field |
| 83 | // :59:35: error: value stored in comptime field does not match the default value of the field | 83 | // :59:35: error: value stored in comptime field does not match the default value of the field |
test/cases/compile_errors/invalid_struct_field.zig+1| ... | @@ -25,5 +25,6 @@ export fn e() void { | ... | @@ -25,5 +25,6 @@ export fn e() void { |
| 25 | // :4:7: error: no field named 'foo' in struct 'tmp.A' | 25 | // :4:7: error: no field named 'foo' in struct 'tmp.A' |
| 26 | // :1:11: note: struct declared here | 26 | // :1:11: note: struct declared here |
| 27 | // :10:17: error: no field named 'bar' in struct 'tmp.A' | 27 | // :10:17: error: no field named 'bar' in struct 'tmp.A' |
| 28 | // :1:11: note: struct declared here | ||
| 28 | // :18:45: error: no field named 'f' in struct 'tmp.e.B' | 29 | // :18:45: error: no field named 'f' in struct 'tmp.e.B' |
| 29 | // :14:15: note: struct declared here | 30 | // :14:15: note: struct declared here |
test/cases/compile_errors/missing_main_fn_in_executable.zig+4-2| ... | @@ -5,5 +5,7 @@ | ... | @@ -5,5 +5,7 @@ |
| 5 | // target=x86_64-linux | 5 | // target=x86_64-linux |
| 6 | // output_mode=Exe | 6 | // output_mode=Exe |
| 7 | // | 7 | // |
| 8 | // :?:?: error: root struct of file 'tmp' has no member named 'main' | 8 | // : error: root struct of file 'tmp' has no member named 'main' |
| 9 | // :?:?: note: called from here | 9 | // : note: called from here |
| 10 | // : note: called from here | ||
| 11 | // : note: called from here |
test/cases/compile_errors/private_main_fn.zig+4-2| ... | @@ -5,6 +5,8 @@ fn main() void {} | ... | @@ -5,6 +5,8 @@ fn main() void {} |
| 5 | // target=x86_64-linux | 5 | // target=x86_64-linux |
| 6 | // output_mode=Exe | 6 | // output_mode=Exe |
| 7 | // | 7 | // |
| 8 | // :?:?: error: 'main' is not marked 'pub' | 8 | // : error: 'main' is not marked 'pub' |
| 9 | // :1:1: note: declared here | 9 | // :1:1: note: declared here |
| 10 | // :?:?: note: called from here | 10 | // : note: called from here |
| 11 | // : note: called from here | ||
| 12 | // : note: called from here |
test/cases/compile_errors/runtime_index_into_comptime_type_slice.zig+3-2| ... | @@ -15,5 +15,6 @@ export fn entry() void { | ... | @@ -15,5 +15,6 @@ export fn entry() void { |
| 15 | // target=native | 15 | // target=native |
| 16 | // | 16 | // |
| 17 | // :9:51: error: values of type '[]const builtin.Type.StructField' must be comptime-known, but index value is runtime-known | 17 | // :9:51: error: values of type '[]const builtin.Type.StructField' must be comptime-known, but index value is runtime-known |
| 18 | // :?:21: note: struct requires comptime because of this field | 18 | // : note: struct requires comptime because of this field |
| 19 | // :?:21: note: types are not available at runtime | 19 | // : note: types are not available at runtime |
| 20 | // : struct requires comptime because of this field |
test/cases/compile_errors/struct_type_mismatch_in_arg.zig+1-1| ... | @@ -13,6 +13,6 @@ comptime { | ... | @@ -13,6 +13,6 @@ comptime { |
| 13 | // target=native | 13 | // target=native |
| 14 | // | 14 | // |
| 15 | // :7:16: error: expected type 'tmp.Foo', found 'tmp.Bar' | 15 | // :7:16: error: expected type 'tmp.Foo', found 'tmp.Bar' |
| 16 | // :1:13: note: struct declared here | ||
| 17 | // :2:13: note: struct declared here | 16 | // :2:13: note: struct declared here |
| 17 | // :1:13: note: struct declared here | ||
| 18 | // :4:18: note: parameter type declared here | 18 | // :4:18: note: parameter type declared here |
test/cases/compile_errors/undefined_as_field_type_is_rejected.zig+8-4| ... | @@ -1,9 +1,13 @@ | ... | @@ -1,9 +1,13 @@ |
| 1 | export fn a() void { | 1 | const Foo = struct { |
| 2 | b(); | 2 | a: undefined, |
| 3 | }; | ||
| 4 | export fn entry1() void { | ||
| 5 | const foo: Foo = undefined; | ||
| 6 | _ = foo; | ||
| 3 | } | 7 | } |
| 4 | 8 | ||
| 5 | // error | 9 | // error |
| 6 | // backend=stage2 | 10 | // backend=stage1 |
| 7 | // target=native | 11 | // target=native |
| 8 | // | 12 | // |
| 9 | // :2:5: error: use of undeclared identifier 'b' | 13 | // tmp.zig:2:8: error: use of undefined value here causes undefined behavior |
test/cases/compile_errors/union_init_with_none_or_multiple_fields.zig+2-1| ... | @@ -28,10 +28,11 @@ export fn u2m() void { | ... | @@ -28,10 +28,11 @@ export fn u2m() void { |
| 28 | // target=native | 28 | // target=native |
| 29 | // | 29 | // |
| 30 | // :9:1: error: union initializer must initialize one field | 30 | // :9:1: error: union initializer must initialize one field |
| 31 | // :1:12: note: union declared here | ||
| 31 | // :14:20: error: cannot initialize multiple union fields at once, unions can only have one active field | 32 | // :14:20: error: cannot initialize multiple union fields at once, unions can only have one active field |
| 32 | // :14:31: note: additional initializer here | 33 | // :14:31: note: additional initializer here |
| 34 | // :1:12: note: union declared here | ||
| 33 | // :18:21: error: union initializer must initialize one field | 35 | // :18:21: error: union initializer must initialize one field |
| 34 | // :22:20: error: cannot initialize multiple union fields at once, unions can only have one active field | 36 | // :22:20: error: cannot initialize multiple union fields at once, unions can only have one active field |
| 35 | // :22:31: note: additional initializer here | 37 | // :22:31: note: additional initializer here |
| 36 | // :1:12: note: union declared here | ||
| 37 | // :5:12: note: union declared here | 38 | // :5:12: note: union declared here |
test/cases/compile_log.0.zig+5| ... | @@ -15,3 +15,8 @@ fn x() void {} | ... | @@ -15,3 +15,8 @@ fn x() void {} |
| 15 | // error | 15 | // error |
| 16 | // | 16 | // |
| 17 | // :6:23: error: expected type 'usize', found 'bool' | 17 | // :6:23: error: expected type 'usize', found 'bool' |
| 18 | // | ||
| 19 | // Compile Log Output: | ||
| 20 | // @as(bool, true), @as(comptime_int, 20), @as(u32, [runtime value]), @as(fn() void, (function 'x')) | ||
| 21 | // @as(comptime_int, 1000) | ||
| 22 | // @as(comptime_int, 1234) |
test/cases/compile_log.1.zig+4| ... | @@ -14,3 +14,7 @@ fn x() void {} | ... | @@ -14,3 +14,7 @@ fn x() void {} |
| 14 | // | 14 | // |
| 15 | // :9:5: error: found compile log statement | 15 | // :9:5: error: found compile log statement |
| 16 | // :4:5: note: also here | 16 | // :4:5: note: also here |
| 17 | // | ||
| 18 | // Compile Log Output: | ||
| 19 | // @as(bool, true), @as(comptime_int, 20), @as(u32, [runtime value]), @as(fn() void, (function 'x')) | ||
| 20 | // @as(comptime_int, 1000) |
test/cases/f32_passed_to_variadic_fn.zig+2-1| ... | @@ -9,7 +9,8 @@ pub fn main() void { | ... | @@ -9,7 +9,8 @@ pub fn main() void { |
| 9 | // run | 9 | // run |
| 10 | // backend=llvm | 10 | // backend=llvm |
| 11 | // target=x86_64-linux-gnu | 11 | // target=x86_64-linux-gnu |
| 12 | // link_libc=1 | ||
| 12 | // | 13 | // |
| 13 | // f64: 2.000000 | 14 | // f64: 2.000000 |
| 14 | // f32: 10.000000 | 15 | // f32: 10.000000 |
| 15 | // | ||
| \ No newline at end of file | |||
| 16 | // | ||
test/cases/fn_typeinfo_passed_to_comptime_fn.zig+1| ... | @@ -14,4 +14,5 @@ fn foo(comptime info: std.builtin.Type) !void { | ... | @@ -14,4 +14,5 @@ fn foo(comptime info: std.builtin.Type) !void { |
| 14 | 14 | ||
| 15 | // run | 15 | // run |
| 16 | // is_test=1 | 16 | // is_test=1 |
| 17 | // backend=llvm | ||
| 17 | // | 18 | // |
test/cases/llvm/address_space_pointer_access_chaining_pointer_to_optional_array.zig+1-1| ... | @@ -5,7 +5,7 @@ pub fn main() void { | ... | @@ -5,7 +5,7 @@ pub fn main() void { |
| 5 | _ = entry; | 5 | _ = entry; |
| 6 | } | 6 | } |
| 7 | 7 | ||
| 8 | // error | 8 | // compile |
| 9 | // output_mode=Exe | 9 | // output_mode=Exe |
| 10 | // backend=llvm | 10 | // backend=llvm |
| 11 | // target=x86_64-linux,x86_64-macos | 11 | // target=x86_64-linux,x86_64-macos |
test/cases/llvm/address_spaces_pointer_access_chaining_array_pointer.zig+1-1| ... | @@ -5,7 +5,7 @@ pub fn main() void { | ... | @@ -5,7 +5,7 @@ pub fn main() void { |
| 5 | _ = entry; | 5 | _ = entry; |
| 6 | } | 6 | } |
| 7 | 7 | ||
| 8 | // error | 8 | // compile |
| 9 | // output_mode=Exe | 9 | // output_mode=Exe |
| 10 | // backend=stage2,llvm | 10 | // backend=stage2,llvm |
| 11 | // target=x86_64-linux,x86_64-macos | 11 | // target=x86_64-linux,x86_64-macos |
test/cases/llvm/address_spaces_pointer_access_chaining_complex.zig+1-1| ... | @@ -6,7 +6,7 @@ pub fn main() void { | ... | @@ -6,7 +6,7 @@ pub fn main() void { |
| 6 | _ = entry; | 6 | _ = entry; |
| 7 | } | 7 | } |
| 8 | 8 | ||
| 9 | // error | 9 | // compile |
| 10 | // output_mode=Exe | 10 | // output_mode=Exe |
| 11 | // backend=llvm | 11 | // backend=llvm |
| 12 | // target=x86_64-linux,x86_64-macos | 12 | // target=x86_64-linux,x86_64-macos |
test/cases/llvm/address_spaces_pointer_access_chaining_struct_pointer.zig+1-1| ... | @@ -6,7 +6,7 @@ pub fn main() void { | ... | @@ -6,7 +6,7 @@ pub fn main() void { |
| 6 | _ = entry; | 6 | _ = entry; |
| 7 | } | 7 | } |
| 8 | 8 | ||
| 9 | // error | 9 | // compile |
| 10 | // output_mode=Exe | 10 | // output_mode=Exe |
| 11 | // backend=stage2,llvm | 11 | // backend=stage2,llvm |
| 12 | // target=x86_64-linux,x86_64-macos | 12 | // target=x86_64-linux,x86_64-macos |
test/cases/llvm/dereferencing_though_multiple_pointers_with_address_spaces.zig+1-1| ... | @@ -5,7 +5,7 @@ pub fn main() void { | ... | @@ -5,7 +5,7 @@ pub fn main() void { |
| 5 | _ = entry; | 5 | _ = entry; |
| 6 | } | 6 | } |
| 7 | 7 | ||
| 8 | // error | 8 | // compile |
| 9 | // output_mode=Exe | 9 | // output_mode=Exe |
| 10 | // backend=stage2,llvm | 10 | // backend=stage2,llvm |
| 11 | // target=x86_64-linux,x86_64-macos | 11 | // target=x86_64-linux,x86_64-macos |
test/cases/llvm/hello_world.zig+1| ... | @@ -7,6 +7,7 @@ pub fn main() void { | ... | @@ -7,6 +7,7 @@ pub fn main() void { |
| 7 | // run | 7 | // run |
| 8 | // backend=llvm | 8 | // backend=llvm |
| 9 | // target=x86_64-linux,x86_64-macos | 9 | // target=x86_64-linux,x86_64-macos |
| 10 | // link_libc=1 | ||
| 10 | // | 11 | // |
| 11 | // hello world! | 12 | // hello world! |
| 12 | // | 13 | // |
test/cases/llvm/pointer_keeps_address_space.zig+1-1| ... | @@ -5,7 +5,7 @@ pub fn main() void { | ... | @@ -5,7 +5,7 @@ pub fn main() void { |
| 5 | _ = entry; | 5 | _ = entry; |
| 6 | } | 6 | } |
| 7 | 7 | ||
| 8 | // error | 8 | // compile |
| 9 | // output_mode=Exe | 9 | // output_mode=Exe |
| 10 | // backend=stage2,llvm | 10 | // backend=stage2,llvm |
| 11 | // target=x86_64-linux,x86_64-macos | 11 | // target=x86_64-linux,x86_64-macos |
test/cases/llvm/pointer_keeps_address_space_when_taking_address_of_dereference.zig+1-1| ... | @@ -5,7 +5,7 @@ pub fn main() void { | ... | @@ -5,7 +5,7 @@ pub fn main() void { |
| 5 | _ = entry; | 5 | _ = entry; |
| 6 | } | 6 | } |
| 7 | 7 | ||
| 8 | // error | 8 | // compile |
| 9 | // output_mode=Exe | 9 | // output_mode=Exe |
| 10 | // backend=stage2,llvm | 10 | // backend=stage2,llvm |
| 11 | // target=x86_64-linux,x86_64-macos | 11 | // target=x86_64-linux,x86_64-macos |
test/cases/llvm/pointer_to_explicit_generic_address_space_coerces_to_implicit_pointer.zig+1-1| ... | @@ -5,7 +5,7 @@ pub fn main() void { | ... | @@ -5,7 +5,7 @@ pub fn main() void { |
| 5 | _ = entry; | 5 | _ = entry; |
| 6 | } | 6 | } |
| 7 | 7 | ||
| 8 | // error | 8 | // compile |
| 9 | // output_mode=Exe | 9 | // output_mode=Exe |
| 10 | // backend=stage2,llvm | 10 | // backend=stage2,llvm |
| 11 | // target=x86_64-linux,x86_64-macos | 11 | // target=x86_64-linux,x86_64-macos |
test/cbe.zig created+950| ... | @@ -0,0 +1,950 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const Cases = @import("src/Cases.zig"); | ||
| 3 | |||
| 4 | // These tests should work with all platforms, but we're using linux_x64 for | ||
| 5 | // now for consistency. Will be expanded eventually. | ||
| 6 | const linux_x64 = std.zig.CrossTarget{ | ||
| 7 | .cpu_arch = .x86_64, | ||
| 8 | .os_tag = .linux, | ||
| 9 | }; | ||
| 10 | |||
| 11 | pub fn addCases(ctx: *Cases) !void { | ||
| 12 | { | ||
| 13 | var case = ctx.exeFromCompiledC("hello world with updates", .{}); | ||
| 14 | |||
| 15 | // Regular old hello world | ||
| 16 | case.addCompareOutput( | ||
| 17 | \\extern fn puts(s: [*:0]const u8) c_int; | ||
| 18 | \\pub export fn main() c_int { | ||
| 19 | \\ _ = puts("hello world!"); | ||
| 20 | \\ return 0; | ||
| 21 | \\} | ||
| 22 | , "hello world!" ++ std.cstr.line_sep); | ||
| 23 | |||
| 24 | // Now change the message only | ||
| 25 | case.addCompareOutput( | ||
| 26 | \\extern fn puts(s: [*:0]const u8) c_int; | ||
| 27 | \\pub export fn main() c_int { | ||
| 28 | \\ _ = puts("yo"); | ||
| 29 | \\ return 0; | ||
| 30 | \\} | ||
| 31 | , "yo" ++ std.cstr.line_sep); | ||
| 32 | |||
| 33 | // Add an unused Decl | ||
| 34 | case.addCompareOutput( | ||
| 35 | \\extern fn puts(s: [*:0]const u8) c_int; | ||
| 36 | \\pub export fn main() c_int { | ||
| 37 | \\ _ = puts("yo!"); | ||
| 38 | \\ return 0; | ||
| 39 | \\} | ||
| 40 | \\fn unused() void {} | ||
| 41 | , "yo!" ++ std.cstr.line_sep); | ||
| 42 | |||
| 43 | // Comptime return type and calling convention expected. | ||
| 44 | case.addError( | ||
| 45 | \\var x: i32 = 1234; | ||
| 46 | \\pub export fn main() x { | ||
| 47 | \\ return 0; | ||
| 48 | \\} | ||
| 49 | \\export fn foo() callconv(y) c_int { | ||
| 50 | \\ return 0; | ||
| 51 | \\} | ||
| 52 | \\var y: @import("std").builtin.CallingConvention = .C; | ||
| 53 | , &.{ | ||
| 54 | ":2:22: error: expected type 'type', found 'i32'", | ||
| 55 | ":5:26: error: unable to resolve comptime value", | ||
| 56 | ":5:26: note: calling convention must be comptime-known", | ||
| 57 | }); | ||
| 58 | } | ||
| 59 | |||
| 60 | { | ||
| 61 | var case = ctx.exeFromCompiledC("var args", .{}); | ||
| 62 | |||
| 63 | case.addCompareOutput( | ||
| 64 | \\extern fn printf(format: [*:0]const u8, ...) c_int; | ||
| 65 | \\ | ||
| 66 | \\pub export fn main() c_int { | ||
| 67 | \\ _ = printf("Hello, %s!\n", "world"); | ||
| 68 | \\ return 0; | ||
| 69 | \\} | ||
| 70 | , "Hello, world!" ++ std.cstr.line_sep); | ||
| 71 | } | ||
| 72 | |||
| 73 | { | ||
| 74 | var case = ctx.exeFromCompiledC("intToError", .{}); | ||
| 75 | |||
| 76 | case.addCompareOutput( | ||
| 77 | \\pub export fn main() c_int { | ||
| 78 | \\ // comptime checks | ||
| 79 | \\ const a = error.A; | ||
| 80 | \\ const b = error.B; | ||
| 81 | \\ const c = @intToError(2); | ||
| 82 | \\ const d = @intToError(1); | ||
| 83 | \\ if (!(c == b)) unreachable; | ||
| 84 | \\ if (!(a == d)) unreachable; | ||
| 85 | \\ // runtime checks | ||
| 86 | \\ var x = error.A; | ||
| 87 | \\ var y = error.B; | ||
| 88 | \\ var z = @intToError(2); | ||
| 89 | \\ var f = @intToError(1); | ||
| 90 | \\ if (!(y == z)) unreachable; | ||
| 91 | \\ if (!(x == f)) unreachable; | ||
| 92 | \\ return 0; | ||
| 93 | \\} | ||
| 94 | , ""); | ||
| 95 | case.addError( | ||
| 96 | \\pub export fn main() c_int { | ||
| 97 | \\ _ = @intToError(0); | ||
| 98 | \\ return 0; | ||
| 99 | \\} | ||
| 100 | , &.{":2:21: error: integer value '0' represents no error"}); | ||
| 101 | case.addError( | ||
| 102 | \\pub export fn main() c_int { | ||
| 103 | \\ _ = @intToError(3); | ||
| 104 | \\ return 0; | ||
| 105 | \\} | ||
| 106 | , &.{":2:21: error: integer value '3' represents no error"}); | ||
| 107 | } | ||
| 108 | |||
| 109 | { | ||
| 110 | var case = ctx.exeFromCompiledC("x86_64-linux inline assembly", linux_x64); | ||
| 111 | |||
| 112 | // Exit with 0 | ||
| 113 | case.addCompareOutput( | ||
| 114 | \\fn exitGood() noreturn { | ||
| 115 | \\ asm volatile ("syscall" | ||
| 116 | \\ : | ||
| 117 | \\ : [number] "{rax}" (231), | ||
| 118 | \\ [arg1] "{rdi}" (0) | ||
| 119 | \\ ); | ||
| 120 | \\ unreachable; | ||
| 121 | \\} | ||
| 122 | \\ | ||
| 123 | \\pub export fn main() c_int { | ||
| 124 | \\ exitGood(); | ||
| 125 | \\} | ||
| 126 | , ""); | ||
| 127 | |||
| 128 | // Pass a usize parameter to exit | ||
| 129 | case.addCompareOutput( | ||
| 130 | \\pub export fn main() c_int { | ||
| 131 | \\ exit(0); | ||
| 132 | \\} | ||
| 133 | \\ | ||
| 134 | \\fn exit(code: usize) noreturn { | ||
| 135 | \\ asm volatile ("syscall" | ||
| 136 | \\ : | ||
| 137 | \\ : [number] "{rax}" (231), | ||
| 138 | \\ [arg1] "{rdi}" (code) | ||
| 139 | \\ ); | ||
| 140 | \\ unreachable; | ||
| 141 | \\} | ||
| 142 | , ""); | ||
| 143 | |||
| 144 | // Change the parameter to u8 | ||
| 145 | case.addCompareOutput( | ||
| 146 | \\pub export fn main() c_int { | ||
| 147 | \\ exit(0); | ||
| 148 | \\} | ||
| 149 | \\ | ||
| 150 | \\fn exit(code: u8) noreturn { | ||
| 151 | \\ asm volatile ("syscall" | ||
| 152 | \\ : | ||
| 153 | \\ : [number] "{rax}" (231), | ||
| 154 | \\ [arg1] "{rdi}" (code) | ||
| 155 | \\ ); | ||
| 156 | \\ unreachable; | ||
| 157 | \\} | ||
| 158 | , ""); | ||
| 159 | |||
| 160 | // Do some arithmetic at the exit callsite | ||
| 161 | case.addCompareOutput( | ||
| 162 | \\pub export fn main() c_int { | ||
| 163 | \\ exitMath(1); | ||
| 164 | \\} | ||
| 165 | \\ | ||
| 166 | \\fn exitMath(a: u8) noreturn { | ||
| 167 | \\ exit(0 + a - a); | ||
| 168 | \\} | ||
| 169 | \\ | ||
| 170 | \\fn exit(code: u8) noreturn { | ||
| 171 | \\ asm volatile ("syscall" | ||
| 172 | \\ : | ||
| 173 | \\ : [number] "{rax}" (231), | ||
| 174 | \\ [arg1] "{rdi}" (code) | ||
| 175 | \\ ); | ||
| 176 | \\ unreachable; | ||
| 177 | \\} | ||
| 178 | \\ | ||
| 179 | , ""); | ||
| 180 | |||
| 181 | // Invert the arithmetic | ||
| 182 | case.addCompareOutput( | ||
| 183 | \\pub export fn main() c_int { | ||
| 184 | \\ exitMath(1); | ||
| 185 | \\} | ||
| 186 | \\ | ||
| 187 | \\fn exitMath(a: u8) noreturn { | ||
| 188 | \\ exit(a + 0 - a); | ||
| 189 | \\} | ||
| 190 | \\ | ||
| 191 | \\fn exit(code: u8) noreturn { | ||
| 192 | \\ asm volatile ("syscall" | ||
| 193 | \\ : | ||
| 194 | \\ : [number] "{rax}" (231), | ||
| 195 | \\ [arg1] "{rdi}" (code) | ||
| 196 | \\ ); | ||
| 197 | \\ unreachable; | ||
| 198 | \\} | ||
| 199 | \\ | ||
| 200 | , ""); | ||
| 201 | } | ||
| 202 | |||
| 203 | { | ||
| 204 | var case = ctx.exeFromCompiledC("alloc and retptr", .{}); | ||
| 205 | |||
| 206 | case.addCompareOutput( | ||
| 207 | \\fn add(a: i32, b: i32) i32 { | ||
| 208 | \\ return a + b; | ||
| 209 | \\} | ||
| 210 | \\ | ||
| 211 | \\fn addIndirect(a: i32, b: i32) i32 { | ||
| 212 | \\ return add(a, b); | ||
| 213 | \\} | ||
| 214 | \\ | ||
| 215 | \\pub export fn main() c_int { | ||
| 216 | \\ return addIndirect(1, 2) - 3; | ||
| 217 | \\} | ||
| 218 | , ""); | ||
| 219 | } | ||
| 220 | |||
| 221 | { | ||
| 222 | var case = ctx.exeFromCompiledC("inferred local const and var", .{}); | ||
| 223 | |||
| 224 | case.addCompareOutput( | ||
| 225 | \\fn add(a: i32, b: i32) i32 { | ||
| 226 | \\ return a + b; | ||
| 227 | \\} | ||
| 228 | \\ | ||
| 229 | \\pub export fn main() c_int { | ||
| 230 | \\ const x = add(1, 2); | ||
| 231 | \\ var y = add(3, 0); | ||
| 232 | \\ y -= x; | ||
| 233 | \\ return y; | ||
| 234 | \\} | ||
| 235 | , ""); | ||
| 236 | } | ||
| 237 | { | ||
| 238 | var case = ctx.exeFromCompiledC("control flow", .{}); | ||
| 239 | |||
| 240 | // Simple while loop | ||
| 241 | case.addCompareOutput( | ||
| 242 | \\pub export fn main() c_int { | ||
| 243 | \\ var a: c_int = 0; | ||
| 244 | \\ while (a < 5) : (a+=1) {} | ||
| 245 | \\ return a - 5; | ||
| 246 | \\} | ||
| 247 | , ""); | ||
| 248 | case.addCompareOutput( | ||
| 249 | \\pub export fn main() c_int { | ||
| 250 | \\ var a = true; | ||
| 251 | \\ while (!a) {} | ||
| 252 | \\ return 0; | ||
| 253 | \\} | ||
| 254 | , ""); | ||
| 255 | |||
| 256 | // If expression | ||
| 257 | case.addCompareOutput( | ||
| 258 | \\pub export fn main() c_int { | ||
| 259 | \\ var cond: c_int = 0; | ||
| 260 | \\ var a: c_int = @as(c_int, if (cond == 0) | ||
| 261 | \\ 2 | ||
| 262 | \\ else | ||
| 263 | \\ 3) + 9; | ||
| 264 | \\ return a - 11; | ||
| 265 | \\} | ||
| 266 | , ""); | ||
| 267 | |||
| 268 | // If expression with breakpoint that does not get hit | ||
| 269 | case.addCompareOutput( | ||
| 270 | \\pub export fn main() c_int { | ||
| 271 | \\ var x: i32 = 1; | ||
| 272 | \\ if (x != 1) @breakpoint(); | ||
| 273 | \\ return 0; | ||
| 274 | \\} | ||
| 275 | , ""); | ||
| 276 | |||
| 277 | // Switch expression | ||
| 278 | case.addCompareOutput( | ||
| 279 | \\pub export fn main() c_int { | ||
| 280 | \\ var cond: c_int = 0; | ||
| 281 | \\ var a: c_int = switch (cond) { | ||
| 282 | \\ 1 => 1, | ||
| 283 | \\ 2 => 2, | ||
| 284 | \\ 99...300, 12 => 3, | ||
| 285 | \\ 0 => 4, | ||
| 286 | \\ else => 5, | ||
| 287 | \\ }; | ||
| 288 | \\ return a - 4; | ||
| 289 | \\} | ||
| 290 | , ""); | ||
| 291 | |||
| 292 | // Switch expression missing else case. | ||
| 293 | case.addError( | ||
| 294 | \\pub export fn main() c_int { | ||
| 295 | \\ var cond: c_int = 0; | ||
| 296 | \\ const a: c_int = switch (cond) { | ||
| 297 | \\ 1 => 1, | ||
| 298 | \\ 2 => 2, | ||
| 299 | \\ 3 => 3, | ||
| 300 | \\ 4 => 4, | ||
| 301 | \\ }; | ||
| 302 | \\ return a - 4; | ||
| 303 | \\} | ||
| 304 | , &.{":3:22: error: switch must handle all possibilities"}); | ||
| 305 | |||
| 306 | // Switch expression, has an unreachable prong. | ||
| 307 | case.addCompareOutput( | ||
| 308 | \\pub export fn main() c_int { | ||
| 309 | \\ var cond: c_int = 0; | ||
| 310 | \\ const a: c_int = switch (cond) { | ||
| 311 | \\ 1 => 1, | ||
| 312 | \\ 2 => 2, | ||
| 313 | \\ 99...300, 12 => 3, | ||
| 314 | \\ 0 => 4, | ||
| 315 | \\ 13 => unreachable, | ||
| 316 | \\ else => 5, | ||
| 317 | \\ }; | ||
| 318 | \\ return a - 4; | ||
| 319 | \\} | ||
| 320 | , ""); | ||
| 321 | |||
| 322 | // Switch expression, has an unreachable prong and prongs write | ||
| 323 | // to result locations. | ||
| 324 | case.addCompareOutput( | ||
| 325 | \\pub export fn main() c_int { | ||
| 326 | \\ var cond: c_int = 0; | ||
| 327 | \\ var a: c_int = switch (cond) { | ||
| 328 | \\ 1 => 1, | ||
| 329 | \\ 2 => 2, | ||
| 330 | \\ 99...300, 12 => 3, | ||
| 331 | \\ 0 => 4, | ||
| 332 | \\ 13 => unreachable, | ||
| 333 | \\ else => 5, | ||
| 334 | \\ }; | ||
| 335 | \\ return a - 4; | ||
| 336 | \\} | ||
| 337 | , ""); | ||
| 338 | |||
| 339 | // Integer switch expression has duplicate case value. | ||
| 340 | case.addError( | ||
| 341 | \\pub export fn main() c_int { | ||
| 342 | \\ var cond: c_int = 0; | ||
| 343 | \\ const a: c_int = switch (cond) { | ||
| 344 | \\ 1 => 1, | ||
| 345 | \\ 2 => 2, | ||
| 346 | \\ 96, 11...13, 97 => 3, | ||
| 347 | \\ 0 => 4, | ||
| 348 | \\ 90, 12 => 100, | ||
| 349 | \\ else => 5, | ||
| 350 | \\ }; | ||
| 351 | \\ return a - 4; | ||
| 352 | \\} | ||
| 353 | , &.{ | ||
| 354 | ":8:13: error: duplicate switch value", | ||
| 355 | ":6:15: note: previous value here", | ||
| 356 | }); | ||
| 357 | |||
| 358 | // Boolean switch expression has duplicate case value. | ||
| 359 | case.addError( | ||
| 360 | \\pub export fn main() c_int { | ||
| 361 | \\ var a: bool = false; | ||
| 362 | \\ const b: c_int = switch (a) { | ||
| 363 | \\ false => 1, | ||
| 364 | \\ true => 2, | ||
| 365 | \\ false => 3, | ||
| 366 | \\ }; | ||
| 367 | \\ _ = b; | ||
| 368 | \\} | ||
| 369 | , &.{ | ||
| 370 | ":6:9: error: duplicate switch value", | ||
| 371 | }); | ||
| 372 | |||
| 373 | // Sparse (no range capable) switch expression has duplicate case value. | ||
| 374 | case.addError( | ||
| 375 | \\pub export fn main() c_int { | ||
| 376 | \\ const A: type = i32; | ||
| 377 | \\ const b: c_int = switch (A) { | ||
| 378 | \\ i32 => 1, | ||
| 379 | \\ bool => 2, | ||
| 380 | \\ f64, i32 => 3, | ||
| 381 | \\ else => 4, | ||
| 382 | \\ }; | ||
| 383 | \\ _ = b; | ||
| 384 | \\} | ||
| 385 | , &.{ | ||
| 386 | ":6:14: error: duplicate switch value", | ||
| 387 | ":4:9: note: previous value here", | ||
| 388 | }); | ||
| 389 | |||
| 390 | // Ranges not allowed for some kinds of switches. | ||
| 391 | case.addError( | ||
| 392 | \\pub export fn main() c_int { | ||
| 393 | \\ const A: type = i32; | ||
| 394 | \\ const b: c_int = switch (A) { | ||
| 395 | \\ i32 => 1, | ||
| 396 | \\ bool => 2, | ||
| 397 | \\ f16...f64 => 3, | ||
| 398 | \\ else => 4, | ||
| 399 | \\ }; | ||
| 400 | \\ _ = b; | ||
| 401 | \\} | ||
| 402 | , &.{ | ||
| 403 | ":3:30: error: ranges not allowed when switching on type 'type'", | ||
| 404 | ":6:12: note: range here", | ||
| 405 | }); | ||
| 406 | |||
| 407 | // Switch expression has unreachable else prong. | ||
| 408 | case.addError( | ||
| 409 | \\pub export fn main() c_int { | ||
| 410 | \\ var a: u2 = 0; | ||
| 411 | \\ const b: i32 = switch (a) { | ||
| 412 | \\ 0 => 10, | ||
| 413 | \\ 1 => 20, | ||
| 414 | \\ 2 => 30, | ||
| 415 | \\ 3 => 40, | ||
| 416 | \\ else => 50, | ||
| 417 | \\ }; | ||
| 418 | \\ _ = b; | ||
| 419 | \\} | ||
| 420 | , &.{ | ||
| 421 | ":8:14: error: unreachable else prong; all cases already handled", | ||
| 422 | }); | ||
| 423 | } | ||
| 424 | //{ | ||
| 425 | // var case = ctx.exeFromCompiledC("optionals", .{}); | ||
| 426 | |||
| 427 | // // Simple while loop | ||
| 428 | // case.addCompareOutput( | ||
| 429 | // \\pub export fn main() c_int { | ||
| 430 | // \\ var count: c_int = 0; | ||
| 431 | // \\ var opt_ptr: ?*c_int = &count; | ||
| 432 | // \\ while (opt_ptr) |_| : (count += 1) { | ||
| 433 | // \\ if (count == 4) opt_ptr = null; | ||
| 434 | // \\ } | ||
| 435 | // \\ return count - 5; | ||
| 436 | // \\} | ||
| 437 | // , ""); | ||
| 438 | |||
| 439 | // // Same with non pointer optionals | ||
| 440 | // case.addCompareOutput( | ||
| 441 | // \\pub export fn main() c_int { | ||
| 442 | // \\ var count: c_int = 0; | ||
| 443 | // \\ var opt_ptr: ?c_int = count; | ||
| 444 | // \\ while (opt_ptr) |_| : (count += 1) { | ||
| 445 | // \\ if (count == 4) opt_ptr = null; | ||
| 446 | // \\ } | ||
| 447 | // \\ return count - 5; | ||
| 448 | // \\} | ||
| 449 | // , ""); | ||
| 450 | //} | ||
| 451 | |||
| 452 | { | ||
| 453 | var case = ctx.exeFromCompiledC("errors", .{}); | ||
| 454 | case.addCompareOutput( | ||
| 455 | \\pub export fn main() c_int { | ||
| 456 | \\ var e1 = error.Foo; | ||
| 457 | \\ var e2 = error.Bar; | ||
| 458 | \\ assert(e1 != e2); | ||
| 459 | \\ assert(e1 == error.Foo); | ||
| 460 | \\ assert(e2 == error.Bar); | ||
| 461 | \\ return 0; | ||
| 462 | \\} | ||
| 463 | \\fn assert(b: bool) void { | ||
| 464 | \\ if (!b) unreachable; | ||
| 465 | \\} | ||
| 466 | , ""); | ||
| 467 | case.addCompareOutput( | ||
| 468 | \\pub export fn main() c_int { | ||
| 469 | \\ var e: anyerror!c_int = 0; | ||
| 470 | \\ const i = e catch 69; | ||
| 471 | \\ return i; | ||
| 472 | \\} | ||
| 473 | , ""); | ||
| 474 | case.addCompareOutput( | ||
| 475 | \\pub export fn main() c_int { | ||
| 476 | \\ var e: anyerror!c_int = error.Foo; | ||
| 477 | \\ const i = e catch 69; | ||
| 478 | \\ return 69 - i; | ||
| 479 | \\} | ||
| 480 | , ""); | ||
| 481 | case.addCompareOutput( | ||
| 482 | \\const E = error{e}; | ||
| 483 | \\const S = struct { x: u32 }; | ||
| 484 | \\fn f() E!u32 { | ||
| 485 | \\ const x = (try @as(E!S, S{ .x = 1 })).x; | ||
| 486 | \\ return x; | ||
| 487 | \\} | ||
| 488 | \\pub export fn main() c_int { | ||
| 489 | \\ const x = f() catch @as(u32, 0); | ||
| 490 | \\ if (x != 1) unreachable; | ||
| 491 | \\ return 0; | ||
| 492 | \\} | ||
| 493 | , ""); | ||
| 494 | } | ||
| 495 | |||
| 496 | { | ||
| 497 | var case = ctx.exeFromCompiledC("structs", .{}); | ||
| 498 | case.addError( | ||
| 499 | \\const Point = struct { x: i32, y: i32 }; | ||
| 500 | \\pub export fn main() c_int { | ||
| 501 | \\ var p: Point = .{ | ||
| 502 | \\ .y = 24, | ||
| 503 | \\ .x = 12, | ||
| 504 | \\ .y = 24, | ||
| 505 | \\ }; | ||
| 506 | \\ return p.y - p.x - p.x; | ||
| 507 | \\} | ||
| 508 | , &.{ | ||
| 509 | ":6:10: error: duplicate field", | ||
| 510 | ":4:10: note: other field here", | ||
| 511 | }); | ||
| 512 | case.addError( | ||
| 513 | \\const Point = struct { x: i32, y: i32 }; | ||
| 514 | \\pub export fn main() c_int { | ||
| 515 | \\ var p: Point = .{ | ||
| 516 | \\ .y = 24, | ||
| 517 | \\ }; | ||
| 518 | \\ return p.y - p.x - p.x; | ||
| 519 | \\} | ||
| 520 | , &.{ | ||
| 521 | ":3:21: error: missing struct field: x", | ||
| 522 | ":1:15: note: struct 'tmp.Point' declared here", | ||
| 523 | }); | ||
| 524 | case.addError( | ||
| 525 | \\const Point = struct { x: i32, y: i32 }; | ||
| 526 | \\pub export fn main() c_int { | ||
| 527 | \\ var p: Point = .{ | ||
| 528 | \\ .x = 12, | ||
| 529 | \\ .y = 24, | ||
| 530 | \\ .z = 48, | ||
| 531 | \\ }; | ||
| 532 | \\ return p.y - p.x - p.x; | ||
| 533 | \\} | ||
| 534 | , &.{ | ||
| 535 | ":6:10: error: no field named 'z' in struct 'tmp.Point'", | ||
| 536 | ":1:15: note: struct declared here", | ||
| 537 | }); | ||
| 538 | case.addCompareOutput( | ||
| 539 | \\const Point = struct { x: i32, y: i32 }; | ||
| 540 | \\pub export fn main() c_int { | ||
| 541 | \\ var p: Point = .{ | ||
| 542 | \\ .x = 12, | ||
| 543 | \\ .y = 24, | ||
| 544 | \\ }; | ||
| 545 | \\ return p.y - p.x - p.x; | ||
| 546 | \\} | ||
| 547 | , ""); | ||
| 548 | case.addCompareOutput( | ||
| 549 | \\const Point = struct { x: i32, y: i32, z: i32, a: i32, b: i32 }; | ||
| 550 | \\pub export fn main() c_int { | ||
| 551 | \\ var p: Point = .{ | ||
| 552 | \\ .x = 18, | ||
| 553 | \\ .y = 24, | ||
| 554 | \\ .z = 1, | ||
| 555 | \\ .a = 2, | ||
| 556 | \\ .b = 3, | ||
| 557 | \\ }; | ||
| 558 | \\ return p.y - p.x - p.z - p.a - p.b; | ||
| 559 | \\} | ||
| 560 | , ""); | ||
| 561 | } | ||
| 562 | |||
| 563 | { | ||
| 564 | var case = ctx.exeFromCompiledC("unions", .{}); | ||
| 565 | |||
| 566 | case.addError( | ||
| 567 | \\const U = union { | ||
| 568 | \\ a: u32, | ||
| 569 | \\ b | ||
| 570 | \\}; | ||
| 571 | , &.{ | ||
| 572 | ":3:5: error: union field missing type", | ||
| 573 | }); | ||
| 574 | |||
| 575 | case.addError( | ||
| 576 | \\const E = enum { a, b }; | ||
| 577 | \\const U = union(E) { | ||
| 578 | \\ a: u32 = 1, | ||
| 579 | \\ b: f32 = 2, | ||
| 580 | \\}; | ||
| 581 | , &.{ | ||
| 582 | ":2:11: error: explicitly valued tagged union requires inferred enum tag type", | ||
| 583 | ":3:14: note: tag value specified here", | ||
| 584 | }); | ||
| 585 | |||
| 586 | case.addError( | ||
| 587 | \\const U = union(enum) { | ||
| 588 | \\ a: u32 = 1, | ||
| 589 | \\ b: f32 = 2, | ||
| 590 | \\}; | ||
| 591 | , &.{ | ||
| 592 | ":1:11: error: explicitly valued tagged union missing integer tag type", | ||
| 593 | ":2:14: note: tag value specified here", | ||
| 594 | }); | ||
| 595 | } | ||
| 596 | |||
| 597 | { | ||
| 598 | var case = ctx.exeFromCompiledC("enums", .{}); | ||
| 599 | |||
| 600 | case.addError( | ||
| 601 | \\const E1 = packed enum { a, b, c }; | ||
| 602 | \\const E2 = extern enum { a, b, c }; | ||
| 603 | \\export fn foo() void { | ||
| 604 | \\ _ = E1.a; | ||
| 605 | \\} | ||
| 606 | \\export fn bar() void { | ||
| 607 | \\ _ = E2.a; | ||
| 608 | \\} | ||
| 609 | , &.{ | ||
| 610 | ":1:12: error: enums do not support 'packed' or 'extern'; instead provide an explicit integer tag type", | ||
| 611 | ":2:12: error: enums do not support 'packed' or 'extern'; instead provide an explicit integer tag type", | ||
| 612 | }); | ||
| 613 | |||
| 614 | // comptime and types are caught in AstGen. | ||
| 615 | case.addError( | ||
| 616 | \\const E1 = enum { | ||
| 617 | \\ a, | ||
| 618 | \\ comptime b, | ||
| 619 | \\ c, | ||
| 620 | \\}; | ||
| 621 | \\const E2 = enum { | ||
| 622 | \\ a, | ||
| 623 | \\ b: i32, | ||
| 624 | \\ c, | ||
| 625 | \\}; | ||
| 626 | \\export fn foo() void { | ||
| 627 | \\ _ = E1.a; | ||
| 628 | \\} | ||
| 629 | \\export fn bar() void { | ||
| 630 | \\ _ = E2.a; | ||
| 631 | \\} | ||
| 632 | , &.{ | ||
| 633 | ":3:5: error: enum fields cannot be marked comptime", | ||
| 634 | ":8:8: error: enum fields do not have types", | ||
| 635 | ":6:12: note: consider 'union(enum)' here to make it a tagged union", | ||
| 636 | }); | ||
| 637 | |||
| 638 | // @enumToInt, @intToEnum, enum literal coercion, field access syntax, comparison, switch | ||
| 639 | case.addCompareOutput( | ||
| 640 | \\const Number = enum { One, Two, Three }; | ||
| 641 | \\ | ||
| 642 | \\pub export fn main() c_int { | ||
| 643 | \\ var number1 = Number.One; | ||
| 644 | \\ var number2: Number = .Two; | ||
| 645 | \\ const number3 = @intToEnum(Number, 2); | ||
| 646 | \\ if (number1 == number2) return 1; | ||
| 647 | \\ if (number2 == number3) return 1; | ||
| 648 | \\ if (@enumToInt(number1) != 0) return 1; | ||
| 649 | \\ if (@enumToInt(number2) != 1) return 1; | ||
| 650 | \\ if (@enumToInt(number3) != 2) return 1; | ||
| 651 | \\ var x: Number = .Two; | ||
| 652 | \\ if (number2 != x) return 1; | ||
| 653 | \\ switch (x) { | ||
| 654 | \\ .One => return 1, | ||
| 655 | \\ .Two => return 0, | ||
| 656 | \\ number3 => return 2, | ||
| 657 | \\ } | ||
| 658 | \\} | ||
| 659 | , ""); | ||
| 660 | |||
| 661 | // Specifying alignment is a parse error. | ||
| 662 | // This also tests going from a successful build to a parse error. | ||
| 663 | case.addError( | ||
| 664 | \\const E1 = enum { | ||
| 665 | \\ a, | ||
| 666 | \\ b align(4), | ||
| 667 | \\ c, | ||
| 668 | \\}; | ||
| 669 | \\export fn foo() void { | ||
| 670 | \\ _ = E1.a; | ||
| 671 | \\} | ||
| 672 | , &.{ | ||
| 673 | ":3:13: error: enum fields cannot be aligned", | ||
| 674 | }); | ||
| 675 | |||
| 676 | // Redundant non-exhaustive enum mark. | ||
| 677 | // This also tests going from a parse error to an AstGen error. | ||
| 678 | case.addError( | ||
| 679 | \\const E1 = enum { | ||
| 680 | \\ a, | ||
| 681 | \\ _, | ||
| 682 | \\ b, | ||
| 683 | \\ c, | ||
| 684 | \\ _, | ||
| 685 | \\}; | ||
| 686 | \\export fn foo() void { | ||
| 687 | \\ _ = E1.a; | ||
| 688 | \\} | ||
| 689 | , &.{ | ||
| 690 | ":6:5: error: redundant non-exhaustive enum mark", | ||
| 691 | ":3:5: note: other mark here", | ||
| 692 | }); | ||
| 693 | |||
| 694 | case.addError( | ||
| 695 | \\const E1 = enum { | ||
| 696 | \\ a, | ||
| 697 | \\ b, | ||
| 698 | \\ c, | ||
| 699 | \\ _ = 10, | ||
| 700 | \\}; | ||
| 701 | \\export fn foo() void { | ||
| 702 | \\ _ = E1.a; | ||
| 703 | \\} | ||
| 704 | , &.{ | ||
| 705 | ":5:9: error: '_' is used to mark an enum as non-exhaustive and cannot be assigned a value", | ||
| 706 | }); | ||
| 707 | |||
| 708 | case.addError( | ||
| 709 | \\const E1 = enum { a, b, _ }; | ||
| 710 | \\export fn foo() void { | ||
| 711 | \\ _ = E1.a; | ||
| 712 | \\} | ||
| 713 | , &.{ | ||
| 714 | ":1:12: error: non-exhaustive enum missing integer tag type", | ||
| 715 | ":1:25: note: marked non-exhaustive here", | ||
| 716 | }); | ||
| 717 | |||
| 718 | case.addError( | ||
| 719 | \\const E1 = enum { a, b, c, b, d }; | ||
| 720 | \\pub export fn main() c_int { | ||
| 721 | \\ _ = E1.a; | ||
| 722 | \\} | ||
| 723 | , &.{ | ||
| 724 | ":1:28: error: duplicate enum field 'b'", | ||
| 725 | ":1:22: note: other field here", | ||
| 726 | }); | ||
| 727 | |||
| 728 | case.addError( | ||
| 729 | \\pub export fn main() c_int { | ||
| 730 | \\ const a = true; | ||
| 731 | \\ _ = @enumToInt(a); | ||
| 732 | \\} | ||
| 733 | , &.{ | ||
| 734 | ":3:20: error: expected enum or tagged union, found 'bool'", | ||
| 735 | }); | ||
| 736 | |||
| 737 | case.addError( | ||
| 738 | \\pub export fn main() c_int { | ||
| 739 | \\ const a = 1; | ||
| 740 | \\ _ = @intToEnum(bool, a); | ||
| 741 | \\} | ||
| 742 | , &.{ | ||
| 743 | ":3:20: error: expected enum, found 'bool'", | ||
| 744 | }); | ||
| 745 | |||
| 746 | case.addError( | ||
| 747 | \\const E = enum { a, b, c }; | ||
| 748 | \\pub export fn main() c_int { | ||
| 749 | \\ _ = @intToEnum(E, 3); | ||
| 750 | \\} | ||
| 751 | , &.{ | ||
| 752 | ":3:9: error: enum 'tmp.E' has no tag with value '3'", | ||
| 753 | ":1:11: note: enum declared here", | ||
| 754 | }); | ||
| 755 | |||
| 756 | case.addError( | ||
| 757 | \\const E = enum { a, b, c }; | ||
| 758 | \\pub export fn main() c_int { | ||
| 759 | \\ var x: E = .a; | ||
| 760 | \\ switch (x) { | ||
| 761 | \\ .a => {}, | ||
| 762 | \\ .c => {}, | ||
| 763 | \\ } | ||
| 764 | \\} | ||
| 765 | , &.{ | ||
| 766 | ":4:5: error: switch must handle all possibilities", | ||
| 767 | ":1:21: note: unhandled enumeration value: 'b'", | ||
| 768 | ":1:11: note: enum 'tmp.E' declared here", | ||
| 769 | }); | ||
| 770 | |||
| 771 | case.addError( | ||
| 772 | \\const E = enum { a, b, c }; | ||
| 773 | \\pub export fn main() c_int { | ||
| 774 | \\ var x: E = .a; | ||
| 775 | \\ switch (x) { | ||
| 776 | \\ .a => {}, | ||
| 777 | \\ .b => {}, | ||
| 778 | \\ .b => {}, | ||
| 779 | \\ .c => {}, | ||
| 780 | \\ } | ||
| 781 | \\} | ||
| 782 | , &.{ | ||
| 783 | ":7:10: error: duplicate switch value", | ||
| 784 | ":6:10: note: previous value here", | ||
| 785 | }); | ||
| 786 | |||
| 787 | case.addError( | ||
| 788 | \\const E = enum { a, b, c }; | ||
| 789 | \\pub export fn main() c_int { | ||
| 790 | \\ var x: E = .a; | ||
| 791 | \\ switch (x) { | ||
| 792 | \\ .a => {}, | ||
| 793 | \\ .b => {}, | ||
| 794 | \\ .c => {}, | ||
| 795 | \\ else => {}, | ||
| 796 | \\ } | ||
| 797 | \\} | ||
| 798 | , &.{ | ||
| 799 | ":8:14: error: unreachable else prong; all cases already handled", | ||
| 800 | }); | ||
| 801 | |||
| 802 | case.addError( | ||
| 803 | \\const E = enum { a, b, c }; | ||
| 804 | \\pub export fn main() c_int { | ||
| 805 | \\ var x: E = .a; | ||
| 806 | \\ switch (x) { | ||
| 807 | \\ .a => {}, | ||
| 808 | \\ .b => {}, | ||
| 809 | \\ _ => {}, | ||
| 810 | \\ } | ||
| 811 | \\} | ||
| 812 | , &.{ | ||
| 813 | ":4:5: error: '_' prong only allowed when switching on non-exhaustive enums", | ||
| 814 | ":7:11: note: '_' prong here", | ||
| 815 | }); | ||
| 816 | |||
| 817 | case.addError( | ||
| 818 | \\const E = enum { a, b, c }; | ||
| 819 | \\pub export fn main() c_int { | ||
| 820 | \\ _ = E.d; | ||
| 821 | \\} | ||
| 822 | , &.{ | ||
| 823 | ":3:11: error: enum 'tmp.E' has no member named 'd'", | ||
| 824 | ":1:11: note: enum declared here", | ||
| 825 | }); | ||
| 826 | |||
| 827 | case.addError( | ||
| 828 | \\const E = enum { a, b, c }; | ||
| 829 | \\pub export fn main() c_int { | ||
| 830 | \\ var x: E = .d; | ||
| 831 | \\ _ = x; | ||
| 832 | \\} | ||
| 833 | , &.{ | ||
| 834 | ":3:17: error: no field named 'd' in enum 'tmp.E'", | ||
| 835 | ":1:11: note: enum declared here", | ||
| 836 | }); | ||
| 837 | } | ||
| 838 | |||
| 839 | { | ||
| 840 | var case = ctx.exeFromCompiledC("shift right and left", .{}); | ||
| 841 | case.addCompareOutput( | ||
| 842 | \\pub export fn main() c_int { | ||
| 843 | \\ var i: u32 = 16; | ||
| 844 | \\ assert(i >> 1, 8); | ||
| 845 | \\ return 0; | ||
| 846 | \\} | ||
| 847 | \\fn assert(a: u32, b: u32) void { | ||
| 848 | \\ if (a != b) unreachable; | ||
| 849 | \\} | ||
| 850 | , ""); | ||
| 851 | |||
| 852 | case.addCompareOutput( | ||
| 853 | \\pub export fn main() c_int { | ||
| 854 | \\ var i: u32 = 16; | ||
| 855 | \\ assert(i << 1, 32); | ||
| 856 | \\ return 0; | ||
| 857 | \\} | ||
| 858 | \\fn assert(a: u32, b: u32) void { | ||
| 859 | \\ if (a != b) unreachable; | ||
| 860 | \\} | ||
| 861 | , ""); | ||
| 862 | } | ||
| 863 | |||
| 864 | { | ||
| 865 | var case = ctx.exeFromCompiledC("inferred error sets", .{}); | ||
| 866 | |||
| 867 | case.addCompareOutput( | ||
| 868 | \\pub export fn main() c_int { | ||
| 869 | \\ if (foo()) |_| { | ||
| 870 | \\ @panic("test fail"); | ||
| 871 | \\ } else |err| { | ||
| 872 | \\ if (err != error.ItBroke) { | ||
| 873 | \\ @panic("test fail"); | ||
| 874 | \\ } | ||
| 875 | \\ } | ||
| 876 | \\ return 0; | ||
| 877 | \\} | ||
| 878 | \\fn foo() !void { | ||
| 879 | \\ return error.ItBroke; | ||
| 880 | \\} | ||
| 881 | , ""); | ||
| 882 | } | ||
| 883 | |||
| 884 | { | ||
| 885 | // TODO: add u64 tests, ran into issues with the literal generated for std.math.maxInt(u64) | ||
| 886 | var case = ctx.exeFromCompiledC("add and sub wrapping operations", .{}); | ||
| 887 | case.addCompareOutput( | ||
| 888 | \\pub export fn main() c_int { | ||
| 889 | \\ // Addition | ||
| 890 | \\ if (!add_u3(1, 1, 2)) return 1; | ||
| 891 | \\ if (!add_u3(7, 1, 0)) return 1; | ||
| 892 | \\ if (!add_i3(1, 1, 2)) return 1; | ||
| 893 | \\ if (!add_i3(3, 2, -3)) return 1; | ||
| 894 | \\ if (!add_i3(-3, -2, 3)) return 1; | ||
| 895 | \\ if (!add_c_int(1, 1, 2)) return 1; | ||
| 896 | \\ // TODO enable these when stage2 supports std.math.maxInt | ||
| 897 | \\ //if (!add_c_int(maxInt(c_int), 2, minInt(c_int) + 1)) return 1; | ||
| 898 | \\ //if (!add_c_int(maxInt(c_int) + 1, -2, maxInt(c_int))) return 1; | ||
| 899 | \\ | ||
| 900 | \\ // Subtraction | ||
| 901 | \\ if (!sub_u3(2, 1, 1)) return 1; | ||
| 902 | \\ if (!sub_u3(0, 1, 7)) return 1; | ||
| 903 | \\ if (!sub_i3(2, 1, 1)) return 1; | ||
| 904 | \\ if (!sub_i3(3, -2, -3)) return 1; | ||
| 905 | \\ if (!sub_i3(-3, 2, 3)) return 1; | ||
| 906 | \\ if (!sub_c_int(2, 1, 1)) return 1; | ||
| 907 | \\ // TODO enable these when stage2 supports std.math.maxInt | ||
| 908 | \\ //if (!sub_c_int(maxInt(c_int), -2, minInt(c_int) + 1)) return 1; | ||
| 909 | \\ //if (!sub_c_int(minInt(c_int) + 1, 2, maxInt(c_int))) return 1; | ||
| 910 | \\ | ||
| 911 | \\ return 0; | ||
| 912 | \\} | ||
| 913 | \\fn add_u3(lhs: u3, rhs: u3, expected: u3) bool { | ||
| 914 | \\ return expected == lhs +% rhs; | ||
| 915 | \\} | ||
| 916 | \\fn add_i3(lhs: i3, rhs: i3, expected: i3) bool { | ||
| 917 | \\ return expected == lhs +% rhs; | ||
| 918 | \\} | ||
| 919 | \\fn add_c_int(lhs: c_int, rhs: c_int, expected: c_int) bool { | ||
| 920 | \\ return expected == lhs +% rhs; | ||
| 921 | \\} | ||
| 922 | \\fn sub_u3(lhs: u3, rhs: u3, expected: u3) bool { | ||
| 923 | \\ return expected == lhs -% rhs; | ||
| 924 | \\} | ||
| 925 | \\fn sub_i3(lhs: i3, rhs: i3, expected: i3) bool { | ||
| 926 | \\ return expected == lhs -% rhs; | ||
| 927 | \\} | ||
| 928 | \\fn sub_c_int(lhs: c_int, rhs: c_int, expected: c_int) bool { | ||
| 929 | \\ return expected == lhs -% rhs; | ||
| 930 | \\} | ||
| 931 | , ""); | ||
| 932 | } | ||
| 933 | |||
| 934 | { | ||
| 935 | var case = ctx.exeFromCompiledC("rem", linux_x64); | ||
| 936 | case.addCompareOutput( | ||
| 937 | \\fn assert(ok: bool) void { | ||
| 938 | \\ if (!ok) unreachable; | ||
| 939 | \\} | ||
| 940 | \\fn rem(lhs: i32, rhs: i32, expected: i32) bool { | ||
| 941 | \\ return @rem(lhs, rhs) == expected; | ||
| 942 | \\} | ||
| 943 | \\pub export fn main() c_int { | ||
| 944 | \\ assert(rem(-5, 3, -2)); | ||
| 945 | \\ assert(rem(5, 3, 2)); | ||
| 946 | \\ return 0; | ||
| 947 | \\} | ||
| 948 | , ""); | ||
| 949 | } | ||
| 950 | } | ||
test/cli.zig deleted-195| ... | @@ -1,195 +0,0 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const builtin = @import("builtin"); | ||
| 3 | const testing = std.testing; | ||
| 4 | const process = std.process; | ||
| 5 | const fs = std.fs; | ||
| 6 | const ChildProcess = std.ChildProcess; | ||
| 7 | |||
| 8 | var a: std.mem.Allocator = undefined; | ||
| 9 | |||
| 10 | pub fn main() !void { | ||
| 11 | var gpa = std.heap.GeneralPurposeAllocator(.{}){}; | ||
| 12 | defer _ = gpa.deinit(); | ||
| 13 | var arena = std.heap.ArenaAllocator.init(gpa.allocator()); | ||
| 14 | defer arena.deinit(); | ||
| 15 | |||
| 16 | a = arena.allocator(); | ||
| 17 | var arg_it = try process.argsWithAllocator(a); | ||
| 18 | |||
| 19 | // skip my own exe name | ||
| 20 | _ = arg_it.skip(); | ||
| 21 | |||
| 22 | const zig_exe_rel = arg_it.next() orelse { | ||
| 23 | std.debug.print("Expected first argument to be path to zig compiler\n", .{}); | ||
| 24 | return error.InvalidArgs; | ||
| 25 | }; | ||
| 26 | const cache_root = arg_it.next() orelse { | ||
| 27 | std.debug.print("Expected second argument to be cache root directory path\n", .{}); | ||
| 28 | return error.InvalidArgs; | ||
| 29 | }; | ||
| 30 | const zig_exe = try fs.path.resolve(a, &[_][]const u8{zig_exe_rel}); | ||
| 31 | |||
| 32 | const dir_path = try fs.path.join(a, &[_][]const u8{ cache_root, "clitest" }); | ||
| 33 | defer fs.cwd().deleteTree(dir_path) catch {}; | ||
| 34 | |||
| 35 | const TestFn = fn ([]const u8, []const u8) anyerror!void; | ||
| 36 | const Test = struct { | ||
| 37 | func: TestFn, | ||
| 38 | name: []const u8, | ||
| 39 | }; | ||
| 40 | const tests = [_]Test{ | ||
| 41 | .{ .func = testZigInitLib, .name = "zig init-lib" }, | ||
| 42 | .{ .func = testZigInitExe, .name = "zig init-exe" }, | ||
| 43 | .{ .func = testGodboltApi, .name = "godbolt API" }, | ||
| 44 | .{ .func = testMissingOutputPath, .name = "missing output path" }, | ||
| 45 | .{ .func = testZigFmt, .name = "zig fmt" }, | ||
| 46 | }; | ||
| 47 | inline for (tests) |t| { | ||
| 48 | try fs.cwd().deleteTree(dir_path); | ||
| 49 | try fs.cwd().makeDir(dir_path); | ||
| 50 | t.func(zig_exe, dir_path) catch |err| { | ||
| 51 | std.debug.print("test '{s}' failed: {s}\n", .{ | ||
| 52 | t.name, @errorName(err), | ||
| 53 | }); | ||
| 54 | return err; | ||
| 55 | }; | ||
| 56 | } | ||
| 57 | } | ||
| 58 | |||
| 59 | fn printCmd(cwd: []const u8, argv: []const []const u8) void { | ||
| 60 | std.debug.print("cd {s} && ", .{cwd}); | ||
| 61 | for (argv) |arg| { | ||
| 62 | std.debug.print("{s} ", .{arg}); | ||
| 63 | } | ||
| 64 | std.debug.print("\n", .{}); | ||
| 65 | } | ||
| 66 | |||
| 67 | fn exec(cwd: []const u8, expect_0: bool, argv: []const []const u8) !ChildProcess.ExecResult { | ||
| 68 | const max_output_size = 100 * 1024; | ||
| 69 | const result = ChildProcess.exec(.{ | ||
| 70 | .allocator = a, | ||
| 71 | .argv = argv, | ||
| 72 | .cwd = cwd, | ||
| 73 | .max_output_bytes = max_output_size, | ||
| 74 | }) catch |err| { | ||
| 75 | std.debug.print("The following command failed:\n", .{}); | ||
| 76 | printCmd(cwd, argv); | ||
| 77 | return err; | ||
| 78 | }; | ||
| 79 | switch (result.term) { | ||
| 80 | .Exited => |code| { | ||
| 81 | if ((code != 0) == expect_0) { | ||
| 82 | std.debug.print("The following command exited with error code {}:\n", .{code}); | ||
| 83 | printCmd(cwd, argv); | ||
| 84 | std.debug.print("stderr:\n{s}\n", .{result.stderr}); | ||
| 85 | return error.CommandFailed; | ||
| 86 | } | ||
| 87 | }, | ||
| 88 | else => { | ||
| 89 | std.debug.print("The following command terminated unexpectedly:\n", .{}); | ||
| 90 | printCmd(cwd, argv); | ||
| 91 | std.debug.print("stderr:\n{s}\n", .{result.stderr}); | ||
| 92 | return error.CommandFailed; | ||
| 93 | }, | ||
| 94 | } | ||
| 95 | return result; | ||
| 96 | } | ||
| 97 | |||
| 98 | fn testZigInitLib(zig_exe: []const u8, dir_path: []const u8) !void { | ||
| 99 | _ = try exec(dir_path, true, &[_][]const u8{ zig_exe, "init-lib" }); | ||
| 100 | const test_result = try exec(dir_path, true, &[_][]const u8{ zig_exe, "build", "test" }); | ||
| 101 | try testing.expectStringEndsWith(test_result.stderr, "All 1 tests passed.\n"); | ||
| 102 | } | ||
| 103 | |||
| 104 | fn testZigInitExe(zig_exe: []const u8, dir_path: []const u8) !void { | ||
| 105 | _ = try exec(dir_path, true, &[_][]const u8{ zig_exe, "init-exe" }); | ||
| 106 | const run_result = try exec(dir_path, true, &[_][]const u8{ zig_exe, "build", "run" }); | ||
| 107 | try testing.expectEqualStrings("All your codebase are belong to us.\n", run_result.stderr); | ||
| 108 | try testing.expectEqualStrings("Run `zig build test` to run the tests.\n", run_result.stdout); | ||
| 109 | } | ||
| 110 | |||
| 111 | fn testGodboltApi(zig_exe: []const u8, dir_path: []const u8) anyerror!void { | ||
| 112 | if (builtin.os.tag != .linux or builtin.cpu.arch != .x86_64) return; | ||
| 113 | |||
| 114 | const example_zig_path = try fs.path.join(a, &[_][]const u8{ dir_path, "example.zig" }); | ||
| 115 | const example_s_path = try fs.path.join(a, &[_][]const u8{ dir_path, "example.s" }); | ||
| 116 | |||
| 117 | try fs.cwd().writeFile(example_zig_path, | ||
| 118 | \\// Type your code here, or load an example. | ||
| 119 | \\export fn square(num: i32) i32 { | ||
| 120 | \\ return num * num; | ||
| 121 | \\} | ||
| 122 | \\extern fn zig_panic() noreturn; | ||
| 123 | \\pub fn panic(msg: []const u8, error_return_trace: ?*@import("std").builtin.StackTrace, _: ?usize) noreturn { | ||
| 124 | \\ _ = msg; | ||
| 125 | \\ _ = error_return_trace; | ||
| 126 | \\ zig_panic(); | ||
| 127 | \\} | ||
| 128 | ); | ||
| 129 | |||
| 130 | var args = std.ArrayList([]const u8).init(a); | ||
| 131 | try args.appendSlice(&[_][]const u8{ | ||
| 132 | zig_exe, "build-obj", | ||
| 133 | "--cache-dir", dir_path, | ||
| 134 | "--name", "example", | ||
| 135 | "-fno-emit-bin", "-fno-emit-h", | ||
| 136 | "-fstrip", "-OReleaseFast", | ||
| 137 | example_zig_path, | ||
| 138 | }); | ||
| 139 | |||
| 140 | const emit_asm_arg = try std.fmt.allocPrint(a, "-femit-asm={s}", .{example_s_path}); | ||
| 141 | try args.append(emit_asm_arg); | ||
| 142 | |||
| 143 | _ = try exec(dir_path, true, args.items); | ||
| 144 | |||
| 145 | const out_asm = try std.fs.cwd().readFileAlloc(a, example_s_path, std.math.maxInt(usize)); | ||
| 146 | try testing.expect(std.mem.indexOf(u8, out_asm, "square:") != null); | ||
| 147 | try testing.expect(std.mem.indexOf(u8, out_asm, "mov\teax, edi") != null); | ||
| 148 | try testing.expect(std.mem.indexOf(u8, out_asm, "imul\teax, edi") != null); | ||
| 149 | } | ||
| 150 | |||
| 151 | fn testMissingOutputPath(zig_exe: []const u8, dir_path: []const u8) !void { | ||
| 152 | _ = try exec(dir_path, true, &[_][]const u8{ zig_exe, "init-exe" }); | ||
| 153 | const output_path = try fs.path.join(a, &[_][]const u8{ "does", "not", "exist", "foo.exe" }); | ||
| 154 | const output_arg = try std.fmt.allocPrint(a, "-femit-bin={s}", .{output_path}); | ||
| 155 | const source_path = try fs.path.join(a, &[_][]const u8{ "src", "main.zig" }); | ||
| 156 | const result = try exec(dir_path, false, &[_][]const u8{ zig_exe, "build-exe", source_path, output_arg }); | ||
| 157 | const s = std.fs.path.sep_str; | ||
| 158 | const expected: []const u8 = "error: unable to open output directory 'does" ++ s ++ "not" ++ s ++ "exist': FileNotFound\n"; | ||
| 159 | try testing.expectEqualStrings(expected, result.stderr); | ||
| 160 | } | ||
| 161 | |||
| 162 | fn testZigFmt(zig_exe: []const u8, dir_path: []const u8) !void { | ||
| 163 | _ = try exec(dir_path, true, &[_][]const u8{ zig_exe, "init-exe" }); | ||
| 164 | |||
| 165 | const unformatted_code = " // no reason for indent"; | ||
| 166 | |||
| 167 | const fmt1_zig_path = try fs.path.join(a, &[_][]const u8{ dir_path, "fmt1.zig" }); | ||
| 168 | try fs.cwd().writeFile(fmt1_zig_path, unformatted_code); | ||
| 169 | |||
| 170 | const run_result1 = try exec(dir_path, true, &[_][]const u8{ zig_exe, "fmt", fmt1_zig_path }); | ||
| 171 | // stderr should be file path + \n | ||
| 172 | try testing.expect(std.mem.startsWith(u8, run_result1.stdout, fmt1_zig_path)); | ||
| 173 | try testing.expect(run_result1.stdout.len == fmt1_zig_path.len + 1 and run_result1.stdout[run_result1.stdout.len - 1] == '\n'); | ||
| 174 | |||
| 175 | const fmt2_zig_path = try fs.path.join(a, &[_][]const u8{ dir_path, "fmt2.zig" }); | ||
| 176 | try fs.cwd().writeFile(fmt2_zig_path, unformatted_code); | ||
| 177 | |||
| 178 | const run_result2 = try exec(dir_path, true, &[_][]const u8{ zig_exe, "fmt", dir_path }); | ||
| 179 | // running it on the dir, only the new file should be changed | ||
| 180 | try testing.expect(std.mem.startsWith(u8, run_result2.stdout, fmt2_zig_path)); | ||
| 181 | try testing.expect(run_result2.stdout.len == fmt2_zig_path.len + 1 and run_result2.stdout[run_result2.stdout.len - 1] == '\n'); | ||
| 182 | |||
| 183 | const run_result3 = try exec(dir_path, true, &[_][]const u8{ zig_exe, "fmt", dir_path }); | ||
| 184 | // both files have been formatted, nothing should change now | ||
| 185 | try testing.expect(run_result3.stdout.len == 0); | ||
| 186 | |||
| 187 | // Check UTF-16 decoding | ||
| 188 | const fmt4_zig_path = try fs.path.join(a, &[_][]const u8{ dir_path, "fmt4.zig" }); | ||
| 189 | var unformatted_code_utf16 = "\xff\xfe \x00 \x00 \x00 \x00/\x00/\x00 \x00n\x00o\x00 \x00r\x00e\x00a\x00s\x00o\x00n\x00"; | ||
| 190 | try fs.cwd().writeFile(fmt4_zig_path, unformatted_code_utf16); | ||
| 191 | |||
| 192 | const run_result4 = try exec(dir_path, true, &[_][]const u8{ zig_exe, "fmt", dir_path }); | ||
| 193 | try testing.expect(std.mem.startsWith(u8, run_result4.stdout, fmt4_zig_path)); | ||
| 194 | try testing.expect(run_result4.stdout.len == fmt4_zig_path.len + 1 and run_result4.stdout[run_result4.stdout.len - 1] == '\n'); | ||
| 195 | } | ||
test/compile_errors.zig+25-199| ... | @@ -1,146 +1,10 @@ | ... | @@ -1,146 +1,10 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const TestContext = @import("../src/test.zig").TestContext; | 3 | const Cases = @import("src/Cases.zig"); |
| 4 | |||
| 5 | pub fn addCases(ctx: *TestContext) !void { | ||
| 6 | { | ||
| 7 | const case = ctx.obj("wrong same named struct", .{}); | ||
| 8 | case.backend = .stage1; | ||
| 9 | |||
| 10 | case.addSourceFile("a.zig", | ||
| 11 | \\pub const Foo = struct { | ||
| 12 | \\ x: i32, | ||
| 13 | \\}; | ||
| 14 | ); | ||
| 15 | |||
| 16 | case.addSourceFile("b.zig", | ||
| 17 | \\pub const Foo = struct { | ||
| 18 | \\ z: f64, | ||
| 19 | \\}; | ||
| 20 | ); | ||
| 21 | |||
| 22 | case.addError( | ||
| 23 | \\const a = @import("a.zig"); | ||
| 24 | \\const b = @import("b.zig"); | ||
| 25 | \\ | ||
| 26 | \\export fn entry() void { | ||
| 27 | \\ var a1: a.Foo = undefined; | ||
| 28 | \\ bar(&a1); | ||
| 29 | \\} | ||
| 30 | \\ | ||
| 31 | \\fn bar(x: *b.Foo) void {_ = x;} | ||
| 32 | , &[_][]const u8{ | ||
| 33 | "tmp.zig:6:10: error: expected type '*b.Foo', found '*a.Foo'", | ||
| 34 | "tmp.zig:6:10: note: pointer type child 'a.Foo' cannot cast into pointer type child 'b.Foo'", | ||
| 35 | "a.zig:1:17: note: a.Foo declared here", | ||
| 36 | "b.zig:1:17: note: b.Foo declared here", | ||
| 37 | }); | ||
| 38 | } | ||
| 39 | |||
| 40 | { | ||
| 41 | const case = ctx.obj("multiple files with private function error", .{}); | ||
| 42 | case.backend = .stage1; | ||
| 43 | |||
| 44 | case.addSourceFile("foo.zig", | ||
| 45 | \\fn privateFunction() void { } | ||
| 46 | ); | ||
| 47 | |||
| 48 | case.addError( | ||
| 49 | \\const foo = @import("foo.zig",); | ||
| 50 | \\ | ||
| 51 | \\export fn callPrivFunction() void { | ||
| 52 | \\ foo.privateFunction(); | ||
| 53 | \\} | ||
| 54 | , &[_][]const u8{ | ||
| 55 | "tmp.zig:4:8: error: 'privateFunction' is private", | ||
| 56 | "foo.zig:1:1: note: declared here", | ||
| 57 | }); | ||
| 58 | } | ||
| 59 | |||
| 60 | { | ||
| 61 | const case = ctx.obj("multiple files with private member instance function (canonical invocation) error", .{}); | ||
| 62 | case.backend = .stage1; | ||
| 63 | |||
| 64 | case.addSourceFile("foo.zig", | ||
| 65 | \\pub const Foo = struct { | ||
| 66 | \\ fn privateFunction(self: *Foo) void { _ = self; } | ||
| 67 | \\}; | ||
| 68 | ); | ||
| 69 | |||
| 70 | case.addError( | ||
| 71 | \\const Foo = @import("foo.zig",).Foo; | ||
| 72 | \\ | ||
| 73 | \\export fn callPrivFunction() void { | ||
| 74 | \\ var foo = Foo{}; | ||
| 75 | \\ Foo.privateFunction(foo); | ||
| 76 | \\} | ||
| 77 | , &[_][]const u8{ | ||
| 78 | "tmp.zig:5:8: error: 'privateFunction' is private", | ||
| 79 | "foo.zig:2:5: note: declared here", | ||
| 80 | }); | ||
| 81 | } | ||
| 82 | |||
| 83 | { | ||
| 84 | const case = ctx.obj("multiple files with private member instance function error", .{}); | ||
| 85 | case.backend = .stage1; | ||
| 86 | |||
| 87 | case.addSourceFile("foo.zig", | ||
| 88 | \\pub const Foo = struct { | ||
| 89 | \\ fn privateFunction(self: *Foo) void { _ = self; } | ||
| 90 | \\}; | ||
| 91 | ); | ||
| 92 | |||
| 93 | case.addError( | ||
| 94 | \\const Foo = @import("foo.zig",).Foo; | ||
| 95 | \\ | ||
| 96 | \\export fn callPrivFunction() void { | ||
| 97 | \\ var foo = Foo{}; | ||
| 98 | \\ foo.privateFunction(); | ||
| 99 | \\} | ||
| 100 | , &[_][]const u8{ | ||
| 101 | "tmp.zig:5:8: error: 'privateFunction' is private", | ||
| 102 | "foo.zig:2:5: note: declared here", | ||
| 103 | }); | ||
| 104 | } | ||
| 105 | |||
| 106 | { | ||
| 107 | const case = ctx.obj("export collision", .{}); | ||
| 108 | case.backend = .stage1; | ||
| 109 | |||
| 110 | case.addSourceFile("foo.zig", | ||
| 111 | \\export fn bar() void {} | ||
| 112 | \\pub const baz = 1234; | ||
| 113 | ); | ||
| 114 | |||
| 115 | case.addError( | ||
| 116 | \\const foo = @import("foo.zig",); | ||
| 117 | \\ | ||
| 118 | \\export fn bar() usize { | ||
| 119 | \\ return foo.baz; | ||
| 120 | \\} | ||
| 121 | , &[_][]const u8{ | ||
| 122 | "foo.zig:1:1: error: exported symbol collision: 'bar'", | ||
| 123 | "tmp.zig:3:1: note: other symbol here", | ||
| 124 | }); | ||
| 125 | } | ||
| 126 | |||
| 127 | ctx.objErrStage1("non-printable invalid character", "\xff\xfe" ++ | ||
| 128 | "fn foo() bool {\r\n" ++ | ||
| 129 | " return true;\r\n" ++ | ||
| 130 | "}\r\n", &[_][]const u8{ | ||
| 131 | "tmp.zig:1:1: error: expected test, comptime, var decl, or container field, found 'invalid bytes'", | ||
| 132 | "tmp.zig:1:1: note: invalid byte: '\\xff'", | ||
| 133 | }); | ||
| 134 | |||
| 135 | ctx.objErrStage1("non-printable invalid character with escape alternative", "fn foo() bool {\n" ++ | ||
| 136 | "\treturn true;\n" ++ | ||
| 137 | "}\n", &[_][]const u8{ | ||
| 138 | "tmp.zig:2:1: error: invalid character: '\\t'", | ||
| 139 | }); | ||
| 140 | 4 | ||
| 5 | pub fn addCases(ctx: *Cases) !void { | ||
| 141 | { | 6 | { |
| 142 | const case = ctx.obj("multiline error messages", .{}); | 7 | const case = ctx.obj("multiline error messages", .{}); |
| 143 | case.backend = .stage2; | ||
| 144 | 8 | ||
| 145 | case.addError( | 9 | case.addError( |
| 146 | \\comptime { | 10 | \\comptime { |
| ... | @@ -176,7 +40,6 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -176,7 +40,6 @@ pub fn addCases(ctx: *TestContext) !void { |
| 176 | 40 | ||
| 177 | { | 41 | { |
| 178 | const case = ctx.obj("isolated carriage return in multiline string literal", .{}); | 42 | const case = ctx.obj("isolated carriage return in multiline string literal", .{}); |
| 179 | case.backend = .stage2; | ||
| 180 | 43 | ||
| 181 | case.addError("const foo = \\\\\test\r\r rogue carriage return\n;", &[_][]const u8{ | 44 | case.addError("const foo = \\\\\test\r\r rogue carriage return\n;", &[_][]const u8{ |
| 182 | ":1:19: error: expected ';' after declaration", | 45 | ":1:19: error: expected ';' after declaration", |
| ... | @@ -195,16 +58,6 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -195,16 +58,6 @@ pub fn addCases(ctx: *TestContext) !void { |
| 195 | 58 | ||
| 196 | { | 59 | { |
| 197 | const case = ctx.obj("argument causes error", .{}); | 60 | const case = ctx.obj("argument causes error", .{}); |
| 198 | case.backend = .stage2; | ||
| 199 | |||
| 200 | case.addSourceFile("b.zig", | ||
| 201 | \\pub const ElfDynLib = struct { | ||
| 202 | \\ pub fn lookup(self: *ElfDynLib, comptime T: type) ?T { | ||
| 203 | \\ _ = self; | ||
| 204 | \\ return undefined; | ||
| 205 | \\ } | ||
| 206 | \\}; | ||
| 207 | ); | ||
| 208 | 61 | ||
| 209 | case.addError( | 62 | case.addError( |
| 210 | \\pub export fn entry() void { | 63 | \\pub export fn entry() void { |
| ... | @@ -216,15 +69,18 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -216,15 +69,18 @@ pub fn addCases(ctx: *TestContext) !void { |
| 216 | ":3:12: note: argument to function being called at comptime must be comptime-known", | 69 | ":3:12: note: argument to function being called at comptime must be comptime-known", |
| 217 | ":2:55: note: expression is evaluated at comptime because the generic function was instantiated with a comptime-only return type", | 70 | ":2:55: note: expression is evaluated at comptime because the generic function was instantiated with a comptime-only return type", |
| 218 | }); | 71 | }); |
| 72 | case.addSourceFile("b.zig", | ||
| 73 | \\pub const ElfDynLib = struct { | ||
| 74 | \\ pub fn lookup(self: *ElfDynLib, comptime T: type) ?T { | ||
| 75 | \\ _ = self; | ||
| 76 | \\ return undefined; | ||
| 77 | \\ } | ||
| 78 | \\}; | ||
| 79 | ); | ||
| 219 | } | 80 | } |
| 220 | 81 | ||
| 221 | { | 82 | { |
| 222 | const case = ctx.obj("astgen failure in file struct", .{}); | 83 | const case = ctx.obj("astgen failure in file struct", .{}); |
| 223 | case.backend = .stage2; | ||
| 224 | |||
| 225 | case.addSourceFile("b.zig", | ||
| 226 | \\+ | ||
| 227 | ); | ||
| 228 | 84 | ||
| 229 | case.addError( | 85 | case.addError( |
| 230 | \\pub export fn entry() void { | 86 | \\pub export fn entry() void { |
| ... | @@ -233,21 +89,13 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -233,21 +89,13 @@ pub fn addCases(ctx: *TestContext) !void { |
| 233 | , &[_][]const u8{ | 89 | , &[_][]const u8{ |
| 234 | ":1:1: error: expected type expression, found '+'", | 90 | ":1:1: error: expected type expression, found '+'", |
| 235 | }); | 91 | }); |
| 92 | case.addSourceFile("b.zig", | ||
| 93 | \\+ | ||
| 94 | ); | ||
| 236 | } | 95 | } |
| 237 | 96 | ||
| 238 | { | 97 | { |
| 239 | const case = ctx.obj("invalid store to comptime field", .{}); | 98 | const case = ctx.obj("invalid store to comptime field", .{}); |
| 240 | case.backend = .stage2; | ||
| 241 | |||
| 242 | case.addSourceFile("a.zig", | ||
| 243 | \\pub const S = struct { | ||
| 244 | \\ comptime foo: u32 = 1, | ||
| 245 | \\ bar: u32, | ||
| 246 | \\ pub fn foo(x: @This()) void { | ||
| 247 | \\ _ = x; | ||
| 248 | \\ } | ||
| 249 | \\}; | ||
| 250 | ); | ||
| 251 | 99 | ||
| 252 | case.addError( | 100 | case.addError( |
| 253 | \\const a = @import("a.zig"); | 101 | \\const a = @import("a.zig"); |
| ... | @@ -259,44 +107,19 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -259,44 +107,19 @@ pub fn addCases(ctx: *TestContext) !void { |
| 259 | ":4:23: error: value stored in comptime field does not match the default value of the field", | 107 | ":4:23: error: value stored in comptime field does not match the default value of the field", |
| 260 | ":2:25: note: default value set here", | 108 | ":2:25: note: default value set here", |
| 261 | }); | 109 | }); |
| 110 | case.addSourceFile("a.zig", | ||
| 111 | \\pub const S = struct { | ||
| 112 | \\ comptime foo: u32 = 1, | ||
| 113 | \\ bar: u32, | ||
| 114 | \\ pub fn foo(x: @This()) void { | ||
| 115 | \\ _ = x; | ||
| 116 | \\ } | ||
| 117 | \\}; | ||
| 118 | ); | ||
| 262 | } | 119 | } |
| 263 | 120 | ||
| 264 | // TODO test this in stage2, but we won't even try in stage1 | ||
| 265 | //ctx.objErrStage1("inline fn calls itself indirectly", | ||
| 266 | // \\export fn foo() void { | ||
| 267 | // \\ bar(); | ||
| 268 | // \\} | ||
| 269 | // \\fn bar() callconv(.Inline) void { | ||
| 270 | // \\ baz(); | ||
| 271 | // \\ quux(); | ||
| 272 | // \\} | ||
| 273 | // \\fn baz() callconv(.Inline) void { | ||
| 274 | // \\ bar(); | ||
| 275 | // \\ quux(); | ||
| 276 | // \\} | ||
| 277 | // \\extern fn quux() void; | ||
| 278 | //, &[_][]const u8{ | ||
| 279 | // "tmp.zig:4:1: error: unable to inline function", | ||
| 280 | //}); | ||
| 281 | |||
| 282 | //ctx.objErrStage1("save reference to inline function", | ||
| 283 | // \\export fn foo() void { | ||
| 284 | // \\ quux(@ptrToInt(bar)); | ||
| 285 | // \\} | ||
| 286 | // \\fn bar() callconv(.Inline) void { } | ||
| 287 | // \\extern fn quux(usize) void; | ||
| 288 | //, &[_][]const u8{ | ||
| 289 | // "tmp.zig:4:1: error: unable to inline function", | ||
| 290 | //}); | ||
| 291 | |||
| 292 | { | 121 | { |
| 293 | const case = ctx.obj("file in multiple modules", .{}); | 122 | const case = ctx.obj("file in multiple modules", .{}); |
| 294 | case.backend = .stage2; | ||
| 295 | |||
| 296 | case.addSourceFile("foo.zig", | ||
| 297 | \\const dummy = 0; | ||
| 298 | ); | ||
| 299 | |||
| 300 | case.addDepModule("foo", "foo.zig"); | 123 | case.addDepModule("foo", "foo.zig"); |
| 301 | 124 | ||
| 302 | case.addError( | 125 | case.addError( |
| ... | @@ -309,5 +132,8 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -309,5 +132,8 @@ pub fn addCases(ctx: *TestContext) !void { |
| 309 | ":1:1: note: root of module root.foo", | 132 | ":1:1: note: root of module root.foo", |
| 310 | ":3:17: note: imported from module root", | 133 | ":3:17: note: imported from module root", |
| 311 | }); | 134 | }); |
| 135 | case.addSourceFile("foo.zig", | ||
| 136 | \\const dummy = 0; | ||
| 137 | ); | ||
| 312 | } | 138 | } |
| 313 | } | 139 | } |
test/link.zig+172-213| ... | @@ -1,213 +1,172 @@ | ... | @@ -1,213 +1,172 @@ |
| 1 | const std = @import("std"); | 1 | pub const Case = struct { |
| 2 | const builtin = @import("builtin"); | 2 | build_root: []const u8, |
| 3 | const tests = @import("tests.zig"); | 3 | import: type, |
| 4 | 4 | }; | |
| 5 | pub fn addCases(cases: *tests.StandaloneContext) void { | 5 | |
| 6 | cases.addBuildFile("test/link/bss/build.zig", .{ | 6 | pub const cases = [_]Case{ |
| 7 | .build_modes = false, // we only guarantee zerofill for undefined in Debug | 7 | .{ |
| 8 | }); | 8 | .build_root = "test/link/bss", |
| 9 | 9 | .import = @import("link/bss/build.zig"), | |
| 10 | cases.addBuildFile("test/link/common_symbols/build.zig", .{ | 10 | }, |
| 11 | .build_modes = true, | 11 | .{ |
| 12 | }); | 12 | .build_root = "test/link/common_symbols", |
| 13 | 13 | .import = @import("link/common_symbols/build.zig"), | |
| 14 | cases.addBuildFile("test/link/common_symbols_alignment/build.zig", .{ | 14 | }, |
| 15 | .build_modes = true, | 15 | .{ |
| 16 | }); | 16 | .build_root = "test/link/common_symbols_alignment", |
| 17 | 17 | .import = @import("link/common_symbols_alignment/build.zig"), | |
| 18 | cases.addBuildFile("test/link/interdependent_static_c_libs/build.zig", .{ | 18 | }, |
| 19 | .build_modes = true, | 19 | .{ |
| 20 | }); | 20 | .build_root = "test/link/interdependent_static_c_libs", |
| 21 | 21 | .import = @import("link/interdependent_static_c_libs/build.zig"), | |
| 22 | cases.addBuildFile("test/link/static_lib_as_system_lib/build.zig", .{ | 22 | }, |
| 23 | .build_modes = true, | 23 | |
| 24 | }); | 24 | // WASM Cases |
| 25 | 25 | .{ | |
| 26 | addWasmCases(cases); | 26 | .build_root = "test/link/wasm/archive", |
| 27 | addMachOCases(cases); | 27 | .import = @import("link/wasm/archive/build.zig"), |
| 28 | } | 28 | }, |
| 29 | 29 | .{ | |
| 30 | fn addWasmCases(cases: *tests.StandaloneContext) void { | 30 | .build_root = "test/link/wasm/basic-features", |
| 31 | cases.addBuildFile("test/link/wasm/archive/build.zig", .{ | 31 | .import = @import("link/wasm/basic-features/build.zig"), |
| 32 | .build_modes = true, | 32 | }, |
| 33 | .requires_stage2 = true, | 33 | .{ |
| 34 | }); | 34 | .build_root = "test/link/wasm/bss", |
| 35 | 35 | .import = @import("link/wasm/bss/build.zig"), | |
| 36 | cases.addBuildFile("test/link/wasm/basic-features/build.zig", .{ | 36 | }, |
| 37 | .requires_stage2 = true, | 37 | .{ |
| 38 | }); | 38 | .build_root = "test/link/wasm/export", |
| 39 | 39 | .import = @import("link/wasm/export/build.zig"), | |
| 40 | cases.addBuildFile("test/link/wasm/bss/build.zig", .{ | 40 | }, |
| 41 | .build_modes = false, | 41 | .{ |
| 42 | .requires_stage2 = true, | 42 | .build_root = "test/link/wasm/export-data", |
| 43 | }); | 43 | .import = @import("link/wasm/export-data/build.zig"), |
| 44 | 44 | }, | |
| 45 | cases.addBuildFile("test/link/wasm/export/build.zig", .{ | 45 | .{ |
| 46 | .build_modes = true, | 46 | .build_root = "test/link/wasm/extern", |
| 47 | .requires_stage2 = true, | 47 | .import = @import("link/wasm/extern/build.zig"), |
| 48 | }); | 48 | }, |
| 49 | 49 | .{ | |
| 50 | // TODO: Fix open handle in wasm-linker refraining rename from working on Windows. | 50 | .build_root = "test/link/wasm/extern-mangle", |
| 51 | if (builtin.os.tag != .windows) { | 51 | .import = @import("link/wasm/extern-mangle/build.zig"), |
| 52 | cases.addBuildFile("test/link/wasm/export-data/build.zig", .{}); | 52 | }, |
| 53 | } | 53 | .{ |
| 54 | 54 | .build_root = "test/link/wasm/function-table", | |
| 55 | cases.addBuildFile("test/link/wasm/extern/build.zig", .{ | 55 | .import = @import("link/wasm/function-table/build.zig"), |
| 56 | .build_modes = true, | 56 | }, |
| 57 | .requires_stage2 = true, | 57 | .{ |
| 58 | .use_emulation = true, | 58 | .build_root = "test/link/wasm/infer-features", |
| 59 | }); | 59 | .import = @import("link/wasm/infer-features/build.zig"), |
| 60 | 60 | }, | |
| 61 | cases.addBuildFile("test/link/wasm/extern-mangle/build.zig", .{ | 61 | .{ |
| 62 | .build_modes = true, | 62 | .build_root = "test/link/wasm/producers", |
| 63 | .requires_stage2 = true, | 63 | .import = @import("link/wasm/producers/build.zig"), |
| 64 | }); | 64 | }, |
| 65 | 65 | .{ | |
| 66 | cases.addBuildFile("test/link/wasm/function-table/build.zig", .{ | 66 | .build_root = "test/link/wasm/segments", |
| 67 | .build_modes = true, | 67 | .import = @import("link/wasm/segments/build.zig"), |
| 68 | .requires_stage2 = true, | 68 | }, |
| 69 | }); | 69 | .{ |
| 70 | 70 | .build_root = "test/link/wasm/stack_pointer", | |
| 71 | cases.addBuildFile("test/link/wasm/infer-features/build.zig", .{ | 71 | .import = @import("link/wasm/stack_pointer/build.zig"), |
| 72 | .requires_stage2 = true, | 72 | }, |
| 73 | }); | 73 | .{ |
| 74 | 74 | .build_root = "test/link/wasm/type", | |
| 75 | cases.addBuildFile("test/link/wasm/producers/build.zig", .{ | 75 | .import = @import("link/wasm/type/build.zig"), |
| 76 | .build_modes = true, | 76 | }, |
| 77 | .requires_stage2 = true, | 77 | |
| 78 | }); | 78 | // Mach-O Cases |
| 79 | 79 | .{ | |
| 80 | cases.addBuildFile("test/link/wasm/segments/build.zig", .{ | 80 | .build_root = "test/link/macho/bugs/13056", |
| 81 | .build_modes = true, | 81 | .import = @import("link/macho/bugs/13056/build.zig"), |
| 82 | .requires_stage2 = true, | 82 | }, |
| 83 | }); | 83 | .{ |
| 84 | 84 | .build_root = "test/link/macho/bugs/13457", | |
| 85 | cases.addBuildFile("test/link/wasm/stack_pointer/build.zig", .{ | 85 | .import = @import("link/macho/bugs/13457/build.zig"), |
| 86 | .build_modes = true, | 86 | }, |
| 87 | .requires_stage2 = true, | 87 | .{ |
| 88 | }); | 88 | .build_root = "test/link/macho/dead_strip", |
| 89 | 89 | .import = @import("link/macho/dead_strip/build.zig"), | |
| 90 | cases.addBuildFile("test/link/wasm/type/build.zig", .{ | 90 | }, |
| 91 | .build_modes = true, | 91 | .{ |
| 92 | .requires_stage2 = true, | 92 | .build_root = "test/link/macho/dead_strip_dylibs", |
| 93 | }); | 93 | .import = @import("link/macho/dead_strip_dylibs/build.zig"), |
| 94 | } | 94 | }, |
| 95 | 95 | .{ | |
| 96 | fn addMachOCases(cases: *tests.StandaloneContext) void { | 96 | .build_root = "test/link/macho/dylib", |
| 97 | cases.addBuildFile("test/link/macho/bugs/13056/build.zig", .{ | 97 | .import = @import("link/macho/dylib/build.zig"), |
| 98 | .build_modes = true, | 98 | }, |
| 99 | .requires_macos_sdk = true, | 99 | .{ |
| 100 | .requires_symlinks = true, | 100 | .build_root = "test/link/macho/empty", |
| 101 | }); | 101 | .import = @import("link/macho/empty/build.zig"), |
| 102 | 102 | }, | |
| 103 | cases.addBuildFile("test/link/macho/bugs/13457/build.zig", .{ | 103 | .{ |
| 104 | .build_modes = true, | 104 | .build_root = "test/link/macho/entry", |
| 105 | .requires_symlinks = true, | 105 | .import = @import("link/macho/entry/build.zig"), |
| 106 | }); | 106 | }, |
| 107 | 107 | .{ | |
| 108 | cases.addBuildFile("test/link/macho/dead_strip/build.zig", .{ | 108 | .build_root = "test/link/macho/headerpad", |
| 109 | .build_modes = false, | 109 | .import = @import("link/macho/headerpad/build.zig"), |
| 110 | .requires_symlinks = true, | 110 | }, |
| 111 | }); | 111 | .{ |
| 112 | 112 | .build_root = "test/link/macho/linksection", | |
| 113 | cases.addBuildFile("test/link/macho/dead_strip_dylibs/build.zig", .{ | 113 | .import = @import("link/macho/linksection/build.zig"), |
| 114 | .build_modes = true, | 114 | }, |
| 115 | .requires_macos_sdk = true, | 115 | .{ |
| 116 | .requires_symlinks = true, | 116 | .build_root = "test/link/macho/needed_framework", |
| 117 | }); | 117 | .import = @import("link/macho/needed_framework/build.zig"), |
| 118 | 118 | }, | |
| 119 | cases.addBuildFile("test/link/macho/dylib/build.zig", .{ | 119 | .{ |
| 120 | .build_modes = true, | 120 | .build_root = "test/link/macho/needed_library", |
| 121 | .requires_symlinks = true, | 121 | .import = @import("link/macho/needed_library/build.zig"), |
| 122 | }); | 122 | }, |
| 123 | 123 | .{ | |
| 124 | cases.addBuildFile("test/link/macho/empty/build.zig", .{ | 124 | .build_root = "test/link/macho/objc", |
| 125 | .build_modes = true, | 125 | .import = @import("link/macho/objc/build.zig"), |
| 126 | .requires_symlinks = true, | 126 | }, |
| 127 | }); | 127 | .{ |
| 128 | 128 | .build_root = "test/link/macho/objcpp", | |
| 129 | cases.addBuildFile("test/link/macho/entry/build.zig", .{ | 129 | .import = @import("link/macho/objcpp/build.zig"), |
| 130 | .build_modes = true, | 130 | }, |
| 131 | .requires_symlinks = true, | 131 | .{ |
| 132 | }); | 132 | .build_root = "test/link/macho/pagezero", |
| 133 | 133 | .import = @import("link/macho/pagezero/build.zig"), | |
| 134 | cases.addBuildFile("test/link/macho/headerpad/build.zig", .{ | 134 | }, |
| 135 | .build_modes = true, | 135 | .{ |
| 136 | .requires_macos_sdk = true, | 136 | .build_root = "test/link/macho/search_strategy", |
| 137 | .requires_symlinks = true, | 137 | .import = @import("link/macho/search_strategy/build.zig"), |
| 138 | }); | 138 | }, |
| 139 | 139 | .{ | |
| 140 | cases.addBuildFile("test/link/macho/linksection/build.zig", .{ | 140 | .build_root = "test/link/macho/stack_size", |
| 141 | .build_modes = true, | 141 | .import = @import("link/macho/stack_size/build.zig"), |
| 142 | .requires_symlinks = true, | 142 | }, |
| 143 | }); | 143 | .{ |
| 144 | 144 | .build_root = "test/link/macho/strict_validation", | |
| 145 | cases.addBuildFile("test/link/macho/needed_framework/build.zig", .{ | 145 | .import = @import("link/macho/strict_validation/build.zig"), |
| 146 | .build_modes = true, | 146 | }, |
| 147 | .requires_macos_sdk = true, | 147 | .{ |
| 148 | .requires_symlinks = true, | 148 | .build_root = "test/link/macho/tls", |
| 149 | }); | 149 | .import = @import("link/macho/tls/build.zig"), |
| 150 | 150 | }, | |
| 151 | cases.addBuildFile("test/link/macho/needed_library/build.zig", .{ | 151 | .{ |
| 152 | .build_modes = true, | 152 | .build_root = "test/link/macho/unwind_info", |
| 153 | .requires_symlinks = true, | 153 | .import = @import("link/macho/unwind_info/build.zig"), |
| 154 | }); | 154 | }, |
| 155 | 155 | // TODO: re-enable this test. It currently has some incompatibilities with | |
| 156 | cases.addBuildFile("test/link/macho/objc/build.zig", .{ | 156 | // the new build system API. In particular, it depends on installing the build |
| 157 | .build_modes = true, | 157 | // artifacts, which should be unnecessary, and it has a custom build step that |
| 158 | .requires_macos_sdk = true, | 158 | // prints directly to stderr instead of failing the step with an error message. |
| 159 | .requires_symlinks = true, | 159 | //.{ |
| 160 | }); | 160 | // .build_root = "test/link/macho/uuid", |
| 161 | 161 | // .import = @import("link/macho/uuid/build.zig"), | |
| 162 | cases.addBuildFile("test/link/macho/objcpp/build.zig", .{ | 162 | //}, |
| 163 | .build_modes = true, | 163 | |
| 164 | .requires_macos_sdk = true, | 164 | .{ |
| 165 | .requires_symlinks = true, | 165 | .build_root = "test/link/macho/weak_library", |
| 166 | }); | 166 | .import = @import("link/macho/weak_library/build.zig"), |
| 167 | 167 | }, | |
| 168 | cases.addBuildFile("test/link/macho/pagezero/build.zig", .{ | 168 | .{ |
| 169 | .build_modes = false, | 169 | .build_root = "test/link/macho/weak_framework", |
| 170 | .requires_symlinks = true, | 170 | .import = @import("link/macho/weak_framework/build.zig"), |
| 171 | }); | 171 | }, |
| 172 | 172 | }; | |
| 173 | cases.addBuildFile("test/link/macho/search_strategy/build.zig", .{ | ||
| 174 | .build_modes = true, | ||
| 175 | .requires_symlinks = true, | ||
| 176 | }); | ||
| 177 | |||
| 178 | cases.addBuildFile("test/link/macho/stack_size/build.zig", .{ | ||
| 179 | .build_modes = true, | ||
| 180 | .requires_symlinks = true, | ||
| 181 | }); | ||
| 182 | |||
| 183 | cases.addBuildFile("test/link/macho/strict_validation/build.zig", .{ | ||
| 184 | .build_modes = true, | ||
| 185 | .requires_symlinks = true, | ||
| 186 | }); | ||
| 187 | |||
| 188 | cases.addBuildFile("test/link/macho/tls/build.zig", .{ | ||
| 189 | .build_modes = true, | ||
| 190 | .requires_symlinks = true, | ||
| 191 | }); | ||
| 192 | |||
| 193 | cases.addBuildFile("test/link/macho/unwind_info/build.zig", .{ | ||
| 194 | .build_modes = true, | ||
| 195 | .requires_symlinks = true, | ||
| 196 | }); | ||
| 197 | |||
| 198 | cases.addBuildFile("test/link/macho/uuid/build.zig", .{ | ||
| 199 | .build_modes = false, | ||
| 200 | .requires_symlinks = true, | ||
| 201 | }); | ||
| 202 | |||
| 203 | cases.addBuildFile("test/link/macho/weak_library/build.zig", .{ | ||
| 204 | .build_modes = true, | ||
| 205 | .requires_symlinks = true, | ||
| 206 | }); | ||
| 207 | |||
| 208 | cases.addBuildFile("test/link/macho/weak_framework/build.zig", .{ | ||
| 209 | .build_modes = true, | ||
| 210 | .requires_macos_sdk = true, | ||
| 211 | .requires_symlinks = true, | ||
| 212 | }); | ||
| 213 | } |
test/link/bss/build.zig+3-3| ... | @@ -1,17 +1,17 @@ | ... | @@ -1,17 +1,17 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn build(b: *std.Build) void { | 3 | pub fn build(b: *std.Build) void { |
| 4 | const optimize = b.standardOptimizeOption(.{}); | ||
| 5 | const test_step = b.step("test", "Test"); | 4 | const test_step = b.step("test", "Test"); |
| 5 | b.default_step = test_step; | ||
| 6 | 6 | ||
| 7 | const exe = b.addExecutable(.{ | 7 | const exe = b.addExecutable(.{ |
| 8 | .name = "bss", | 8 | .name = "bss", |
| 9 | .root_source_file = .{ .path = "main.zig" }, | 9 | .root_source_file = .{ .path = "main.zig" }, |
| 10 | .optimize = optimize, | 10 | .optimize = .Debug, |
| 11 | }); | 11 | }); |
| 12 | b.default_step.dependOn(&exe.step); | ||
| 13 | 12 | ||
| 14 | const run = exe.run(); | 13 | const run = exe.run(); |
| 15 | run.expectStdOutEqual("0, 1, 0\n"); | 14 | run.expectStdOutEqual("0, 1, 0\n"); |
| 15 | |||
| 16 | test_step.dependOn(&run.step); | 16 | test_step.dependOn(&run.step); |
| 17 | } | 17 | } |
test/link/bss/main.zig+1-1| ... | @@ -1,7 +1,7 @@ | ... | @@ -1,7 +1,7 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | // Stress test zerofill layout | 3 | // Stress test zerofill layout |
| 4 | var buffer: [0x1000000]u64 = undefined; | 4 | var buffer: [0x1000000]u64 = [1]u64{0} ** 0x1000000; |
| 5 | 5 | ||
| 6 | pub fn main() anyerror!void { | 6 | pub fn main() anyerror!void { |
| 7 | buffer[0x10] = 1; | 7 | buffer[0x10] = 1; |
test/link/common_symbols/build.zig+10-3| ... | @@ -1,8 +1,16 @@ | ... | @@ -1,8 +1,16 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn build(b: *std.Build) void { | 3 | pub fn build(b: *std.Build) void { |
| 4 | const optimize = b.standardOptimizeOption(.{}); | 4 | const test_step = b.step("test", "Test it"); |
| 5 | b.default_step = test_step; | ||
| 6 | |||
| 7 | add(b, test_step, .Debug); | ||
| 8 | add(b, test_step, .ReleaseFast); | ||
| 9 | add(b, test_step, .ReleaseSmall); | ||
| 10 | add(b, test_step, .ReleaseSafe); | ||
| 11 | } | ||
| 5 | 12 | ||
| 13 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 6 | const lib_a = b.addStaticLibrary(.{ | 14 | const lib_a = b.addStaticLibrary(.{ |
| 7 | .name = "a", | 15 | .name = "a", |
| 8 | .optimize = optimize, | 16 | .optimize = optimize, |
| ... | @@ -16,6 +24,5 @@ pub fn build(b: *std.Build) void { | ... | @@ -16,6 +24,5 @@ pub fn build(b: *std.Build) void { |
| 16 | }); | 24 | }); |
| 17 | test_exe.linkLibrary(lib_a); | 25 | test_exe.linkLibrary(lib_a); |
| 18 | 26 | ||
| 19 | const test_step = b.step("test", "Test it"); | 27 | test_step.dependOn(&test_exe.run().step); |
| 20 | test_step.dependOn(&test_exe.step); | ||
| 21 | } | 28 | } |
test/link/common_symbols_alignment/build.zig+11-6| ... | @@ -1,23 +1,28 @@ | ... | @@ -1,23 +1,28 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn build(b: *std.Build) void { | 3 | pub fn build(b: *std.Build) void { |
| 4 | const optimize = b.standardOptimizeOption(.{}); | 4 | const test_step = b.step("test", "Test it"); |
| 5 | const target = b.standardTargetOptions(.{}); | 5 | b.default_step = test_step; |
| 6 | |||
| 7 | add(b, test_step, .Debug); | ||
| 8 | add(b, test_step, .ReleaseFast); | ||
| 9 | add(b, test_step, .ReleaseSmall); | ||
| 10 | add(b, test_step, .ReleaseSafe); | ||
| 11 | } | ||
| 6 | 12 | ||
| 13 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 7 | const lib_a = b.addStaticLibrary(.{ | 14 | const lib_a = b.addStaticLibrary(.{ |
| 8 | .name = "a", | 15 | .name = "a", |
| 9 | .optimize = optimize, | 16 | .optimize = optimize, |
| 10 | .target = target, | 17 | .target = .{}, |
| 11 | }); | 18 | }); |
| 12 | lib_a.addCSourceFiles(&.{"a.c"}, &.{"-fcommon"}); | 19 | lib_a.addCSourceFiles(&.{"a.c"}, &.{"-fcommon"}); |
| 13 | 20 | ||
| 14 | const test_exe = b.addTest(.{ | 21 | const test_exe = b.addTest(.{ |
| 15 | .root_source_file = .{ .path = "main.zig" }, | 22 | .root_source_file = .{ .path = "main.zig" }, |
| 16 | .optimize = optimize, | 23 | .optimize = optimize, |
| 17 | .target = target, | ||
| 18 | }); | 24 | }); |
| 19 | test_exe.linkLibrary(lib_a); | 25 | test_exe.linkLibrary(lib_a); |
| 20 | 26 | ||
| 21 | const test_step = b.step("test", "Test it"); | 27 | test_step.dependOn(&test_exe.run().step); |
| 22 | test_step.dependOn(&test_exe.step); | ||
| 23 | } | 28 | } |
test/link/interdependent_static_c_libs/build.zig+12-7| ... | @@ -1,13 +1,20 @@ | ... | @@ -1,13 +1,20 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn build(b: *std.Build) void { | 3 | pub fn build(b: *std.Build) void { |
| 4 | const optimize = b.standardOptimizeOption(.{}); | 4 | const test_step = b.step("test", "Test it"); |
| 5 | const target = b.standardTargetOptions(.{}); | 5 | b.default_step = test_step; |
| 6 | |||
| 7 | add(b, test_step, .Debug); | ||
| 8 | add(b, test_step, .ReleaseFast); | ||
| 9 | add(b, test_step, .ReleaseSmall); | ||
| 10 | add(b, test_step, .ReleaseSafe); | ||
| 11 | } | ||
| 6 | 12 | ||
| 13 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 7 | const lib_a = b.addStaticLibrary(.{ | 14 | const lib_a = b.addStaticLibrary(.{ |
| 8 | .name = "a", | 15 | .name = "a", |
| 9 | .optimize = optimize, | 16 | .optimize = optimize, |
| 10 | .target = target, | 17 | .target = .{}, |
| 11 | }); | 18 | }); |
| 12 | lib_a.addCSourceFile("a.c", &[_][]const u8{}); | 19 | lib_a.addCSourceFile("a.c", &[_][]const u8{}); |
| 13 | lib_a.addIncludePath("."); | 20 | lib_a.addIncludePath("."); |
| ... | @@ -15,7 +22,7 @@ pub fn build(b: *std.Build) void { | ... | @@ -15,7 +22,7 @@ pub fn build(b: *std.Build) void { |
| 15 | const lib_b = b.addStaticLibrary(.{ | 22 | const lib_b = b.addStaticLibrary(.{ |
| 16 | .name = "b", | 23 | .name = "b", |
| 17 | .optimize = optimize, | 24 | .optimize = optimize, |
| 18 | .target = target, | 25 | .target = .{}, |
| 19 | }); | 26 | }); |
| 20 | lib_b.addCSourceFile("b.c", &[_][]const u8{}); | 27 | lib_b.addCSourceFile("b.c", &[_][]const u8{}); |
| 21 | lib_b.addIncludePath("."); | 28 | lib_b.addIncludePath("."); |
| ... | @@ -23,12 +30,10 @@ pub fn build(b: *std.Build) void { | ... | @@ -23,12 +30,10 @@ pub fn build(b: *std.Build) void { |
| 23 | const test_exe = b.addTest(.{ | 30 | const test_exe = b.addTest(.{ |
| 24 | .root_source_file = .{ .path = "main.zig" }, | 31 | .root_source_file = .{ .path = "main.zig" }, |
| 25 | .optimize = optimize, | 32 | .optimize = optimize, |
| 26 | .target = target, | ||
| 27 | }); | 33 | }); |
| 28 | test_exe.linkLibrary(lib_a); | 34 | test_exe.linkLibrary(lib_a); |
| 29 | test_exe.linkLibrary(lib_b); | 35 | test_exe.linkLibrary(lib_b); |
| 30 | test_exe.addIncludePath("."); | 36 | test_exe.addIncludePath("."); |
| 31 | 37 | ||
| 32 | const test_step = b.step("test", "Test it"); | 38 | test_step.dependOn(&test_exe.run().step); |
| 33 | test_step.dependOn(&test_exe.step); | ||
| 34 | } | 39 | } |
test/link/macho/bugs/13056/build.zig+12-4| ... | @@ -1,20 +1,28 @@ | ... | @@ -1,20 +1,28 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_macos_sdk = true; | ||
| 4 | pub const requires_symlinks = true; | ||
| 5 | |||
| 3 | pub fn build(b: *std.Build) void { | 6 | pub fn build(b: *std.Build) void { |
| 4 | const optimize = b.standardOptimizeOption(.{}); | 7 | const test_step = b.step("test", "Test it"); |
| 8 | b.default_step = test_step; | ||
| 9 | |||
| 10 | add(b, test_step, .Debug); | ||
| 11 | add(b, test_step, .ReleaseFast); | ||
| 12 | add(b, test_step, .ReleaseSmall); | ||
| 13 | add(b, test_step, .ReleaseSafe); | ||
| 14 | } | ||
| 5 | 15 | ||
| 16 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 6 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; | 17 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; |
| 7 | const target_info = std.zig.system.NativeTargetInfo.detect(target) catch unreachable; | 18 | const target_info = std.zig.system.NativeTargetInfo.detect(target) catch unreachable; |
| 8 | const sdk = std.zig.system.darwin.getDarwinSDK(b.allocator, target_info.target) orelse | 19 | const sdk = std.zig.system.darwin.getDarwinSDK(b.allocator, target_info.target) orelse |
| 9 | @panic("macOS SDK is required to run the test"); | 20 | @panic("macOS SDK is required to run the test"); |
| 10 | 21 | ||
| 11 | const test_step = b.step("test", "Test the program"); | ||
| 12 | |||
| 13 | const exe = b.addExecutable(.{ | 22 | const exe = b.addExecutable(.{ |
| 14 | .name = "test", | 23 | .name = "test", |
| 15 | .optimize = optimize, | 24 | .optimize = optimize, |
| 16 | }); | 25 | }); |
| 17 | b.default_step.dependOn(&exe.step); | ||
| 18 | exe.addIncludePath(std.fs.path.join(b.allocator, &.{ sdk.path, "/usr/include" }) catch unreachable); | 26 | exe.addIncludePath(std.fs.path.join(b.allocator, &.{ sdk.path, "/usr/include" }) catch unreachable); |
| 19 | exe.addIncludePath(std.fs.path.join(b.allocator, &.{ sdk.path, "/usr/include/c++/v1" }) catch unreachable); | 27 | exe.addIncludePath(std.fs.path.join(b.allocator, &.{ sdk.path, "/usr/include/c++/v1" }) catch unreachable); |
| 20 | exe.addCSourceFile("test.cpp", &.{ | 28 | exe.addCSourceFile("test.cpp", &.{ |
test/link/macho/bugs/13457/build.zig+16-4| ... | @@ -1,10 +1,19 @@ | ... | @@ -1,10 +1,19 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_symlinks = true; | ||
| 4 | |||
| 3 | pub fn build(b: *std.Build) void { | 5 | pub fn build(b: *std.Build) void { |
| 4 | const optimize = b.standardOptimizeOption(.{}); | 6 | const test_step = b.step("test", "Test it"); |
| 5 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; | 7 | b.default_step = test_step; |
| 8 | |||
| 9 | add(b, test_step, .Debug); | ||
| 10 | add(b, test_step, .ReleaseFast); | ||
| 11 | add(b, test_step, .ReleaseSmall); | ||
| 12 | add(b, test_step, .ReleaseSafe); | ||
| 13 | } | ||
| 6 | 14 | ||
| 7 | const test_step = b.step("test", "Test the program"); | 15 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { |
| 16 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; | ||
| 8 | 17 | ||
| 9 | const exe = b.addExecutable(.{ | 18 | const exe = b.addExecutable(.{ |
| 10 | .name = "test", | 19 | .name = "test", |
| ... | @@ -13,6 +22,9 @@ pub fn build(b: *std.Build) void { | ... | @@ -13,6 +22,9 @@ pub fn build(b: *std.Build) void { |
| 13 | .target = target, | 22 | .target = target, |
| 14 | }); | 23 | }); |
| 15 | 24 | ||
| 16 | const run = exe.runEmulatable(); | 25 | const run = b.addRunArtifact(exe); |
| 26 | run.skip_foreign_checks = true; | ||
| 27 | run.expectStdOutEqual(""); | ||
| 28 | |||
| 17 | test_step.dependOn(&run.step); | 29 | test_step.dependOn(&run.step); |
| 18 | } | 30 | } |
test/link/macho/dead_strip/build.zig+10-7| ... | @@ -1,17 +1,19 @@ | ... | @@ -1,17 +1,19 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_symlinks = true; | ||
| 4 | |||
| 3 | pub fn build(b: *std.Build) void { | 5 | pub fn build(b: *std.Build) void { |
| 4 | const optimize = b.standardOptimizeOption(.{}); | 6 | const optimize: std.builtin.OptimizeMode = .Debug; |
| 5 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; | 7 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; |
| 6 | 8 | ||
| 7 | const test_step = b.step("test", "Test the program"); | 9 | const test_step = b.step("test", "Test the program"); |
| 8 | test_step.dependOn(b.getInstallStep()); | 10 | b.default_step = test_step; |
| 9 | 11 | ||
| 10 | { | 12 | { |
| 11 | // Without -dead_strip, we expect `iAmUnused` symbol present | 13 | // Without -dead_strip, we expect `iAmUnused` symbol present |
| 12 | const exe = createScenario(b, optimize, target); | 14 | const exe = createScenario(b, optimize, target, "no-gc"); |
| 13 | 15 | ||
| 14 | const check = exe.checkObject(.macho); | 16 | const check = exe.checkObject(); |
| 15 | check.checkInSymtab(); | 17 | check.checkInSymtab(); |
| 16 | check.checkNext("{*} (__TEXT,__text) external _iAmUnused"); | 18 | check.checkNext("{*} (__TEXT,__text) external _iAmUnused"); |
| 17 | 19 | ||
| ... | @@ -22,10 +24,10 @@ pub fn build(b: *std.Build) void { | ... | @@ -22,10 +24,10 @@ pub fn build(b: *std.Build) void { |
| 22 | 24 | ||
| 23 | { | 25 | { |
| 24 | // With -dead_strip, no `iAmUnused` symbol should be present | 26 | // With -dead_strip, no `iAmUnused` symbol should be present |
| 25 | const exe = createScenario(b, optimize, target); | 27 | const exe = createScenario(b, optimize, target, "yes-gc"); |
| 26 | exe.link_gc_sections = true; | 28 | exe.link_gc_sections = true; |
| 27 | 29 | ||
| 28 | const check = exe.checkObject(.macho); | 30 | const check = exe.checkObject(); |
| 29 | check.checkInSymtab(); | 31 | check.checkInSymtab(); |
| 30 | check.checkNotPresent("{*} (__TEXT,__text) external _iAmUnused"); | 32 | check.checkNotPresent("{*} (__TEXT,__text) external _iAmUnused"); |
| 31 | 33 | ||
| ... | @@ -39,9 +41,10 @@ fn createScenario( | ... | @@ -39,9 +41,10 @@ fn createScenario( |
| 39 | b: *std.Build, | 41 | b: *std.Build, |
| 40 | optimize: std.builtin.OptimizeMode, | 42 | optimize: std.builtin.OptimizeMode, |
| 41 | target: std.zig.CrossTarget, | 43 | target: std.zig.CrossTarget, |
| 44 | name: []const u8, | ||
| 42 | ) *std.Build.CompileStep { | 45 | ) *std.Build.CompileStep { |
| 43 | const exe = b.addExecutable(.{ | 46 | const exe = b.addExecutable(.{ |
| 44 | .name = "test", | 47 | .name = name, |
| 45 | .optimize = optimize, | 48 | .optimize = optimize, |
| 46 | .target = target, | 49 | .target = target, |
| 47 | }); | 50 | }); |
test/link/macho/dead_strip_dylibs/build.zig+22-10| ... | @@ -1,16 +1,24 @@ | ... | @@ -1,16 +1,24 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_macos_sdk = true; | ||
| 4 | pub const requires_symlinks = true; | ||
| 5 | |||
| 3 | pub fn build(b: *std.Build) void { | 6 | pub fn build(b: *std.Build) void { |
| 4 | const optimize = b.standardOptimizeOption(.{}); | 7 | const test_step = b.step("test", "Test it"); |
| 8 | b.default_step = test_step; | ||
| 5 | 9 | ||
| 6 | const test_step = b.step("test", "Test the program"); | 10 | add(b, test_step, .Debug); |
| 7 | test_step.dependOn(b.getInstallStep()); | 11 | add(b, test_step, .ReleaseFast); |
| 12 | add(b, test_step, .ReleaseSmall); | ||
| 13 | add(b, test_step, .ReleaseSafe); | ||
| 14 | } | ||
| 8 | 15 | ||
| 16 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 9 | { | 17 | { |
| 10 | // Without -dead_strip_dylibs we expect `-la` to include liba.dylib in the final executable | 18 | // Without -dead_strip_dylibs we expect `-la` to include liba.dylib in the final executable |
| 11 | const exe = createScenario(b, optimize); | 19 | const exe = createScenario(b, optimize, "no-dead-strip"); |
| 12 | 20 | ||
| 13 | const check = exe.checkObject(.macho); | 21 | const check = exe.checkObject(); |
| 14 | check.checkStart("cmd LOAD_DYLIB"); | 22 | check.checkStart("cmd LOAD_DYLIB"); |
| 15 | check.checkNext("name {*}Cocoa"); | 23 | check.checkNext("name {*}Cocoa"); |
| 16 | 24 | ||
| ... | @@ -25,18 +33,22 @@ pub fn build(b: *std.Build) void { | ... | @@ -25,18 +33,22 @@ pub fn build(b: *std.Build) void { |
| 25 | 33 | ||
| 26 | { | 34 | { |
| 27 | // With -dead_strip_dylibs, we should include liba.dylib as it's unreachable | 35 | // With -dead_strip_dylibs, we should include liba.dylib as it's unreachable |
| 28 | const exe = createScenario(b, optimize); | 36 | const exe = createScenario(b, optimize, "yes-dead-strip"); |
| 29 | exe.dead_strip_dylibs = true; | 37 | exe.dead_strip_dylibs = true; |
| 30 | 38 | ||
| 31 | const run_cmd = exe.run(); | 39 | const run_cmd = b.addRunArtifact(exe); |
| 32 | run_cmd.expected_term = .{ .Exited = @bitCast(u8, @as(i8, -2)) }; // should fail | 40 | run_cmd.expectExitCode(@bitCast(u8, @as(i8, -2))); // should fail |
| 33 | test_step.dependOn(&run_cmd.step); | 41 | test_step.dependOn(&run_cmd.step); |
| 34 | } | 42 | } |
| 35 | } | 43 | } |
| 36 | 44 | ||
| 37 | fn createScenario(b: *std.Build, optimize: std.builtin.OptimizeMode) *std.Build.CompileStep { | 45 | fn createScenario( |
| 46 | b: *std.Build, | ||
| 47 | optimize: std.builtin.OptimizeMode, | ||
| 48 | name: []const u8, | ||
| 49 | ) *std.Build.CompileStep { | ||
| 38 | const exe = b.addExecutable(.{ | 50 | const exe = b.addExecutable(.{ |
| 39 | .name = "test", | 51 | .name = name, |
| 40 | .optimize = optimize, | 52 | .optimize = optimize, |
| 41 | }); | 53 | }); |
| 42 | exe.addCSourceFile("main.c", &[0][]const u8{}); | 54 | exe.addCSourceFile("main.c", &[0][]const u8{}); |
test/link/macho/dylib/build.zig+20-11| ... | @@ -1,11 +1,19 @@ | ... | @@ -1,11 +1,19 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_symlinks = true; | ||
| 4 | |||
| 3 | pub fn build(b: *std.Build) void { | 5 | pub fn build(b: *std.Build) void { |
| 4 | const optimize = b.standardOptimizeOption(.{}); | 6 | const test_step = b.step("test", "Test it"); |
| 5 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; | 7 | b.default_step = test_step; |
| 6 | 8 | ||
| 7 | const test_step = b.step("test", "Test"); | 9 | add(b, test_step, .Debug); |
| 8 | test_step.dependOn(b.getInstallStep()); | 10 | add(b, test_step, .ReleaseFast); |
| 11 | add(b, test_step, .ReleaseSmall); | ||
| 12 | add(b, test_step, .ReleaseSafe); | ||
| 13 | } | ||
| 14 | |||
| 15 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 16 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; | ||
| 9 | 17 | ||
| 10 | const dylib = b.addSharedLibrary(.{ | 18 | const dylib = b.addSharedLibrary(.{ |
| 11 | .name = "a", | 19 | .name = "a", |
| ... | @@ -15,9 +23,8 @@ pub fn build(b: *std.Build) void { | ... | @@ -15,9 +23,8 @@ pub fn build(b: *std.Build) void { |
| 15 | }); | 23 | }); |
| 16 | dylib.addCSourceFile("a.c", &.{}); | 24 | dylib.addCSourceFile("a.c", &.{}); |
| 17 | dylib.linkLibC(); | 25 | dylib.linkLibC(); |
| 18 | dylib.install(); | ||
| 19 | 26 | ||
| 20 | const check_dylib = dylib.checkObject(.macho); | 27 | const check_dylib = dylib.checkObject(); |
| 21 | check_dylib.checkStart("cmd ID_DYLIB"); | 28 | check_dylib.checkStart("cmd ID_DYLIB"); |
| 22 | check_dylib.checkNext("name @rpath/liba.dylib"); | 29 | check_dylib.checkNext("name @rpath/liba.dylib"); |
| 23 | check_dylib.checkNext("timestamp 2"); | 30 | check_dylib.checkNext("timestamp 2"); |
| ... | @@ -33,11 +40,11 @@ pub fn build(b: *std.Build) void { | ... | @@ -33,11 +40,11 @@ pub fn build(b: *std.Build) void { |
| 33 | }); | 40 | }); |
| 34 | exe.addCSourceFile("main.c", &.{}); | 41 | exe.addCSourceFile("main.c", &.{}); |
| 35 | exe.linkSystemLibrary("a"); | 42 | exe.linkSystemLibrary("a"); |
| 43 | exe.addLibraryPathDirectorySource(dylib.getOutputDirectorySource()); | ||
| 44 | exe.addRPathDirectorySource(dylib.getOutputDirectorySource()); | ||
| 36 | exe.linkLibC(); | 45 | exe.linkLibC(); |
| 37 | exe.addLibraryPath(b.pathFromRoot("zig-out/lib/")); | ||
| 38 | exe.addRPath(b.pathFromRoot("zig-out/lib")); | ||
| 39 | 46 | ||
| 40 | const check_exe = exe.checkObject(.macho); | 47 | const check_exe = exe.checkObject(); |
| 41 | check_exe.checkStart("cmd LOAD_DYLIB"); | 48 | check_exe.checkStart("cmd LOAD_DYLIB"); |
| 42 | check_exe.checkNext("name @rpath/liba.dylib"); | 49 | check_exe.checkNext("name @rpath/liba.dylib"); |
| 43 | check_exe.checkNext("timestamp 2"); | 50 | check_exe.checkNext("timestamp 2"); |
| ... | @@ -45,10 +52,12 @@ pub fn build(b: *std.Build) void { | ... | @@ -45,10 +52,12 @@ pub fn build(b: *std.Build) void { |
| 45 | check_exe.checkNext("compatibility version 10000"); | 52 | check_exe.checkNext("compatibility version 10000"); |
| 46 | 53 | ||
| 47 | check_exe.checkStart("cmd RPATH"); | 54 | check_exe.checkStart("cmd RPATH"); |
| 48 | check_exe.checkNext(std.fmt.allocPrint(b.allocator, "path {s}", .{b.pathFromRoot("zig-out/lib")}) catch unreachable); | 55 | // TODO check this (perhaps with `checkNextFileSource(dylib.getOutputDirectorySource())`) |
| 56 | //check_exe.checkNext(std.fmt.allocPrint(b.allocator, "path {s}", .{ | ||
| 57 | // b.pathFromRoot("zig-out/lib"), | ||
| 58 | //}) catch unreachable); | ||
| 49 | 59 | ||
| 50 | const run = check_exe.runAndCompare(); | 60 | const run = check_exe.runAndCompare(); |
| 51 | run.cwd = b.pathFromRoot("."); | ||
| 52 | run.expectStdOutEqual("Hello world"); | 61 | run.expectStdOutEqual("Hello world"); |
| 53 | test_step.dependOn(&run.step); | 62 | test_step.dependOn(&run.step); |
| 54 | } | 63 | } |
test/link/macho/empty/build.zig+14-5| ... | @@ -1,11 +1,19 @@ | ... | @@ -1,11 +1,19 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_symlinks = true; | ||
| 4 | |||
| 3 | pub fn build(b: *std.Build) void { | 5 | pub fn build(b: *std.Build) void { |
| 4 | const optimize = b.standardOptimizeOption(.{}); | 6 | const test_step = b.step("test", "Test it"); |
| 5 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; | 7 | b.default_step = test_step; |
| 6 | 8 | ||
| 7 | const test_step = b.step("test", "Test the program"); | 9 | add(b, test_step, .Debug); |
| 8 | test_step.dependOn(b.getInstallStep()); | 10 | add(b, test_step, .ReleaseFast); |
| 11 | add(b, test_step, .ReleaseSmall); | ||
| 12 | add(b, test_step, .ReleaseSafe); | ||
| 13 | } | ||
| 14 | |||
| 15 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 16 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; | ||
| 9 | 17 | ||
| 10 | const exe = b.addExecutable(.{ | 18 | const exe = b.addExecutable(.{ |
| 11 | .name = "test", | 19 | .name = "test", |
| ... | @@ -16,7 +24,8 @@ pub fn build(b: *std.Build) void { | ... | @@ -16,7 +24,8 @@ pub fn build(b: *std.Build) void { |
| 16 | exe.addCSourceFile("empty.c", &[0][]const u8{}); | 24 | exe.addCSourceFile("empty.c", &[0][]const u8{}); |
| 17 | exe.linkLibC(); | 25 | exe.linkLibC(); |
| 18 | 26 | ||
| 19 | const run_cmd = std.Build.EmulatableRunStep.create(b, "run", exe); | 27 | const run_cmd = b.addRunArtifact(exe); |
| 28 | run_cmd.skip_foreign_checks = true; | ||
| 20 | run_cmd.expectStdOutEqual("Hello!\n"); | 29 | run_cmd.expectStdOutEqual("Hello!\n"); |
| 21 | test_step.dependOn(&run_cmd.step); | 30 | test_step.dependOn(&run_cmd.step); |
| 22 | } | 31 | } |
test/link/macho/entry/build.zig+11-4| ... | @@ -1,11 +1,18 @@ | ... | @@ -1,11 +1,18 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_symlinks = true; | ||
| 4 | |||
| 3 | pub fn build(b: *std.Build) void { | 5 | pub fn build(b: *std.Build) void { |
| 4 | const optimize = b.standardOptimizeOption(.{}); | 6 | const test_step = b.step("test", "Test it"); |
| 7 | b.default_step = test_step; | ||
| 5 | 8 | ||
| 6 | const test_step = b.step("test", "Test"); | 9 | add(b, test_step, .Debug); |
| 7 | test_step.dependOn(b.getInstallStep()); | 10 | add(b, test_step, .ReleaseFast); |
| 11 | add(b, test_step, .ReleaseSmall); | ||
| 12 | add(b, test_step, .ReleaseSafe); | ||
| 13 | } | ||
| 8 | 14 | ||
| 15 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 9 | const exe = b.addExecutable(.{ | 16 | const exe = b.addExecutable(.{ |
| 10 | .name = "main", | 17 | .name = "main", |
| 11 | .optimize = optimize, | 18 | .optimize = optimize, |
| ... | @@ -15,7 +22,7 @@ pub fn build(b: *std.Build) void { | ... | @@ -15,7 +22,7 @@ pub fn build(b: *std.Build) void { |
| 15 | exe.linkLibC(); | 22 | exe.linkLibC(); |
| 16 | exe.entry_symbol_name = "_non_main"; | 23 | exe.entry_symbol_name = "_non_main"; |
| 17 | 24 | ||
| 18 | const check_exe = exe.checkObject(.macho); | 25 | const check_exe = exe.checkObject(); |
| 19 | 26 | ||
| 20 | check_exe.checkStart("segname __TEXT"); | 27 | check_exe.checkStart("segname __TEXT"); |
| 21 | check_exe.checkNext("vmaddr {vmaddr}"); | 28 | check_exe.checkNext("vmaddr {vmaddr}"); |
test/link/macho/headerpad/build.zig+25-13| ... | @@ -1,18 +1,26 @@ | ... | @@ -1,18 +1,26 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | 3 | ||
| 4 | pub const requires_symlinks = true; | ||
| 5 | pub const requires_macos_sdk = true; | ||
| 6 | |||
| 4 | pub fn build(b: *std.Build) void { | 7 | pub fn build(b: *std.Build) void { |
| 5 | const optimize = b.standardOptimizeOption(.{}); | 8 | const test_step = b.step("test", "Test it"); |
| 9 | b.default_step = test_step; | ||
| 6 | 10 | ||
| 7 | const test_step = b.step("test", "Test"); | 11 | add(b, test_step, .Debug); |
| 8 | test_step.dependOn(b.getInstallStep()); | 12 | add(b, test_step, .ReleaseFast); |
| 13 | add(b, test_step, .ReleaseSmall); | ||
| 14 | add(b, test_step, .ReleaseSafe); | ||
| 15 | } | ||
| 9 | 16 | ||
| 17 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 10 | { | 18 | { |
| 11 | // Test -headerpad_max_install_names | 19 | // Test -headerpad_max_install_names |
| 12 | const exe = simpleExe(b, optimize); | 20 | const exe = simpleExe(b, optimize, "headerpad_max_install_names"); |
| 13 | exe.headerpad_max_install_names = true; | 21 | exe.headerpad_max_install_names = true; |
| 14 | 22 | ||
| 15 | const check = exe.checkObject(.macho); | 23 | const check = exe.checkObject(); |
| 16 | check.checkStart("sectname __text"); | 24 | check.checkStart("sectname __text"); |
| 17 | check.checkNext("offset {offset}"); | 25 | check.checkNext("offset {offset}"); |
| 18 | 26 | ||
| ... | @@ -34,10 +42,10 @@ pub fn build(b: *std.Build) void { | ... | @@ -34,10 +42,10 @@ pub fn build(b: *std.Build) void { |
| 34 | 42 | ||
| 35 | { | 43 | { |
| 36 | // Test -headerpad | 44 | // Test -headerpad |
| 37 | const exe = simpleExe(b, optimize); | 45 | const exe = simpleExe(b, optimize, "headerpad"); |
| 38 | exe.headerpad_size = 0x10000; | 46 | exe.headerpad_size = 0x10000; |
| 39 | 47 | ||
| 40 | const check = exe.checkObject(.macho); | 48 | const check = exe.checkObject(); |
| 41 | check.checkStart("sectname __text"); | 49 | check.checkStart("sectname __text"); |
| 42 | check.checkNext("offset {offset}"); | 50 | check.checkNext("offset {offset}"); |
| 43 | check.checkComputeCompare("offset", .{ .op = .gte, .value = .{ .literal = 0x10000 } }); | 51 | check.checkComputeCompare("offset", .{ .op = .gte, .value = .{ .literal = 0x10000 } }); |
| ... | @@ -50,11 +58,11 @@ pub fn build(b: *std.Build) void { | ... | @@ -50,11 +58,11 @@ pub fn build(b: *std.Build) void { |
| 50 | 58 | ||
| 51 | { | 59 | { |
| 52 | // Test both flags with -headerpad overriding -headerpad_max_install_names | 60 | // Test both flags with -headerpad overriding -headerpad_max_install_names |
| 53 | const exe = simpleExe(b, optimize); | 61 | const exe = simpleExe(b, optimize, "headerpad_overriding"); |
| 54 | exe.headerpad_max_install_names = true; | 62 | exe.headerpad_max_install_names = true; |
| 55 | exe.headerpad_size = 0x10000; | 63 | exe.headerpad_size = 0x10000; |
| 56 | 64 | ||
| 57 | const check = exe.checkObject(.macho); | 65 | const check = exe.checkObject(); |
| 58 | check.checkStart("sectname __text"); | 66 | check.checkStart("sectname __text"); |
| 59 | check.checkNext("offset {offset}"); | 67 | check.checkNext("offset {offset}"); |
| 60 | check.checkComputeCompare("offset", .{ .op = .gte, .value = .{ .literal = 0x10000 } }); | 68 | check.checkComputeCompare("offset", .{ .op = .gte, .value = .{ .literal = 0x10000 } }); |
| ... | @@ -67,11 +75,11 @@ pub fn build(b: *std.Build) void { | ... | @@ -67,11 +75,11 @@ pub fn build(b: *std.Build) void { |
| 67 | 75 | ||
| 68 | { | 76 | { |
| 69 | // Test both flags with -headerpad_max_install_names overriding -headerpad | 77 | // Test both flags with -headerpad_max_install_names overriding -headerpad |
| 70 | const exe = simpleExe(b, optimize); | 78 | const exe = simpleExe(b, optimize, "headerpad_max_install_names_overriding"); |
| 71 | exe.headerpad_size = 0x1000; | 79 | exe.headerpad_size = 0x1000; |
| 72 | exe.headerpad_max_install_names = true; | 80 | exe.headerpad_max_install_names = true; |
| 73 | 81 | ||
| 74 | const check = exe.checkObject(.macho); | 82 | const check = exe.checkObject(); |
| 75 | check.checkStart("sectname __text"); | 83 | check.checkStart("sectname __text"); |
| 76 | check.checkNext("offset {offset}"); | 84 | check.checkNext("offset {offset}"); |
| 77 | 85 | ||
| ... | @@ -92,9 +100,13 @@ pub fn build(b: *std.Build) void { | ... | @@ -92,9 +100,13 @@ pub fn build(b: *std.Build) void { |
| 92 | } | 100 | } |
| 93 | } | 101 | } |
| 94 | 102 | ||
| 95 | fn simpleExe(b: *std.Build, optimize: std.builtin.OptimizeMode) *std.Build.CompileStep { | 103 | fn simpleExe( |
| 104 | b: *std.Build, | ||
| 105 | optimize: std.builtin.OptimizeMode, | ||
| 106 | name: []const u8, | ||
| 107 | ) *std.Build.CompileStep { | ||
| 96 | const exe = b.addExecutable(.{ | 108 | const exe = b.addExecutable(.{ |
| 97 | .name = "main", | 109 | .name = name, |
| 98 | .optimize = optimize, | 110 | .optimize = optimize, |
| 99 | }); | 111 | }); |
| 100 | exe.addCSourceFile("main.c", &.{}); | 112 | exe.addCSourceFile("main.c", &.{}); |
test/link/macho/linksection/build.zig+13-5| ... | @@ -1,11 +1,19 @@ | ... | @@ -1,11 +1,19 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_symlinks = true; | ||
| 4 | |||
| 3 | pub fn build(b: *std.Build) void { | 5 | pub fn build(b: *std.Build) void { |
| 4 | const optimize = b.standardOptimizeOption(.{}); | 6 | const test_step = b.step("test", "Test it"); |
| 5 | const target = std.zig.CrossTarget{ .os_tag = .macos }; | 7 | b.default_step = test_step; |
| 6 | 8 | ||
| 7 | const test_step = b.step("test", "Test"); | 9 | add(b, test_step, .Debug); |
| 8 | test_step.dependOn(b.getInstallStep()); | 10 | add(b, test_step, .ReleaseFast); |
| 11 | add(b, test_step, .ReleaseSmall); | ||
| 12 | add(b, test_step, .ReleaseSafe); | ||
| 13 | } | ||
| 14 | |||
| 15 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 16 | const target = std.zig.CrossTarget{ .os_tag = .macos }; | ||
| 9 | 17 | ||
| 10 | const obj = b.addObject(.{ | 18 | const obj = b.addObject(.{ |
| 11 | .name = "test", | 19 | .name = "test", |
| ... | @@ -14,7 +22,7 @@ pub fn build(b: *std.Build) void { | ... | @@ -14,7 +22,7 @@ pub fn build(b: *std.Build) void { |
| 14 | .target = target, | 22 | .target = target, |
| 15 | }); | 23 | }); |
| 16 | 24 | ||
| 17 | const check = obj.checkObject(.macho); | 25 | const check = obj.checkObject(); |
| 18 | 26 | ||
| 19 | check.checkInSymtab(); | 27 | check.checkInSymtab(); |
| 20 | check.checkNext("{*} (__DATA,__TestGlobal) external _test_global"); | 28 | check.checkNext("{*} (__DATA,__TestGlobal) external _test_global"); |
test/link/macho/needed_framework/build.zig+12-4| ... | @@ -1,11 +1,19 @@ | ... | @@ -1,11 +1,19 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_symlinks = true; | ||
| 4 | pub const requires_macos_sdk = true; | ||
| 5 | |||
| 3 | pub fn build(b: *std.Build) void { | 6 | pub fn build(b: *std.Build) void { |
| 4 | const optimize = b.standardOptimizeOption(.{}); | 7 | const test_step = b.step("test", "Test it"); |
| 8 | b.default_step = test_step; | ||
| 5 | 9 | ||
| 6 | const test_step = b.step("test", "Test the program"); | 10 | add(b, test_step, .Debug); |
| 7 | test_step.dependOn(b.getInstallStep()); | 11 | add(b, test_step, .ReleaseFast); |
| 12 | add(b, test_step, .ReleaseSmall); | ||
| 13 | add(b, test_step, .ReleaseSafe); | ||
| 14 | } | ||
| 8 | 15 | ||
| 16 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 9 | // -dead_strip_dylibs | 17 | // -dead_strip_dylibs |
| 10 | // -needed_framework Cocoa | 18 | // -needed_framework Cocoa |
| 11 | const exe = b.addExecutable(.{ | 19 | const exe = b.addExecutable(.{ |
| ... | @@ -17,7 +25,7 @@ pub fn build(b: *std.Build) void { | ... | @@ -17,7 +25,7 @@ pub fn build(b: *std.Build) void { |
| 17 | exe.linkFrameworkNeeded("Cocoa"); | 25 | exe.linkFrameworkNeeded("Cocoa"); |
| 18 | exe.dead_strip_dylibs = true; | 26 | exe.dead_strip_dylibs = true; |
| 19 | 27 | ||
| 20 | const check = exe.checkObject(.macho); | 28 | const check = exe.checkObject(); |
| 21 | check.checkStart("cmd LOAD_DYLIB"); | 29 | check.checkStart("cmd LOAD_DYLIB"); |
| 22 | check.checkNext("name {*}Cocoa"); | 30 | check.checkNext("name {*}Cocoa"); |
| 23 | test_step.dependOn(&check.step); | 31 | test_step.dependOn(&check.step); |
test/link/macho/needed_library/build.zig+16-8| ... | @@ -1,11 +1,19 @@ | ... | @@ -1,11 +1,19 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_symlinks = true; | ||
| 4 | |||
| 3 | pub fn build(b: *std.Build) void { | 5 | pub fn build(b: *std.Build) void { |
| 4 | const optimize = b.standardOptimizeOption(.{}); | 6 | const test_step = b.step("test", "Test it"); |
| 5 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; | 7 | b.default_step = test_step; |
| 6 | 8 | ||
| 7 | const test_step = b.step("test", "Test the program"); | 9 | add(b, test_step, .Debug); |
| 8 | test_step.dependOn(b.getInstallStep()); | 10 | add(b, test_step, .ReleaseFast); |
| 11 | add(b, test_step, .ReleaseSmall); | ||
| 12 | add(b, test_step, .ReleaseSafe); | ||
| 13 | } | ||
| 14 | |||
| 15 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 16 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; | ||
| 9 | 17 | ||
| 10 | const dylib = b.addSharedLibrary(.{ | 18 | const dylib = b.addSharedLibrary(.{ |
| 11 | .name = "a", | 19 | .name = "a", |
| ... | @@ -15,7 +23,6 @@ pub fn build(b: *std.Build) void { | ... | @@ -15,7 +23,6 @@ pub fn build(b: *std.Build) void { |
| 15 | }); | 23 | }); |
| 16 | dylib.addCSourceFile("a.c", &.{}); | 24 | dylib.addCSourceFile("a.c", &.{}); |
| 17 | dylib.linkLibC(); | 25 | dylib.linkLibC(); |
| 18 | dylib.install(); | ||
| 19 | 26 | ||
| 20 | // -dead_strip_dylibs | 27 | // -dead_strip_dylibs |
| 21 | // -needed-la | 28 | // -needed-la |
| ... | @@ -27,14 +34,15 @@ pub fn build(b: *std.Build) void { | ... | @@ -27,14 +34,15 @@ pub fn build(b: *std.Build) void { |
| 27 | exe.addCSourceFile("main.c", &[0][]const u8{}); | 34 | exe.addCSourceFile("main.c", &[0][]const u8{}); |
| 28 | exe.linkLibC(); | 35 | exe.linkLibC(); |
| 29 | exe.linkSystemLibraryNeeded("a"); | 36 | exe.linkSystemLibraryNeeded("a"); |
| 30 | exe.addLibraryPath(b.pathFromRoot("zig-out/lib")); | 37 | exe.addLibraryPathDirectorySource(dylib.getOutputDirectorySource()); |
| 31 | exe.addRPath(b.pathFromRoot("zig-out/lib")); | 38 | exe.addRPathDirectorySource(dylib.getOutputDirectorySource()); |
| 32 | exe.dead_strip_dylibs = true; | 39 | exe.dead_strip_dylibs = true; |
| 33 | 40 | ||
| 34 | const check = exe.checkObject(.macho); | 41 | const check = exe.checkObject(); |
| 35 | check.checkStart("cmd LOAD_DYLIB"); | 42 | check.checkStart("cmd LOAD_DYLIB"); |
| 36 | check.checkNext("name @rpath/liba.dylib"); | 43 | check.checkNext("name @rpath/liba.dylib"); |
| 37 | 44 | ||
| 38 | const run_cmd = check.runAndCompare(); | 45 | const run_cmd = check.runAndCompare(); |
| 46 | run_cmd.expectStdOutEqual(""); | ||
| 39 | test_step.dependOn(&run_cmd.step); | 47 | test_step.dependOn(&run_cmd.step); |
| 40 | } | 48 | } |
test/link/macho/objc/build.zig+14-3| ... | @@ -1,10 +1,19 @@ | ... | @@ -1,10 +1,19 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_symlinks = true; | ||
| 4 | pub const requires_macos_sdk = true; | ||
| 5 | |||
| 3 | pub fn build(b: *std.Build) void { | 6 | pub fn build(b: *std.Build) void { |
| 4 | const optimize = b.standardOptimizeOption(.{}); | 7 | const test_step = b.step("test", "Test it"); |
| 8 | b.default_step = test_step; | ||
| 5 | 9 | ||
| 6 | const test_step = b.step("test", "Test the program"); | 10 | add(b, test_step, .Debug); |
| 11 | add(b, test_step, .ReleaseFast); | ||
| 12 | add(b, test_step, .ReleaseSmall); | ||
| 13 | add(b, test_step, .ReleaseSafe); | ||
| 14 | } | ||
| 7 | 15 | ||
| 16 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 8 | const exe = b.addExecutable(.{ | 17 | const exe = b.addExecutable(.{ |
| 9 | .name = "test", | 18 | .name = "test", |
| 10 | .optimize = optimize, | 19 | .optimize = optimize, |
| ... | @@ -17,6 +26,8 @@ pub fn build(b: *std.Build) void { | ... | @@ -17,6 +26,8 @@ pub fn build(b: *std.Build) void { |
| 17 | // populate paths to the sysroot here. | 26 | // populate paths to the sysroot here. |
| 18 | exe.linkFramework("Foundation"); | 27 | exe.linkFramework("Foundation"); |
| 19 | 28 | ||
| 20 | const run_cmd = std.Build.EmulatableRunStep.create(b, "run", exe); | 29 | const run_cmd = b.addRunArtifact(exe); |
| 30 | run_cmd.skip_foreign_checks = true; | ||
| 31 | run_cmd.expectStdOutEqual(""); | ||
| 21 | test_step.dependOn(&run_cmd.step); | 32 | test_step.dependOn(&run_cmd.step); |
| 22 | } | 33 | } |
test/link/macho/objcpp/build.zig+11-2| ... | @@ -1,10 +1,19 @@ | ... | @@ -1,10 +1,19 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_symlinks = true; | ||
| 4 | pub const requires_macos_sdk = true; | ||
| 5 | |||
| 3 | pub fn build(b: *std.Build) void { | 6 | pub fn build(b: *std.Build) void { |
| 4 | const optimize = b.standardOptimizeOption(.{}); | 7 | const test_step = b.step("test", "Test it"); |
| 8 | b.default_step = test_step; | ||
| 5 | 9 | ||
| 6 | const test_step = b.step("test", "Test the program"); | 10 | add(b, test_step, .Debug); |
| 11 | add(b, test_step, .ReleaseFast); | ||
| 12 | add(b, test_step, .ReleaseSmall); | ||
| 13 | add(b, test_step, .ReleaseSafe); | ||
| 14 | } | ||
| 7 | 15 | ||
| 16 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 8 | const exe = b.addExecutable(.{ | 17 | const exe = b.addExecutable(.{ |
| 9 | .name = "test", | 18 | .name = "test", |
| 10 | .optimize = optimize, | 19 | .optimize = optimize, |
test/link/macho/pagezero/build.zig+8-6| ... | @@ -1,11 +1,13 @@ | ... | @@ -1,11 +1,13 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_symlinks = true; | ||
| 4 | |||
| 3 | pub fn build(b: *std.Build) void { | 5 | pub fn build(b: *std.Build) void { |
| 4 | const optimize = b.standardOptimizeOption(.{}); | 6 | const test_step = b.step("test", "Test it"); |
| 5 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; | 7 | b.default_step = test_step; |
| 6 | 8 | ||
| 7 | const test_step = b.step("test", "Test"); | 9 | const optimize: std.builtin.OptimizeMode = .Debug; |
| 8 | test_step.dependOn(b.getInstallStep()); | 10 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; |
| 9 | 11 | ||
| 10 | { | 12 | { |
| 11 | const exe = b.addExecutable(.{ | 13 | const exe = b.addExecutable(.{ |
| ... | @@ -17,7 +19,7 @@ pub fn build(b: *std.Build) void { | ... | @@ -17,7 +19,7 @@ pub fn build(b: *std.Build) void { |
| 17 | exe.linkLibC(); | 19 | exe.linkLibC(); |
| 18 | exe.pagezero_size = 0x4000; | 20 | exe.pagezero_size = 0x4000; |
| 19 | 21 | ||
| 20 | const check = exe.checkObject(.macho); | 22 | const check = exe.checkObject(); |
| 21 | check.checkStart("LC 0"); | 23 | check.checkStart("LC 0"); |
| 22 | check.checkNext("segname __PAGEZERO"); | 24 | check.checkNext("segname __PAGEZERO"); |
| 23 | check.checkNext("vmaddr 0"); | 25 | check.checkNext("vmaddr 0"); |
| ... | @@ -39,7 +41,7 @@ pub fn build(b: *std.Build) void { | ... | @@ -39,7 +41,7 @@ pub fn build(b: *std.Build) void { |
| 39 | exe.linkLibC(); | 41 | exe.linkLibC(); |
| 40 | exe.pagezero_size = 0; | 42 | exe.pagezero_size = 0; |
| 41 | 43 | ||
| 42 | const check = exe.checkObject(.macho); | 44 | const check = exe.checkObject(); |
| 43 | check.checkStart("LC 0"); | 45 | check.checkStart("LC 0"); |
| 44 | check.checkNext("segname __TEXT"); | 46 | check.checkNext("segname __TEXT"); |
| 45 | check.checkNext("vmaddr 0"); | 47 | check.checkNext("vmaddr 0"); |
test/link/macho/search_strategy/build.zig+26-20| ... | @@ -1,34 +1,41 @@ | ... | @@ -1,34 +1,41 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_symlinks = true; | ||
| 4 | |||
| 3 | pub fn build(b: *std.Build) void { | 5 | pub fn build(b: *std.Build) void { |
| 4 | const optimize = b.standardOptimizeOption(.{}); | 6 | const test_step = b.step("test", "Test it"); |
| 5 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; | 7 | b.default_step = test_step; |
| 6 | 8 | ||
| 7 | const test_step = b.step("test", "Test"); | 9 | add(b, test_step, .Debug); |
| 8 | test_step.dependOn(b.getInstallStep()); | 10 | add(b, test_step, .ReleaseFast); |
| 11 | add(b, test_step, .ReleaseSmall); | ||
| 12 | add(b, test_step, .ReleaseSafe); | ||
| 13 | } | ||
| 14 | |||
| 15 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 16 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; | ||
| 9 | 17 | ||
| 10 | { | 18 | { |
| 11 | // -search_dylibs_first | 19 | // -search_dylibs_first |
| 12 | const exe = createScenario(b, optimize, target); | 20 | const exe = createScenario(b, optimize, target, "search_dylibs_first"); |
| 13 | exe.search_strategy = .dylibs_first; | 21 | exe.search_strategy = .dylibs_first; |
| 14 | 22 | ||
| 15 | const check = exe.checkObject(.macho); | 23 | const check = exe.checkObject(); |
| 16 | check.checkStart("cmd LOAD_DYLIB"); | 24 | check.checkStart("cmd LOAD_DYLIB"); |
| 17 | check.checkNext("name @rpath/liba.dylib"); | 25 | check.checkNext("name @rpath/libsearch_dylibs_first.dylib"); |
| 18 | 26 | ||
| 19 | const run = check.runAndCompare(); | 27 | const run = check.runAndCompare(); |
| 20 | run.cwd = b.pathFromRoot("."); | ||
| 21 | run.expectStdOutEqual("Hello world"); | 28 | run.expectStdOutEqual("Hello world"); |
| 22 | test_step.dependOn(&run.step); | 29 | test_step.dependOn(&run.step); |
| 23 | } | 30 | } |
| 24 | 31 | ||
| 25 | { | 32 | { |
| 26 | // -search_paths_first | 33 | // -search_paths_first |
| 27 | const exe = createScenario(b, optimize, target); | 34 | const exe = createScenario(b, optimize, target, "search_paths_first"); |
| 28 | exe.search_strategy = .paths_first; | 35 | exe.search_strategy = .paths_first; |
| 29 | 36 | ||
| 30 | const run = std.Build.EmulatableRunStep.create(b, "run", exe); | 37 | const run = b.addRunArtifact(exe); |
| 31 | run.cwd = b.pathFromRoot("."); | 38 | run.skip_foreign_checks = true; |
| 32 | run.expectStdOutEqual("Hello world"); | 39 | run.expectStdOutEqual("Hello world"); |
| 33 | test_step.dependOn(&run.step); | 40 | test_step.dependOn(&run.step); |
| 34 | } | 41 | } |
| ... | @@ -38,9 +45,10 @@ fn createScenario( | ... | @@ -38,9 +45,10 @@ fn createScenario( |
| 38 | b: *std.Build, | 45 | b: *std.Build, |
| 39 | optimize: std.builtin.OptimizeMode, | 46 | optimize: std.builtin.OptimizeMode, |
| 40 | target: std.zig.CrossTarget, | 47 | target: std.zig.CrossTarget, |
| 48 | name: []const u8, | ||
| 41 | ) *std.Build.CompileStep { | 49 | ) *std.Build.CompileStep { |
| 42 | const static = b.addStaticLibrary(.{ | 50 | const static = b.addStaticLibrary(.{ |
| 43 | .name = "a", | 51 | .name = name, |
| 44 | .optimize = optimize, | 52 | .optimize = optimize, |
| 45 | .target = target, | 53 | .target = target, |
| 46 | }); | 54 | }); |
| ... | @@ -49,10 +57,9 @@ fn createScenario( | ... | @@ -49,10 +57,9 @@ fn createScenario( |
| 49 | static.override_dest_dir = std.Build.InstallDir{ | 57 | static.override_dest_dir = std.Build.InstallDir{ |
| 50 | .custom = "static", | 58 | .custom = "static", |
| 51 | }; | 59 | }; |
| 52 | static.install(); | ||
| 53 | 60 | ||
| 54 | const dylib = b.addSharedLibrary(.{ | 61 | const dylib = b.addSharedLibrary(.{ |
| 55 | .name = "a", | 62 | .name = name, |
| 56 | .version = .{ .major = 1, .minor = 0 }, | 63 | .version = .{ .major = 1, .minor = 0 }, |
| 57 | .optimize = optimize, | 64 | .optimize = optimize, |
| 58 | .target = target, | 65 | .target = target, |
| ... | @@ -62,18 +69,17 @@ fn createScenario( | ... | @@ -62,18 +69,17 @@ fn createScenario( |
| 62 | dylib.override_dest_dir = std.Build.InstallDir{ | 69 | dylib.override_dest_dir = std.Build.InstallDir{ |
| 63 | .custom = "dynamic", | 70 | .custom = "dynamic", |
| 64 | }; | 71 | }; |
| 65 | dylib.install(); | ||
| 66 | 72 | ||
| 67 | const exe = b.addExecutable(.{ | 73 | const exe = b.addExecutable(.{ |
| 68 | .name = "main", | 74 | .name = name, |
| 69 | .optimize = optimize, | 75 | .optimize = optimize, |
| 70 | .target = target, | 76 | .target = target, |
| 71 | }); | 77 | }); |
| 72 | exe.addCSourceFile("main.c", &.{}); | 78 | exe.addCSourceFile("main.c", &.{}); |
| 73 | exe.linkSystemLibraryName("a"); | 79 | exe.linkSystemLibraryName(name); |
| 74 | exe.linkLibC(); | 80 | exe.linkLibC(); |
| 75 | exe.addLibraryPath(b.pathFromRoot("zig-out/static")); | 81 | exe.addLibraryPathDirectorySource(static.getOutputDirectorySource()); |
| 76 | exe.addLibraryPath(b.pathFromRoot("zig-out/dynamic")); | 82 | exe.addLibraryPathDirectorySource(dylib.getOutputDirectorySource()); |
| 77 | exe.addRPath(b.pathFromRoot("zig-out/dynamic")); | 83 | exe.addRPathDirectorySource(dylib.getOutputDirectorySource()); |
| 78 | return exe; | 84 | return exe; |
| 79 | } | 85 | } |
test/link/macho/stack_size/build.zig+14-5| ... | @@ -1,11 +1,19 @@ | ... | @@ -1,11 +1,19 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_symlinks = true; | ||
| 4 | |||
| 3 | pub fn build(b: *std.Build) void { | 5 | pub fn build(b: *std.Build) void { |
| 4 | const optimize = b.standardOptimizeOption(.{}); | 6 | const test_step = b.step("test", "Test it"); |
| 5 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; | 7 | b.default_step = test_step; |
| 6 | 8 | ||
| 7 | const test_step = b.step("test", "Test"); | 9 | add(b, test_step, .Debug); |
| 8 | test_step.dependOn(b.getInstallStep()); | 10 | add(b, test_step, .ReleaseFast); |
| 11 | add(b, test_step, .ReleaseSmall); | ||
| 12 | add(b, test_step, .ReleaseSafe); | ||
| 13 | } | ||
| 14 | |||
| 15 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 16 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; | ||
| 9 | 17 | ||
| 10 | const exe = b.addExecutable(.{ | 18 | const exe = b.addExecutable(.{ |
| 11 | .name = "main", | 19 | .name = "main", |
| ... | @@ -16,10 +24,11 @@ pub fn build(b: *std.Build) void { | ... | @@ -16,10 +24,11 @@ pub fn build(b: *std.Build) void { |
| 16 | exe.linkLibC(); | 24 | exe.linkLibC(); |
| 17 | exe.stack_size = 0x100000000; | 25 | exe.stack_size = 0x100000000; |
| 18 | 26 | ||
| 19 | const check_exe = exe.checkObject(.macho); | 27 | const check_exe = exe.checkObject(); |
| 20 | check_exe.checkStart("cmd MAIN"); | 28 | check_exe.checkStart("cmd MAIN"); |
| 21 | check_exe.checkNext("stacksize 100000000"); | 29 | check_exe.checkNext("stacksize 100000000"); |
| 22 | 30 | ||
| 23 | const run = check_exe.runAndCompare(); | 31 | const run = check_exe.runAndCompare(); |
| 32 | run.expectStdOutEqual(""); | ||
| 24 | test_step.dependOn(&run.step); | 33 | test_step.dependOn(&run.step); |
| 25 | } | 34 | } |
test/link/macho/strict_validation/build.zig+13-5| ... | @@ -1,12 +1,20 @@ | ... | @@ -1,12 +1,20 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | 3 | ||
| 4 | pub const requires_symlinks = true; | ||
| 5 | |||
| 4 | pub fn build(b: *std.Build) void { | 6 | pub fn build(b: *std.Build) void { |
| 5 | const optimize = b.standardOptimizeOption(.{}); | 7 | const test_step = b.step("test", "Test it"); |
| 6 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; | 8 | b.default_step = test_step; |
| 7 | 9 | ||
| 8 | const test_step = b.step("test", "Test"); | 10 | add(b, test_step, .Debug); |
| 9 | test_step.dependOn(b.getInstallStep()); | 11 | add(b, test_step, .ReleaseFast); |
| 12 | add(b, test_step, .ReleaseSmall); | ||
| 13 | add(b, test_step, .ReleaseSafe); | ||
| 14 | } | ||
| 15 | |||
| 16 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 17 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; | ||
| 10 | 18 | ||
| 11 | const exe = b.addExecutable(.{ | 19 | const exe = b.addExecutable(.{ |
| 12 | .name = "main", | 20 | .name = "main", |
| ... | @@ -16,7 +24,7 @@ pub fn build(b: *std.Build) void { | ... | @@ -16,7 +24,7 @@ pub fn build(b: *std.Build) void { |
| 16 | }); | 24 | }); |
| 17 | exe.linkLibC(); | 25 | exe.linkLibC(); |
| 18 | 26 | ||
| 19 | const check_exe = exe.checkObject(.macho); | 27 | const check_exe = exe.checkObject(); |
| 20 | 28 | ||
| 21 | check_exe.checkStart("cmd SEGMENT_64"); | 29 | check_exe.checkStart("cmd SEGMENT_64"); |
| 22 | check_exe.checkNext("segname __LINKEDIT"); | 30 | check_exe.checkNext("segname __LINKEDIT"); |
test/link/macho/tls/build.zig+16-3| ... | @@ -1,7 +1,18 @@ | ... | @@ -1,7 +1,18 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_symlinks = true; | ||
| 4 | |||
| 3 | pub fn build(b: *std.Build) void { | 5 | pub fn build(b: *std.Build) void { |
| 4 | const optimize = b.standardOptimizeOption(.{}); | 6 | const test_step = b.step("test", "Test it"); |
| 7 | b.default_step = test_step; | ||
| 8 | |||
| 9 | add(b, test_step, .Debug); | ||
| 10 | add(b, test_step, .ReleaseFast); | ||
| 11 | add(b, test_step, .ReleaseSmall); | ||
| 12 | add(b, test_step, .ReleaseSafe); | ||
| 13 | } | ||
| 14 | |||
| 15 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 5 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; | 16 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; |
| 6 | 17 | ||
| 7 | const lib = b.addSharedLibrary(.{ | 18 | const lib = b.addSharedLibrary(.{ |
| ... | @@ -21,6 +32,8 @@ pub fn build(b: *std.Build) void { | ... | @@ -21,6 +32,8 @@ pub fn build(b: *std.Build) void { |
| 21 | test_exe.linkLibrary(lib); | 32 | test_exe.linkLibrary(lib); |
| 22 | test_exe.linkLibC(); | 33 | test_exe.linkLibC(); |
| 23 | 34 | ||
| 24 | const test_step = b.step("test", "Test it"); | 35 | const run = test_exe.run(); |
| 25 | test_step.dependOn(&test_exe.step); | 36 | run.skip_foreign_checks = true; |
| 37 | |||
| 38 | test_step.dependOn(&run.step); | ||
| 26 | } | 39 | } |
test/link/macho/unwind_info/build.zig+19-8| ... | @@ -1,14 +1,23 @@ | ... | @@ -1,14 +1,23 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | 3 | ||
| 4 | pub const requires_symlinks = true; | ||
| 5 | |||
| 4 | pub fn build(b: *std.Build) void { | 6 | pub fn build(b: *std.Build) void { |
| 5 | const optimize = b.standardOptimizeOption(.{}); | 7 | const test_step = b.step("test", "Test it"); |
| 6 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; | 8 | b.default_step = test_step; |
| 7 | 9 | ||
| 8 | const test_step = b.step("test", "Test the program"); | 10 | add(b, test_step, .Debug); |
| 11 | add(b, test_step, .ReleaseFast); | ||
| 12 | add(b, test_step, .ReleaseSmall); | ||
| 13 | add(b, test_step, .ReleaseSafe); | ||
| 14 | } | ||
| 15 | |||
| 16 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 17 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; | ||
| 9 | 18 | ||
| 10 | testUnwindInfo(b, test_step, optimize, target, false); | 19 | testUnwindInfo(b, test_step, optimize, target, false, "no-dead-strip"); |
| 11 | testUnwindInfo(b, test_step, optimize, target, true); | 20 | testUnwindInfo(b, test_step, optimize, target, true, "yes-dead-strip"); |
| 12 | } | 21 | } |
| 13 | 22 | ||
| 14 | fn testUnwindInfo( | 23 | fn testUnwindInfo( |
| ... | @@ -17,11 +26,12 @@ fn testUnwindInfo( | ... | @@ -17,11 +26,12 @@ fn testUnwindInfo( |
| 17 | optimize: std.builtin.OptimizeMode, | 26 | optimize: std.builtin.OptimizeMode, |
| 18 | target: std.zig.CrossTarget, | 27 | target: std.zig.CrossTarget, |
| 19 | dead_strip: bool, | 28 | dead_strip: bool, |
| 29 | name: []const u8, | ||
| 20 | ) void { | 30 | ) void { |
| 21 | const exe = createScenario(b, optimize, target); | 31 | const exe = createScenario(b, optimize, target, name); |
| 22 | exe.link_gc_sections = dead_strip; | 32 | exe.link_gc_sections = dead_strip; |
| 23 | 33 | ||
| 24 | const check = exe.checkObject(.macho); | 34 | const check = exe.checkObject(); |
| 25 | check.checkStart("segname __TEXT"); | 35 | check.checkStart("segname __TEXT"); |
| 26 | check.checkNext("sectname __gcc_except_tab"); | 36 | check.checkNext("sectname __gcc_except_tab"); |
| 27 | check.checkNext("sectname __unwind_info"); | 37 | check.checkNext("sectname __unwind_info"); |
| ... | @@ -54,9 +64,10 @@ fn createScenario( | ... | @@ -54,9 +64,10 @@ fn createScenario( |
| 54 | b: *std.Build, | 64 | b: *std.Build, |
| 55 | optimize: std.builtin.OptimizeMode, | 65 | optimize: std.builtin.OptimizeMode, |
| 56 | target: std.zig.CrossTarget, | 66 | target: std.zig.CrossTarget, |
| 67 | name: []const u8, | ||
| 57 | ) *std.Build.CompileStep { | 68 | ) *std.Build.CompileStep { |
| 58 | const exe = b.addExecutable(.{ | 69 | const exe = b.addExecutable(.{ |
| 59 | .name = "test", | 70 | .name = name, |
| 60 | .optimize = optimize, | 71 | .optimize = optimize, |
| 61 | .target = target, | 72 | .target = target, |
| 62 | }); | 73 | }); |
test/link/macho/uuid/build.zig+23-62| ... | @@ -1,14 +1,16 @@ | ... | @@ -1,14 +1,16 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const Builder = std.Build.Builder; | ||
| 3 | const CompileStep = std.Build.CompileStep; | 2 | const CompileStep = std.Build.CompileStep; |
| 4 | const FileSource = std.Build.FileSource; | 3 | const FileSource = std.Build.FileSource; |
| 5 | const Step = std.Build.Step; | 4 | const Step = std.Build.Step; |
| 6 | 5 | ||
| 6 | pub const requires_symlinks = true; | ||
| 7 | |||
| 7 | pub fn build(b: *std.Build) void { | 8 | pub fn build(b: *std.Build) void { |
| 8 | const test_step = b.step("test", "Test"); | 9 | const test_step = b.step("test", "Test"); |
| 9 | test_step.dependOn(b.getInstallStep()); | 10 | b.default_step = test_step; |
| 10 | 11 | ||
| 11 | // We force cross-compilation to ensure we always pick a generic CPU with constant set of CPU features. | 12 | // We force cross-compilation to ensure we always pick a generic CPU with |
| 13 | // constant set of CPU features. | ||
| 12 | const aarch64_macos = std.zig.CrossTarget{ | 14 | const aarch64_macos = std.zig.CrossTarget{ |
| 13 | .cpu_arch = .aarch64, | 15 | .cpu_arch = .aarch64, |
| 14 | .os_tag = .macos, | 16 | .os_tag = .macos, |
| ... | @@ -38,13 +40,15 @@ fn testUuid( | ... | @@ -38,13 +40,15 @@ fn testUuid( |
| 38 | // stay the same across builds. | 40 | // stay the same across builds. |
| 39 | { | 41 | { |
| 40 | const dylib = simpleDylib(b, optimize, target); | 42 | const dylib = simpleDylib(b, optimize, target); |
| 41 | const install_step = installWithRename(dylib, "test1.dylib"); | 43 | const install_step = b.addInstallArtifact(dylib); |
| 44 | install_step.dest_sub_path = "test1.dylib"; | ||
| 42 | install_step.step.dependOn(&dylib.step); | 45 | install_step.step.dependOn(&dylib.step); |
| 43 | } | 46 | } |
| 44 | { | 47 | { |
| 45 | const dylib = simpleDylib(b, optimize, target); | 48 | const dylib = simpleDylib(b, optimize, target); |
| 46 | dylib.strip = true; | 49 | dylib.strip = true; |
| 47 | const install_step = installWithRename(dylib, "test2.dylib"); | 50 | const install_step = b.addInstallArtifact(dylib); |
| 51 | install_step.dest_sub_path = "test2.dylib"; | ||
| 48 | install_step.step.dependOn(&dylib.step); | 52 | install_step.step.dependOn(&dylib.step); |
| 49 | } | 53 | } |
| 50 | 54 | ||
| ... | @@ -68,86 +72,43 @@ fn simpleDylib( | ... | @@ -68,86 +72,43 @@ fn simpleDylib( |
| 68 | return dylib; | 72 | return dylib; |
| 69 | } | 73 | } |
| 70 | 74 | ||
| 71 | fn installWithRename(cs: *CompileStep, name: []const u8) *InstallWithRename { | ||
| 72 | const step = InstallWithRename.create(cs.builder, cs.getOutputSource(), name); | ||
| 73 | cs.builder.getInstallStep().dependOn(&step.step); | ||
| 74 | return step; | ||
| 75 | } | ||
| 76 | |||
| 77 | const InstallWithRename = struct { | ||
| 78 | pub const base_id = .custom; | ||
| 79 | |||
| 80 | step: Step, | ||
| 81 | builder: *Builder, | ||
| 82 | source: FileSource, | ||
| 83 | name: []const u8, | ||
| 84 | |||
| 85 | pub fn create( | ||
| 86 | builder: *Builder, | ||
| 87 | source: FileSource, | ||
| 88 | name: []const u8, | ||
| 89 | ) *InstallWithRename { | ||
| 90 | const self = builder.allocator.create(InstallWithRename) catch @panic("OOM"); | ||
| 91 | self.* = InstallWithRename{ | ||
| 92 | .builder = builder, | ||
| 93 | .step = Step.init(.custom, builder.fmt("install and rename: {s} -> {s}", .{ | ||
| 94 | source.getDisplayName(), | ||
| 95 | name, | ||
| 96 | }), builder.allocator, make), | ||
| 97 | .source = source, | ||
| 98 | .name = builder.dupe(name), | ||
| 99 | }; | ||
| 100 | return self; | ||
| 101 | } | ||
| 102 | |||
| 103 | fn make(step: *Step) anyerror!void { | ||
| 104 | const self = @fieldParentPtr(InstallWithRename, "step", step); | ||
| 105 | const source_path = self.source.getPath(self.builder); | ||
| 106 | const target_path = self.builder.getInstallPath(.lib, self.name); | ||
| 107 | self.builder.updateFile(source_path, target_path) catch |err| { | ||
| 108 | std.log.err("Unable to rename: {s} -> {s}", .{ source_path, target_path }); | ||
| 109 | return err; | ||
| 110 | }; | ||
| 111 | } | ||
| 112 | }; | ||
| 113 | |||
| 114 | const CompareUuid = struct { | 75 | const CompareUuid = struct { |
| 115 | pub const base_id = .custom; | 76 | pub const base_id = .custom; |
| 116 | 77 | ||
| 117 | step: Step, | 78 | step: Step, |
| 118 | builder: *Builder, | ||
| 119 | lhs: []const u8, | 79 | lhs: []const u8, |
| 120 | rhs: []const u8, | 80 | rhs: []const u8, |
| 121 | 81 | ||
| 122 | pub fn create(builder: *Builder, lhs: []const u8, rhs: []const u8) *CompareUuid { | 82 | pub fn create(owner: *std.Build, lhs: []const u8, rhs: []const u8) *CompareUuid { |
| 123 | const self = builder.allocator.create(CompareUuid) catch @panic("OOM"); | 83 | const self = owner.allocator.create(CompareUuid) catch @panic("OOM"); |
| 124 | self.* = CompareUuid{ | 84 | self.* = CompareUuid{ |
| 125 | .builder = builder, | 85 | .step = Step.init(.{ |
| 126 | .step = Step.init( | 86 | .id = base_id, |
| 127 | .custom, | 87 | .name = owner.fmt("compare uuid: {s} and {s}", .{ |
| 128 | builder.fmt("compare uuid: {s} and {s}", .{ | ||
| 129 | lhs, | 88 | lhs, |
| 130 | rhs, | 89 | rhs, |
| 131 | }), | 90 | }), |
| 132 | builder.allocator, | 91 | .owner = owner, |
| 133 | make, | 92 | .makeFn = make, |
| 134 | ), | 93 | }), |
| 135 | .lhs = lhs, | 94 | .lhs = lhs, |
| 136 | .rhs = rhs, | 95 | .rhs = rhs, |
| 137 | }; | 96 | }; |
| 138 | return self; | 97 | return self; |
| 139 | } | 98 | } |
| 140 | 99 | ||
| 141 | fn make(step: *Step) anyerror!void { | 100 | fn make(step: *Step, prog_node: *std.Progress.Node) anyerror!void { |
| 101 | _ = prog_node; | ||
| 102 | const b = step.owner; | ||
| 142 | const self = @fieldParentPtr(CompareUuid, "step", step); | 103 | const self = @fieldParentPtr(CompareUuid, "step", step); |
| 143 | const gpa = self.builder.allocator; | 104 | const gpa = b.allocator; |
| 144 | 105 | ||
| 145 | var lhs_uuid: [16]u8 = undefined; | 106 | var lhs_uuid: [16]u8 = undefined; |
| 146 | const lhs_path = self.builder.getInstallPath(.lib, self.lhs); | 107 | const lhs_path = b.getInstallPath(.lib, self.lhs); |
| 147 | try parseUuid(gpa, lhs_path, &lhs_uuid); | 108 | try parseUuid(gpa, lhs_path, &lhs_uuid); |
| 148 | 109 | ||
| 149 | var rhs_uuid: [16]u8 = undefined; | 110 | var rhs_uuid: [16]u8 = undefined; |
| 150 | const rhs_path = self.builder.getInstallPath(.lib, self.rhs); | 111 | const rhs_path = b.getInstallPath(.lib, self.rhs); |
| 151 | try parseUuid(gpa, rhs_path, &rhs_uuid); | 112 | try parseUuid(gpa, rhs_path, &rhs_uuid); |
| 152 | 113 | ||
| 153 | try std.testing.expectEqualStrings(&lhs_uuid, &rhs_uuid); | 114 | try std.testing.expectEqualStrings(&lhs_uuid, &rhs_uuid); |
test/link/macho/weak_framework/build.zig+12-4| ... | @@ -1,11 +1,19 @@ | ... | @@ -1,11 +1,19 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_symlinks = true; | ||
| 4 | pub const requires_macos_sdk = true; | ||
| 5 | |||
| 3 | pub fn build(b: *std.Build) void { | 6 | pub fn build(b: *std.Build) void { |
| 4 | const optimize = b.standardOptimizeOption(.{}); | 7 | const test_step = b.step("test", "Test it"); |
| 8 | b.default_step = test_step; | ||
| 5 | 9 | ||
| 6 | const test_step = b.step("test", "Test the program"); | 10 | add(b, test_step, .Debug); |
| 7 | test_step.dependOn(b.getInstallStep()); | 11 | add(b, test_step, .ReleaseFast); |
| 12 | add(b, test_step, .ReleaseSmall); | ||
| 13 | add(b, test_step, .ReleaseSafe); | ||
| 14 | } | ||
| 8 | 15 | ||
| 16 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 9 | const exe = b.addExecutable(.{ | 17 | const exe = b.addExecutable(.{ |
| 10 | .name = "test", | 18 | .name = "test", |
| 11 | .optimize = optimize, | 19 | .optimize = optimize, |
| ... | @@ -14,7 +22,7 @@ pub fn build(b: *std.Build) void { | ... | @@ -14,7 +22,7 @@ pub fn build(b: *std.Build) void { |
| 14 | exe.linkLibC(); | 22 | exe.linkLibC(); |
| 15 | exe.linkFrameworkWeak("Cocoa"); | 23 | exe.linkFrameworkWeak("Cocoa"); |
| 16 | 24 | ||
| 17 | const check = exe.checkObject(.macho); | 25 | const check = exe.checkObject(); |
| 18 | check.checkStart("cmd LOAD_WEAK_DYLIB"); | 26 | check.checkStart("cmd LOAD_WEAK_DYLIB"); |
| 19 | check.checkNext("name {*}Cocoa"); | 27 | check.checkNext("name {*}Cocoa"); |
| 20 | test_step.dependOn(&check.step); | 28 | test_step.dependOn(&check.step); |
test/link/macho/weak_library/build.zig+15-7| ... | @@ -1,11 +1,19 @@ | ... | @@ -1,11 +1,19 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_symlinks = true; | ||
| 4 | |||
| 3 | pub fn build(b: *std.Build) void { | 5 | pub fn build(b: *std.Build) void { |
| 4 | const optimize = b.standardOptimizeOption(.{}); | 6 | const test_step = b.step("test", "Test it"); |
| 5 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; | 7 | b.default_step = test_step; |
| 6 | 8 | ||
| 7 | const test_step = b.step("test", "Test the program"); | 9 | add(b, test_step, .Debug); |
| 8 | test_step.dependOn(b.getInstallStep()); | 10 | add(b, test_step, .ReleaseFast); |
| 11 | add(b, test_step, .ReleaseSmall); | ||
| 12 | add(b, test_step, .ReleaseSafe); | ||
| 13 | } | ||
| 14 | |||
| 15 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 16 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; | ||
| 9 | 17 | ||
| 10 | const dylib = b.addSharedLibrary(.{ | 18 | const dylib = b.addSharedLibrary(.{ |
| 11 | .name = "a", | 19 | .name = "a", |
| ... | @@ -25,10 +33,10 @@ pub fn build(b: *std.Build) void { | ... | @@ -25,10 +33,10 @@ pub fn build(b: *std.Build) void { |
| 25 | exe.addCSourceFile("main.c", &[0][]const u8{}); | 33 | exe.addCSourceFile("main.c", &[0][]const u8{}); |
| 26 | exe.linkLibC(); | 34 | exe.linkLibC(); |
| 27 | exe.linkSystemLibraryWeak("a"); | 35 | exe.linkSystemLibraryWeak("a"); |
| 28 | exe.addLibraryPath(b.pathFromRoot("zig-out/lib")); | 36 | exe.addLibraryPathDirectorySource(dylib.getOutputDirectorySource()); |
| 29 | exe.addRPath(b.pathFromRoot("zig-out/lib")); | 37 | exe.addRPathDirectorySource(dylib.getOutputDirectorySource()); |
| 30 | 38 | ||
| 31 | const check = exe.checkObject(.macho); | 39 | const check = exe.checkObject(); |
| 32 | check.checkStart("cmd LOAD_WEAK_DYLIB"); | 40 | check.checkStart("cmd LOAD_WEAK_DYLIB"); |
| 33 | check.checkNext("name @rpath/liba.dylib"); | 41 | check.checkNext("name @rpath/liba.dylib"); |
| 34 | 42 |
test/link/static_lib_as_system_lib/a.c deleted-4| ... | @@ -1,4 +0,0 @@ | ||
| 1 | #include "a.h" | ||
| 2 | int32_t add(int32_t a, int32_t b) { | ||
| 3 | return a + b; | ||
| 4 | } | ||
test/link/static_lib_as_system_lib/a.h deleted-2| ... | @@ -1,2 +0,0 @@ | ||
| 1 | #include <stdint.h> | ||
| 2 | int32_t add(int32_t a, int32_t b); | ||
test/link/static_lib_as_system_lib/build.zig deleted-29| ... | @@ -1,29 +0,0 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | |||
| 3 | pub fn build(b: *std.Build) void { | ||
| 4 | const optimize = b.standardOptimizeOption(.{}); | ||
| 5 | const target = b.standardTargetOptions(.{}); | ||
| 6 | |||
| 7 | const lib_a = b.addStaticLibrary(.{ | ||
| 8 | .name = "a", | ||
| 9 | .optimize = optimize, | ||
| 10 | .target = target, | ||
| 11 | }); | ||
| 12 | lib_a.addCSourceFile("a.c", &[_][]const u8{}); | ||
| 13 | lib_a.addIncludePath("."); | ||
| 14 | lib_a.install(); | ||
| 15 | |||
| 16 | const test_exe = b.addTest(.{ | ||
| 17 | .root_source_file = .{ .path = "main.zig" }, | ||
| 18 | .optimize = optimize, | ||
| 19 | .target = target, | ||
| 20 | }); | ||
| 21 | test_exe.linkSystemLibrary("a"); // force linking liba.a as -la | ||
| 22 | test_exe.addSystemIncludePath("."); | ||
| 23 | const search_path = std.fs.path.join(b.allocator, &[_][]const u8{ b.install_path, "lib" }) catch unreachable; | ||
| 24 | test_exe.addLibraryPath(search_path); | ||
| 25 | |||
| 26 | const test_step = b.step("test", "Test it"); | ||
| 27 | test_step.dependOn(b.getInstallStep()); | ||
| 28 | test_step.dependOn(&test_exe.step); | ||
| 29 | } | ||
test/link/static_lib_as_system_lib/main.zig deleted-8| ... | @@ -1,8 +0,0 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const expect = std.testing.expect; | ||
| 3 | const c = @cImport(@cInclude("a.h")); | ||
| 4 | |||
| 5 | test "import C add" { | ||
| 6 | const result = c.add(2, 1); | ||
| 7 | try expect(result == 3); | ||
| 8 | } | ||
test/link/wasm/archive/build.zig+13-4| ... | @@ -1,22 +1,31 @@ | ... | @@ -1,22 +1,31 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_stage2 = true; | ||
| 4 | |||
| 3 | pub fn build(b: *std.Build) void { | 5 | pub fn build(b: *std.Build) void { |
| 4 | const test_step = b.step("test", "Test"); | 6 | const test_step = b.step("test", "Test it"); |
| 5 | test_step.dependOn(b.getInstallStep()); | 7 | b.default_step = test_step; |
| 8 | |||
| 9 | add(b, test_step, .Debug); | ||
| 10 | add(b, test_step, .ReleaseFast); | ||
| 11 | add(b, test_step, .ReleaseSmall); | ||
| 12 | add(b, test_step, .ReleaseSafe); | ||
| 13 | } | ||
| 6 | 14 | ||
| 15 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 7 | // The code in question will pull-in compiler-rt, | 16 | // The code in question will pull-in compiler-rt, |
| 8 | // and therefore link with its archive file. | 17 | // and therefore link with its archive file. |
| 9 | const lib = b.addSharedLibrary(.{ | 18 | const lib = b.addSharedLibrary(.{ |
| 10 | .name = "main", | 19 | .name = "main", |
| 11 | .root_source_file = .{ .path = "main.zig" }, | 20 | .root_source_file = .{ .path = "main.zig" }, |
| 12 | .optimize = b.standardOptimizeOption(.{}), | 21 | .optimize = optimize, |
| 13 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, | 22 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, |
| 14 | }); | 23 | }); |
| 15 | lib.use_llvm = false; | 24 | lib.use_llvm = false; |
| 16 | lib.use_lld = false; | 25 | lib.use_lld = false; |
| 17 | lib.strip = false; | 26 | lib.strip = false; |
| 18 | 27 | ||
| 19 | const check = lib.checkObject(.wasm); | 28 | const check = lib.checkObject(); |
| 20 | check.checkStart("Section custom"); | 29 | check.checkStart("Section custom"); |
| 21 | check.checkNext("name __truncsfhf2"); // Ensure it was imported and resolved | 30 | check.checkNext("name __truncsfhf2"); // Ensure it was imported and resolved |
| 22 | 31 |
test/link/wasm/basic-features/build.zig+5-2| ... | @@ -1,11 +1,13 @@ | ... | @@ -1,11 +1,13 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_stage2 = true; | ||
| 4 | |||
| 3 | pub fn build(b: *std.Build) void { | 5 | pub fn build(b: *std.Build) void { |
| 4 | // Library with explicitly set cpu features | 6 | // Library with explicitly set cpu features |
| 5 | const lib = b.addSharedLibrary(.{ | 7 | const lib = b.addSharedLibrary(.{ |
| 6 | .name = "lib", | 8 | .name = "lib", |
| 7 | .root_source_file = .{ .path = "main.zig" }, | 9 | .root_source_file = .{ .path = "main.zig" }, |
| 8 | .optimize = b.standardOptimizeOption(.{}), | 10 | .optimize = .Debug, |
| 9 | .target = .{ | 11 | .target = .{ |
| 10 | .cpu_arch = .wasm32, | 12 | .cpu_arch = .wasm32, |
| 11 | .cpu_model = .{ .explicit = &std.Target.wasm.cpu.mvp }, | 13 | .cpu_model = .{ .explicit = &std.Target.wasm.cpu.mvp }, |
| ... | @@ -17,11 +19,12 @@ pub fn build(b: *std.Build) void { | ... | @@ -17,11 +19,12 @@ pub fn build(b: *std.Build) void { |
| 17 | lib.use_lld = false; | 19 | lib.use_lld = false; |
| 18 | 20 | ||
| 19 | // Verify the result contains the features explicitly set on the target for the library. | 21 | // Verify the result contains the features explicitly set on the target for the library. |
| 20 | const check = lib.checkObject(.wasm); | 22 | const check = lib.checkObject(); |
| 21 | check.checkStart("name target_features"); | 23 | check.checkStart("name target_features"); |
| 22 | check.checkNext("features 1"); | 24 | check.checkNext("features 1"); |
| 23 | check.checkNext("+ atomics"); | 25 | check.checkNext("+ atomics"); |
| 24 | 26 | ||
| 25 | const test_step = b.step("test", "Run linker test"); | 27 | const test_step = b.step("test", "Run linker test"); |
| 26 | test_step.dependOn(&check.step); | 28 | test_step.dependOn(&check.step); |
| 29 | b.default_step = test_step; | ||
| 27 | } | 30 | } |
test/link/wasm/bss/build.zig+6-3| ... | @@ -1,14 +1,16 @@ | ... | @@ -1,14 +1,16 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_stage2 = true; | ||
| 4 | |||
| 3 | pub fn build(b: *std.Build) void { | 5 | pub fn build(b: *std.Build) void { |
| 4 | const test_step = b.step("test", "Test"); | 6 | const test_step = b.step("test", "Test"); |
| 5 | test_step.dependOn(b.getInstallStep()); | 7 | b.default_step = test_step; |
| 6 | 8 | ||
| 7 | const lib = b.addSharedLibrary(.{ | 9 | const lib = b.addSharedLibrary(.{ |
| 8 | .name = "lib", | 10 | .name = "lib", |
| 9 | .root_source_file = .{ .path = "lib.zig" }, | 11 | .root_source_file = .{ .path = "lib.zig" }, |
| 10 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, | 12 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, |
| 11 | .optimize = b.standardOptimizeOption(.{}), | 13 | .optimize = .Debug, |
| 12 | }); | 14 | }); |
| 13 | lib.use_llvm = false; | 15 | lib.use_llvm = false; |
| 14 | lib.use_lld = false; | 16 | lib.use_lld = false; |
| ... | @@ -17,7 +19,7 @@ pub fn build(b: *std.Build) void { | ... | @@ -17,7 +19,7 @@ pub fn build(b: *std.Build) void { |
| 17 | lib.import_memory = true; | 19 | lib.import_memory = true; |
| 18 | lib.install(); | 20 | lib.install(); |
| 19 | 21 | ||
| 20 | const check_lib = lib.checkObject(.wasm); | 22 | const check_lib = lib.checkObject(); |
| 21 | 23 | ||
| 22 | // since we import memory, make sure it exists with the correct naming | 24 | // since we import memory, make sure it exists with the correct naming |
| 23 | check_lib.checkStart("Section import"); | 25 | check_lib.checkStart("Section import"); |
| ... | @@ -36,5 +38,6 @@ pub fn build(b: *std.Build) void { | ... | @@ -36,5 +38,6 @@ pub fn build(b: *std.Build) void { |
| 36 | check_lib.checkNext("name .rodata"); | 38 | check_lib.checkNext("name .rodata"); |
| 37 | check_lib.checkNext("index 1"); // bss section always last | 39 | check_lib.checkNext("index 1"); // bss section always last |
| 38 | check_lib.checkNext("name .bss"); | 40 | check_lib.checkNext("name .bss"); |
| 41 | |||
| 39 | test_step.dependOn(&check_lib.step); | 42 | test_step.dependOn(&check_lib.step); |
| 40 | } | 43 | } |
test/link/wasm/export-data/build.zig+7-2| ... | @@ -2,7 +2,12 @@ const std = @import("std"); | ... | @@ -2,7 +2,12 @@ const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn build(b: *std.Build) void { | 3 | pub fn build(b: *std.Build) void { |
| 4 | const test_step = b.step("test", "Test"); | 4 | const test_step = b.step("test", "Test"); |
| 5 | test_step.dependOn(b.getInstallStep()); | 5 | b.default_step = test_step; |
| 6 | |||
| 7 | if (@import("builtin").os.tag == .windows) { | ||
| 8 | // TODO: Fix open handle in wasm-linker refraining rename from working on Windows. | ||
| 9 | return; | ||
| 10 | } | ||
| 6 | 11 | ||
| 7 | const lib = b.addSharedLibrary(.{ | 12 | const lib = b.addSharedLibrary(.{ |
| 8 | .name = "lib", | 13 | .name = "lib", |
| ... | @@ -14,7 +19,7 @@ pub fn build(b: *std.Build) void { | ... | @@ -14,7 +19,7 @@ pub fn build(b: *std.Build) void { |
| 14 | lib.export_symbol_names = &.{ "foo", "bar" }; | 19 | lib.export_symbol_names = &.{ "foo", "bar" }; |
| 15 | lib.global_base = 0; // put data section at address 0 to make data symbols easier to parse | 20 | lib.global_base = 0; // put data section at address 0 to make data symbols easier to parse |
| 16 | 21 | ||
| 17 | const check_lib = lib.checkObject(.wasm); | 22 | const check_lib = lib.checkObject(); |
| 18 | 23 | ||
| 19 | check_lib.checkStart("Section global"); | 24 | check_lib.checkStart("Section global"); |
| 20 | check_lib.checkNext("entries 3"); | 25 | check_lib.checkNext("entries 3"); |
test/link/wasm/export/build.zig+14-5| ... | @@ -1,8 +1,18 @@ | ... | @@ -1,8 +1,18 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_stage2 = true; | ||
| 4 | |||
| 3 | pub fn build(b: *std.Build) void { | 5 | pub fn build(b: *std.Build) void { |
| 4 | const optimize = b.standardOptimizeOption(.{}); | 6 | const test_step = b.step("test", "Test it"); |
| 7 | b.default_step = test_step; | ||
| 8 | |||
| 9 | add(b, test_step, .Debug); | ||
| 10 | add(b, test_step, .ReleaseFast); | ||
| 11 | add(b, test_step, .ReleaseSmall); | ||
| 12 | add(b, test_step, .ReleaseSafe); | ||
| 13 | } | ||
| 5 | 14 | ||
| 15 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 6 | const no_export = b.addSharedLibrary(.{ | 16 | const no_export = b.addSharedLibrary(.{ |
| 7 | .name = "no-export", | 17 | .name = "no-export", |
| 8 | .root_source_file = .{ .path = "main.zig" }, | 18 | .root_source_file = .{ .path = "main.zig" }, |
| ... | @@ -32,25 +42,24 @@ pub fn build(b: *std.Build) void { | ... | @@ -32,25 +42,24 @@ pub fn build(b: *std.Build) void { |
| 32 | force_export.use_llvm = false; | 42 | force_export.use_llvm = false; |
| 33 | force_export.use_lld = false; | 43 | force_export.use_lld = false; |
| 34 | 44 | ||
| 35 | const check_no_export = no_export.checkObject(.wasm); | 45 | const check_no_export = no_export.checkObject(); |
| 36 | check_no_export.checkStart("Section export"); | 46 | check_no_export.checkStart("Section export"); |
| 37 | check_no_export.checkNext("entries 1"); | 47 | check_no_export.checkNext("entries 1"); |
| 38 | check_no_export.checkNext("name memory"); | 48 | check_no_export.checkNext("name memory"); |
| 39 | check_no_export.checkNext("kind memory"); | 49 | check_no_export.checkNext("kind memory"); |
| 40 | 50 | ||
| 41 | const check_dynamic_export = dynamic_export.checkObject(.wasm); | 51 | const check_dynamic_export = dynamic_export.checkObject(); |
| 42 | check_dynamic_export.checkStart("Section export"); | 52 | check_dynamic_export.checkStart("Section export"); |
| 43 | check_dynamic_export.checkNext("entries 2"); | 53 | check_dynamic_export.checkNext("entries 2"); |
| 44 | check_dynamic_export.checkNext("name foo"); | 54 | check_dynamic_export.checkNext("name foo"); |
| 45 | check_dynamic_export.checkNext("kind function"); | 55 | check_dynamic_export.checkNext("kind function"); |
| 46 | 56 | ||
| 47 | const check_force_export = force_export.checkObject(.wasm); | 57 | const check_force_export = force_export.checkObject(); |
| 48 | check_force_export.checkStart("Section export"); | 58 | check_force_export.checkStart("Section export"); |
| 49 | check_force_export.checkNext("entries 2"); | 59 | check_force_export.checkNext("entries 2"); |
| 50 | check_force_export.checkNext("name foo"); | 60 | check_force_export.checkNext("name foo"); |
| 51 | check_force_export.checkNext("kind function"); | 61 | check_force_export.checkNext("kind function"); |
| 52 | 62 | ||
| 53 | const test_step = b.step("test", "Run linker test"); | ||
| 54 | test_step.dependOn(&check_no_export.step); | 63 | test_step.dependOn(&check_no_export.step); |
| 55 | test_step.dependOn(&check_dynamic_export.step); | 64 | test_step.dependOn(&check_dynamic_export.step); |
| 56 | test_step.dependOn(&check_force_export.step); | 65 | test_step.dependOn(&check_force_export.step); |
test/link/wasm/extern-mangle/build.zig+11-5| ... | @@ -1,20 +1,26 @@ | ... | @@ -1,20 +1,26 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn build(b: *std.Build) void { | 3 | pub fn build(b: *std.Build) void { |
| 4 | const test_step = b.step("test", "Test"); | 4 | const test_step = b.step("test", "Test it"); |
| 5 | test_step.dependOn(b.getInstallStep()); | 5 | b.default_step = test_step; |
| 6 | 6 | ||
| 7 | add(b, test_step, .Debug); | ||
| 8 | add(b, test_step, .ReleaseFast); | ||
| 9 | add(b, test_step, .ReleaseSmall); | ||
| 10 | add(b, test_step, .ReleaseSafe); | ||
| 11 | } | ||
| 12 | |||
| 13 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 7 | const lib = b.addSharedLibrary(.{ | 14 | const lib = b.addSharedLibrary(.{ |
| 8 | .name = "lib", | 15 | .name = "lib", |
| 9 | .root_source_file = .{ .path = "lib.zig" }, | 16 | .root_source_file = .{ .path = "lib.zig" }, |
| 10 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, | 17 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, |
| 11 | .optimize = b.standardOptimizeOption(.{}), | 18 | .optimize = optimize, |
| 12 | }); | 19 | }); |
| 13 | lib.import_symbols = true; // import `a` and `b` | 20 | lib.import_symbols = true; // import `a` and `b` |
| 14 | lib.rdynamic = true; // export `foo` | 21 | lib.rdynamic = true; // export `foo` |
| 15 | lib.install(); | ||
| 16 | 22 | ||
| 17 | const check_lib = lib.checkObject(.wasm); | 23 | const check_lib = lib.checkObject(); |
| 18 | check_lib.checkStart("Section import"); | 24 | check_lib.checkStart("Section import"); |
| 19 | check_lib.checkNext("entries 2"); // a.hello & b.hello | 25 | check_lib.checkNext("entries 2"); // a.hello & b.hello |
| 20 | check_lib.checkNext("module a"); | 26 | check_lib.checkNext("module a"); |
test/link/wasm/extern/build.zig+15-3| ... | @@ -1,19 +1,31 @@ | ... | @@ -1,19 +1,31 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_stage2 = true; | ||
| 4 | |||
| 3 | pub fn build(b: *std.Build) void { | 5 | pub fn build(b: *std.Build) void { |
| 6 | const test_step = b.step("test", "Test it"); | ||
| 7 | b.default_step = test_step; | ||
| 8 | |||
| 9 | add(b, test_step, .Debug); | ||
| 10 | add(b, test_step, .ReleaseFast); | ||
| 11 | add(b, test_step, .ReleaseSmall); | ||
| 12 | add(b, test_step, .ReleaseSafe); | ||
| 13 | } | ||
| 14 | |||
| 15 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 4 | const exe = b.addExecutable(.{ | 16 | const exe = b.addExecutable(.{ |
| 5 | .name = "extern", | 17 | .name = "extern", |
| 6 | .root_source_file = .{ .path = "main.zig" }, | 18 | .root_source_file = .{ .path = "main.zig" }, |
| 7 | .optimize = b.standardOptimizeOption(.{}), | 19 | .optimize = optimize, |
| 8 | .target = .{ .cpu_arch = .wasm32, .os_tag = .wasi }, | 20 | .target = .{ .cpu_arch = .wasm32, .os_tag = .wasi }, |
| 9 | }); | 21 | }); |
| 10 | exe.addCSourceFile("foo.c", &.{}); | 22 | exe.addCSourceFile("foo.c", &.{}); |
| 11 | exe.use_llvm = false; | 23 | exe.use_llvm = false; |
| 12 | exe.use_lld = false; | 24 | exe.use_lld = false; |
| 13 | 25 | ||
| 14 | const run = exe.runEmulatable(); | 26 | const run = b.addRunArtifact(exe); |
| 27 | run.skip_foreign_checks = true; | ||
| 15 | run.expectStdOutEqual("Result: 30"); | 28 | run.expectStdOutEqual("Result: 30"); |
| 16 | 29 | ||
| 17 | const test_step = b.step("test", "Run linker test"); | ||
| 18 | test_step.dependOn(&run.step); | 30 | test_step.dependOn(&run.step); |
| 19 | } | 31 | } |
test/link/wasm/function-table/build.zig+16-9| ... | @@ -1,13 +1,20 @@ | ... | @@ -1,13 +1,20 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_stage2 = true; | ||
| 4 | |||
| 3 | pub fn build(b: *std.Build) void { | 5 | pub fn build(b: *std.Build) void { |
| 4 | const optimize = b.standardOptimizeOption(.{}); | 6 | const test_step = b.step("test", "Test it"); |
| 7 | b.default_step = test_step; | ||
| 5 | 8 | ||
| 6 | const test_step = b.step("test", "Test"); | 9 | add(b, test_step, .Debug); |
| 7 | test_step.dependOn(b.getInstallStep()); | 10 | add(b, test_step, .ReleaseFast); |
| 11 | add(b, test_step, .ReleaseSmall); | ||
| 12 | add(b, test_step, .ReleaseSafe); | ||
| 13 | } | ||
| 8 | 14 | ||
| 15 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 9 | const import_table = b.addSharedLibrary(.{ | 16 | const import_table = b.addSharedLibrary(.{ |
| 10 | .name = "lib", | 17 | .name = "import_table", |
| 11 | .root_source_file = .{ .path = "lib.zig" }, | 18 | .root_source_file = .{ .path = "lib.zig" }, |
| 12 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, | 19 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, |
| 13 | .optimize = optimize, | 20 | .optimize = optimize, |
| ... | @@ -17,7 +24,7 @@ pub fn build(b: *std.Build) void { | ... | @@ -17,7 +24,7 @@ pub fn build(b: *std.Build) void { |
| 17 | import_table.import_table = true; | 24 | import_table.import_table = true; |
| 18 | 25 | ||
| 19 | const export_table = b.addSharedLibrary(.{ | 26 | const export_table = b.addSharedLibrary(.{ |
| 20 | .name = "lib", | 27 | .name = "export_table", |
| 21 | .root_source_file = .{ .path = "lib.zig" }, | 28 | .root_source_file = .{ .path = "lib.zig" }, |
| 22 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, | 29 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, |
| 23 | .optimize = optimize, | 30 | .optimize = optimize, |
| ... | @@ -27,7 +34,7 @@ pub fn build(b: *std.Build) void { | ... | @@ -27,7 +34,7 @@ pub fn build(b: *std.Build) void { |
| 27 | export_table.export_table = true; | 34 | export_table.export_table = true; |
| 28 | 35 | ||
| 29 | const regular_table = b.addSharedLibrary(.{ | 36 | const regular_table = b.addSharedLibrary(.{ |
| 30 | .name = "lib", | 37 | .name = "regular_table", |
| 31 | .root_source_file = .{ .path = "lib.zig" }, | 38 | .root_source_file = .{ .path = "lib.zig" }, |
| 32 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, | 39 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, |
| 33 | .optimize = optimize, | 40 | .optimize = optimize, |
| ... | @@ -35,9 +42,9 @@ pub fn build(b: *std.Build) void { | ... | @@ -35,9 +42,9 @@ pub fn build(b: *std.Build) void { |
| 35 | regular_table.use_llvm = false; | 42 | regular_table.use_llvm = false; |
| 36 | regular_table.use_lld = false; | 43 | regular_table.use_lld = false; |
| 37 | 44 | ||
| 38 | const check_import = import_table.checkObject(.wasm); | 45 | const check_import = import_table.checkObject(); |
| 39 | const check_export = export_table.checkObject(.wasm); | 46 | const check_export = export_table.checkObject(); |
| 40 | const check_regular = regular_table.checkObject(.wasm); | 47 | const check_regular = regular_table.checkObject(); |
| 41 | 48 | ||
| 42 | check_import.checkStart("Section import"); | 49 | check_import.checkStart("Section import"); |
| 43 | check_import.checkNext("entries 1"); | 50 | check_import.checkNext("entries 1"); |
test/link/wasm/infer-features/build.zig+6-5| ... | @@ -1,12 +1,12 @@ | ... | @@ -1,12 +1,12 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn build(b: *std.Build) void { | 3 | pub const requires_stage2 = true; |
| 4 | const optimize = b.standardOptimizeOption(.{}); | ||
| 5 | 4 | ||
| 5 | pub fn build(b: *std.Build) void { | ||
| 6 | // Wasm Object file which we will use to infer the features from | 6 | // Wasm Object file which we will use to infer the features from |
| 7 | const c_obj = b.addObject(.{ | 7 | const c_obj = b.addObject(.{ |
| 8 | .name = "c_obj", | 8 | .name = "c_obj", |
| 9 | .optimize = optimize, | 9 | .optimize = .Debug, |
| 10 | .target = .{ | 10 | .target = .{ |
| 11 | .cpu_arch = .wasm32, | 11 | .cpu_arch = .wasm32, |
| 12 | .cpu_model = .{ .explicit = &std.Target.wasm.cpu.bleeding_edge }, | 12 | .cpu_model = .{ .explicit = &std.Target.wasm.cpu.bleeding_edge }, |
| ... | @@ -20,7 +20,7 @@ pub fn build(b: *std.Build) void { | ... | @@ -20,7 +20,7 @@ pub fn build(b: *std.Build) void { |
| 20 | const lib = b.addSharedLibrary(.{ | 20 | const lib = b.addSharedLibrary(.{ |
| 21 | .name = "lib", | 21 | .name = "lib", |
| 22 | .root_source_file = .{ .path = "main.zig" }, | 22 | .root_source_file = .{ .path = "main.zig" }, |
| 23 | .optimize = optimize, | 23 | .optimize = .Debug, |
| 24 | .target = .{ | 24 | .target = .{ |
| 25 | .cpu_arch = .wasm32, | 25 | .cpu_arch = .wasm32, |
| 26 | .cpu_model = .{ .explicit = &std.Target.wasm.cpu.mvp }, | 26 | .cpu_model = .{ .explicit = &std.Target.wasm.cpu.mvp }, |
| ... | @@ -32,7 +32,7 @@ pub fn build(b: *std.Build) void { | ... | @@ -32,7 +32,7 @@ pub fn build(b: *std.Build) void { |
| 32 | lib.addObject(c_obj); | 32 | lib.addObject(c_obj); |
| 33 | 33 | ||
| 34 | // Verify the result contains the features from the C Object file. | 34 | // Verify the result contains the features from the C Object file. |
| 35 | const check = lib.checkObject(.wasm); | 35 | const check = lib.checkObject(); |
| 36 | check.checkStart("name target_features"); | 36 | check.checkStart("name target_features"); |
| 37 | check.checkNext("features 7"); | 37 | check.checkNext("features 7"); |
| 38 | check.checkNext("+ atomics"); | 38 | check.checkNext("+ atomics"); |
| ... | @@ -45,4 +45,5 @@ pub fn build(b: *std.Build) void { | ... | @@ -45,4 +45,5 @@ pub fn build(b: *std.Build) void { |
| 45 | 45 | ||
| 46 | const test_step = b.step("test", "Run linker test"); | 46 | const test_step = b.step("test", "Run linker test"); |
| 47 | test_step.dependOn(&check.step); | 47 | test_step.dependOn(&check.step); |
| 48 | b.default_step = test_step; | ||
| 48 | } | 49 | } |
test/link/wasm/producers/build.zig+14-7| ... | @@ -1,26 +1,33 @@ | ... | @@ -1,26 +1,33 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | 3 | ||
| 4 | pub const requires_stage2 = true; | ||
| 5 | |||
| 4 | pub fn build(b: *std.Build) void { | 6 | pub fn build(b: *std.Build) void { |
| 5 | const test_step = b.step("test", "Test"); | 7 | const test_step = b.step("test", "Test it"); |
| 6 | test_step.dependOn(b.getInstallStep()); | 8 | b.default_step = test_step; |
| 9 | |||
| 10 | add(b, test_step, .Debug); | ||
| 11 | add(b, test_step, .ReleaseFast); | ||
| 12 | add(b, test_step, .ReleaseSmall); | ||
| 13 | add(b, test_step, .ReleaseSafe); | ||
| 14 | } | ||
| 7 | 15 | ||
| 16 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 8 | const lib = b.addSharedLibrary(.{ | 17 | const lib = b.addSharedLibrary(.{ |
| 9 | .name = "lib", | 18 | .name = "lib", |
| 10 | .root_source_file = .{ .path = "lib.zig" }, | 19 | .root_source_file = .{ .path = "lib.zig" }, |
| 11 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, | 20 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, |
| 12 | .optimize = b.standardOptimizeOption(.{}), | 21 | .optimize = optimize, |
| 13 | }); | 22 | }); |
| 14 | lib.use_llvm = false; | 23 | lib.use_llvm = false; |
| 15 | lib.use_lld = false; | 24 | lib.use_lld = false; |
| 16 | lib.strip = false; | 25 | lib.strip = false; |
| 17 | lib.install(); | 26 | lib.install(); |
| 18 | 27 | ||
| 19 | const zig_version = builtin.zig_version; | 28 | const version_fmt = "version " ++ builtin.zig_version_string; |
| 20 | var version_buf: [100]u8 = undefined; | ||
| 21 | const version_fmt = std.fmt.bufPrint(&version_buf, "version {}", .{zig_version}) catch unreachable; | ||
| 22 | 29 | ||
| 23 | const check_lib = lib.checkObject(.wasm); | 30 | const check_lib = lib.checkObject(); |
| 24 | check_lib.checkStart("name producers"); | 31 | check_lib.checkStart("name producers"); |
| 25 | check_lib.checkNext("fields 2"); | 32 | check_lib.checkNext("fields 2"); |
| 26 | check_lib.checkNext("field_name language"); | 33 | check_lib.checkNext("field_name language"); |
test/link/wasm/segments/build.zig+13-4| ... | @@ -1,21 +1,30 @@ | ... | @@ -1,21 +1,30 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_stage2 = true; | ||
| 4 | |||
| 3 | pub fn build(b: *std.Build) void { | 5 | pub fn build(b: *std.Build) void { |
| 4 | const test_step = b.step("test", "Test"); | 6 | const test_step = b.step("test", "Test it"); |
| 5 | test_step.dependOn(b.getInstallStep()); | 7 | b.default_step = test_step; |
| 8 | |||
| 9 | add(b, test_step, .Debug); | ||
| 10 | add(b, test_step, .ReleaseFast); | ||
| 11 | add(b, test_step, .ReleaseSmall); | ||
| 12 | add(b, test_step, .ReleaseSafe); | ||
| 13 | } | ||
| 6 | 14 | ||
| 15 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 7 | const lib = b.addSharedLibrary(.{ | 16 | const lib = b.addSharedLibrary(.{ |
| 8 | .name = "lib", | 17 | .name = "lib", |
| 9 | .root_source_file = .{ .path = "lib.zig" }, | 18 | .root_source_file = .{ .path = "lib.zig" }, |
| 10 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, | 19 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, |
| 11 | .optimize = b.standardOptimizeOption(.{}), | 20 | .optimize = optimize, |
| 12 | }); | 21 | }); |
| 13 | lib.use_llvm = false; | 22 | lib.use_llvm = false; |
| 14 | lib.use_lld = false; | 23 | lib.use_lld = false; |
| 15 | lib.strip = false; | 24 | lib.strip = false; |
| 16 | lib.install(); | 25 | lib.install(); |
| 17 | 26 | ||
| 18 | const check_lib = lib.checkObject(.wasm); | 27 | const check_lib = lib.checkObject(); |
| 19 | check_lib.checkStart("Section data"); | 28 | check_lib.checkStart("Section data"); |
| 20 | check_lib.checkNext("entries 2"); // rodata & data, no bss because we're exporting memory | 29 | check_lib.checkNext("entries 2"); // rodata & data, no bss because we're exporting memory |
| 21 | 30 |
test/link/wasm/stack_pointer/build.zig+13-4| ... | @@ -1,14 +1,23 @@ | ... | @@ -1,14 +1,23 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_stage2 = true; | ||
| 4 | |||
| 3 | pub fn build(b: *std.Build) void { | 5 | pub fn build(b: *std.Build) void { |
| 4 | const test_step = b.step("test", "Test"); | 6 | const test_step = b.step("test", "Test it"); |
| 5 | test_step.dependOn(b.getInstallStep()); | 7 | b.default_step = test_step; |
| 8 | |||
| 9 | add(b, test_step, .Debug); | ||
| 10 | add(b, test_step, .ReleaseFast); | ||
| 11 | add(b, test_step, .ReleaseSmall); | ||
| 12 | add(b, test_step, .ReleaseSafe); | ||
| 13 | } | ||
| 6 | 14 | ||
| 15 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 7 | const lib = b.addSharedLibrary(.{ | 16 | const lib = b.addSharedLibrary(.{ |
| 8 | .name = "lib", | 17 | .name = "lib", |
| 9 | .root_source_file = .{ .path = "lib.zig" }, | 18 | .root_source_file = .{ .path = "lib.zig" }, |
| 10 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, | 19 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, |
| 11 | .optimize = b.standardOptimizeOption(.{}), | 20 | .optimize = optimize, |
| 12 | }); | 21 | }); |
| 13 | lib.use_llvm = false; | 22 | lib.use_llvm = false; |
| 14 | lib.use_lld = false; | 23 | lib.use_lld = false; |
| ... | @@ -16,7 +25,7 @@ pub fn build(b: *std.Build) void { | ... | @@ -16,7 +25,7 @@ pub fn build(b: *std.Build) void { |
| 16 | lib.stack_size = std.wasm.page_size * 2; // set an explicit stack size | 25 | lib.stack_size = std.wasm.page_size * 2; // set an explicit stack size |
| 17 | lib.install(); | 26 | lib.install(); |
| 18 | 27 | ||
| 19 | const check_lib = lib.checkObject(.wasm); | 28 | const check_lib = lib.checkObject(); |
| 20 | 29 | ||
| 21 | // ensure global exists and its initial value is equal to explitic stack size | 30 | // ensure global exists and its initial value is equal to explitic stack size |
| 22 | check_lib.checkStart("Section global"); | 31 | check_lib.checkStart("Section global"); |
test/link/wasm/type/build.zig+13-4| ... | @@ -1,21 +1,30 @@ | ... | @@ -1,21 +1,30 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_stage2 = true; | ||
| 4 | |||
| 3 | pub fn build(b: *std.Build) void { | 5 | pub fn build(b: *std.Build) void { |
| 4 | const test_step = b.step("test", "Test"); | 6 | const test_step = b.step("test", "Test it"); |
| 5 | test_step.dependOn(b.getInstallStep()); | 7 | b.default_step = test_step; |
| 8 | |||
| 9 | add(b, test_step, .Debug); | ||
| 10 | add(b, test_step, .ReleaseFast); | ||
| 11 | add(b, test_step, .ReleaseSmall); | ||
| 12 | add(b, test_step, .ReleaseSafe); | ||
| 13 | } | ||
| 6 | 14 | ||
| 15 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 7 | const lib = b.addSharedLibrary(.{ | 16 | const lib = b.addSharedLibrary(.{ |
| 8 | .name = "lib", | 17 | .name = "lib", |
| 9 | .root_source_file = .{ .path = "lib.zig" }, | 18 | .root_source_file = .{ .path = "lib.zig" }, |
| 10 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, | 19 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, |
| 11 | .optimize = b.standardOptimizeOption(.{}), | 20 | .optimize = optimize, |
| 12 | }); | 21 | }); |
| 13 | lib.use_llvm = false; | 22 | lib.use_llvm = false; |
| 14 | lib.use_lld = false; | 23 | lib.use_lld = false; |
| 15 | lib.strip = false; | 24 | lib.strip = false; |
| 16 | lib.install(); | 25 | lib.install(); |
| 17 | 26 | ||
| 18 | const check_lib = lib.checkObject(.wasm); | 27 | const check_lib = lib.checkObject(); |
| 19 | check_lib.checkStart("Section type"); | 28 | check_lib.checkStart("Section type"); |
| 20 | // only 2 entries, although we have 3 functions. | 29 | // only 2 entries, although we have 3 functions. |
| 21 | // This is to test functions with the same function signature | 30 | // This is to test functions with the same function signature |
test/nvptx.zig created+106| ... | @@ -0,0 +1,106 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const Cases = @import("src/Cases.zig"); | ||
| 3 | |||
| 4 | pub fn addCases(ctx: *Cases) !void { | ||
| 5 | { | ||
| 6 | var case = addPtx(ctx, "simple addition and subtraction"); | ||
| 7 | |||
| 8 | case.addCompile( | ||
| 9 | \\fn add(a: i32, b: i32) i32 { | ||
| 10 | \\ return a + b; | ||
| 11 | \\} | ||
| 12 | \\ | ||
| 13 | \\pub export fn add_and_substract(a: i32, out: *i32) callconv(.PtxKernel) void { | ||
| 14 | \\ const x = add(a, 7); | ||
| 15 | \\ var y = add(2, 0); | ||
| 16 | \\ y -= x; | ||
| 17 | \\ out.* = y; | ||
| 18 | \\} | ||
| 19 | ); | ||
| 20 | } | ||
| 21 | |||
| 22 | { | ||
| 23 | var case = addPtx(ctx, "read special registers"); | ||
| 24 | |||
| 25 | case.addCompile( | ||
| 26 | \\fn threadIdX() u32 { | ||
| 27 | \\ return asm ("mov.u32 \t%[r], %tid.x;" | ||
| 28 | \\ : [r] "=r" (-> u32), | ||
| 29 | \\ ); | ||
| 30 | \\} | ||
| 31 | \\ | ||
| 32 | \\pub export fn special_reg(a: []const i32, out: []i32) callconv(.PtxKernel) void { | ||
| 33 | \\ const i = threadIdX(); | ||
| 34 | \\ out[i] = a[i] + 7; | ||
| 35 | \\} | ||
| 36 | ); | ||
| 37 | } | ||
| 38 | |||
| 39 | { | ||
| 40 | var case = addPtx(ctx, "address spaces"); | ||
| 41 | |||
| 42 | case.addCompile( | ||
| 43 | \\var x: i32 addrspace(.global) = 0; | ||
| 44 | \\ | ||
| 45 | \\pub export fn increment(out: *i32) callconv(.PtxKernel) void { | ||
| 46 | \\ x += 1; | ||
| 47 | \\ out.* = x; | ||
| 48 | \\} | ||
| 49 | ); | ||
| 50 | } | ||
| 51 | |||
| 52 | { | ||
| 53 | var case = addPtx(ctx, "reduce in shared mem"); | ||
| 54 | case.addCompile( | ||
| 55 | \\fn threadIdX() u32 { | ||
| 56 | \\ return asm ("mov.u32 \t%[r], %tid.x;" | ||
| 57 | \\ : [r] "=r" (-> u32), | ||
| 58 | \\ ); | ||
| 59 | \\} | ||
| 60 | \\ | ||
| 61 | \\ var _sdata: [1024]f32 addrspace(.shared) = undefined; | ||
| 62 | \\ pub export fn reduceSum(d_x: []const f32, out: *f32) callconv(.PtxKernel) void { | ||
| 63 | \\ var sdata = @addrSpaceCast(.generic, &_sdata); | ||
| 64 | \\ const tid: u32 = threadIdX(); | ||
| 65 | \\ var sum = d_x[tid]; | ||
| 66 | \\ sdata[tid] = sum; | ||
| 67 | \\ asm volatile ("bar.sync \t0;"); | ||
| 68 | \\ var s: u32 = 512; | ||
| 69 | \\ while (s > 0) : (s = s >> 1) { | ||
| 70 | \\ if (tid < s) { | ||
| 71 | \\ sum += sdata[tid + s]; | ||
| 72 | \\ sdata[tid] = sum; | ||
| 73 | \\ } | ||
| 74 | \\ asm volatile ("bar.sync \t0;"); | ||
| 75 | \\ } | ||
| 76 | \\ | ||
| 77 | \\ if (tid == 0) { | ||
| 78 | \\ out.* = sum; | ||
| 79 | \\ } | ||
| 80 | \\ } | ||
| 81 | ); | ||
| 82 | } | ||
| 83 | } | ||
| 84 | |||
| 85 | const nvptx_target = std.zig.CrossTarget{ | ||
| 86 | .cpu_arch = .nvptx64, | ||
| 87 | .os_tag = .cuda, | ||
| 88 | }; | ||
| 89 | |||
| 90 | pub fn addPtx( | ||
| 91 | ctx: *Cases, | ||
| 92 | name: []const u8, | ||
| 93 | ) *Cases.Case { | ||
| 94 | ctx.cases.append(.{ | ||
| 95 | .name = name, | ||
| 96 | .target = nvptx_target, | ||
| 97 | .updates = std.ArrayList(Cases.Update).init(ctx.cases.allocator), | ||
| 98 | .output_mode = .Obj, | ||
| 99 | .deps = std.ArrayList(Cases.DepModule).init(ctx.cases.allocator), | ||
| 100 | .link_libc = false, | ||
| 101 | .backend = .llvm, | ||
| 102 | // Bug in Debug mode | ||
| 103 | .optimize_mode = .ReleaseSafe, | ||
| 104 | }) catch @panic("out of memory"); | ||
| 105 | return &ctx.cases.items[ctx.cases.items.len - 1]; | ||
| 106 | } | ||
test/src/Cases.zig created+1587| ... | @@ -0,0 +1,1587 @@ | ||
| 1 | gpa: Allocator, | ||
| 2 | arena: Allocator, | ||
| 3 | cases: std.ArrayList(Case), | ||
| 4 | incremental_cases: std.ArrayList(IncrementalCase), | ||
| 5 | |||
| 6 | pub const IncrementalCase = struct { | ||
| 7 | base_path: []const u8, | ||
| 8 | }; | ||
| 9 | |||
| 10 | pub const Update = struct { | ||
| 11 | /// The input to the current update. We simulate an incremental update | ||
| 12 | /// with the file's contents changed to this value each update. | ||
| 13 | /// | ||
| 14 | /// This value can change entirely between updates, which would be akin | ||
| 15 | /// to deleting the source file and creating a new one from scratch; or | ||
| 16 | /// you can keep it mostly consistent, with small changes, testing the | ||
| 17 | /// effects of the incremental compilation. | ||
| 18 | files: std.ArrayList(File), | ||
| 19 | /// This is a description of what happens with the update, for debugging | ||
| 20 | /// purposes. | ||
| 21 | name: []const u8, | ||
| 22 | case: union(enum) { | ||
| 23 | /// Check that it compiles with no errors. | ||
| 24 | Compile: void, | ||
| 25 | /// Check the main binary output file against an expected set of bytes. | ||
| 26 | /// This is most useful with, for example, `-ofmt=c`. | ||
| 27 | CompareObjectFile: []const u8, | ||
| 28 | /// An error update attempts to compile bad code, and ensures that it | ||
| 29 | /// fails to compile, and for the expected reasons. | ||
| 30 | /// A slice containing the expected stderr template, which | ||
| 31 | /// gets some values substituted. | ||
| 32 | Error: []const []const u8, | ||
| 33 | /// An execution update compiles and runs the input, testing the | ||
| 34 | /// stdout against the expected results | ||
| 35 | /// This is a slice containing the expected message. | ||
| 36 | Execution: []const u8, | ||
| 37 | /// A header update compiles the input with the equivalent of | ||
| 38 | /// `-femit-h` and tests the produced header against the | ||
| 39 | /// expected result | ||
| 40 | Header: []const u8, | ||
| 41 | }, | ||
| 42 | |||
| 43 | pub fn addSourceFile(update: *Update, name: []const u8, src: [:0]const u8) void { | ||
| 44 | update.files.append(.{ .path = name, .src = src }) catch @panic("out of memory"); | ||
| 45 | } | ||
| 46 | }; | ||
| 47 | |||
| 48 | pub const File = struct { | ||
| 49 | src: [:0]const u8, | ||
| 50 | path: []const u8, | ||
| 51 | }; | ||
| 52 | |||
| 53 | pub const DepModule = struct { | ||
| 54 | name: []const u8, | ||
| 55 | path: []const u8, | ||
| 56 | }; | ||
| 57 | |||
| 58 | pub const Backend = enum { | ||
| 59 | stage1, | ||
| 60 | stage2, | ||
| 61 | llvm, | ||
| 62 | }; | ||
| 63 | |||
| 64 | /// A `Case` consists of a list of `Update`. The same `Compilation` is used for each | ||
| 65 | /// update, so each update's source is treated as a single file being | ||
| 66 | /// updated by the test harness and incrementally compiled. | ||
| 67 | pub const Case = struct { | ||
| 68 | /// The name of the test case. This is shown if a test fails, and | ||
| 69 | /// otherwise ignored. | ||
| 70 | name: []const u8, | ||
| 71 | /// The platform the test targets. For non-native platforms, an emulator | ||
| 72 | /// such as QEMU is required for tests to complete. | ||
| 73 | target: CrossTarget, | ||
| 74 | /// In order to be able to run e.g. Execution updates, this must be set | ||
| 75 | /// to Executable. | ||
| 76 | output_mode: std.builtin.OutputMode, | ||
| 77 | optimize_mode: std.builtin.Mode = .Debug, | ||
| 78 | updates: std.ArrayList(Update), | ||
| 79 | emit_h: bool = false, | ||
| 80 | is_test: bool = false, | ||
| 81 | expect_exact: bool = false, | ||
| 82 | backend: Backend = .stage2, | ||
| 83 | link_libc: bool = false, | ||
| 84 | |||
| 85 | deps: std.ArrayList(DepModule), | ||
| 86 | |||
| 87 | pub fn addSourceFile(case: *Case, name: []const u8, src: [:0]const u8) void { | ||
| 88 | const update = &case.updates.items[case.updates.items.len - 1]; | ||
| 89 | update.files.append(.{ .path = name, .src = src }) catch @panic("OOM"); | ||
| 90 | } | ||
| 91 | |||
| 92 | pub fn addDepModule(case: *Case, name: []const u8, path: []const u8) void { | ||
| 93 | case.deps.append(.{ | ||
| 94 | .name = name, | ||
| 95 | .path = path, | ||
| 96 | }) catch @panic("out of memory"); | ||
| 97 | } | ||
| 98 | |||
| 99 | /// Adds a subcase in which the module is updated with `src`, compiled, | ||
| 100 | /// run, and the output is tested against `result`. | ||
| 101 | pub fn addCompareOutput(self: *Case, src: [:0]const u8, result: []const u8) void { | ||
| 102 | self.updates.append(.{ | ||
| 103 | .files = std.ArrayList(File).init(self.updates.allocator), | ||
| 104 | .name = "update", | ||
| 105 | .case = .{ .Execution = result }, | ||
| 106 | }) catch @panic("out of memory"); | ||
| 107 | addSourceFile(self, "tmp.zig", src); | ||
| 108 | } | ||
| 109 | |||
| 110 | pub fn addError(self: *Case, src: [:0]const u8, errors: []const []const u8) void { | ||
| 111 | return self.addErrorNamed("update", src, errors); | ||
| 112 | } | ||
| 113 | |||
| 114 | /// Adds a subcase in which the module is updated with `src`, which | ||
| 115 | /// should contain invalid input, and ensures that compilation fails | ||
| 116 | /// for the expected reasons, given in sequential order in `errors` in | ||
| 117 | /// the form `:line:column: error: message`. | ||
| 118 | pub fn addErrorNamed( | ||
| 119 | self: *Case, | ||
| 120 | name: []const u8, | ||
| 121 | src: [:0]const u8, | ||
| 122 | errors: []const []const u8, | ||
| 123 | ) void { | ||
| 124 | assert(errors.len != 0); | ||
| 125 | self.updates.append(.{ | ||
| 126 | .files = std.ArrayList(File).init(self.updates.allocator), | ||
| 127 | .name = name, | ||
| 128 | .case = .{ .Error = errors }, | ||
| 129 | }) catch @panic("out of memory"); | ||
| 130 | addSourceFile(self, "tmp.zig", src); | ||
| 131 | } | ||
| 132 | |||
| 133 | /// Adds a subcase in which the module is updated with `src`, and | ||
| 134 | /// asserts that it compiles without issue | ||
| 135 | pub fn addCompile(self: *Case, src: [:0]const u8) void { | ||
| 136 | self.updates.append(.{ | ||
| 137 | .files = std.ArrayList(File).init(self.updates.allocator), | ||
| 138 | .name = "compile", | ||
| 139 | .case = .{ .Compile = {} }, | ||
| 140 | }) catch @panic("out of memory"); | ||
| 141 | addSourceFile(self, "tmp.zig", src); | ||
| 142 | } | ||
| 143 | }; | ||
| 144 | |||
| 145 | pub fn addExe( | ||
| 146 | ctx: *Cases, | ||
| 147 | name: []const u8, | ||
| 148 | target: CrossTarget, | ||
| 149 | ) *Case { | ||
| 150 | ctx.cases.append(Case{ | ||
| 151 | .name = name, | ||
| 152 | .target = target, | ||
| 153 | .updates = std.ArrayList(Update).init(ctx.cases.allocator), | ||
| 154 | .output_mode = .Exe, | ||
| 155 | .deps = std.ArrayList(DepModule).init(ctx.arena), | ||
| 156 | }) catch @panic("out of memory"); | ||
| 157 | return &ctx.cases.items[ctx.cases.items.len - 1]; | ||
| 158 | } | ||
| 159 | |||
| 160 | /// Adds a test case for Zig input, producing an executable | ||
| 161 | pub fn exe(ctx: *Cases, name: []const u8, target: CrossTarget) *Case { | ||
| 162 | return ctx.addExe(name, target); | ||
| 163 | } | ||
| 164 | |||
| 165 | pub fn exeFromCompiledC(ctx: *Cases, name: []const u8, target: CrossTarget) *Case { | ||
| 166 | var target_adjusted = target; | ||
| 167 | target_adjusted.ofmt = .c; | ||
| 168 | ctx.cases.append(Case{ | ||
| 169 | .name = name, | ||
| 170 | .target = target_adjusted, | ||
| 171 | .updates = std.ArrayList(Update).init(ctx.cases.allocator), | ||
| 172 | .output_mode = .Exe, | ||
| 173 | .deps = std.ArrayList(DepModule).init(ctx.arena), | ||
| 174 | .link_libc = true, | ||
| 175 | }) catch @panic("out of memory"); | ||
| 176 | return &ctx.cases.items[ctx.cases.items.len - 1]; | ||
| 177 | } | ||
| 178 | |||
| 179 | /// Adds a test case that uses the LLVM backend to emit an executable. | ||
| 180 | /// Currently this implies linking libc, because only then we can generate a testable executable. | ||
| 181 | pub fn exeUsingLlvmBackend(ctx: *Cases, name: []const u8, target: CrossTarget) *Case { | ||
| 182 | ctx.cases.append(Case{ | ||
| 183 | .name = name, | ||
| 184 | .target = target, | ||
| 185 | .updates = std.ArrayList(Update).init(ctx.cases.allocator), | ||
| 186 | .output_mode = .Exe, | ||
| 187 | .deps = std.ArrayList(DepModule).init(ctx.arena), | ||
| 188 | .backend = .llvm, | ||
| 189 | .link_libc = true, | ||
| 190 | }) catch @panic("out of memory"); | ||
| 191 | return &ctx.cases.items[ctx.cases.items.len - 1]; | ||
| 192 | } | ||
| 193 | |||
| 194 | pub fn addObj( | ||
| 195 | ctx: *Cases, | ||
| 196 | name: []const u8, | ||
| 197 | target: CrossTarget, | ||
| 198 | ) *Case { | ||
| 199 | ctx.cases.append(Case{ | ||
| 200 | .name = name, | ||
| 201 | .target = target, | ||
| 202 | .updates = std.ArrayList(Update).init(ctx.cases.allocator), | ||
| 203 | .output_mode = .Obj, | ||
| 204 | .deps = std.ArrayList(DepModule).init(ctx.arena), | ||
| 205 | }) catch @panic("out of memory"); | ||
| 206 | return &ctx.cases.items[ctx.cases.items.len - 1]; | ||
| 207 | } | ||
| 208 | |||
| 209 | pub fn addTest( | ||
| 210 | ctx: *Cases, | ||
| 211 | name: []const u8, | ||
| 212 | target: CrossTarget, | ||
| 213 | ) *Case { | ||
| 214 | ctx.cases.append(Case{ | ||
| 215 | .name = name, | ||
| 216 | .target = target, | ||
| 217 | .updates = std.ArrayList(Update).init(ctx.cases.allocator), | ||
| 218 | .output_mode = .Exe, | ||
| 219 | .is_test = true, | ||
| 220 | .deps = std.ArrayList(DepModule).init(ctx.arena), | ||
| 221 | }) catch @panic("out of memory"); | ||
| 222 | return &ctx.cases.items[ctx.cases.items.len - 1]; | ||
| 223 | } | ||
| 224 | |||
| 225 | /// Adds a test case for Zig input, producing an object file. | ||
| 226 | pub fn obj(ctx: *Cases, name: []const u8, target: CrossTarget) *Case { | ||
| 227 | return ctx.addObj(name, target); | ||
| 228 | } | ||
| 229 | |||
| 230 | /// Adds a test case for ZIR input, producing an object file. | ||
| 231 | pub fn objZIR(ctx: *Cases, name: []const u8, target: CrossTarget) *Case { | ||
| 232 | return ctx.addObj(name, target, .ZIR); | ||
| 233 | } | ||
| 234 | |||
| 235 | /// Adds a test case for Zig or ZIR input, producing C code. | ||
| 236 | pub fn addC(ctx: *Cases, name: []const u8, target: CrossTarget) *Case { | ||
| 237 | var target_adjusted = target; | ||
| 238 | target_adjusted.ofmt = std.Target.ObjectFormat.c; | ||
| 239 | ctx.cases.append(Case{ | ||
| 240 | .name = name, | ||
| 241 | .target = target_adjusted, | ||
| 242 | .updates = std.ArrayList(Update).init(ctx.cases.allocator), | ||
| 243 | .output_mode = .Obj, | ||
| 244 | .deps = std.ArrayList(DepModule).init(ctx.arena), | ||
| 245 | }) catch @panic("out of memory"); | ||
| 246 | return &ctx.cases.items[ctx.cases.items.len - 1]; | ||
| 247 | } | ||
| 248 | |||
| 249 | pub fn addCompareOutput( | ||
| 250 | ctx: *Cases, | ||
| 251 | name: []const u8, | ||
| 252 | src: [:0]const u8, | ||
| 253 | expected_stdout: []const u8, | ||
| 254 | ) void { | ||
| 255 | ctx.addExe(name, .{}).addCompareOutput(src, expected_stdout); | ||
| 256 | } | ||
| 257 | |||
| 258 | /// Adds a test case that compiles the Zig source given in `src`, executes | ||
| 259 | /// it, runs it, and tests the output against `expected_stdout` | ||
| 260 | pub fn compareOutput( | ||
| 261 | ctx: *Cases, | ||
| 262 | name: []const u8, | ||
| 263 | src: [:0]const u8, | ||
| 264 | expected_stdout: []const u8, | ||
| 265 | ) void { | ||
| 266 | return ctx.addCompareOutput(name, src, expected_stdout); | ||
| 267 | } | ||
| 268 | |||
| 269 | pub fn addTransform( | ||
| 270 | ctx: *Cases, | ||
| 271 | name: []const u8, | ||
| 272 | target: CrossTarget, | ||
| 273 | src: [:0]const u8, | ||
| 274 | result: [:0]const u8, | ||
| 275 | ) void { | ||
| 276 | ctx.addObj(name, target).addTransform(src, result); | ||
| 277 | } | ||
| 278 | |||
| 279 | /// Adds a test case that compiles the Zig given in `src` to ZIR and tests | ||
| 280 | /// the ZIR against `result` | ||
| 281 | pub fn transform( | ||
| 282 | ctx: *Cases, | ||
| 283 | name: []const u8, | ||
| 284 | target: CrossTarget, | ||
| 285 | src: [:0]const u8, | ||
| 286 | result: [:0]const u8, | ||
| 287 | ) void { | ||
| 288 | ctx.addTransform(name, target, src, result); | ||
| 289 | } | ||
| 290 | |||
| 291 | pub fn addError( | ||
| 292 | ctx: *Cases, | ||
| 293 | name: []const u8, | ||
| 294 | target: CrossTarget, | ||
| 295 | src: [:0]const u8, | ||
| 296 | expected_errors: []const []const u8, | ||
| 297 | ) void { | ||
| 298 | ctx.addObj(name, target).addError(src, expected_errors); | ||
| 299 | } | ||
| 300 | |||
| 301 | /// Adds a test case that ensures that the Zig given in `src` fails to | ||
| 302 | /// compile for the expected reasons, given in sequential order in | ||
| 303 | /// `expected_errors` in the form `:line:column: error: message`. | ||
| 304 | pub fn compileError( | ||
| 305 | ctx: *Cases, | ||
| 306 | name: []const u8, | ||
| 307 | target: CrossTarget, | ||
| 308 | src: [:0]const u8, | ||
| 309 | expected_errors: []const []const u8, | ||
| 310 | ) void { | ||
| 311 | ctx.addError(name, target, src, expected_errors); | ||
| 312 | } | ||
| 313 | |||
| 314 | /// Adds a test case that asserts that the Zig given in `src` compiles | ||
| 315 | /// without any errors. | ||
| 316 | pub fn addCompile( | ||
| 317 | ctx: *Cases, | ||
| 318 | name: []const u8, | ||
| 319 | target: CrossTarget, | ||
| 320 | src: [:0]const u8, | ||
| 321 | ) void { | ||
| 322 | ctx.addObj(name, target).addCompile(src); | ||
| 323 | } | ||
| 324 | |||
| 325 | /// Adds a test for each file in the provided directory. | ||
| 326 | /// Testing strategy (TestStrategy) is inferred automatically from filenames. | ||
| 327 | /// Recurses nested directories. | ||
| 328 | /// | ||
| 329 | /// Each file should include a test manifest as a contiguous block of comments at | ||
| 330 | /// the end of the file. The first line should be the test type, followed by a set of | ||
| 331 | /// key-value config values, followed by a blank line, then the expected output. | ||
| 332 | pub fn addFromDir(ctx: *Cases, dir: std.fs.IterableDir) void { | ||
| 333 | var current_file: []const u8 = "none"; | ||
| 334 | ctx.addFromDirInner(dir, &current_file) catch |err| { | ||
| 335 | std.debug.panic("test harness failed to process file '{s}': {s}\n", .{ | ||
| 336 | current_file, @errorName(err), | ||
| 337 | }); | ||
| 338 | }; | ||
| 339 | } | ||
| 340 | |||
| 341 | fn addFromDirInner( | ||
| 342 | ctx: *Cases, | ||
| 343 | iterable_dir: std.fs.IterableDir, | ||
| 344 | /// This is kept up to date with the currently being processed file so | ||
| 345 | /// that if any errors occur the caller knows it happened during this file. | ||
| 346 | current_file: *[]const u8, | ||
| 347 | ) !void { | ||
| 348 | var it = try iterable_dir.walk(ctx.arena); | ||
| 349 | var filenames = std.ArrayList([]const u8).init(ctx.arena); | ||
| 350 | |||
| 351 | while (try it.next()) |entry| { | ||
| 352 | if (entry.kind != .File) continue; | ||
| 353 | |||
| 354 | // Ignore stuff such as .swp files | ||
| 355 | switch (Compilation.classifyFileExt(entry.basename)) { | ||
| 356 | .unknown => continue, | ||
| 357 | else => {}, | ||
| 358 | } | ||
| 359 | try filenames.append(try ctx.arena.dupe(u8, entry.path)); | ||
| 360 | } | ||
| 361 | |||
| 362 | // Sort filenames, so that incremental tests are contiguous and in-order | ||
| 363 | sortTestFilenames(filenames.items); | ||
| 364 | |||
| 365 | var test_it = TestIterator{ .filenames = filenames.items }; | ||
| 366 | while (test_it.next()) |maybe_batch| { | ||
| 367 | const batch = maybe_batch orelse break; | ||
| 368 | const strategy: TestStrategy = if (batch.len > 1) .incremental else .independent; | ||
| 369 | const filename = batch[0]; | ||
| 370 | current_file.* = filename; | ||
| 371 | if (strategy == .incremental) { | ||
| 372 | try ctx.incremental_cases.append(.{ .base_path = filename }); | ||
| 373 | continue; | ||
| 374 | } | ||
| 375 | |||
| 376 | const max_file_size = 10 * 1024 * 1024; | ||
| 377 | const src = try iterable_dir.dir.readFileAllocOptions(ctx.arena, filename, max_file_size, null, 1, 0); | ||
| 378 | |||
| 379 | // Parse the manifest | ||
| 380 | var manifest = try TestManifest.parse(ctx.arena, src); | ||
| 381 | |||
| 382 | const backends = try manifest.getConfigForKeyAlloc(ctx.arena, "backend", Backend); | ||
| 383 | const targets = try manifest.getConfigForKeyAlloc(ctx.arena, "target", CrossTarget); | ||
| 384 | const is_test = try manifest.getConfigForKeyAssertSingle("is_test", bool); | ||
| 385 | const link_libc = try manifest.getConfigForKeyAssertSingle("link_libc", bool); | ||
| 386 | const output_mode = try manifest.getConfigForKeyAssertSingle("output_mode", std.builtin.OutputMode); | ||
| 387 | |||
| 388 | var cases = std.ArrayList(usize).init(ctx.arena); | ||
| 389 | |||
| 390 | // Cross-product to get all possible test combinations | ||
| 391 | for (backends) |backend| { | ||
| 392 | for (targets) |target| { | ||
| 393 | const next = ctx.cases.items.len; | ||
| 394 | try ctx.cases.append(.{ | ||
| 395 | .name = std.fs.path.stem(filename), | ||
| 396 | .target = target, | ||
| 397 | .backend = backend, | ||
| 398 | .updates = std.ArrayList(Cases.Update).init(ctx.cases.allocator), | ||
| 399 | .is_test = is_test, | ||
| 400 | .output_mode = output_mode, | ||
| 401 | .link_libc = link_libc, | ||
| 402 | .deps = std.ArrayList(DepModule).init(ctx.cases.allocator), | ||
| 403 | }); | ||
| 404 | try cases.append(next); | ||
| 405 | } | ||
| 406 | } | ||
| 407 | |||
| 408 | for (cases.items) |case_index| { | ||
| 409 | const case = &ctx.cases.items[case_index]; | ||
| 410 | switch (manifest.type) { | ||
| 411 | .compile => { | ||
| 412 | case.addCompile(src); | ||
| 413 | }, | ||
| 414 | .@"error" => { | ||
| 415 | const errors = try manifest.trailingAlloc(ctx.arena); | ||
| 416 | case.addError(src, errors); | ||
| 417 | }, | ||
| 418 | .run => { | ||
| 419 | var output = std.ArrayList(u8).init(ctx.arena); | ||
| 420 | var trailing_it = manifest.trailing(); | ||
| 421 | while (trailing_it.next()) |line| { | ||
| 422 | try output.appendSlice(line); | ||
| 423 | try output.append('\n'); | ||
| 424 | } | ||
| 425 | if (output.items.len > 0) { | ||
| 426 | try output.resize(output.items.len - 1); | ||
| 427 | } | ||
| 428 | case.addCompareOutput(src, try output.toOwnedSlice()); | ||
| 429 | }, | ||
| 430 | .cli => @panic("TODO cli tests"), | ||
| 431 | } | ||
| 432 | } | ||
| 433 | } else |err| { | ||
| 434 | // make sure the current file is set to the file that produced an error | ||
| 435 | current_file.* = test_it.currentFilename(); | ||
| 436 | return err; | ||
| 437 | } | ||
| 438 | } | ||
| 439 | |||
| 440 | pub fn init(gpa: Allocator, arena: Allocator) Cases { | ||
| 441 | return .{ | ||
| 442 | .gpa = gpa, | ||
| 443 | .cases = std.ArrayList(Case).init(gpa), | ||
| 444 | .incremental_cases = std.ArrayList(IncrementalCase).init(gpa), | ||
| 445 | .arena = arena, | ||
| 446 | }; | ||
| 447 | } | ||
| 448 | |||
| 449 | pub fn lowerToBuildSteps( | ||
| 450 | self: *Cases, | ||
| 451 | b: *std.Build, | ||
| 452 | parent_step: *std.Build.Step, | ||
| 453 | opt_test_filter: ?[]const u8, | ||
| 454 | cases_dir_path: []const u8, | ||
| 455 | incremental_exe: *std.Build.CompileStep, | ||
| 456 | ) void { | ||
| 457 | for (self.incremental_cases.items) |incr_case| { | ||
| 458 | if (opt_test_filter) |test_filter| { | ||
| 459 | if (std.mem.indexOf(u8, incr_case.base_path, test_filter) == null) continue; | ||
| 460 | } | ||
| 461 | const case_base_path_with_dir = std.fs.path.join(b.allocator, &.{ | ||
| 462 | cases_dir_path, incr_case.base_path, | ||
| 463 | }) catch @panic("OOM"); | ||
| 464 | const run = b.addRunArtifact(incremental_exe); | ||
| 465 | run.setName(incr_case.base_path); | ||
| 466 | run.addArgs(&.{ | ||
| 467 | case_base_path_with_dir, | ||
| 468 | b.zig_exe, | ||
| 469 | }); | ||
| 470 | run.expectStdOutEqual(""); | ||
| 471 | parent_step.dependOn(&run.step); | ||
| 472 | } | ||
| 473 | |||
| 474 | for (self.cases.items) |case| { | ||
| 475 | if (case.updates.items.len != 1) continue; // handled with incremental_cases above | ||
| 476 | assert(case.updates.items.len == 1); | ||
| 477 | const update = case.updates.items[0]; | ||
| 478 | |||
| 479 | if (opt_test_filter) |test_filter| { | ||
| 480 | if (std.mem.indexOf(u8, case.name, test_filter) == null) continue; | ||
| 481 | } | ||
| 482 | |||
| 483 | const writefiles = b.addWriteFiles(); | ||
| 484 | for (update.files.items) |file| { | ||
| 485 | writefiles.add(file.path, file.src); | ||
| 486 | } | ||
| 487 | const root_source_file = writefiles.getFileSource(update.files.items[0].path).?; | ||
| 488 | |||
| 489 | const artifact = if (case.is_test) b.addTest(.{ | ||
| 490 | .root_source_file = root_source_file, | ||
| 491 | .name = case.name, | ||
| 492 | .target = case.target, | ||
| 493 | .optimize = case.optimize_mode, | ||
| 494 | }) else switch (case.output_mode) { | ||
| 495 | .Obj => b.addObject(.{ | ||
| 496 | .root_source_file = root_source_file, | ||
| 497 | .name = case.name, | ||
| 498 | .target = case.target, | ||
| 499 | .optimize = case.optimize_mode, | ||
| 500 | }), | ||
| 501 | .Lib => b.addStaticLibrary(.{ | ||
| 502 | .root_source_file = root_source_file, | ||
| 503 | .name = case.name, | ||
| 504 | .target = case.target, | ||
| 505 | .optimize = case.optimize_mode, | ||
| 506 | }), | ||
| 507 | .Exe => b.addExecutable(.{ | ||
| 508 | .root_source_file = root_source_file, | ||
| 509 | .name = case.name, | ||
| 510 | .target = case.target, | ||
| 511 | .optimize = case.optimize_mode, | ||
| 512 | }), | ||
| 513 | }; | ||
| 514 | |||
| 515 | if (case.link_libc) artifact.linkLibC(); | ||
| 516 | |||
| 517 | switch (case.backend) { | ||
| 518 | .stage1 => continue, | ||
| 519 | .stage2 => { | ||
| 520 | artifact.use_llvm = false; | ||
| 521 | artifact.use_lld = false; | ||
| 522 | }, | ||
| 523 | .llvm => { | ||
| 524 | artifact.use_llvm = true; | ||
| 525 | }, | ||
| 526 | } | ||
| 527 | |||
| 528 | for (case.deps.items) |dep| { | ||
| 529 | artifact.addAnonymousModule(dep.name, .{ | ||
| 530 | .source_file = writefiles.getFileSource(dep.path).?, | ||
| 531 | }); | ||
| 532 | } | ||
| 533 | |||
| 534 | switch (update.case) { | ||
| 535 | .Compile => { | ||
| 536 | parent_step.dependOn(&artifact.step); | ||
| 537 | }, | ||
| 538 | .CompareObjectFile => |expected_output| { | ||
| 539 | const check = b.addCheckFile(artifact.getOutputSource(), .{ | ||
| 540 | .expected_exact = expected_output, | ||
| 541 | }); | ||
| 542 | |||
| 543 | parent_step.dependOn(&check.step); | ||
| 544 | }, | ||
| 545 | .Error => |expected_msgs| { | ||
| 546 | assert(expected_msgs.len != 0); | ||
| 547 | artifact.expect_errors = expected_msgs; | ||
| 548 | parent_step.dependOn(&artifact.step); | ||
| 549 | }, | ||
| 550 | .Execution => |expected_stdout| { | ||
| 551 | const run = b.addRunArtifact(artifact); | ||
| 552 | run.skip_foreign_checks = true; | ||
| 553 | if (!case.is_test) { | ||
| 554 | run.expectStdOutEqual(expected_stdout); | ||
| 555 | } | ||
| 556 | parent_step.dependOn(&run.step); | ||
| 557 | }, | ||
| 558 | .Header => @panic("TODO"), | ||
| 559 | } | ||
| 560 | } | ||
| 561 | } | ||
| 562 | |||
| 563 | /// Sort test filenames in-place, so that incremental test cases ("foo.0.zig", | ||
| 564 | /// "foo.1.zig", etc.) are contiguous and appear in numerical order. | ||
| 565 | fn sortTestFilenames(filenames: [][]const u8) void { | ||
| 566 | const Context = struct { | ||
| 567 | pub fn lessThan(_: @This(), a: []const u8, b: []const u8) bool { | ||
| 568 | const a_parts = getTestFileNameParts(a); | ||
| 569 | const b_parts = getTestFileNameParts(b); | ||
| 570 | |||
| 571 | // Sort "<base_name>.X.<file_ext>" based on "<base_name>" and "<file_ext>" first | ||
| 572 | return switch (std.mem.order(u8, a_parts.base_name, b_parts.base_name)) { | ||
| 573 | .lt => true, | ||
| 574 | .gt => false, | ||
| 575 | .eq => switch (std.mem.order(u8, a_parts.file_ext, b_parts.file_ext)) { | ||
| 576 | .lt => true, | ||
| 577 | .gt => false, | ||
| 578 | .eq => { | ||
| 579 | // a and b differ only in their ".X" part | ||
| 580 | |||
| 581 | // Sort "<base_name>.<file_ext>" before any "<base_name>.X.<file_ext>" | ||
| 582 | if (a_parts.test_index) |a_index| { | ||
| 583 | if (b_parts.test_index) |b_index| { | ||
| 584 | // Make sure that incremental tests appear in linear order | ||
| 585 | return a_index < b_index; | ||
| 586 | } else { | ||
| 587 | return false; | ||
| 588 | } | ||
| 589 | } else { | ||
| 590 | return b_parts.test_index != null; | ||
| 591 | } | ||
| 592 | }, | ||
| 593 | }, | ||
| 594 | }; | ||
| 595 | } | ||
| 596 | }; | ||
| 597 | std.sort.sort([]const u8, filenames, Context{}, Context.lessThan); | ||
| 598 | } | ||
| 599 | |||
| 600 | /// Iterates a set of filenames extracting batches that are either incremental | ||
| 601 | /// ("foo.0.zig", "foo.1.zig", etc.) or independent ("foo.zig", "bar.zig", etc.). | ||
| 602 | /// Assumes filenames are sorted. | ||
| 603 | const TestIterator = struct { | ||
| 604 | start: usize = 0, | ||
| 605 | end: usize = 0, | ||
| 606 | filenames: []const []const u8, | ||
| 607 | /// reset on each call to `next` | ||
| 608 | index: usize = 0, | ||
| 609 | |||
| 610 | const Error = error{InvalidIncrementalTestIndex}; | ||
| 611 | |||
| 612 | fn next(it: *TestIterator) Error!?[]const []const u8 { | ||
| 613 | try it.nextInner(); | ||
| 614 | if (it.start == it.end) return null; | ||
| 615 | return it.filenames[it.start..it.end]; | ||
| 616 | } | ||
| 617 | |||
| 618 | fn nextInner(it: *TestIterator) Error!void { | ||
| 619 | it.start = it.end; | ||
| 620 | if (it.end == it.filenames.len) return; | ||
| 621 | if (it.end + 1 == it.filenames.len) { | ||
| 622 | it.end += 1; | ||
| 623 | return; | ||
| 624 | } | ||
| 625 | |||
| 626 | const remaining = it.filenames[it.end..]; | ||
| 627 | it.index = 0; | ||
| 628 | while (it.index < remaining.len - 1) : (it.index += 1) { | ||
| 629 | // First, check if this file is part of an incremental update sequence | ||
| 630 | // Split filename into "<base_name>.<index>.<file_ext>" | ||
| 631 | const prev_parts = getTestFileNameParts(remaining[it.index]); | ||
| 632 | const new_parts = getTestFileNameParts(remaining[it.index + 1]); | ||
| 633 | |||
| 634 | // If base_name and file_ext match, these files are in the same test sequence | ||
| 635 | // and the new one should be the incremented version of the previous test | ||
| 636 | if (std.mem.eql(u8, prev_parts.base_name, new_parts.base_name) and | ||
| 637 | std.mem.eql(u8, prev_parts.file_ext, new_parts.file_ext)) | ||
| 638 | { | ||
| 639 | // This is "foo.X.zig" followed by "foo.Y.zig". Make sure that X = Y + 1 | ||
| 640 | if (prev_parts.test_index == null) | ||
| 641 | return error.InvalidIncrementalTestIndex; | ||
| 642 | if (new_parts.test_index == null) | ||
| 643 | return error.InvalidIncrementalTestIndex; | ||
| 644 | if (new_parts.test_index.? != prev_parts.test_index.? + 1) | ||
| 645 | return error.InvalidIncrementalTestIndex; | ||
| 646 | } else { | ||
| 647 | // This is not the same test sequence, so the new file must be the first file | ||
| 648 | // in a new sequence ("*.0.zig") or an independent test file ("*.zig") | ||
| 649 | if (new_parts.test_index != null and new_parts.test_index.? != 0) | ||
| 650 | return error.InvalidIncrementalTestIndex; | ||
| 651 | |||
| 652 | it.end += it.index + 1; | ||
| 653 | break; | ||
| 654 | } | ||
| 655 | } else { | ||
| 656 | it.end += remaining.len; | ||
| 657 | } | ||
| 658 | } | ||
| 659 | |||
| 660 | /// In the event of an `error.InvalidIncrementalTestIndex`, this function can | ||
| 661 | /// be used to find the current filename that was being processed. | ||
| 662 | /// Asserts the iterator hasn't reached the end. | ||
| 663 | fn currentFilename(it: TestIterator) []const u8 { | ||
| 664 | assert(it.end != it.filenames.len); | ||
| 665 | const remaining = it.filenames[it.end..]; | ||
| 666 | return remaining[it.index + 1]; | ||
| 667 | } | ||
| 668 | }; | ||
| 669 | |||
| 670 | /// For a filename in the format "<filename>.X.<ext>" or "<filename>.<ext>", returns | ||
| 671 | /// "<filename>", "<ext>" and X parsed as a decimal number. If X is not present, or | ||
| 672 | /// cannot be parsed as a decimal number, it is treated as part of <filename> | ||
| 673 | fn getTestFileNameParts(name: []const u8) struct { | ||
| 674 | base_name: []const u8, | ||
| 675 | file_ext: []const u8, | ||
| 676 | test_index: ?usize, | ||
| 677 | } { | ||
| 678 | const file_ext = std.fs.path.extension(name); | ||
| 679 | const trimmed = name[0 .. name.len - file_ext.len]; // Trim off ".<ext>" | ||
| 680 | const maybe_index = std.fs.path.extension(trimmed); // Extract ".X" | ||
| 681 | |||
| 682 | // Attempt to parse index | ||
| 683 | const index: ?usize = if (maybe_index.len > 0) | ||
| 684 | std.fmt.parseInt(usize, maybe_index[1..], 10) catch null | ||
| 685 | else | ||
| 686 | null; | ||
| 687 | |||
| 688 | // Adjust "<filename>" extent based on parsing success | ||
| 689 | const base_name_end = trimmed.len - if (index != null) maybe_index.len else 0; | ||
| 690 | return .{ | ||
| 691 | .base_name = name[0..base_name_end], | ||
| 692 | .file_ext = if (file_ext.len > 0) file_ext[1..] else file_ext, | ||
| 693 | .test_index = index, | ||
| 694 | }; | ||
| 695 | } | ||
| 696 | |||
| 697 | const TestStrategy = enum { | ||
| 698 | /// Execute tests as independent compilations, unless they are explicitly | ||
| 699 | /// incremental ("foo.0.zig", "foo.1.zig", etc.) | ||
| 700 | independent, | ||
| 701 | /// Execute all tests as incremental updates to a single compilation. Explicitly | ||
| 702 | /// incremental tests ("foo.0.zig", "foo.1.zig", etc.) still execute in order | ||
| 703 | incremental, | ||
| 704 | }; | ||
| 705 | |||
| 706 | /// Default config values for known test manifest key-value pairings. | ||
| 707 | /// Currently handled defaults are: | ||
| 708 | /// * backend | ||
| 709 | /// * target | ||
| 710 | /// * output_mode | ||
| 711 | /// * is_test | ||
| 712 | const TestManifestConfigDefaults = struct { | ||
| 713 | /// Asserts if the key doesn't exist - yep, it's an oversight alright. | ||
| 714 | fn get(@"type": TestManifest.Type, key: []const u8) []const u8 { | ||
| 715 | if (std.mem.eql(u8, key, "backend")) { | ||
| 716 | return "stage2"; | ||
| 717 | } else if (std.mem.eql(u8, key, "target")) { | ||
| 718 | if (@"type" == .@"error") { | ||
| 719 | return "native"; | ||
| 720 | } | ||
| 721 | comptime { | ||
| 722 | var defaults: []const u8 = ""; | ||
| 723 | // TODO should we only return "mainstream" targets by default here? | ||
| 724 | // TODO we should also specify ABIs explicitly as the backends are | ||
| 725 | // getting more and more complete | ||
| 726 | // Linux | ||
| 727 | inline for (&[_][]const u8{ "x86_64", "arm", "aarch64" }) |arch| { | ||
| 728 | defaults = defaults ++ arch ++ "-linux" ++ ","; | ||
| 729 | } | ||
| 730 | // macOS | ||
| 731 | inline for (&[_][]const u8{ "x86_64", "aarch64" }) |arch| { | ||
| 732 | defaults = defaults ++ arch ++ "-macos" ++ ","; | ||
| 733 | } | ||
| 734 | // Windows | ||
| 735 | defaults = defaults ++ "x86_64-windows" ++ ","; | ||
| 736 | // Wasm | ||
| 737 | defaults = defaults ++ "wasm32-wasi"; | ||
| 738 | return defaults; | ||
| 739 | } | ||
| 740 | } else if (std.mem.eql(u8, key, "output_mode")) { | ||
| 741 | return switch (@"type") { | ||
| 742 | .@"error" => "Obj", | ||
| 743 | .run => "Exe", | ||
| 744 | .compile => "Obj", | ||
| 745 | .cli => @panic("TODO test harness for CLI tests"), | ||
| 746 | }; | ||
| 747 | } else if (std.mem.eql(u8, key, "is_test")) { | ||
| 748 | return "0"; | ||
| 749 | } else if (std.mem.eql(u8, key, "link_libc")) { | ||
| 750 | return "0"; | ||
| 751 | } else unreachable; | ||
| 752 | } | ||
| 753 | }; | ||
| 754 | |||
| 755 | /// Manifest syntax example: | ||
| 756 | /// (see https://github.com/ziglang/zig/issues/11288) | ||
| 757 | /// | ||
| 758 | /// error | ||
| 759 | /// backend=stage1,stage2 | ||
| 760 | /// output_mode=exe | ||
| 761 | /// | ||
| 762 | /// :3:19: error: foo | ||
| 763 | /// | ||
| 764 | /// run | ||
| 765 | /// target=x86_64-linux,aarch64-macos | ||
| 766 | /// | ||
| 767 | /// I am expected stdout! Hello! | ||
| 768 | /// | ||
| 769 | /// cli | ||
| 770 | /// | ||
| 771 | /// build test | ||
| 772 | const TestManifest = struct { | ||
| 773 | type: Type, | ||
| 774 | config_map: std.StringHashMap([]const u8), | ||
| 775 | trailing_bytes: []const u8 = "", | ||
| 776 | |||
| 777 | const Type = enum { | ||
| 778 | @"error", | ||
| 779 | run, | ||
| 780 | cli, | ||
| 781 | compile, | ||
| 782 | }; | ||
| 783 | |||
| 784 | const TrailingIterator = struct { | ||
| 785 | inner: std.mem.TokenIterator(u8), | ||
| 786 | |||
| 787 | fn next(self: *TrailingIterator) ?[]const u8 { | ||
| 788 | const next_inner = self.inner.next() orelse return null; | ||
| 789 | return std.mem.trim(u8, next_inner[2..], " \t"); | ||
| 790 | } | ||
| 791 | }; | ||
| 792 | |||
| 793 | fn ConfigValueIterator(comptime T: type) type { | ||
| 794 | return struct { | ||
| 795 | inner: std.mem.SplitIterator(u8), | ||
| 796 | |||
| 797 | fn next(self: *@This()) !?T { | ||
| 798 | const next_raw = self.inner.next() orelse return null; | ||
| 799 | const parseFn = getDefaultParser(T); | ||
| 800 | return try parseFn(next_raw); | ||
| 801 | } | ||
| 802 | }; | ||
| 803 | } | ||
| 804 | |||
| 805 | fn parse(arena: Allocator, bytes: []const u8) !TestManifest { | ||
| 806 | // The manifest is the last contiguous block of comments in the file | ||
| 807 | // We scan for the beginning by searching backward for the first non-empty line that does not start with "//" | ||
| 808 | var start: ?usize = null; | ||
| 809 | var end: usize = bytes.len; | ||
| 810 | if (bytes.len > 0) { | ||
| 811 | var cursor: usize = bytes.len - 1; | ||
| 812 | while (true) { | ||
| 813 | // Move to beginning of line | ||
| 814 | while (cursor > 0 and bytes[cursor - 1] != '\n') cursor -= 1; | ||
| 815 | |||
| 816 | if (std.mem.startsWith(u8, bytes[cursor..], "//")) { | ||
| 817 | start = cursor; // Contiguous comment line, include in manifest | ||
| 818 | } else { | ||
| 819 | if (start != null) break; // Encountered non-comment line, end of manifest | ||
| 820 | |||
| 821 | // We ignore all-whitespace lines following the comment block, but anything else | ||
| 822 | // means that there is no manifest present. | ||
| 823 | if (std.mem.trim(u8, bytes[cursor..end], " \r\n\t").len == 0) { | ||
| 824 | end = cursor; | ||
| 825 | } else break; // If it's not whitespace, there is no manifest | ||
| 826 | } | ||
| 827 | |||
| 828 | // Move to previous line | ||
| 829 | if (cursor != 0) cursor -= 1 else break; | ||
| 830 | } | ||
| 831 | } | ||
| 832 | |||
| 833 | const actual_start = start orelse return error.MissingTestManifest; | ||
| 834 | const manifest_bytes = bytes[actual_start..end]; | ||
| 835 | |||
| 836 | var it = std.mem.tokenize(u8, manifest_bytes, "\r\n"); | ||
| 837 | |||
| 838 | // First line is the test type | ||
| 839 | const tt: Type = blk: { | ||
| 840 | const line = it.next() orelse return error.MissingTestCaseType; | ||
| 841 | const raw = std.mem.trim(u8, line[2..], " \t"); | ||
| 842 | if (std.mem.eql(u8, raw, "error")) { | ||
| 843 | break :blk .@"error"; | ||
| 844 | } else if (std.mem.eql(u8, raw, "run")) { | ||
| 845 | break :blk .run; | ||
| 846 | } else if (std.mem.eql(u8, raw, "cli")) { | ||
| 847 | break :blk .cli; | ||
| 848 | } else if (std.mem.eql(u8, raw, "compile")) { | ||
| 849 | break :blk .compile; | ||
| 850 | } else { | ||
| 851 | std.log.warn("unknown test case type requested: {s}", .{raw}); | ||
| 852 | return error.UnknownTestCaseType; | ||
| 853 | } | ||
| 854 | }; | ||
| 855 | |||
| 856 | var manifest: TestManifest = .{ | ||
| 857 | .type = tt, | ||
| 858 | .config_map = std.StringHashMap([]const u8).init(arena), | ||
| 859 | }; | ||
| 860 | |||
| 861 | // Any subsequent line until a blank comment line is key=value(s) pair | ||
| 862 | while (it.next()) |line| { | ||
| 863 | const trimmed = std.mem.trim(u8, line[2..], " \t"); | ||
| 864 | if (trimmed.len == 0) break; | ||
| 865 | |||
| 866 | // Parse key=value(s) | ||
| 867 | var kv_it = std.mem.split(u8, trimmed, "="); | ||
| 868 | const key = kv_it.first(); | ||
| 869 | try manifest.config_map.putNoClobber(key, kv_it.next() orelse return error.MissingValuesForConfig); | ||
| 870 | } | ||
| 871 | |||
| 872 | // Finally, trailing is expected output | ||
| 873 | manifest.trailing_bytes = manifest_bytes[it.index..]; | ||
| 874 | |||
| 875 | return manifest; | ||
| 876 | } | ||
| 877 | |||
| 878 | fn getConfigForKey( | ||
| 879 | self: TestManifest, | ||
| 880 | key: []const u8, | ||
| 881 | comptime T: type, | ||
| 882 | ) ConfigValueIterator(T) { | ||
| 883 | const bytes = self.config_map.get(key) orelse TestManifestConfigDefaults.get(self.type, key); | ||
| 884 | return ConfigValueIterator(T){ | ||
| 885 | .inner = std.mem.split(u8, bytes, ","), | ||
| 886 | }; | ||
| 887 | } | ||
| 888 | |||
| 889 | fn getConfigForKeyAlloc( | ||
| 890 | self: TestManifest, | ||
| 891 | allocator: Allocator, | ||
| 892 | key: []const u8, | ||
| 893 | comptime T: type, | ||
| 894 | ) ![]const T { | ||
| 895 | var out = std.ArrayList(T).init(allocator); | ||
| 896 | defer out.deinit(); | ||
| 897 | var it = self.getConfigForKey(key, T); | ||
| 898 | while (try it.next()) |item| { | ||
| 899 | try out.append(item); | ||
| 900 | } | ||
| 901 | return try out.toOwnedSlice(); | ||
| 902 | } | ||
| 903 | |||
| 904 | fn getConfigForKeyAssertSingle(self: TestManifest, key: []const u8, comptime T: type) !T { | ||
| 905 | var it = self.getConfigForKey(key, T); | ||
| 906 | const res = (try it.next()) orelse unreachable; | ||
| 907 | assert((try it.next()) == null); | ||
| 908 | return res; | ||
| 909 | } | ||
| 910 | |||
| 911 | fn trailing(self: TestManifest) TrailingIterator { | ||
| 912 | return .{ | ||
| 913 | .inner = std.mem.tokenize(u8, self.trailing_bytes, "\r\n"), | ||
| 914 | }; | ||
| 915 | } | ||
| 916 | |||
| 917 | fn trailingAlloc(self: TestManifest, allocator: Allocator) error{OutOfMemory}![]const []const u8 { | ||
| 918 | var out = std.ArrayList([]const u8).init(allocator); | ||
| 919 | defer out.deinit(); | ||
| 920 | var it = self.trailing(); | ||
| 921 | while (it.next()) |line| { | ||
| 922 | try out.append(line); | ||
| 923 | } | ||
| 924 | return try out.toOwnedSlice(); | ||
| 925 | } | ||
| 926 | |||
| 927 | fn ParseFn(comptime T: type) type { | ||
| 928 | return fn ([]const u8) anyerror!T; | ||
| 929 | } | ||
| 930 | |||
| 931 | fn getDefaultParser(comptime T: type) ParseFn(T) { | ||
| 932 | if (T == CrossTarget) return struct { | ||
| 933 | fn parse(str: []const u8) anyerror!T { | ||
| 934 | var opts = CrossTarget.ParseOptions{ | ||
| 935 | .arch_os_abi = str, | ||
| 936 | }; | ||
| 937 | return try CrossTarget.parse(opts); | ||
| 938 | } | ||
| 939 | }.parse; | ||
| 940 | |||
| 941 | switch (@typeInfo(T)) { | ||
| 942 | .Int => return struct { | ||
| 943 | fn parse(str: []const u8) anyerror!T { | ||
| 944 | return try std.fmt.parseInt(T, str, 0); | ||
| 945 | } | ||
| 946 | }.parse, | ||
| 947 | .Bool => return struct { | ||
| 948 | fn parse(str: []const u8) anyerror!T { | ||
| 949 | const as_int = try std.fmt.parseInt(u1, str, 0); | ||
| 950 | return as_int > 0; | ||
| 951 | } | ||
| 952 | }.parse, | ||
| 953 | .Enum => return struct { | ||
| 954 | fn parse(str: []const u8) anyerror!T { | ||
| 955 | return std.meta.stringToEnum(T, str) orelse { | ||
| 956 | std.log.err("unknown enum variant for {s}: {s}", .{ @typeName(T), str }); | ||
| 957 | return error.UnknownEnumVariant; | ||
| 958 | }; | ||
| 959 | } | ||
| 960 | }.parse, | ||
| 961 | .Struct => @compileError("no default parser for " ++ @typeName(T)), | ||
| 962 | else => @compileError("no default parser for " ++ @typeName(T)), | ||
| 963 | } | ||
| 964 | } | ||
| 965 | }; | ||
| 966 | |||
| 967 | const Cases = @This(); | ||
| 968 | const builtin = @import("builtin"); | ||
| 969 | const std = @import("std"); | ||
| 970 | const assert = std.debug.assert; | ||
| 971 | const Allocator = std.mem.Allocator; | ||
| 972 | const CrossTarget = std.zig.CrossTarget; | ||
| 973 | const Compilation = @import("../../src/Compilation.zig"); | ||
| 974 | const zig_h = @import("../../src/link.zig").File.C.zig_h; | ||
| 975 | const introspect = @import("../../src/introspect.zig"); | ||
| 976 | const ThreadPool = std.Thread.Pool; | ||
| 977 | const WaitGroup = std.Thread.WaitGroup; | ||
| 978 | const build_options = @import("build_options"); | ||
| 979 | const Package = @import("../../src/Package.zig"); | ||
| 980 | |||
| 981 | pub const std_options = struct { | ||
| 982 | pub const log_level: std.log.Level = .err; | ||
| 983 | }; | ||
| 984 | |||
| 985 | var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{ | ||
| 986 | .stack_trace_frames = build_options.mem_leak_frames, | ||
| 987 | }){}; | ||
| 988 | |||
| 989 | // TODO: instead of embedding the compiler in this process, spawn the compiler | ||
| 990 | // as a sub-process and communicate the updates using the compiler protocol. | ||
| 991 | pub fn main() !void { | ||
| 992 | const use_gpa = build_options.force_gpa or !builtin.link_libc; | ||
| 993 | const gpa = gpa: { | ||
| 994 | if (use_gpa) { | ||
| 995 | break :gpa general_purpose_allocator.allocator(); | ||
| 996 | } | ||
| 997 | // We would prefer to use raw libc allocator here, but cannot | ||
| 998 | // use it if it won't support the alignment we need. | ||
| 999 | if (@alignOf(std.c.max_align_t) < @alignOf(i128)) { | ||
| 1000 | break :gpa std.heap.c_allocator; | ||
| 1001 | } | ||
| 1002 | break :gpa std.heap.raw_c_allocator; | ||
| 1003 | }; | ||
| 1004 | |||
| 1005 | var single_threaded_arena = std.heap.ArenaAllocator.init(gpa); | ||
| 1006 | defer single_threaded_arena.deinit(); | ||
| 1007 | |||
| 1008 | var thread_safe_arena: std.heap.ThreadSafeAllocator = .{ | ||
| 1009 | .child_allocator = single_threaded_arena.allocator(), | ||
| 1010 | }; | ||
| 1011 | const arena = thread_safe_arena.allocator(); | ||
| 1012 | |||
| 1013 | const args = try std.process.argsAlloc(arena); | ||
| 1014 | const case_file_path = args[1]; | ||
| 1015 | const zig_exe_path = args[2]; | ||
| 1016 | |||
| 1017 | var filenames = std.ArrayList([]const u8).init(arena); | ||
| 1018 | |||
| 1019 | const case_dirname = std.fs.path.dirname(case_file_path).?; | ||
| 1020 | var iterable_dir = try std.fs.cwd().openIterableDir(case_dirname, .{}); | ||
| 1021 | defer iterable_dir.close(); | ||
| 1022 | |||
| 1023 | if (std.mem.endsWith(u8, case_file_path, ".0.zig")) { | ||
| 1024 | const stem = case_file_path[case_dirname.len + 1 .. case_file_path.len - "0.zig".len]; | ||
| 1025 | var it = iterable_dir.iterate(); | ||
| 1026 | while (try it.next()) |entry| { | ||
| 1027 | if (entry.kind != .File) continue; | ||
| 1028 | if (!std.mem.startsWith(u8, entry.name, stem)) continue; | ||
| 1029 | try filenames.append(try std.fs.path.join(arena, &.{ case_dirname, entry.name })); | ||
| 1030 | } | ||
| 1031 | } else { | ||
| 1032 | try filenames.append(case_file_path); | ||
| 1033 | } | ||
| 1034 | |||
| 1035 | if (filenames.items.len == 0) { | ||
| 1036 | std.debug.print("failed to find the input source file(s) from '{s}'\n", .{ | ||
| 1037 | case_file_path, | ||
| 1038 | }); | ||
| 1039 | std.process.exit(1); | ||
| 1040 | } | ||
| 1041 | |||
| 1042 | // Sort filenames, so that incremental tests are contiguous and in-order | ||
| 1043 | sortTestFilenames(filenames.items); | ||
| 1044 | |||
| 1045 | var ctx = Cases.init(gpa, arena); | ||
| 1046 | |||
| 1047 | var test_it = TestIterator{ .filenames = filenames.items }; | ||
| 1048 | while (test_it.next()) |maybe_batch| { | ||
| 1049 | const batch = maybe_batch orelse break; | ||
| 1050 | const strategy: TestStrategy = if (batch.len > 1) .incremental else .independent; | ||
| 1051 | var cases = std.ArrayList(usize).init(arena); | ||
| 1052 | |||
| 1053 | for (batch) |filename| { | ||
| 1054 | const max_file_size = 10 * 1024 * 1024; | ||
| 1055 | const src = try iterable_dir.dir.readFileAllocOptions(arena, filename, max_file_size, null, 1, 0); | ||
| 1056 | |||
| 1057 | // Parse the manifest | ||
| 1058 | var manifest = try TestManifest.parse(arena, src); | ||
| 1059 | |||
| 1060 | if (cases.items.len == 0) { | ||
| 1061 | const backends = try manifest.getConfigForKeyAlloc(arena, "backend", Backend); | ||
| 1062 | const targets = try manifest.getConfigForKeyAlloc(arena, "target", CrossTarget); | ||
| 1063 | const is_test = try manifest.getConfigForKeyAssertSingle("is_test", bool); | ||
| 1064 | const output_mode = try manifest.getConfigForKeyAssertSingle("output_mode", std.builtin.OutputMode); | ||
| 1065 | |||
| 1066 | // Cross-product to get all possible test combinations | ||
| 1067 | for (backends) |backend| { | ||
| 1068 | for (targets) |target| { | ||
| 1069 | const next = ctx.cases.items.len; | ||
| 1070 | try ctx.cases.append(.{ | ||
| 1071 | .name = std.fs.path.stem(filename), | ||
| 1072 | .target = target, | ||
| 1073 | .backend = backend, | ||
| 1074 | .updates = std.ArrayList(Cases.Update).init(ctx.cases.allocator), | ||
| 1075 | .is_test = is_test, | ||
| 1076 | .output_mode = output_mode, | ||
| 1077 | .link_libc = backend == .llvm, | ||
| 1078 | .deps = std.ArrayList(DepModule).init(ctx.cases.allocator), | ||
| 1079 | }); | ||
| 1080 | try cases.append(next); | ||
| 1081 | } | ||
| 1082 | } | ||
| 1083 | } | ||
| 1084 | |||
| 1085 | for (cases.items) |case_index| { | ||
| 1086 | const case = &ctx.cases.items[case_index]; | ||
| 1087 | switch (manifest.type) { | ||
| 1088 | .compile => { | ||
| 1089 | case.addCompile(src); | ||
| 1090 | }, | ||
| 1091 | .@"error" => { | ||
| 1092 | const errors = try manifest.trailingAlloc(arena); | ||
| 1093 | switch (strategy) { | ||
| 1094 | .independent => { | ||
| 1095 | case.addError(src, errors); | ||
| 1096 | }, | ||
| 1097 | .incremental => { | ||
| 1098 | case.addErrorNamed("update", src, errors); | ||
| 1099 | }, | ||
| 1100 | } | ||
| 1101 | }, | ||
| 1102 | .run => { | ||
| 1103 | var output = std.ArrayList(u8).init(arena); | ||
| 1104 | var trailing_it = manifest.trailing(); | ||
| 1105 | while (trailing_it.next()) |line| { | ||
| 1106 | try output.appendSlice(line); | ||
| 1107 | try output.append('\n'); | ||
| 1108 | } | ||
| 1109 | if (output.items.len > 0) { | ||
| 1110 | try output.resize(output.items.len - 1); | ||
| 1111 | } | ||
| 1112 | case.addCompareOutput(src, try output.toOwnedSlice()); | ||
| 1113 | }, | ||
| 1114 | .cli => @panic("TODO cli tests"), | ||
| 1115 | } | ||
| 1116 | } | ||
| 1117 | } | ||
| 1118 | } else |err| { | ||
| 1119 | return err; | ||
| 1120 | } | ||
| 1121 | |||
| 1122 | return runCases(&ctx, zig_exe_path); | ||
| 1123 | } | ||
| 1124 | |||
| 1125 | fn runCases(self: *Cases, zig_exe_path: []const u8) !void { | ||
| 1126 | const host = try std.zig.system.NativeTargetInfo.detect(.{}); | ||
| 1127 | |||
| 1128 | var progress = std.Progress{}; | ||
| 1129 | const root_node = progress.start("compiler", self.cases.items.len); | ||
| 1130 | progress.terminal = null; | ||
| 1131 | defer root_node.end(); | ||
| 1132 | |||
| 1133 | var zig_lib_directory = try introspect.findZigLibDir(self.gpa); | ||
| 1134 | defer zig_lib_directory.handle.close(); | ||
| 1135 | defer self.gpa.free(zig_lib_directory.path.?); | ||
| 1136 | |||
| 1137 | var aux_thread_pool: ThreadPool = undefined; | ||
| 1138 | try aux_thread_pool.init(.{ .allocator = self.gpa }); | ||
| 1139 | defer aux_thread_pool.deinit(); | ||
| 1140 | |||
| 1141 | // Use the same global cache dir for all the tests, such that we for example don't have to | ||
| 1142 | // rebuild musl libc for every case (when LLVM backend is enabled). | ||
| 1143 | var global_tmp = std.testing.tmpDir(.{}); | ||
| 1144 | defer global_tmp.cleanup(); | ||
| 1145 | |||
| 1146 | var cache_dir = try global_tmp.dir.makeOpenPath("zig-cache", .{}); | ||
| 1147 | defer cache_dir.close(); | ||
| 1148 | const tmp_dir_path = try std.fs.path.join(self.gpa, &[_][]const u8{ ".", "zig-cache", "tmp", &global_tmp.sub_path }); | ||
| 1149 | defer self.gpa.free(tmp_dir_path); | ||
| 1150 | |||
| 1151 | const global_cache_directory: Compilation.Directory = .{ | ||
| 1152 | .handle = cache_dir, | ||
| 1153 | .path = try std.fs.path.join(self.gpa, &[_][]const u8{ tmp_dir_path, "zig-cache" }), | ||
| 1154 | }; | ||
| 1155 | defer self.gpa.free(global_cache_directory.path.?); | ||
| 1156 | |||
| 1157 | { | ||
| 1158 | for (self.cases.items) |*case| { | ||
| 1159 | if (build_options.skip_non_native) { | ||
| 1160 | if (case.target.getCpuArch() != builtin.cpu.arch) | ||
| 1161 | continue; | ||
| 1162 | if (case.target.getObjectFormat() != builtin.object_format) | ||
| 1163 | continue; | ||
| 1164 | } | ||
| 1165 | |||
| 1166 | // Skip tests that require LLVM backend when it is not available | ||
| 1167 | if (!build_options.have_llvm and case.backend == .llvm) | ||
| 1168 | continue; | ||
| 1169 | |||
| 1170 | assert(case.backend != .stage1); | ||
| 1171 | |||
| 1172 | if (build_options.test_filter) |test_filter| { | ||
| 1173 | if (std.mem.indexOf(u8, case.name, test_filter) == null) continue; | ||
| 1174 | } | ||
| 1175 | |||
| 1176 | var prg_node = root_node.start(case.name, case.updates.items.len); | ||
| 1177 | prg_node.activate(); | ||
| 1178 | defer prg_node.end(); | ||
| 1179 | |||
| 1180 | try runOneCase( | ||
| 1181 | self.gpa, | ||
| 1182 | &prg_node, | ||
| 1183 | case.*, | ||
| 1184 | zig_lib_directory, | ||
| 1185 | zig_exe_path, | ||
| 1186 | &aux_thread_pool, | ||
| 1187 | global_cache_directory, | ||
| 1188 | host, | ||
| 1189 | ); | ||
| 1190 | } | ||
| 1191 | } | ||
| 1192 | } | ||
| 1193 | |||
| 1194 | fn runOneCase( | ||
| 1195 | allocator: Allocator, | ||
| 1196 | root_node: *std.Progress.Node, | ||
| 1197 | case: Case, | ||
| 1198 | zig_lib_directory: Compilation.Directory, | ||
| 1199 | zig_exe_path: []const u8, | ||
| 1200 | thread_pool: *ThreadPool, | ||
| 1201 | global_cache_directory: Compilation.Directory, | ||
| 1202 | host: std.zig.system.NativeTargetInfo, | ||
| 1203 | ) !void { | ||
| 1204 | const tmp_src_path = "tmp.zig"; | ||
| 1205 | const enable_rosetta = build_options.enable_rosetta; | ||
| 1206 | const enable_qemu = build_options.enable_qemu; | ||
| 1207 | const enable_wine = build_options.enable_wine; | ||
| 1208 | const enable_wasmtime = build_options.enable_wasmtime; | ||
| 1209 | const enable_darling = build_options.enable_darling; | ||
| 1210 | const glibc_runtimes_dir: ?[]const u8 = build_options.glibc_runtimes_dir; | ||
| 1211 | |||
| 1212 | const target_info = try std.zig.system.NativeTargetInfo.detect(case.target); | ||
| 1213 | const target = target_info.target; | ||
| 1214 | |||
| 1215 | var arena_allocator = std.heap.ArenaAllocator.init(allocator); | ||
| 1216 | defer arena_allocator.deinit(); | ||
| 1217 | const arena = arena_allocator.allocator(); | ||
| 1218 | |||
| 1219 | var tmp = std.testing.tmpDir(.{}); | ||
| 1220 | defer tmp.cleanup(); | ||
| 1221 | |||
| 1222 | var cache_dir = try tmp.dir.makeOpenPath("zig-cache", .{}); | ||
| 1223 | defer cache_dir.close(); | ||
| 1224 | |||
| 1225 | const tmp_dir_path = try std.fs.path.join( | ||
| 1226 | arena, | ||
| 1227 | &[_][]const u8{ ".", "zig-cache", "tmp", &tmp.sub_path }, | ||
| 1228 | ); | ||
| 1229 | const local_cache_path = try std.fs.path.join( | ||
| 1230 | arena, | ||
| 1231 | &[_][]const u8{ tmp_dir_path, "zig-cache" }, | ||
| 1232 | ); | ||
| 1233 | |||
| 1234 | const zig_cache_directory: Compilation.Directory = .{ | ||
| 1235 | .handle = cache_dir, | ||
| 1236 | .path = local_cache_path, | ||
| 1237 | }; | ||
| 1238 | |||
| 1239 | var main_pkg: Package = .{ | ||
| 1240 | .root_src_directory = .{ .path = tmp_dir_path, .handle = tmp.dir }, | ||
| 1241 | .root_src_path = tmp_src_path, | ||
| 1242 | }; | ||
| 1243 | defer { | ||
| 1244 | var it = main_pkg.table.iterator(); | ||
| 1245 | while (it.next()) |kv| { | ||
| 1246 | allocator.free(kv.key_ptr.*); | ||
| 1247 | kv.value_ptr.*.destroy(allocator); | ||
| 1248 | } | ||
| 1249 | main_pkg.table.deinit(allocator); | ||
| 1250 | } | ||
| 1251 | |||
| 1252 | for (case.deps.items) |dep| { | ||
| 1253 | var pkg = try Package.create( | ||
| 1254 | allocator, | ||
| 1255 | tmp_dir_path, | ||
| 1256 | dep.path, | ||
| 1257 | ); | ||
| 1258 | errdefer pkg.destroy(allocator); | ||
| 1259 | try main_pkg.add(allocator, dep.name, pkg); | ||
| 1260 | } | ||
| 1261 | |||
| 1262 | const bin_name = try std.zig.binNameAlloc(arena, .{ | ||
| 1263 | .root_name = "test_case", | ||
| 1264 | .target = target, | ||
| 1265 | .output_mode = case.output_mode, | ||
| 1266 | }); | ||
| 1267 | |||
| 1268 | const emit_directory: Compilation.Directory = .{ | ||
| 1269 | .path = tmp_dir_path, | ||
| 1270 | .handle = tmp.dir, | ||
| 1271 | }; | ||
| 1272 | const emit_bin: Compilation.EmitLoc = .{ | ||
| 1273 | .directory = emit_directory, | ||
| 1274 | .basename = bin_name, | ||
| 1275 | }; | ||
| 1276 | const emit_h: ?Compilation.EmitLoc = if (case.emit_h) .{ | ||
| 1277 | .directory = emit_directory, | ||
| 1278 | .basename = "test_case.h", | ||
| 1279 | } else null; | ||
| 1280 | const use_llvm: bool = switch (case.backend) { | ||
| 1281 | .llvm => true, | ||
| 1282 | else => false, | ||
| 1283 | }; | ||
| 1284 | const comp = try Compilation.create(allocator, .{ | ||
| 1285 | .local_cache_directory = zig_cache_directory, | ||
| 1286 | .global_cache_directory = global_cache_directory, | ||
| 1287 | .zig_lib_directory = zig_lib_directory, | ||
| 1288 | .thread_pool = thread_pool, | ||
| 1289 | .root_name = "test_case", | ||
| 1290 | .target = target, | ||
| 1291 | // TODO: support tests for object file building, and library builds | ||
| 1292 | // and linking. This will require a rework to support multi-file | ||
| 1293 | // tests. | ||
| 1294 | .output_mode = case.output_mode, | ||
| 1295 | .is_test = case.is_test, | ||
| 1296 | .optimize_mode = case.optimize_mode, | ||
| 1297 | .emit_bin = emit_bin, | ||
| 1298 | .emit_h = emit_h, | ||
| 1299 | .main_pkg = &main_pkg, | ||
| 1300 | .keep_source_files_loaded = true, | ||
| 1301 | .is_native_os = case.target.isNativeOs(), | ||
| 1302 | .is_native_abi = case.target.isNativeAbi(), | ||
| 1303 | .dynamic_linker = target_info.dynamic_linker.get(), | ||
| 1304 | .link_libc = case.link_libc, | ||
| 1305 | .use_llvm = use_llvm, | ||
| 1306 | .self_exe_path = zig_exe_path, | ||
| 1307 | // TODO instead of turning off color, pass in a std.Progress.Node | ||
| 1308 | .color = .off, | ||
| 1309 | .reference_trace = 0, | ||
| 1310 | // TODO: force self-hosted linkers with stage2 backend to avoid LLD creeping in | ||
| 1311 | // until the auto-select mechanism deems them worthy | ||
| 1312 | .use_lld = switch (case.backend) { | ||
| 1313 | .stage2 => false, | ||
| 1314 | else => null, | ||
| 1315 | }, | ||
| 1316 | }); | ||
| 1317 | defer comp.destroy(); | ||
| 1318 | |||
| 1319 | update: for (case.updates.items, 0..) |update, update_index| { | ||
| 1320 | var update_node = root_node.start(update.name, 3); | ||
| 1321 | update_node.activate(); | ||
| 1322 | defer update_node.end(); | ||
| 1323 | |||
| 1324 | var sync_node = update_node.start("write", 0); | ||
| 1325 | sync_node.activate(); | ||
| 1326 | for (update.files.items) |file| { | ||
| 1327 | try tmp.dir.writeFile(file.path, file.src); | ||
| 1328 | } | ||
| 1329 | sync_node.end(); | ||
| 1330 | |||
| 1331 | var module_node = update_node.start("parse/analysis/codegen", 0); | ||
| 1332 | module_node.activate(); | ||
| 1333 | try comp.makeBinFileWritable(); | ||
| 1334 | try comp.update(&module_node); | ||
| 1335 | module_node.end(); | ||
| 1336 | |||
| 1337 | if (update.case != .Error) { | ||
| 1338 | var all_errors = try comp.getAllErrorsAlloc(); | ||
| 1339 | defer all_errors.deinit(allocator); | ||
| 1340 | if (all_errors.errorMessageCount() > 0) { | ||
| 1341 | all_errors.renderToStdErr(.{ | ||
| 1342 | .ttyconf = std.debug.detectTTYConfig(std.io.getStdErr()), | ||
| 1343 | }); | ||
| 1344 | // TODO print generated C code | ||
| 1345 | return error.UnexpectedCompileErrors; | ||
| 1346 | } | ||
| 1347 | } | ||
| 1348 | |||
| 1349 | switch (update.case) { | ||
| 1350 | .Header => |expected_output| { | ||
| 1351 | var file = try tmp.dir.openFile("test_case.h", .{ .mode = .read_only }); | ||
| 1352 | defer file.close(); | ||
| 1353 | const out = try file.reader().readAllAlloc(arena, 5 * 1024 * 1024); | ||
| 1354 | |||
| 1355 | try std.testing.expectEqualStrings(expected_output, out); | ||
| 1356 | }, | ||
| 1357 | .CompareObjectFile => |expected_output| { | ||
| 1358 | var file = try tmp.dir.openFile(bin_name, .{ .mode = .read_only }); | ||
| 1359 | defer file.close(); | ||
| 1360 | const out = try file.reader().readAllAlloc(arena, 5 * 1024 * 1024); | ||
| 1361 | |||
| 1362 | try std.testing.expectEqualStrings(expected_output, out); | ||
| 1363 | }, | ||
| 1364 | .Compile => {}, | ||
| 1365 | .Error => |expected_errors| { | ||
| 1366 | var test_node = update_node.start("assert", 0); | ||
| 1367 | test_node.activate(); | ||
| 1368 | defer test_node.end(); | ||
| 1369 | |||
| 1370 | var error_bundle = try comp.getAllErrorsAlloc(); | ||
| 1371 | defer error_bundle.deinit(allocator); | ||
| 1372 | |||
| 1373 | if (error_bundle.errorMessageCount() == 0) { | ||
| 1374 | return error.ExpectedCompilationErrors; | ||
| 1375 | } | ||
| 1376 | |||
| 1377 | var actual_stderr = std.ArrayList(u8).init(arena); | ||
| 1378 | try error_bundle.renderToWriter(.{ | ||
| 1379 | .ttyconf = .no_color, | ||
| 1380 | .include_reference_trace = false, | ||
| 1381 | .include_source_line = false, | ||
| 1382 | }, actual_stderr.writer()); | ||
| 1383 | |||
| 1384 | // Render the expected lines into a string that we can compare verbatim. | ||
| 1385 | var expected_generated = std.ArrayList(u8).init(arena); | ||
| 1386 | |||
| 1387 | var actual_line_it = std.mem.split(u8, actual_stderr.items, "\n"); | ||
| 1388 | for (expected_errors) |expect_line| { | ||
| 1389 | const actual_line = actual_line_it.next() orelse { | ||
| 1390 | try expected_generated.appendSlice(expect_line); | ||
| 1391 | try expected_generated.append('\n'); | ||
| 1392 | continue; | ||
| 1393 | }; | ||
| 1394 | if (std.mem.endsWith(u8, actual_line, expect_line)) { | ||
| 1395 | try expected_generated.appendSlice(actual_line); | ||
| 1396 | try expected_generated.append('\n'); | ||
| 1397 | continue; | ||
| 1398 | } | ||
| 1399 | if (std.mem.startsWith(u8, expect_line, ":?:?: ")) { | ||
| 1400 | if (std.mem.endsWith(u8, actual_line, expect_line[":?:?: ".len..])) { | ||
| 1401 | try expected_generated.appendSlice(actual_line); | ||
| 1402 | try expected_generated.append('\n'); | ||
| 1403 | continue; | ||
| 1404 | } | ||
| 1405 | } | ||
| 1406 | try expected_generated.appendSlice(expect_line); | ||
| 1407 | try expected_generated.append('\n'); | ||
| 1408 | } | ||
| 1409 | |||
| 1410 | try std.testing.expectEqualStrings(expected_generated.items, actual_stderr.items); | ||
| 1411 | }, | ||
| 1412 | .Execution => |expected_stdout| { | ||
| 1413 | if (!std.process.can_spawn) { | ||
| 1414 | std.debug.print("Unable to spawn child processes on {s}, skipping test.\n", .{@tagName(builtin.os.tag)}); | ||
| 1415 | continue :update; // Pass test. | ||
| 1416 | } | ||
| 1417 | |||
| 1418 | update_node.setEstimatedTotalItems(4); | ||
| 1419 | |||
| 1420 | var argv = std.ArrayList([]const u8).init(allocator); | ||
| 1421 | defer argv.deinit(); | ||
| 1422 | |||
| 1423 | var exec_result = x: { | ||
| 1424 | var exec_node = update_node.start("execute", 0); | ||
| 1425 | exec_node.activate(); | ||
| 1426 | defer exec_node.end(); | ||
| 1427 | |||
| 1428 | // We go out of our way here to use the unique temporary directory name in | ||
| 1429 | // the exe_path so that it makes its way into the cache hash, avoiding | ||
| 1430 | // cache collisions from multiple threads doing `zig run` at the same time | ||
| 1431 | // on the same test_case.c input filename. | ||
| 1432 | const ss = std.fs.path.sep_str; | ||
| 1433 | const exe_path = try std.fmt.allocPrint( | ||
| 1434 | arena, | ||
| 1435 | ".." ++ ss ++ "{s}" ++ ss ++ "{s}", | ||
| 1436 | .{ &tmp.sub_path, bin_name }, | ||
| 1437 | ); | ||
| 1438 | if (case.target.ofmt != null and case.target.ofmt.? == .c) { | ||
| 1439 | if (host.getExternalExecutor(target_info, .{ .link_libc = true }) != .native) { | ||
| 1440 | // We wouldn't be able to run the compiled C code. | ||
| 1441 | continue :update; // Pass test. | ||
| 1442 | } | ||
| 1443 | try argv.appendSlice(&[_][]const u8{ | ||
| 1444 | zig_exe_path, | ||
| 1445 | "run", | ||
| 1446 | "-cflags", | ||
| 1447 | "-std=c99", | ||
| 1448 | "-pedantic", | ||
| 1449 | "-Werror", | ||
| 1450 | "-Wno-incompatible-library-redeclaration", // https://github.com/ziglang/zig/issues/875 | ||
| 1451 | "--", | ||
| 1452 | "-lc", | ||
| 1453 | exe_path, | ||
| 1454 | }); | ||
| 1455 | if (zig_lib_directory.path) |p| { | ||
| 1456 | try argv.appendSlice(&.{ "-I", p }); | ||
| 1457 | } | ||
| 1458 | } else switch (host.getExternalExecutor(target_info, .{ .link_libc = case.link_libc })) { | ||
| 1459 | .native => { | ||
| 1460 | if (case.backend == .stage2 and case.target.getCpuArch() == .arm) { | ||
| 1461 | // https://github.com/ziglang/zig/issues/13623 | ||
| 1462 | continue :update; // Pass test. | ||
| 1463 | } | ||
| 1464 | try argv.append(exe_path); | ||
| 1465 | }, | ||
| 1466 | .bad_dl, .bad_os_or_cpu => continue :update, // Pass test. | ||
| 1467 | |||
| 1468 | .rosetta => if (enable_rosetta) { | ||
| 1469 | try argv.append(exe_path); | ||
| 1470 | } else { | ||
| 1471 | continue :update; // Rosetta not available, pass test. | ||
| 1472 | }, | ||
| 1473 | |||
| 1474 | .qemu => |qemu_bin_name| if (enable_qemu) { | ||
| 1475 | const need_cross_glibc = target.isGnuLibC() and case.link_libc; | ||
| 1476 | const glibc_dir_arg: ?[]const u8 = if (need_cross_glibc) | ||
| 1477 | glibc_runtimes_dir orelse continue :update // glibc dir not available; pass test | ||
| 1478 | else | ||
| 1479 | null; | ||
| 1480 | try argv.append(qemu_bin_name); | ||
| 1481 | if (glibc_dir_arg) |dir| { | ||
| 1482 | const linux_triple = try target.linuxTriple(arena); | ||
| 1483 | const full_dir = try std.fs.path.join(arena, &[_][]const u8{ | ||
| 1484 | dir, | ||
| 1485 | linux_triple, | ||
| 1486 | }); | ||
| 1487 | |||
| 1488 | try argv.append("-L"); | ||
| 1489 | try argv.append(full_dir); | ||
| 1490 | } | ||
| 1491 | try argv.append(exe_path); | ||
| 1492 | } else { | ||
| 1493 | continue :update; // QEMU not available; pass test. | ||
| 1494 | }, | ||
| 1495 | |||
| 1496 | .wine => |wine_bin_name| if (enable_wine) { | ||
| 1497 | try argv.append(wine_bin_name); | ||
| 1498 | try argv.append(exe_path); | ||
| 1499 | } else { | ||
| 1500 | continue :update; // Wine not available; pass test. | ||
| 1501 | }, | ||
| 1502 | |||
| 1503 | .wasmtime => |wasmtime_bin_name| if (enable_wasmtime) { | ||
| 1504 | try argv.append(wasmtime_bin_name); | ||
| 1505 | try argv.append("--dir=."); | ||
| 1506 | try argv.append(exe_path); | ||
| 1507 | } else { | ||
| 1508 | continue :update; // wasmtime not available; pass test. | ||
| 1509 | }, | ||
| 1510 | |||
| 1511 | .darling => |darling_bin_name| if (enable_darling) { | ||
| 1512 | try argv.append(darling_bin_name); | ||
| 1513 | // Since we use relative to cwd here, we invoke darling with | ||
| 1514 | // "shell" subcommand. | ||
| 1515 | try argv.append("shell"); | ||
| 1516 | try argv.append(exe_path); | ||
| 1517 | } else { | ||
| 1518 | continue :update; // Darling not available; pass test. | ||
| 1519 | }, | ||
| 1520 | } | ||
| 1521 | |||
| 1522 | try comp.makeBinFileExecutable(); | ||
| 1523 | |||
| 1524 | while (true) { | ||
| 1525 | break :x std.ChildProcess.exec(.{ | ||
| 1526 | .allocator = allocator, | ||
| 1527 | .argv = argv.items, | ||
| 1528 | .cwd_dir = tmp.dir, | ||
| 1529 | .cwd = tmp_dir_path, | ||
| 1530 | }) catch |err| switch (err) { | ||
| 1531 | error.FileBusy => { | ||
| 1532 | // There is a fundamental design flaw in Unix systems with how | ||
| 1533 | // ETXTBSY interacts with fork+exec. | ||
| 1534 | // https://github.com/golang/go/issues/22315 | ||
| 1535 | // https://bugs.openjdk.org/browse/JDK-8068370 | ||
| 1536 | // Unfortunately, this could be a real error, but we can't | ||
| 1537 | // tell the difference here. | ||
| 1538 | continue; | ||
| 1539 | }, | ||
| 1540 | else => { | ||
| 1541 | std.debug.print("\n{s}.{d} The following command failed with {s}:\n", .{ | ||
| 1542 | case.name, update_index, @errorName(err), | ||
| 1543 | }); | ||
| 1544 | dumpArgs(argv.items); | ||
| 1545 | return error.ChildProcessExecution; | ||
| 1546 | }, | ||
| 1547 | }; | ||
| 1548 | } | ||
| 1549 | }; | ||
| 1550 | var test_node = update_node.start("test", 0); | ||
| 1551 | test_node.activate(); | ||
| 1552 | defer test_node.end(); | ||
| 1553 | defer allocator.free(exec_result.stdout); | ||
| 1554 | defer allocator.free(exec_result.stderr); | ||
| 1555 | switch (exec_result.term) { | ||
| 1556 | .Exited => |code| { | ||
| 1557 | if (code != 0) { | ||
| 1558 | std.debug.print("\n{s}\n{s}: execution exited with code {d}:\n", .{ | ||
| 1559 | exec_result.stderr, case.name, code, | ||
| 1560 | }); | ||
| 1561 | dumpArgs(argv.items); | ||
| 1562 | return error.ChildProcessExecution; | ||
| 1563 | } | ||
| 1564 | }, | ||
| 1565 | else => { | ||
| 1566 | std.debug.print("\n{s}\n{s}: execution crashed:\n", .{ | ||
| 1567 | exec_result.stderr, case.name, | ||
| 1568 | }); | ||
| 1569 | dumpArgs(argv.items); | ||
| 1570 | return error.ChildProcessExecution; | ||
| 1571 | }, | ||
| 1572 | } | ||
| 1573 | try std.testing.expectEqualStrings(expected_stdout, exec_result.stdout); | ||
| 1574 | // We allow stderr to have garbage in it because wasmtime prints a | ||
| 1575 | // warning about --invoke even though we don't pass it. | ||
| 1576 | //std.testing.expectEqualStrings("", exec_result.stderr); | ||
| 1577 | }, | ||
| 1578 | } | ||
| 1579 | } | ||
| 1580 | } | ||
| 1581 | |||
| 1582 | fn dumpArgs(argv: []const []const u8) void { | ||
| 1583 | for (argv) |arg| { | ||
| 1584 | std.debug.print("{s} ", .{arg}); | ||
| 1585 | } | ||
| 1586 | std.debug.print("\n", .{}); | ||
| 1587 | } | ||
test/src/CompareOutput.zig created+174| ... | @@ -0,0 +1,174 @@ | ||
| 1 | //! This is the implementation of the test harness. | ||
| 2 | //! For the actual test cases, see test/compare_output.zig. | ||
| 3 | |||
| 4 | b: *std.Build, | ||
| 5 | step: *std.Build.Step, | ||
| 6 | test_index: usize, | ||
| 7 | test_filter: ?[]const u8, | ||
| 8 | optimize_modes: []const OptimizeMode, | ||
| 9 | |||
| 10 | const Special = enum { | ||
| 11 | None, | ||
| 12 | Asm, | ||
| 13 | RuntimeSafety, | ||
| 14 | }; | ||
| 15 | |||
| 16 | const TestCase = struct { | ||
| 17 | name: []const u8, | ||
| 18 | sources: ArrayList(SourceFile), | ||
| 19 | expected_output: []const u8, | ||
| 20 | link_libc: bool, | ||
| 21 | special: Special, | ||
| 22 | cli_args: []const []const u8, | ||
| 23 | |||
| 24 | const SourceFile = struct { | ||
| 25 | filename: []const u8, | ||
| 26 | source: []const u8, | ||
| 27 | }; | ||
| 28 | |||
| 29 | pub fn addSourceFile(self: *TestCase, filename: []const u8, source: []const u8) void { | ||
| 30 | self.sources.append(SourceFile{ | ||
| 31 | .filename = filename, | ||
| 32 | .source = source, | ||
| 33 | }) catch @panic("OOM"); | ||
| 34 | } | ||
| 35 | |||
| 36 | pub fn setCommandLineArgs(self: *TestCase, args: []const []const u8) void { | ||
| 37 | self.cli_args = args; | ||
| 38 | } | ||
| 39 | }; | ||
| 40 | |||
| 41 | pub fn createExtra(self: *CompareOutput, name: []const u8, source: []const u8, expected_output: []const u8, special: Special) TestCase { | ||
| 42 | var tc = TestCase{ | ||
| 43 | .name = name, | ||
| 44 | .sources = ArrayList(TestCase.SourceFile).init(self.b.allocator), | ||
| 45 | .expected_output = expected_output, | ||
| 46 | .link_libc = false, | ||
| 47 | .special = special, | ||
| 48 | .cli_args = &[_][]const u8{}, | ||
| 49 | }; | ||
| 50 | const root_src_name = if (special == Special.Asm) "source.s" else "source.zig"; | ||
| 51 | tc.addSourceFile(root_src_name, source); | ||
| 52 | return tc; | ||
| 53 | } | ||
| 54 | |||
| 55 | pub fn create(self: *CompareOutput, name: []const u8, source: []const u8, expected_output: []const u8) TestCase { | ||
| 56 | return createExtra(self, name, source, expected_output, Special.None); | ||
| 57 | } | ||
| 58 | |||
| 59 | pub fn addC(self: *CompareOutput, name: []const u8, source: []const u8, expected_output: []const u8) void { | ||
| 60 | var tc = self.create(name, source, expected_output); | ||
| 61 | tc.link_libc = true; | ||
| 62 | self.addCase(tc); | ||
| 63 | } | ||
| 64 | |||
| 65 | pub fn add(self: *CompareOutput, name: []const u8, source: []const u8, expected_output: []const u8) void { | ||
| 66 | const tc = self.create(name, source, expected_output); | ||
| 67 | self.addCase(tc); | ||
| 68 | } | ||
| 69 | |||
| 70 | pub fn addAsm(self: *CompareOutput, name: []const u8, source: []const u8, expected_output: []const u8) void { | ||
| 71 | const tc = self.createExtra(name, source, expected_output, Special.Asm); | ||
| 72 | self.addCase(tc); | ||
| 73 | } | ||
| 74 | |||
| 75 | pub fn addRuntimeSafety(self: *CompareOutput, name: []const u8, source: []const u8) void { | ||
| 76 | const tc = self.createExtra(name, source, undefined, Special.RuntimeSafety); | ||
| 77 | self.addCase(tc); | ||
| 78 | } | ||
| 79 | |||
| 80 | pub fn addCase(self: *CompareOutput, case: TestCase) void { | ||
| 81 | const b = self.b; | ||
| 82 | |||
| 83 | const write_src = b.addWriteFiles(); | ||
| 84 | for (case.sources.items) |src_file| { | ||
| 85 | write_src.add(src_file.filename, src_file.source); | ||
| 86 | } | ||
| 87 | |||
| 88 | switch (case.special) { | ||
| 89 | Special.Asm => { | ||
| 90 | const annotated_case_name = fmt.allocPrint(self.b.allocator, "run assemble-and-link {s}", .{ | ||
| 91 | case.name, | ||
| 92 | }) catch @panic("OOM"); | ||
| 93 | if (self.test_filter) |filter| { | ||
| 94 | if (mem.indexOf(u8, annotated_case_name, filter) == null) return; | ||
| 95 | } | ||
| 96 | |||
| 97 | const exe = b.addExecutable(.{ | ||
| 98 | .name = "test", | ||
| 99 | .target = .{}, | ||
| 100 | .optimize = .Debug, | ||
| 101 | }); | ||
| 102 | exe.addAssemblyFileSource(write_src.getFileSource(case.sources.items[0].filename).?); | ||
| 103 | |||
| 104 | const run = exe.run(); | ||
| 105 | run.setName(annotated_case_name); | ||
| 106 | run.addArgs(case.cli_args); | ||
| 107 | run.expectStdOutEqual(case.expected_output); | ||
| 108 | |||
| 109 | self.step.dependOn(&run.step); | ||
| 110 | }, | ||
| 111 | Special.None => { | ||
| 112 | for (self.optimize_modes) |optimize| { | ||
| 113 | const annotated_case_name = fmt.allocPrint(self.b.allocator, "run compare-output {s} ({s})", .{ | ||
| 114 | case.name, @tagName(optimize), | ||
| 115 | }) catch @panic("OOM"); | ||
| 116 | if (self.test_filter) |filter| { | ||
| 117 | if (mem.indexOf(u8, annotated_case_name, filter) == null) continue; | ||
| 118 | } | ||
| 119 | |||
| 120 | const basename = case.sources.items[0].filename; | ||
| 121 | const exe = b.addExecutable(.{ | ||
| 122 | .name = "test", | ||
| 123 | .root_source_file = write_src.getFileSource(basename).?, | ||
| 124 | .optimize = optimize, | ||
| 125 | .target = .{}, | ||
| 126 | }); | ||
| 127 | if (case.link_libc) { | ||
| 128 | exe.linkSystemLibrary("c"); | ||
| 129 | } | ||
| 130 | |||
| 131 | const run = exe.run(); | ||
| 132 | run.setName(annotated_case_name); | ||
| 133 | run.addArgs(case.cli_args); | ||
| 134 | run.expectStdOutEqual(case.expected_output); | ||
| 135 | |||
| 136 | self.step.dependOn(&run.step); | ||
| 137 | } | ||
| 138 | }, | ||
| 139 | Special.RuntimeSafety => { | ||
| 140 | // TODO iterate over self.optimize_modes and test this in both | ||
| 141 | // debug and release safe mode | ||
| 142 | const annotated_case_name = fmt.allocPrint(self.b.allocator, "run safety {s}", .{case.name}) catch @panic("OOM"); | ||
| 143 | if (self.test_filter) |filter| { | ||
| 144 | if (mem.indexOf(u8, annotated_case_name, filter) == null) return; | ||
| 145 | } | ||
| 146 | |||
| 147 | const basename = case.sources.items[0].filename; | ||
| 148 | const exe = b.addExecutable(.{ | ||
| 149 | .name = "test", | ||
| 150 | .root_source_file = write_src.getFileSource(basename).?, | ||
| 151 | .target = .{}, | ||
| 152 | .optimize = .Debug, | ||
| 153 | }); | ||
| 154 | if (case.link_libc) { | ||
| 155 | exe.linkSystemLibrary("c"); | ||
| 156 | } | ||
| 157 | |||
| 158 | const run = exe.run(); | ||
| 159 | run.setName(annotated_case_name); | ||
| 160 | run.addArgs(case.cli_args); | ||
| 161 | run.expectExitCode(126); | ||
| 162 | |||
| 163 | self.step.dependOn(&run.step); | ||
| 164 | }, | ||
| 165 | } | ||
| 166 | } | ||
| 167 | |||
| 168 | const CompareOutput = @This(); | ||
| 169 | const std = @import("std"); | ||
| 170 | const ArrayList = std.ArrayList; | ||
| 171 | const fmt = std.fmt; | ||
| 172 | const mem = std.mem; | ||
| 173 | const fs = std.fs; | ||
| 174 | const OptimizeMode = std.builtin.OptimizeMode; | ||
test/src/StackTrace.zig created+107| ... | @@ -0,0 +1,107 @@ | ||
| 1 | b: *std.Build, | ||
| 2 | step: *Step, | ||
| 3 | test_index: usize, | ||
| 4 | test_filter: ?[]const u8, | ||
| 5 | optimize_modes: []const OptimizeMode, | ||
| 6 | check_exe: *std.Build.CompileStep, | ||
| 7 | |||
| 8 | const Expect = [@typeInfo(OptimizeMode).Enum.fields.len][]const u8; | ||
| 9 | |||
| 10 | pub fn addCase(self: *StackTrace, config: anytype) void { | ||
| 11 | if (@hasField(@TypeOf(config), "exclude")) { | ||
| 12 | if (config.exclude.exclude()) return; | ||
| 13 | } | ||
| 14 | if (@hasField(@TypeOf(config), "exclude_arch")) { | ||
| 15 | const exclude_arch: []const std.Target.Cpu.Arch = &config.exclude_arch; | ||
| 16 | for (exclude_arch) |arch| if (arch == builtin.cpu.arch) return; | ||
| 17 | } | ||
| 18 | if (@hasField(@TypeOf(config), "exclude_os")) { | ||
| 19 | const exclude_os: []const std.Target.Os.Tag = &config.exclude_os; | ||
| 20 | for (exclude_os) |os| if (os == builtin.os.tag) return; | ||
| 21 | } | ||
| 22 | for (self.optimize_modes) |optimize_mode| { | ||
| 23 | switch (optimize_mode) { | ||
| 24 | .Debug => { | ||
| 25 | if (@hasField(@TypeOf(config), "Debug")) { | ||
| 26 | self.addExpect(config.name, config.source, optimize_mode, config.Debug); | ||
| 27 | } | ||
| 28 | }, | ||
| 29 | .ReleaseSafe => { | ||
| 30 | if (@hasField(@TypeOf(config), "ReleaseSafe")) { | ||
| 31 | self.addExpect(config.name, config.source, optimize_mode, config.ReleaseSafe); | ||
| 32 | } | ||
| 33 | }, | ||
| 34 | .ReleaseFast => { | ||
| 35 | if (@hasField(@TypeOf(config), "ReleaseFast")) { | ||
| 36 | self.addExpect(config.name, config.source, optimize_mode, config.ReleaseFast); | ||
| 37 | } | ||
| 38 | }, | ||
| 39 | .ReleaseSmall => { | ||
| 40 | if (@hasField(@TypeOf(config), "ReleaseSmall")) { | ||
| 41 | self.addExpect(config.name, config.source, optimize_mode, config.ReleaseSmall); | ||
| 42 | } | ||
| 43 | }, | ||
| 44 | } | ||
| 45 | } | ||
| 46 | } | ||
| 47 | |||
| 48 | fn addExpect( | ||
| 49 | self: *StackTrace, | ||
| 50 | name: []const u8, | ||
| 51 | source: []const u8, | ||
| 52 | optimize_mode: OptimizeMode, | ||
| 53 | mode_config: anytype, | ||
| 54 | ) void { | ||
| 55 | if (@hasField(@TypeOf(mode_config), "exclude")) { | ||
| 56 | if (mode_config.exclude.exclude()) return; | ||
| 57 | } | ||
| 58 | if (@hasField(@TypeOf(mode_config), "exclude_arch")) { | ||
| 59 | const exclude_arch: []const std.Target.Cpu.Arch = &mode_config.exclude_arch; | ||
| 60 | for (exclude_arch) |arch| if (arch == builtin.cpu.arch) return; | ||
| 61 | } | ||
| 62 | if (@hasField(@TypeOf(mode_config), "exclude_os")) { | ||
| 63 | const exclude_os: []const std.Target.Os.Tag = &mode_config.exclude_os; | ||
| 64 | for (exclude_os) |os| if (os == builtin.os.tag) return; | ||
| 65 | } | ||
| 66 | |||
| 67 | const b = self.b; | ||
| 68 | const annotated_case_name = fmt.allocPrint(b.allocator, "check {s} ({s})", .{ | ||
| 69 | name, @tagName(optimize_mode), | ||
| 70 | }) catch @panic("OOM"); | ||
| 71 | if (self.test_filter) |filter| { | ||
| 72 | if (mem.indexOf(u8, annotated_case_name, filter) == null) return; | ||
| 73 | } | ||
| 74 | |||
| 75 | const src_basename = "source.zig"; | ||
| 76 | const write_src = b.addWriteFile(src_basename, source); | ||
| 77 | const exe = b.addExecutable(.{ | ||
| 78 | .name = "test", | ||
| 79 | .root_source_file = write_src.getFileSource(src_basename).?, | ||
| 80 | .optimize = optimize_mode, | ||
| 81 | .target = .{}, | ||
| 82 | }); | ||
| 83 | |||
| 84 | const run = b.addRunArtifact(exe); | ||
| 85 | run.removeEnvironmentVariable("ZIG_DEBUG_COLOR"); | ||
| 86 | run.setEnvironmentVariable("NO_COLOR", "1"); | ||
| 87 | run.expectExitCode(1); | ||
| 88 | run.expectStdOutEqual(""); | ||
| 89 | |||
| 90 | const check_run = b.addRunArtifact(self.check_exe); | ||
| 91 | check_run.setName(annotated_case_name); | ||
| 92 | check_run.addFileSourceArg(run.captureStdErr()); | ||
| 93 | check_run.addArgs(&.{ | ||
| 94 | @tagName(optimize_mode), | ||
| 95 | }); | ||
| 96 | check_run.expectStdOutEqual(mode_config.expect); | ||
| 97 | |||
| 98 | self.step.dependOn(&check_run.step); | ||
| 99 | } | ||
| 100 | |||
| 101 | const StackTrace = @This(); | ||
| 102 | const std = @import("std"); | ||
| 103 | const builtin = @import("builtin"); | ||
| 104 | const Step = std.Build.Step; | ||
| 105 | const OptimizeMode = std.builtin.OptimizeMode; | ||
| 106 | const fmt = std.fmt; | ||
| 107 | const mem = std.mem; | ||
test/src/check-stack-trace.zig created+79| ... | @@ -0,0 +1,79 @@ | ||
| 1 | const builtin = @import("builtin"); | ||
| 2 | const std = @import("std"); | ||
| 3 | const mem = std.mem; | ||
| 4 | const fs = std.fs; | ||
| 5 | |||
| 6 | pub fn main() !void { | ||
| 7 | var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator); | ||
| 8 | defer arena_instance.deinit(); | ||
| 9 | const arena = arena_instance.allocator(); | ||
| 10 | |||
| 11 | const args = try std.process.argsAlloc(arena); | ||
| 12 | |||
| 13 | const input_path = args[1]; | ||
| 14 | const optimize_mode_text = args[2]; | ||
| 15 | |||
| 16 | const input_bytes = try std.fs.cwd().readFileAlloc(arena, input_path, 5 * 1024 * 1024); | ||
| 17 | const optimize_mode = std.meta.stringToEnum(std.builtin.OptimizeMode, optimize_mode_text).?; | ||
| 18 | |||
| 19 | var stderr = input_bytes; | ||
| 20 | |||
| 21 | // process result | ||
| 22 | // - keep only basename of source file path | ||
| 23 | // - replace address with symbolic string | ||
| 24 | // - replace function name with symbolic string when optimize_mode != .Debug | ||
| 25 | // - skip empty lines | ||
| 26 | const got: []const u8 = got_result: { | ||
| 27 | var buf = std.ArrayList(u8).init(arena); | ||
| 28 | defer buf.deinit(); | ||
| 29 | if (stderr.len != 0 and stderr[stderr.len - 1] == '\n') stderr = stderr[0 .. stderr.len - 1]; | ||
| 30 | var it = mem.split(u8, stderr, "\n"); | ||
| 31 | process_lines: while (it.next()) |line| { | ||
| 32 | if (line.len == 0) continue; | ||
| 33 | |||
| 34 | // offset search past `[drive]:` on windows | ||
| 35 | var pos: usize = if (builtin.os.tag == .windows) 2 else 0; | ||
| 36 | // locate delims/anchor | ||
| 37 | const delims = [_][]const u8{ ":", ":", ":", " in ", "(", ")" }; | ||
| 38 | var marks = [_]usize{0} ** delims.len; | ||
| 39 | for (delims, 0..) |delim, i| { | ||
| 40 | marks[i] = mem.indexOfPos(u8, line, pos, delim) orelse { | ||
| 41 | // unexpected pattern: emit raw line and cont | ||
| 42 | try buf.appendSlice(line); | ||
| 43 | try buf.appendSlice("\n"); | ||
| 44 | continue :process_lines; | ||
| 45 | }; | ||
| 46 | pos = marks[i] + delim.len; | ||
| 47 | } | ||
| 48 | // locate source basename | ||
| 49 | pos = mem.lastIndexOfScalar(u8, line[0..marks[0]], fs.path.sep) orelse { | ||
| 50 | // unexpected pattern: emit raw line and cont | ||
| 51 | try buf.appendSlice(line); | ||
| 52 | try buf.appendSlice("\n"); | ||
| 53 | continue :process_lines; | ||
| 54 | }; | ||
| 55 | // end processing if source basename changes | ||
| 56 | if (!mem.eql(u8, "source.zig", line[pos + 1 .. marks[0]])) break; | ||
| 57 | // emit substituted line | ||
| 58 | try buf.appendSlice(line[pos + 1 .. marks[2] + delims[2].len]); | ||
| 59 | try buf.appendSlice(" [address]"); | ||
| 60 | if (optimize_mode == .Debug) { | ||
| 61 | // On certain platforms (windows) or possibly depending on how we choose to link main | ||
| 62 | // the object file extension may be present so we simply strip any extension. | ||
| 63 | if (mem.indexOfScalar(u8, line[marks[4]..marks[5]], '.')) |idot| { | ||
| 64 | try buf.appendSlice(line[marks[3] .. marks[4] + idot]); | ||
| 65 | try buf.appendSlice(line[marks[5]..]); | ||
| 66 | } else { | ||
| 67 | try buf.appendSlice(line[marks[3]..]); | ||
| 68 | } | ||
| 69 | } else { | ||
| 70 | try buf.appendSlice(line[marks[3] .. marks[3] + delims[3].len]); | ||
| 71 | try buf.appendSlice("[function]"); | ||
| 72 | } | ||
| 73 | try buf.appendSlice("\n"); | ||
| 74 | } | ||
| 75 | break :got_result try buf.toOwnedSlice(); | ||
| 76 | }; | ||
| 77 | |||
| 78 | try std.io.getStdOut().writeAll(got); | ||
| 79 | } | ||
test/src/compare_output.zig deleted-177| ... | @@ -1,177 +0,0 @@ | ||
| 1 | // This is the implementation of the test harness. | ||
| 2 | // For the actual test cases, see test/compare_output.zig. | ||
| 3 | const std = @import("std"); | ||
| 4 | const ArrayList = std.ArrayList; | ||
| 5 | const fmt = std.fmt; | ||
| 6 | const mem = std.mem; | ||
| 7 | const fs = std.fs; | ||
| 8 | const OptimizeMode = std.builtin.OptimizeMode; | ||
| 9 | |||
| 10 | pub const CompareOutputContext = struct { | ||
| 11 | b: *std.Build, | ||
| 12 | step: *std.Build.Step, | ||
| 13 | test_index: usize, | ||
| 14 | test_filter: ?[]const u8, | ||
| 15 | optimize_modes: []const OptimizeMode, | ||
| 16 | |||
| 17 | const Special = enum { | ||
| 18 | None, | ||
| 19 | Asm, | ||
| 20 | RuntimeSafety, | ||
| 21 | }; | ||
| 22 | |||
| 23 | const TestCase = struct { | ||
| 24 | name: []const u8, | ||
| 25 | sources: ArrayList(SourceFile), | ||
| 26 | expected_output: []const u8, | ||
| 27 | link_libc: bool, | ||
| 28 | special: Special, | ||
| 29 | cli_args: []const []const u8, | ||
| 30 | |||
| 31 | const SourceFile = struct { | ||
| 32 | filename: []const u8, | ||
| 33 | source: []const u8, | ||
| 34 | }; | ||
| 35 | |||
| 36 | pub fn addSourceFile(self: *TestCase, filename: []const u8, source: []const u8) void { | ||
| 37 | self.sources.append(SourceFile{ | ||
| 38 | .filename = filename, | ||
| 39 | .source = source, | ||
| 40 | }) catch unreachable; | ||
| 41 | } | ||
| 42 | |||
| 43 | pub fn setCommandLineArgs(self: *TestCase, args: []const []const u8) void { | ||
| 44 | self.cli_args = args; | ||
| 45 | } | ||
| 46 | }; | ||
| 47 | |||
| 48 | pub fn createExtra(self: *CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8, special: Special) TestCase { | ||
| 49 | var tc = TestCase{ | ||
| 50 | .name = name, | ||
| 51 | .sources = ArrayList(TestCase.SourceFile).init(self.b.allocator), | ||
| 52 | .expected_output = expected_output, | ||
| 53 | .link_libc = false, | ||
| 54 | .special = special, | ||
| 55 | .cli_args = &[_][]const u8{}, | ||
| 56 | }; | ||
| 57 | const root_src_name = if (special == Special.Asm) "source.s" else "source.zig"; | ||
| 58 | tc.addSourceFile(root_src_name, source); | ||
| 59 | return tc; | ||
| 60 | } | ||
| 61 | |||
| 62 | pub fn create(self: *CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) TestCase { | ||
| 63 | return createExtra(self, name, source, expected_output, Special.None); | ||
| 64 | } | ||
| 65 | |||
| 66 | pub fn addC(self: *CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) void { | ||
| 67 | var tc = self.create(name, source, expected_output); | ||
| 68 | tc.link_libc = true; | ||
| 69 | self.addCase(tc); | ||
| 70 | } | ||
| 71 | |||
| 72 | pub fn add(self: *CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) void { | ||
| 73 | const tc = self.create(name, source, expected_output); | ||
| 74 | self.addCase(tc); | ||
| 75 | } | ||
| 76 | |||
| 77 | pub fn addAsm(self: *CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) void { | ||
| 78 | const tc = self.createExtra(name, source, expected_output, Special.Asm); | ||
| 79 | self.addCase(tc); | ||
| 80 | } | ||
| 81 | |||
| 82 | pub fn addRuntimeSafety(self: *CompareOutputContext, name: []const u8, source: []const u8) void { | ||
| 83 | const tc = self.createExtra(name, source, undefined, Special.RuntimeSafety); | ||
| 84 | self.addCase(tc); | ||
| 85 | } | ||
| 86 | |||
| 87 | pub fn addCase(self: *CompareOutputContext, case: TestCase) void { | ||
| 88 | const b = self.b; | ||
| 89 | |||
| 90 | const write_src = b.addWriteFiles(); | ||
| 91 | for (case.sources.items) |src_file| { | ||
| 92 | write_src.add(src_file.filename, src_file.source); | ||
| 93 | } | ||
| 94 | |||
| 95 | switch (case.special) { | ||
| 96 | Special.Asm => { | ||
| 97 | const annotated_case_name = fmt.allocPrint(self.b.allocator, "assemble-and-link {s}", .{ | ||
| 98 | case.name, | ||
| 99 | }) catch unreachable; | ||
| 100 | if (self.test_filter) |filter| { | ||
| 101 | if (mem.indexOf(u8, annotated_case_name, filter) == null) return; | ||
| 102 | } | ||
| 103 | |||
| 104 | const exe = b.addExecutable(.{ | ||
| 105 | .name = "test", | ||
| 106 | .target = .{}, | ||
| 107 | .optimize = .Debug, | ||
| 108 | }); | ||
| 109 | exe.addAssemblyFileSource(write_src.getFileSource(case.sources.items[0].filename).?); | ||
| 110 | |||
| 111 | const run = exe.run(); | ||
| 112 | run.addArgs(case.cli_args); | ||
| 113 | run.expectStdErrEqual(""); | ||
| 114 | run.expectStdOutEqual(case.expected_output); | ||
| 115 | |||
| 116 | self.step.dependOn(&run.step); | ||
| 117 | }, | ||
| 118 | Special.None => { | ||
| 119 | for (self.optimize_modes) |optimize| { | ||
| 120 | const annotated_case_name = fmt.allocPrint(self.b.allocator, "{s} {s} ({s})", .{ | ||
| 121 | "compare-output", | ||
| 122 | case.name, | ||
| 123 | @tagName(optimize), | ||
| 124 | }) catch unreachable; | ||
| 125 | if (self.test_filter) |filter| { | ||
| 126 | if (mem.indexOf(u8, annotated_case_name, filter) == null) continue; | ||
| 127 | } | ||
| 128 | |||
| 129 | const basename = case.sources.items[0].filename; | ||
| 130 | const exe = b.addExecutable(.{ | ||
| 131 | .name = "test", | ||
| 132 | .root_source_file = write_src.getFileSource(basename).?, | ||
| 133 | .optimize = optimize, | ||
| 134 | .target = .{}, | ||
| 135 | }); | ||
| 136 | if (case.link_libc) { | ||
| 137 | exe.linkSystemLibrary("c"); | ||
| 138 | } | ||
| 139 | |||
| 140 | const run = exe.run(); | ||
| 141 | run.addArgs(case.cli_args); | ||
| 142 | run.expectStdErrEqual(""); | ||
| 143 | run.expectStdOutEqual(case.expected_output); | ||
| 144 | |||
| 145 | self.step.dependOn(&run.step); | ||
| 146 | } | ||
| 147 | }, | ||
| 148 | Special.RuntimeSafety => { | ||
| 149 | // TODO iterate over self.optimize_modes and test this in both | ||
| 150 | // debug and release safe mode | ||
| 151 | const annotated_case_name = fmt.allocPrint(self.b.allocator, "safety {s}", .{case.name}) catch unreachable; | ||
| 152 | if (self.test_filter) |filter| { | ||
| 153 | if (mem.indexOf(u8, annotated_case_name, filter) == null) return; | ||
| 154 | } | ||
| 155 | |||
| 156 | const basename = case.sources.items[0].filename; | ||
| 157 | const exe = b.addExecutable(.{ | ||
| 158 | .name = "test", | ||
| 159 | .root_source_file = write_src.getFileSource(basename).?, | ||
| 160 | .target = .{}, | ||
| 161 | .optimize = .Debug, | ||
| 162 | }); | ||
| 163 | if (case.link_libc) { | ||
| 164 | exe.linkSystemLibrary("c"); | ||
| 165 | } | ||
| 166 | |||
| 167 | const run = exe.run(); | ||
| 168 | run.addArgs(case.cli_args); | ||
| 169 | run.stderr_action = .ignore; | ||
| 170 | run.stdout_action = .ignore; | ||
| 171 | run.expected_term = .{ .Exited = 126 }; | ||
| 172 | |||
| 173 | self.step.dependOn(&run.step); | ||
| 174 | }, | ||
| 175 | } | ||
| 176 | } | ||
| 177 | }; | ||
test/stage2/cbe.zig deleted-1015| ... | @@ -1,1015 +0,0 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const TestContext = @import("../../src/test.zig").TestContext; | ||
| 3 | |||
| 4 | // These tests should work with all platforms, but we're using linux_x64 for | ||
| 5 | // now for consistency. Will be expanded eventually. | ||
| 6 | const linux_x64 = std.zig.CrossTarget{ | ||
| 7 | .cpu_arch = .x86_64, | ||
| 8 | .os_tag = .linux, | ||
| 9 | }; | ||
| 10 | |||
| 11 | pub fn addCases(ctx: *TestContext) !void { | ||
| 12 | { | ||
| 13 | var case = ctx.exeFromCompiledC("hello world with updates", .{}); | ||
| 14 | |||
| 15 | // Regular old hello world | ||
| 16 | case.addCompareOutput( | ||
| 17 | \\extern fn puts(s: [*:0]const u8) c_int; | ||
| 18 | \\pub export fn main() c_int { | ||
| 19 | \\ _ = puts("hello world!"); | ||
| 20 | \\ return 0; | ||
| 21 | \\} | ||
| 22 | , "hello world!" ++ std.cstr.line_sep); | ||
| 23 | |||
| 24 | // Now change the message only | ||
| 25 | case.addCompareOutput( | ||
| 26 | \\extern fn puts(s: [*:0]const u8) c_int; | ||
| 27 | \\pub export fn main() c_int { | ||
| 28 | \\ _ = puts("yo"); | ||
| 29 | \\ return 0; | ||
| 30 | \\} | ||
| 31 | , "yo" ++ std.cstr.line_sep); | ||
| 32 | |||
| 33 | // Add an unused Decl | ||
| 34 | case.addCompareOutput( | ||
| 35 | \\extern fn puts(s: [*:0]const u8) c_int; | ||
| 36 | \\pub export fn main() c_int { | ||
| 37 | \\ _ = puts("yo!"); | ||
| 38 | \\ return 0; | ||
| 39 | \\} | ||
| 40 | \\fn unused() void {} | ||
| 41 | , "yo!" ++ std.cstr.line_sep); | ||
| 42 | |||
| 43 | // Comptime return type and calling convention expected. | ||
| 44 | case.addError( | ||
| 45 | \\var x: i32 = 1234; | ||
| 46 | \\pub export fn main() x { | ||
| 47 | \\ return 0; | ||
| 48 | \\} | ||
| 49 | \\export fn foo() callconv(y) c_int { | ||
| 50 | \\ return 0; | ||
| 51 | \\} | ||
| 52 | \\var y: @import("std").builtin.CallingConvention = .C; | ||
| 53 | , &.{ | ||
| 54 | ":2:22: error: expected type 'type', found 'i32'", | ||
| 55 | ":5:26: error: unable to resolve comptime value", | ||
| 56 | ":5:26: note: calling convention must be comptime-known", | ||
| 57 | }); | ||
| 58 | } | ||
| 59 | |||
| 60 | { | ||
| 61 | var case = ctx.exeFromCompiledC("var args", .{}); | ||
| 62 | |||
| 63 | case.addCompareOutput( | ||
| 64 | \\extern fn printf(format: [*:0]const u8, ...) c_int; | ||
| 65 | \\ | ||
| 66 | \\pub export fn main() c_int { | ||
| 67 | \\ _ = printf("Hello, %s!\n", "world"); | ||
| 68 | \\ return 0; | ||
| 69 | \\} | ||
| 70 | , "Hello, world!" ++ std.cstr.line_sep); | ||
| 71 | } | ||
| 72 | |||
| 73 | { | ||
| 74 | var case = ctx.exeFromCompiledC("@intToError", .{}); | ||
| 75 | |||
| 76 | case.addCompareOutput( | ||
| 77 | \\pub export fn main() c_int { | ||
| 78 | \\ // comptime checks | ||
| 79 | \\ const a = error.A; | ||
| 80 | \\ const b = error.B; | ||
| 81 | \\ const c = @intToError(2); | ||
| 82 | \\ const d = @intToError(1); | ||
| 83 | \\ if (!(c == b)) unreachable; | ||
| 84 | \\ if (!(a == d)) unreachable; | ||
| 85 | \\ // runtime checks | ||
| 86 | \\ var x = error.A; | ||
| 87 | \\ var y = error.B; | ||
| 88 | \\ var z = @intToError(2); | ||
| 89 | \\ var f = @intToError(1); | ||
| 90 | \\ if (!(y == z)) unreachable; | ||
| 91 | \\ if (!(x == f)) unreachable; | ||
| 92 | \\ return 0; | ||
| 93 | \\} | ||
| 94 | , ""); | ||
| 95 | case.addError( | ||
| 96 | \\pub export fn main() c_int { | ||
| 97 | \\ _ = @intToError(0); | ||
| 98 | \\ return 0; | ||
| 99 | \\} | ||
| 100 | , &.{":2:21: error: integer value '0' represents no error"}); | ||
| 101 | case.addError( | ||
| 102 | \\pub export fn main() c_int { | ||
| 103 | \\ _ = @intToError(3); | ||
| 104 | \\ return 0; | ||
| 105 | \\} | ||
| 106 | , &.{":2:21: error: integer value '3' represents no error"}); | ||
| 107 | } | ||
| 108 | |||
| 109 | { | ||
| 110 | var case = ctx.exeFromCompiledC("x86_64-linux inline assembly", linux_x64); | ||
| 111 | |||
| 112 | // Exit with 0 | ||
| 113 | case.addCompareOutput( | ||
| 114 | \\fn exitGood() noreturn { | ||
| 115 | \\ asm volatile ("syscall" | ||
| 116 | \\ : | ||
| 117 | \\ : [number] "{rax}" (231), | ||
| 118 | \\ [arg1] "{rdi}" (0) | ||
| 119 | \\ ); | ||
| 120 | \\ unreachable; | ||
| 121 | \\} | ||
| 122 | \\ | ||
| 123 | \\pub export fn main() c_int { | ||
| 124 | \\ exitGood(); | ||
| 125 | \\} | ||
| 126 | , ""); | ||
| 127 | |||
| 128 | // Pass a usize parameter to exit | ||
| 129 | case.addCompareOutput( | ||
| 130 | \\pub export fn main() c_int { | ||
| 131 | \\ exit(0); | ||
| 132 | \\} | ||
| 133 | \\ | ||
| 134 | \\fn exit(code: usize) noreturn { | ||
| 135 | \\ asm volatile ("syscall" | ||
| 136 | \\ : | ||
| 137 | \\ : [number] "{rax}" (231), | ||
| 138 | \\ [arg1] "{rdi}" (code) | ||
| 139 | \\ ); | ||
| 140 | \\ unreachable; | ||
| 141 | \\} | ||
| 142 | , ""); | ||
| 143 | |||
| 144 | // Change the parameter to u8 | ||
| 145 | case.addCompareOutput( | ||
| 146 | \\pub export fn main() c_int { | ||
| 147 | \\ exit(0); | ||
| 148 | \\} | ||
| 149 | \\ | ||
| 150 | \\fn exit(code: u8) noreturn { | ||
| 151 | \\ asm volatile ("syscall" | ||
| 152 | \\ : | ||
| 153 | \\ : [number] "{rax}" (231), | ||
| 154 | \\ [arg1] "{rdi}" (code) | ||
| 155 | \\ ); | ||
| 156 | \\ unreachable; | ||
| 157 | \\} | ||
| 158 | , ""); | ||
| 159 | |||
| 160 | // Do some arithmetic at the exit callsite | ||
| 161 | case.addCompareOutput( | ||
| 162 | \\pub export fn main() c_int { | ||
| 163 | \\ exitMath(1); | ||
| 164 | \\} | ||
| 165 | \\ | ||
| 166 | \\fn exitMath(a: u8) noreturn { | ||
| 167 | \\ exit(0 + a - a); | ||
| 168 | \\} | ||
| 169 | \\ | ||
| 170 | \\fn exit(code: u8) noreturn { | ||
| 171 | \\ asm volatile ("syscall" | ||
| 172 | \\ : | ||
| 173 | \\ : [number] "{rax}" (231), | ||
| 174 | \\ [arg1] "{rdi}" (code) | ||
| 175 | \\ ); | ||
| 176 | \\ unreachable; | ||
| 177 | \\} | ||
| 178 | \\ | ||
| 179 | , ""); | ||
| 180 | |||
| 181 | // Invert the arithmetic | ||
| 182 | case.addCompareOutput( | ||
| 183 | \\pub export fn main() c_int { | ||
| 184 | \\ exitMath(1); | ||
| 185 | \\} | ||
| 186 | \\ | ||
| 187 | \\fn exitMath(a: u8) noreturn { | ||
| 188 | \\ exit(a + 0 - a); | ||
| 189 | \\} | ||
| 190 | \\ | ||
| 191 | \\fn exit(code: u8) noreturn { | ||
| 192 | \\ asm volatile ("syscall" | ||
| 193 | \\ : | ||
| 194 | \\ : [number] "{rax}" (231), | ||
| 195 | \\ [arg1] "{rdi}" (code) | ||
| 196 | \\ ); | ||
| 197 | \\ unreachable; | ||
| 198 | \\} | ||
| 199 | \\ | ||
| 200 | , ""); | ||
| 201 | } | ||
| 202 | |||
| 203 | { | ||
| 204 | var case = ctx.exeFromCompiledC("alloc and retptr", .{}); | ||
| 205 | |||
| 206 | case.addCompareOutput( | ||
| 207 | \\fn add(a: i32, b: i32) i32 { | ||
| 208 | \\ return a + b; | ||
| 209 | \\} | ||
| 210 | \\ | ||
| 211 | \\fn addIndirect(a: i32, b: i32) i32 { | ||
| 212 | \\ return add(a, b); | ||
| 213 | \\} | ||
| 214 | \\ | ||
| 215 | \\pub export fn main() c_int { | ||
| 216 | \\ return addIndirect(1, 2) - 3; | ||
| 217 | \\} | ||
| 218 | , ""); | ||
| 219 | } | ||
| 220 | |||
| 221 | { | ||
| 222 | var case = ctx.exeFromCompiledC("inferred local const and var", .{}); | ||
| 223 | |||
| 224 | case.addCompareOutput( | ||
| 225 | \\fn add(a: i32, b: i32) i32 { | ||
| 226 | \\ return a + b; | ||
| 227 | \\} | ||
| 228 | \\ | ||
| 229 | \\pub export fn main() c_int { | ||
| 230 | \\ const x = add(1, 2); | ||
| 231 | \\ var y = add(3, 0); | ||
| 232 | \\ y -= x; | ||
| 233 | \\ return y; | ||
| 234 | \\} | ||
| 235 | , ""); | ||
| 236 | } | ||
| 237 | { | ||
| 238 | var case = ctx.exeFromCompiledC("control flow", .{}); | ||
| 239 | |||
| 240 | // Simple while loop | ||
| 241 | case.addCompareOutput( | ||
| 242 | \\pub export fn main() c_int { | ||
| 243 | \\ var a: c_int = 0; | ||
| 244 | \\ while (a < 5) : (a+=1) {} | ||
| 245 | \\ return a - 5; | ||
| 246 | \\} | ||
| 247 | , ""); | ||
| 248 | case.addCompareOutput( | ||
| 249 | \\pub export fn main() c_int { | ||
| 250 | \\ var a = true; | ||
| 251 | \\ while (!a) {} | ||
| 252 | \\ return 0; | ||
| 253 | \\} | ||
| 254 | , ""); | ||
| 255 | |||
| 256 | // If expression | ||
| 257 | case.addCompareOutput( | ||
| 258 | \\pub export fn main() c_int { | ||
| 259 | \\ var cond: c_int = 0; | ||
| 260 | \\ var a: c_int = @as(c_int, if (cond == 0) | ||
| 261 | \\ 2 | ||
| 262 | \\ else | ||
| 263 | \\ 3) + 9; | ||
| 264 | \\ return a - 11; | ||
| 265 | \\} | ||
| 266 | , ""); | ||
| 267 | |||
| 268 | // If expression with breakpoint that does not get hit | ||
| 269 | case.addCompareOutput( | ||
| 270 | \\pub export fn main() c_int { | ||
| 271 | \\ var x: i32 = 1; | ||
| 272 | \\ if (x != 1) @breakpoint(); | ||
| 273 | \\ return 0; | ||
| 274 | \\} | ||
| 275 | , ""); | ||
| 276 | |||
| 277 | // Switch expression | ||
| 278 | case.addCompareOutput( | ||
| 279 | \\pub export fn main() c_int { | ||
| 280 | \\ var cond: c_int = 0; | ||
| 281 | \\ var a: c_int = switch (cond) { | ||
| 282 | \\ 1 => 1, | ||
| 283 | \\ 2 => 2, | ||
| 284 | \\ 99...300, 12 => 3, | ||
| 285 | \\ 0 => 4, | ||
| 286 | \\ else => 5, | ||
| 287 | \\ }; | ||
| 288 | \\ return a - 4; | ||
| 289 | \\} | ||
| 290 | , ""); | ||
| 291 | |||
| 292 | // Switch expression missing else case. | ||
| 293 | case.addError( | ||
| 294 | \\pub export fn main() c_int { | ||
| 295 | \\ var cond: c_int = 0; | ||
| 296 | \\ const a: c_int = switch (cond) { | ||
| 297 | \\ 1 => 1, | ||
| 298 | \\ 2 => 2, | ||
| 299 | \\ 3 => 3, | ||
| 300 | \\ 4 => 4, | ||
| 301 | \\ }; | ||
| 302 | \\ return a - 4; | ||
| 303 | \\} | ||
| 304 | , &.{":3:22: error: switch must handle all possibilities"}); | ||
| 305 | |||
| 306 | // Switch expression, has an unreachable prong. | ||
| 307 | case.addCompareOutput( | ||
| 308 | \\pub export fn main() c_int { | ||
| 309 | \\ var cond: c_int = 0; | ||
| 310 | \\ const a: c_int = switch (cond) { | ||
| 311 | \\ 1 => 1, | ||
| 312 | \\ 2 => 2, | ||
| 313 | \\ 99...300, 12 => 3, | ||
| 314 | \\ 0 => 4, | ||
| 315 | \\ 13 => unreachable, | ||
| 316 | \\ else => 5, | ||
| 317 | \\ }; | ||
| 318 | \\ return a - 4; | ||
| 319 | \\} | ||
| 320 | , ""); | ||
| 321 | |||
| 322 | // Switch expression, has an unreachable prong and prongs write | ||
| 323 | // to result locations. | ||
| 324 | case.addCompareOutput( | ||
| 325 | \\pub export fn main() c_int { | ||
| 326 | \\ var cond: c_int = 0; | ||
| 327 | \\ var a: c_int = switch (cond) { | ||
| 328 | \\ 1 => 1, | ||
| 329 | \\ 2 => 2, | ||
| 330 | \\ 99...300, 12 => 3, | ||
| 331 | \\ 0 => 4, | ||
| 332 | \\ 13 => unreachable, | ||
| 333 | \\ else => 5, | ||
| 334 | \\ }; | ||
| 335 | \\ return a - 4; | ||
| 336 | \\} | ||
| 337 | , ""); | ||
| 338 | |||
| 339 | // Integer switch expression has duplicate case value. | ||
| 340 | case.addError( | ||
| 341 | \\pub export fn main() c_int { | ||
| 342 | \\ var cond: c_int = 0; | ||
| 343 | \\ const a: c_int = switch (cond) { | ||
| 344 | \\ 1 => 1, | ||
| 345 | \\ 2 => 2, | ||
| 346 | \\ 96, 11...13, 97 => 3, | ||
| 347 | \\ 0 => 4, | ||
| 348 | \\ 90, 12 => 100, | ||
| 349 | \\ else => 5, | ||
| 350 | \\ }; | ||
| 351 | \\ return a - 4; | ||
| 352 | \\} | ||
| 353 | , &.{ | ||
| 354 | ":8:13: error: duplicate switch value", | ||
| 355 | ":6:15: note: previous value here", | ||
| 356 | }); | ||
| 357 | |||
| 358 | // Boolean switch expression has duplicate case value. | ||
| 359 | case.addError( | ||
| 360 | \\pub export fn main() c_int { | ||
| 361 | \\ var a: bool = false; | ||
| 362 | \\ const b: c_int = switch (a) { | ||
| 363 | \\ false => 1, | ||
| 364 | \\ true => 2, | ||
| 365 | \\ false => 3, | ||
| 366 | \\ }; | ||
| 367 | \\ _ = b; | ||
| 368 | \\} | ||
| 369 | , &.{ | ||
| 370 | ":6:9: error: duplicate switch value", | ||
| 371 | }); | ||
| 372 | |||
| 373 | // Sparse (no range capable) switch expression has duplicate case value. | ||
| 374 | case.addError( | ||
| 375 | \\pub export fn main() c_int { | ||
| 376 | \\ const A: type = i32; | ||
| 377 | \\ const b: c_int = switch (A) { | ||
| 378 | \\ i32 => 1, | ||
| 379 | \\ bool => 2, | ||
| 380 | \\ f64, i32 => 3, | ||
| 381 | \\ else => 4, | ||
| 382 | \\ }; | ||
| 383 | \\ _ = b; | ||
| 384 | \\} | ||
| 385 | , &.{ | ||
| 386 | ":6:14: error: duplicate switch value", | ||
| 387 | ":4:9: note: previous value here", | ||
| 388 | }); | ||
| 389 | |||
| 390 | // Ranges not allowed for some kinds of switches. | ||
| 391 | case.addError( | ||
| 392 | \\pub export fn main() c_int { | ||
| 393 | \\ const A: type = i32; | ||
| 394 | \\ const b: c_int = switch (A) { | ||
| 395 | \\ i32 => 1, | ||
| 396 | \\ bool => 2, | ||
| 397 | \\ f16...f64 => 3, | ||
| 398 | \\ else => 4, | ||
| 399 | \\ }; | ||
| 400 | \\ _ = b; | ||
| 401 | \\} | ||
| 402 | , &.{ | ||
| 403 | ":3:30: error: ranges not allowed when switching on type 'type'", | ||
| 404 | ":6:12: note: range here", | ||
| 405 | }); | ||
| 406 | |||
| 407 | // Switch expression has unreachable else prong. | ||
| 408 | case.addError( | ||
| 409 | \\pub export fn main() c_int { | ||
| 410 | \\ var a: u2 = 0; | ||
| 411 | \\ const b: i32 = switch (a) { | ||
| 412 | \\ 0 => 10, | ||
| 413 | \\ 1 => 20, | ||
| 414 | \\ 2 => 30, | ||
| 415 | \\ 3 => 40, | ||
| 416 | \\ else => 50, | ||
| 417 | \\ }; | ||
| 418 | \\ _ = b; | ||
| 419 | \\} | ||
| 420 | , &.{ | ||
| 421 | ":8:14: error: unreachable else prong; all cases already handled", | ||
| 422 | }); | ||
| 423 | } | ||
| 424 | //{ | ||
| 425 | // var case = ctx.exeFromCompiledC("optionals", .{}); | ||
| 426 | |||
| 427 | // // Simple while loop | ||
| 428 | // case.addCompareOutput( | ||
| 429 | // \\pub export fn main() c_int { | ||
| 430 | // \\ var count: c_int = 0; | ||
| 431 | // \\ var opt_ptr: ?*c_int = &count; | ||
| 432 | // \\ while (opt_ptr) |_| : (count += 1) { | ||
| 433 | // \\ if (count == 4) opt_ptr = null; | ||
| 434 | // \\ } | ||
| 435 | // \\ return count - 5; | ||
| 436 | // \\} | ||
| 437 | // , ""); | ||
| 438 | |||
| 439 | // // Same with non pointer optionals | ||
| 440 | // case.addCompareOutput( | ||
| 441 | // \\pub export fn main() c_int { | ||
| 442 | // \\ var count: c_int = 0; | ||
| 443 | // \\ var opt_ptr: ?c_int = count; | ||
| 444 | // \\ while (opt_ptr) |_| : (count += 1) { | ||
| 445 | // \\ if (count == 4) opt_ptr = null; | ||
| 446 | // \\ } | ||
| 447 | // \\ return count - 5; | ||
| 448 | // \\} | ||
| 449 | // , ""); | ||
| 450 | //} | ||
| 451 | |||
| 452 | { | ||
| 453 | var case = ctx.exeFromCompiledC("errors", .{}); | ||
| 454 | case.addCompareOutput( | ||
| 455 | \\pub export fn main() c_int { | ||
| 456 | \\ var e1 = error.Foo; | ||
| 457 | \\ var e2 = error.Bar; | ||
| 458 | \\ assert(e1 != e2); | ||
| 459 | \\ assert(e1 == error.Foo); | ||
| 460 | \\ assert(e2 == error.Bar); | ||
| 461 | \\ return 0; | ||
| 462 | \\} | ||
| 463 | \\fn assert(b: bool) void { | ||
| 464 | \\ if (!b) unreachable; | ||
| 465 | \\} | ||
| 466 | , ""); | ||
| 467 | case.addCompareOutput( | ||
| 468 | \\pub export fn main() c_int { | ||
| 469 | \\ var e: anyerror!c_int = 0; | ||
| 470 | \\ const i = e catch 69; | ||
| 471 | \\ return i; | ||
| 472 | \\} | ||
| 473 | , ""); | ||
| 474 | case.addCompareOutput( | ||
| 475 | \\pub export fn main() c_int { | ||
| 476 | \\ var e: anyerror!c_int = error.Foo; | ||
| 477 | \\ const i = e catch 69; | ||
| 478 | \\ return 69 - i; | ||
| 479 | \\} | ||
| 480 | , ""); | ||
| 481 | case.addCompareOutput( | ||
| 482 | \\const E = error{e}; | ||
| 483 | \\const S = struct { x: u32 }; | ||
| 484 | \\fn f() E!u32 { | ||
| 485 | \\ const x = (try @as(E!S, S{ .x = 1 })).x; | ||
| 486 | \\ return x; | ||
| 487 | \\} | ||
| 488 | \\pub export fn main() c_int { | ||
| 489 | \\ const x = f() catch @as(u32, 0); | ||
| 490 | \\ if (x != 1) unreachable; | ||
| 491 | \\ return 0; | ||
| 492 | \\} | ||
| 493 | , ""); | ||
| 494 | } | ||
| 495 | |||
| 496 | { | ||
| 497 | var case = ctx.exeFromCompiledC("structs", .{}); | ||
| 498 | case.addError( | ||
| 499 | \\const Point = struct { x: i32, y: i32 }; | ||
| 500 | \\pub export fn main() c_int { | ||
| 501 | \\ var p: Point = .{ | ||
| 502 | \\ .y = 24, | ||
| 503 | \\ .x = 12, | ||
| 504 | \\ .y = 24, | ||
| 505 | \\ }; | ||
| 506 | \\ return p.y - p.x - p.x; | ||
| 507 | \\} | ||
| 508 | , &.{ | ||
| 509 | ":6:10: error: duplicate field", | ||
| 510 | ":4:10: note: other field here", | ||
| 511 | }); | ||
| 512 | case.addError( | ||
| 513 | \\const Point = struct { x: i32, y: i32 }; | ||
| 514 | \\pub export fn main() c_int { | ||
| 515 | \\ var p: Point = .{ | ||
| 516 | \\ .y = 24, | ||
| 517 | \\ }; | ||
| 518 | \\ return p.y - p.x - p.x; | ||
| 519 | \\} | ||
| 520 | , &.{ | ||
| 521 | ":3:21: error: missing struct field: x", | ||
| 522 | ":1:15: note: struct 'tmp.Point' declared here", | ||
| 523 | }); | ||
| 524 | case.addError( | ||
| 525 | \\const Point = struct { x: i32, y: i32 }; | ||
| 526 | \\pub export fn main() c_int { | ||
| 527 | \\ var p: Point = .{ | ||
| 528 | \\ .x = 12, | ||
| 529 | \\ .y = 24, | ||
| 530 | \\ .z = 48, | ||
| 531 | \\ }; | ||
| 532 | \\ return p.y - p.x - p.x; | ||
| 533 | \\} | ||
| 534 | , &.{ | ||
| 535 | ":6:10: error: no field named 'z' in struct 'tmp.Point'", | ||
| 536 | ":1:15: note: struct declared here", | ||
| 537 | }); | ||
| 538 | case.addCompareOutput( | ||
| 539 | \\const Point = struct { x: i32, y: i32 }; | ||
| 540 | \\pub export fn main() c_int { | ||
| 541 | \\ var p: Point = .{ | ||
| 542 | \\ .x = 12, | ||
| 543 | \\ .y = 24, | ||
| 544 | \\ }; | ||
| 545 | \\ return p.y - p.x - p.x; | ||
| 546 | \\} | ||
| 547 | , ""); | ||
| 548 | case.addCompareOutput( | ||
| 549 | \\const Point = struct { x: i32, y: i32, z: i32, a: i32, b: i32 }; | ||
| 550 | \\pub export fn main() c_int { | ||
| 551 | \\ var p: Point = .{ | ||
| 552 | \\ .x = 18, | ||
| 553 | \\ .y = 24, | ||
| 554 | \\ .z = 1, | ||
| 555 | \\ .a = 2, | ||
| 556 | \\ .b = 3, | ||
| 557 | \\ }; | ||
| 558 | \\ return p.y - p.x - p.z - p.a - p.b; | ||
| 559 | \\} | ||
| 560 | , ""); | ||
| 561 | } | ||
| 562 | |||
| 563 | { | ||
| 564 | var case = ctx.exeFromCompiledC("unions", .{}); | ||
| 565 | |||
| 566 | case.addError( | ||
| 567 | \\const U = union { | ||
| 568 | \\ a: u32, | ||
| 569 | \\ b | ||
| 570 | \\}; | ||
| 571 | , &.{ | ||
| 572 | ":3:5: error: union field missing type", | ||
| 573 | }); | ||
| 574 | |||
| 575 | case.addError( | ||
| 576 | \\const E = enum { a, b }; | ||
| 577 | \\const U = union(E) { | ||
| 578 | \\ a: u32 = 1, | ||
| 579 | \\ b: f32 = 2, | ||
| 580 | \\}; | ||
| 581 | , &.{ | ||
| 582 | ":2:11: error: explicitly valued tagged union requires inferred enum tag type", | ||
| 583 | ":3:14: note: tag value specified here", | ||
| 584 | }); | ||
| 585 | |||
| 586 | case.addError( | ||
| 587 | \\const U = union(enum) { | ||
| 588 | \\ a: u32 = 1, | ||
| 589 | \\ b: f32 = 2, | ||
| 590 | \\}; | ||
| 591 | , &.{ | ||
| 592 | ":1:11: error: explicitly valued tagged union missing integer tag type", | ||
| 593 | ":2:14: note: tag value specified here", | ||
| 594 | }); | ||
| 595 | } | ||
| 596 | |||
| 597 | { | ||
| 598 | var case = ctx.exeFromCompiledC("enums", .{}); | ||
| 599 | |||
| 600 | case.addError( | ||
| 601 | \\const E1 = packed enum { a, b, c }; | ||
| 602 | \\const E2 = extern enum { a, b, c }; | ||
| 603 | \\export fn foo() void { | ||
| 604 | \\ _ = E1.a; | ||
| 605 | \\} | ||
| 606 | \\export fn bar() void { | ||
| 607 | \\ _ = E2.a; | ||
| 608 | \\} | ||
| 609 | , &.{ | ||
| 610 | ":1:12: error: enums do not support 'packed' or 'extern'; instead provide an explicit integer tag type", | ||
| 611 | ":2:12: error: enums do not support 'packed' or 'extern'; instead provide an explicit integer tag type", | ||
| 612 | }); | ||
| 613 | |||
| 614 | // comptime and types are caught in AstGen. | ||
| 615 | case.addError( | ||
| 616 | \\const E1 = enum { | ||
| 617 | \\ a, | ||
| 618 | \\ comptime b, | ||
| 619 | \\ c, | ||
| 620 | \\}; | ||
| 621 | \\const E2 = enum { | ||
| 622 | \\ a, | ||
| 623 | \\ b: i32, | ||
| 624 | \\ c, | ||
| 625 | \\}; | ||
| 626 | \\export fn foo() void { | ||
| 627 | \\ _ = E1.a; | ||
| 628 | \\} | ||
| 629 | \\export fn bar() void { | ||
| 630 | \\ _ = E2.a; | ||
| 631 | \\} | ||
| 632 | , &.{ | ||
| 633 | ":3:5: error: enum fields cannot be marked comptime", | ||
| 634 | ":8:8: error: enum fields do not have types", | ||
| 635 | ":6:12: note: consider 'union(enum)' here to make it a tagged union", | ||
| 636 | }); | ||
| 637 | |||
| 638 | // @enumToInt, @intToEnum, enum literal coercion, field access syntax, comparison, switch | ||
| 639 | case.addCompareOutput( | ||
| 640 | \\const Number = enum { One, Two, Three }; | ||
| 641 | \\ | ||
| 642 | \\pub export fn main() c_int { | ||
| 643 | \\ var number1 = Number.One; | ||
| 644 | \\ var number2: Number = .Two; | ||
| 645 | \\ const number3 = @intToEnum(Number, 2); | ||
| 646 | \\ if (number1 == number2) return 1; | ||
| 647 | \\ if (number2 == number3) return 1; | ||
| 648 | \\ if (@enumToInt(number1) != 0) return 1; | ||
| 649 | \\ if (@enumToInt(number2) != 1) return 1; | ||
| 650 | \\ if (@enumToInt(number3) != 2) return 1; | ||
| 651 | \\ var x: Number = .Two; | ||
| 652 | \\ if (number2 != x) return 1; | ||
| 653 | \\ switch (x) { | ||
| 654 | \\ .One => return 1, | ||
| 655 | \\ .Two => return 0, | ||
| 656 | \\ number3 => return 2, | ||
| 657 | \\ } | ||
| 658 | \\} | ||
| 659 | , ""); | ||
| 660 | |||
| 661 | // Specifying alignment is a parse error. | ||
| 662 | // This also tests going from a successful build to a parse error. | ||
| 663 | case.addError( | ||
| 664 | \\const E1 = enum { | ||
| 665 | \\ a, | ||
| 666 | \\ b align(4), | ||
| 667 | \\ c, | ||
| 668 | \\}; | ||
| 669 | \\export fn foo() void { | ||
| 670 | \\ _ = E1.a; | ||
| 671 | \\} | ||
| 672 | , &.{ | ||
| 673 | ":3:13: error: enum fields cannot be aligned", | ||
| 674 | }); | ||
| 675 | |||
| 676 | // Redundant non-exhaustive enum mark. | ||
| 677 | // This also tests going from a parse error to an AstGen error. | ||
| 678 | case.addError( | ||
| 679 | \\const E1 = enum { | ||
| 680 | \\ a, | ||
| 681 | \\ _, | ||
| 682 | \\ b, | ||
| 683 | \\ c, | ||
| 684 | \\ _, | ||
| 685 | \\}; | ||
| 686 | \\export fn foo() void { | ||
| 687 | \\ _ = E1.a; | ||
| 688 | \\} | ||
| 689 | , &.{ | ||
| 690 | ":6:5: error: redundant non-exhaustive enum mark", | ||
| 691 | ":3:5: note: other mark here", | ||
| 692 | }); | ||
| 693 | |||
| 694 | case.addError( | ||
| 695 | \\const E1 = enum { | ||
| 696 | \\ a, | ||
| 697 | \\ b, | ||
| 698 | \\ c, | ||
| 699 | \\ _ = 10, | ||
| 700 | \\}; | ||
| 701 | \\export fn foo() void { | ||
| 702 | \\ _ = E1.a; | ||
| 703 | \\} | ||
| 704 | , &.{ | ||
| 705 | ":5:9: error: '_' is used to mark an enum as non-exhaustive and cannot be assigned a value", | ||
| 706 | }); | ||
| 707 | |||
| 708 | case.addError( | ||
| 709 | \\const E1 = enum { a, b, _ }; | ||
| 710 | \\export fn foo() void { | ||
| 711 | \\ _ = E1.a; | ||
| 712 | \\} | ||
| 713 | , &.{ | ||
| 714 | ":1:12: error: non-exhaustive enum missing integer tag type", | ||
| 715 | ":1:25: note: marked non-exhaustive here", | ||
| 716 | }); | ||
| 717 | |||
| 718 | case.addError( | ||
| 719 | \\const E1 = enum { a, b, c, b, d }; | ||
| 720 | \\pub export fn main() c_int { | ||
| 721 | \\ _ = E1.a; | ||
| 722 | \\} | ||
| 723 | , &.{ | ||
| 724 | ":1:28: error: duplicate enum field 'b'", | ||
| 725 | ":1:22: note: other field here", | ||
| 726 | }); | ||
| 727 | |||
| 728 | case.addError( | ||
| 729 | \\pub export fn main() c_int { | ||
| 730 | \\ const a = true; | ||
| 731 | \\ _ = @enumToInt(a); | ||
| 732 | \\} | ||
| 733 | , &.{ | ||
| 734 | ":3:20: error: expected enum or tagged union, found 'bool'", | ||
| 735 | }); | ||
| 736 | |||
| 737 | case.addError( | ||
| 738 | \\pub export fn main() c_int { | ||
| 739 | \\ const a = 1; | ||
| 740 | \\ _ = @intToEnum(bool, a); | ||
| 741 | \\} | ||
| 742 | , &.{ | ||
| 743 | ":3:20: error: expected enum, found 'bool'", | ||
| 744 | }); | ||
| 745 | |||
| 746 | case.addError( | ||
| 747 | \\const E = enum { a, b, c }; | ||
| 748 | \\pub export fn main() c_int { | ||
| 749 | \\ _ = @intToEnum(E, 3); | ||
| 750 | \\} | ||
| 751 | , &.{ | ||
| 752 | ":3:9: error: enum 'tmp.E' has no tag with value '3'", | ||
| 753 | ":1:11: note: enum declared here", | ||
| 754 | }); | ||
| 755 | |||
| 756 | case.addError( | ||
| 757 | \\const E = enum { a, b, c }; | ||
| 758 | \\pub export fn main() c_int { | ||
| 759 | \\ var x: E = .a; | ||
| 760 | \\ switch (x) { | ||
| 761 | \\ .a => {}, | ||
| 762 | \\ .c => {}, | ||
| 763 | \\ } | ||
| 764 | \\} | ||
| 765 | , &.{ | ||
| 766 | ":4:5: error: switch must handle all possibilities", | ||
| 767 | ":1:21: note: unhandled enumeration value: 'b'", | ||
| 768 | ":1:11: note: enum 'tmp.E' declared here", | ||
| 769 | }); | ||
| 770 | |||
| 771 | case.addError( | ||
| 772 | \\const E = enum { a, b, c }; | ||
| 773 | \\pub export fn main() c_int { | ||
| 774 | \\ var x: E = .a; | ||
| 775 | \\ switch (x) { | ||
| 776 | \\ .a => {}, | ||
| 777 | \\ .b => {}, | ||
| 778 | \\ .b => {}, | ||
| 779 | \\ .c => {}, | ||
| 780 | \\ } | ||
| 781 | \\} | ||
| 782 | , &.{ | ||
| 783 | ":7:10: error: duplicate switch value", | ||
| 784 | ":6:10: note: previous value here", | ||
| 785 | }); | ||
| 786 | |||
| 787 | case.addError( | ||
| 788 | \\const E = enum { a, b, c }; | ||
| 789 | \\pub export fn main() c_int { | ||
| 790 | \\ var x: E = .a; | ||
| 791 | \\ switch (x) { | ||
| 792 | \\ .a => {}, | ||
| 793 | \\ .b => {}, | ||
| 794 | \\ .c => {}, | ||
| 795 | \\ else => {}, | ||
| 796 | \\ } | ||
| 797 | \\} | ||
| 798 | , &.{ | ||
| 799 | ":8:14: error: unreachable else prong; all cases already handled", | ||
| 800 | }); | ||
| 801 | |||
| 802 | case.addError( | ||
| 803 | \\const E = enum { a, b, c }; | ||
| 804 | \\pub export fn main() c_int { | ||
| 805 | \\ var x: E = .a; | ||
| 806 | \\ switch (x) { | ||
| 807 | \\ .a => {}, | ||
| 808 | \\ .b => {}, | ||
| 809 | \\ _ => {}, | ||
| 810 | \\ } | ||
| 811 | \\} | ||
| 812 | , &.{ | ||
| 813 | ":4:5: error: '_' prong only allowed when switching on non-exhaustive enums", | ||
| 814 | ":7:11: note: '_' prong here", | ||
| 815 | }); | ||
| 816 | |||
| 817 | case.addError( | ||
| 818 | \\const E = enum { a, b, c }; | ||
| 819 | \\pub export fn main() c_int { | ||
| 820 | \\ _ = E.d; | ||
| 821 | \\} | ||
| 822 | , &.{ | ||
| 823 | ":3:11: error: enum 'tmp.E' has no member named 'd'", | ||
| 824 | ":1:11: note: enum declared here", | ||
| 825 | }); | ||
| 826 | |||
| 827 | case.addError( | ||
| 828 | \\const E = enum { a, b, c }; | ||
| 829 | \\pub export fn main() c_int { | ||
| 830 | \\ var x: E = .d; | ||
| 831 | \\ _ = x; | ||
| 832 | \\} | ||
| 833 | , &.{ | ||
| 834 | ":3:17: error: no field named 'd' in enum 'tmp.E'", | ||
| 835 | ":1:11: note: enum declared here", | ||
| 836 | }); | ||
| 837 | } | ||
| 838 | |||
| 839 | { | ||
| 840 | var case = ctx.exeFromCompiledC("shift right + left", .{}); | ||
| 841 | case.addCompareOutput( | ||
| 842 | \\pub export fn main() c_int { | ||
| 843 | \\ var i: u32 = 16; | ||
| 844 | \\ assert(i >> 1, 8); | ||
| 845 | \\ return 0; | ||
| 846 | \\} | ||
| 847 | \\fn assert(a: u32, b: u32) void { | ||
| 848 | \\ if (a != b) unreachable; | ||
| 849 | \\} | ||
| 850 | , ""); | ||
| 851 | |||
| 852 | case.addCompareOutput( | ||
| 853 | \\pub export fn main() c_int { | ||
| 854 | \\ var i: u32 = 16; | ||
| 855 | \\ assert(i << 1, 32); | ||
| 856 | \\ return 0; | ||
| 857 | \\} | ||
| 858 | \\fn assert(a: u32, b: u32) void { | ||
| 859 | \\ if (a != b) unreachable; | ||
| 860 | \\} | ||
| 861 | , ""); | ||
| 862 | } | ||
| 863 | |||
| 864 | { | ||
| 865 | var case = ctx.exeFromCompiledC("inferred error sets", .{}); | ||
| 866 | |||
| 867 | case.addCompareOutput( | ||
| 868 | \\pub export fn main() c_int { | ||
| 869 | \\ if (foo()) |_| { | ||
| 870 | \\ @panic("test fail"); | ||
| 871 | \\ } else |err| { | ||
| 872 | \\ if (err != error.ItBroke) { | ||
| 873 | \\ @panic("test fail"); | ||
| 874 | \\ } | ||
| 875 | \\ } | ||
| 876 | \\ return 0; | ||
| 877 | \\} | ||
| 878 | \\fn foo() !void { | ||
| 879 | \\ return error.ItBroke; | ||
| 880 | \\} | ||
| 881 | , ""); | ||
| 882 | } | ||
| 883 | |||
| 884 | { | ||
| 885 | // TODO: add u64 tests, ran into issues with the literal generated for std.math.maxInt(u64) | ||
| 886 | var case = ctx.exeFromCompiledC("add/sub wrapping operations", .{}); | ||
| 887 | case.addCompareOutput( | ||
| 888 | \\pub export fn main() c_int { | ||
| 889 | \\ // Addition | ||
| 890 | \\ if (!add_u3(1, 1, 2)) return 1; | ||
| 891 | \\ if (!add_u3(7, 1, 0)) return 1; | ||
| 892 | \\ if (!add_i3(1, 1, 2)) return 1; | ||
| 893 | \\ if (!add_i3(3, 2, -3)) return 1; | ||
| 894 | \\ if (!add_i3(-3, -2, 3)) return 1; | ||
| 895 | \\ if (!add_c_int(1, 1, 2)) return 1; | ||
| 896 | \\ // TODO enable these when stage2 supports std.math.maxInt | ||
| 897 | \\ //if (!add_c_int(maxInt(c_int), 2, minInt(c_int) + 1)) return 1; | ||
| 898 | \\ //if (!add_c_int(maxInt(c_int) + 1, -2, maxInt(c_int))) return 1; | ||
| 899 | \\ | ||
| 900 | \\ // Subtraction | ||
| 901 | \\ if (!sub_u3(2, 1, 1)) return 1; | ||
| 902 | \\ if (!sub_u3(0, 1, 7)) return 1; | ||
| 903 | \\ if (!sub_i3(2, 1, 1)) return 1; | ||
| 904 | \\ if (!sub_i3(3, -2, -3)) return 1; | ||
| 905 | \\ if (!sub_i3(-3, 2, 3)) return 1; | ||
| 906 | \\ if (!sub_c_int(2, 1, 1)) return 1; | ||
| 907 | \\ // TODO enable these when stage2 supports std.math.maxInt | ||
| 908 | \\ //if (!sub_c_int(maxInt(c_int), -2, minInt(c_int) + 1)) return 1; | ||
| 909 | \\ //if (!sub_c_int(minInt(c_int) + 1, 2, maxInt(c_int))) return 1; | ||
| 910 | \\ | ||
| 911 | \\ return 0; | ||
| 912 | \\} | ||
| 913 | \\fn add_u3(lhs: u3, rhs: u3, expected: u3) bool { | ||
| 914 | \\ return expected == lhs +% rhs; | ||
| 915 | \\} | ||
| 916 | \\fn add_i3(lhs: i3, rhs: i3, expected: i3) bool { | ||
| 917 | \\ return expected == lhs +% rhs; | ||
| 918 | \\} | ||
| 919 | \\fn add_c_int(lhs: c_int, rhs: c_int, expected: c_int) bool { | ||
| 920 | \\ return expected == lhs +% rhs; | ||
| 921 | \\} | ||
| 922 | \\fn sub_u3(lhs: u3, rhs: u3, expected: u3) bool { | ||
| 923 | \\ return expected == lhs -% rhs; | ||
| 924 | \\} | ||
| 925 | \\fn sub_i3(lhs: i3, rhs: i3, expected: i3) bool { | ||
| 926 | \\ return expected == lhs -% rhs; | ||
| 927 | \\} | ||
| 928 | \\fn sub_c_int(lhs: c_int, rhs: c_int, expected: c_int) bool { | ||
| 929 | \\ return expected == lhs -% rhs; | ||
| 930 | \\} | ||
| 931 | , ""); | ||
| 932 | } | ||
| 933 | |||
| 934 | { | ||
| 935 | var case = ctx.exeFromCompiledC("@rem", linux_x64); | ||
| 936 | case.addCompareOutput( | ||
| 937 | \\fn assert(ok: bool) void { | ||
| 938 | \\ if (!ok) unreachable; | ||
| 939 | \\} | ||
| 940 | \\fn rem(lhs: i32, rhs: i32, expected: i32) bool { | ||
| 941 | \\ return @rem(lhs, rhs) == expected; | ||
| 942 | \\} | ||
| 943 | \\pub export fn main() c_int { | ||
| 944 | \\ assert(rem(-5, 3, -2)); | ||
| 945 | \\ assert(rem(5, 3, 2)); | ||
| 946 | \\ return 0; | ||
| 947 | \\} | ||
| 948 | , ""); | ||
| 949 | } | ||
| 950 | |||
| 951 | ctx.h("simple header", linux_x64, | ||
| 952 | \\export fn start() void{} | ||
| 953 | , | ||
| 954 | \\zig_extern void start(void); | ||
| 955 | \\ | ||
| 956 | ); | ||
| 957 | ctx.h("header with single param function", linux_x64, | ||
| 958 | \\export fn start(a: u8) void{ | ||
| 959 | \\ _ = a; | ||
| 960 | \\} | ||
| 961 | , | ||
| 962 | \\zig_extern void start(uint8_t const a0); | ||
| 963 | \\ | ||
| 964 | ); | ||
| 965 | ctx.h("header with multiple param function", linux_x64, | ||
| 966 | \\export fn start(a: u8, b: u8, c: u8) void{ | ||
| 967 | \\ _ = a; _ = b; _ = c; | ||
| 968 | \\} | ||
| 969 | , | ||
| 970 | \\zig_extern void start(uint8_t const a0, uint8_t const a1, uint8_t const a2); | ||
| 971 | \\ | ||
| 972 | ); | ||
| 973 | ctx.h("header with u32 param function", linux_x64, | ||
| 974 | \\export fn start(a: u32) void{ _ = a; } | ||
| 975 | , | ||
| 976 | \\zig_extern void start(uint32_t const a0); | ||
| 977 | \\ | ||
| 978 | ); | ||
| 979 | ctx.h("header with usize param function", linux_x64, | ||
| 980 | \\export fn start(a: usize) void{ _ = a; } | ||
| 981 | , | ||
| 982 | \\zig_extern void start(uintptr_t const a0); | ||
| 983 | \\ | ||
| 984 | ); | ||
| 985 | ctx.h("header with bool param function", linux_x64, | ||
| 986 | \\export fn start(a: bool) void{_ = a;} | ||
| 987 | , | ||
| 988 | \\zig_extern void start(bool const a0); | ||
| 989 | \\ | ||
| 990 | ); | ||
| 991 | ctx.h("header with noreturn function", linux_x64, | ||
| 992 | \\export fn start() noreturn { | ||
| 993 | \\ unreachable; | ||
| 994 | \\} | ||
| 995 | , | ||
| 996 | \\zig_extern zig_noreturn void start(void); | ||
| 997 | \\ | ||
| 998 | ); | ||
| 999 | ctx.h("header with multiple functions", linux_x64, | ||
| 1000 | \\export fn a() void{} | ||
| 1001 | \\export fn b() void{} | ||
| 1002 | \\export fn c() void{} | ||
| 1003 | , | ||
| 1004 | \\zig_extern void a(void); | ||
| 1005 | \\zig_extern void b(void); | ||
| 1006 | \\zig_extern void c(void); | ||
| 1007 | \\ | ||
| 1008 | ); | ||
| 1009 | ctx.h("header with multiple includes", linux_x64, | ||
| 1010 | \\export fn start(a: u32, b: usize) void{ _ = a; _ = b; } | ||
| 1011 | , | ||
| 1012 | \\zig_extern void start(uint32_t const a0, uintptr_t const a1); | ||
| 1013 | \\ | ||
| 1014 | ); | ||
| 1015 | } | ||
test/stage2/nvptx.zig deleted-107| ... | @@ -1,107 +0,0 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const TestContext = @import("../../src/test.zig").TestContext; | ||
| 3 | |||
| 4 | pub fn addCases(ctx: *TestContext) !void { | ||
| 5 | { | ||
| 6 | var case = addPtx(ctx, "nvptx: simple addition and subtraction"); | ||
| 7 | |||
| 8 | case.compiles( | ||
| 9 | \\fn add(a: i32, b: i32) i32 { | ||
| 10 | \\ return a + b; | ||
| 11 | \\} | ||
| 12 | \\ | ||
| 13 | \\pub export fn add_and_substract(a: i32, out: *i32) callconv(.PtxKernel) void { | ||
| 14 | \\ const x = add(a, 7); | ||
| 15 | \\ var y = add(2, 0); | ||
| 16 | \\ y -= x; | ||
| 17 | \\ out.* = y; | ||
| 18 | \\} | ||
| 19 | ); | ||
| 20 | } | ||
| 21 | |||
| 22 | { | ||
| 23 | var case = addPtx(ctx, "nvptx: read special registers"); | ||
| 24 | |||
| 25 | case.compiles( | ||
| 26 | \\fn threadIdX() u32 { | ||
| 27 | \\ return asm ("mov.u32 \t%[r], %tid.x;" | ||
| 28 | \\ : [r] "=r" (-> u32), | ||
| 29 | \\ ); | ||
| 30 | \\} | ||
| 31 | \\ | ||
| 32 | \\pub export fn special_reg(a: []const i32, out: []i32) callconv(.PtxKernel) void { | ||
| 33 | \\ const i = threadIdX(); | ||
| 34 | \\ out[i] = a[i] + 7; | ||
| 35 | \\} | ||
| 36 | ); | ||
| 37 | } | ||
| 38 | |||
| 39 | { | ||
| 40 | var case = addPtx(ctx, "nvptx: address spaces"); | ||
| 41 | |||
| 42 | case.compiles( | ||
| 43 | \\var x: i32 addrspace(.global) = 0; | ||
| 44 | \\ | ||
| 45 | \\pub export fn increment(out: *i32) callconv(.PtxKernel) void { | ||
| 46 | \\ x += 1; | ||
| 47 | \\ out.* = x; | ||
| 48 | \\} | ||
| 49 | ); | ||
| 50 | } | ||
| 51 | |||
| 52 | { | ||
| 53 | var case = addPtx(ctx, "nvptx: reduce in shared mem"); | ||
| 54 | case.compiles( | ||
| 55 | \\fn threadIdX() u32 { | ||
| 56 | \\ return asm ("mov.u32 \t%[r], %tid.x;" | ||
| 57 | \\ : [r] "=r" (-> u32), | ||
| 58 | \\ ); | ||
| 59 | \\} | ||
| 60 | \\ | ||
| 61 | \\ var _sdata: [1024]f32 addrspace(.shared) = undefined; | ||
| 62 | \\ pub export fn reduceSum(d_x: []const f32, out: *f32) callconv(.PtxKernel) void { | ||
| 63 | \\ var sdata = @addrSpaceCast(.generic, &_sdata); | ||
| 64 | \\ const tid: u32 = threadIdX(); | ||
| 65 | \\ var sum = d_x[tid]; | ||
| 66 | \\ sdata[tid] = sum; | ||
| 67 | \\ asm volatile ("bar.sync \t0;"); | ||
| 68 | \\ var s: u32 = 512; | ||
| 69 | \\ while (s > 0) : (s = s >> 1) { | ||
| 70 | \\ if (tid < s) { | ||
| 71 | \\ sum += sdata[tid + s]; | ||
| 72 | \\ sdata[tid] = sum; | ||
| 73 | \\ } | ||
| 74 | \\ asm volatile ("bar.sync \t0;"); | ||
| 75 | \\ } | ||
| 76 | \\ | ||
| 77 | \\ if (tid == 0) { | ||
| 78 | \\ out.* = sum; | ||
| 79 | \\ } | ||
| 80 | \\ } | ||
| 81 | ); | ||
| 82 | } | ||
| 83 | } | ||
| 84 | |||
| 85 | const nvptx_target = std.zig.CrossTarget{ | ||
| 86 | .cpu_arch = .nvptx64, | ||
| 87 | .os_tag = .cuda, | ||
| 88 | }; | ||
| 89 | |||
| 90 | pub fn addPtx( | ||
| 91 | ctx: *TestContext, | ||
| 92 | name: []const u8, | ||
| 93 | ) *TestContext.Case { | ||
| 94 | ctx.cases.append(TestContext.Case{ | ||
| 95 | .name = name, | ||
| 96 | .target = nvptx_target, | ||
| 97 | .updates = std.ArrayList(TestContext.Update).init(ctx.cases.allocator), | ||
| 98 | .output_mode = .Obj, | ||
| 99 | .files = std.ArrayList(TestContext.File).init(ctx.cases.allocator), | ||
| 100 | .deps = std.ArrayList(TestContext.DepModule).init(ctx.cases.allocator), | ||
| 101 | .link_libc = false, | ||
| 102 | .backend = .llvm, | ||
| 103 | // Bug in Debug mode | ||
| 104 | .optimize_mode = .ReleaseSafe, | ||
| 105 | }) catch @panic("out of memory"); | ||
| 106 | return &ctx.cases.items[ctx.cases.items.len - 1]; | ||
| 107 | } | ||
test/standalone.zig+208-107| ... | @@ -1,117 +1,218 @@ | ... | @@ -1,117 +1,218 @@ |
| 1 | const std = @import("std"); | 1 | pub const SimpleCase = struct { |
| 2 | const builtin = @import("builtin"); | 2 | src_path: []const u8, |
| 3 | const tests = @import("tests.zig"); | 3 | link_libc: bool = false, |
| 4 | all_modes: bool = false, | ||
| 5 | target: std.zig.CrossTarget = .{}, | ||
| 6 | is_test: bool = false, | ||
| 7 | is_exe: bool = true, | ||
| 8 | /// Run only on this OS. | ||
| 9 | os_filter: ?std.Target.Os.Tag = null, | ||
| 10 | }; | ||
| 4 | 11 | ||
| 5 | pub fn addCases(cases: *tests.StandaloneContext) void { | 12 | pub const BuildCase = struct { |
| 6 | cases.add("test/standalone/hello_world/hello.zig"); | 13 | build_root: []const u8, |
| 7 | cases.addC("test/standalone/hello_world/hello_libc.zig"); | 14 | import: type, |
| 15 | }; | ||
| 8 | 16 | ||
| 9 | cases.addBuildFile("test/standalone/options/build.zig", .{ | 17 | pub const simple_cases = [_]SimpleCase{ |
| 10 | .extra_argv = &.{ | 18 | .{ |
| 11 | "-Dbool_true", | 19 | .src_path = "test/standalone/hello_world/hello.zig", |
| 12 | "-Dbool_false=false", | 20 | .all_modes = true, |
| 13 | "-Dint=1234", | 21 | }, |
| 14 | "-De=two", | 22 | .{ |
| 15 | "-Dstring=hello", | 23 | .src_path = "test/standalone/hello_world/hello_libc.zig", |
| 24 | .link_libc = true, | ||
| 25 | .all_modes = true, | ||
| 26 | }, | ||
| 27 | .{ | ||
| 28 | .src_path = "test/standalone/cat/main.zig", | ||
| 29 | }, | ||
| 30 | // https://github.com/ziglang/zig/issues/6025 | ||
| 31 | //.{ | ||
| 32 | // .src_path = "test/standalone/issue_9693/main.zig", | ||
| 33 | //}, | ||
| 34 | .{ | ||
| 35 | .src_path = "test/standalone/brace_expansion.zig", | ||
| 36 | .is_test = true, | ||
| 37 | }, | ||
| 38 | .{ | ||
| 39 | .src_path = "test/standalone/issue_7030.zig", | ||
| 40 | .target = .{ | ||
| 41 | .cpu_arch = .wasm32, | ||
| 42 | .os_tag = .freestanding, | ||
| 16 | }, | 43 | }, |
| 17 | }); | 44 | }, |
| 18 | |||
| 19 | cases.add("test/standalone/cat/main.zig"); | ||
| 20 | if (builtin.zig_backend == .stage1) { // https://github.com/ziglang/zig/issues/6025 | ||
| 21 | cases.add("test/standalone/issue_9693/main.zig"); | ||
| 22 | } | ||
| 23 | cases.add("test/standalone/issue_12471/main.zig"); | ||
| 24 | cases.add("test/standalone/guess_number/main.zig"); | ||
| 25 | cases.add("test/standalone/main_return_error/error_u8.zig"); | ||
| 26 | cases.add("test/standalone/main_return_error/error_u8_non_zero.zig"); | ||
| 27 | cases.add("test/standalone/noreturn_call/inline.zig"); | ||
| 28 | cases.add("test/standalone/noreturn_call/as_arg.zig"); | ||
| 29 | cases.addBuildFile("test/standalone/test_runner_path/build.zig", .{ .requires_stage2 = true }); | ||
| 30 | cases.addBuildFile("test/standalone/issue_13970/build.zig", .{}); | ||
| 31 | cases.addBuildFile("test/standalone/main_pkg_path/build.zig", .{}); | ||
| 32 | cases.addBuildFile("test/standalone/shared_library/build.zig", .{}); | ||
| 33 | cases.addBuildFile("test/standalone/mix_o_files/build.zig", .{}); | ||
| 34 | cases.addBuildFile("test/standalone/mix_c_files/build.zig", .{ | ||
| 35 | .build_modes = true, | ||
| 36 | .cross_targets = true, | ||
| 37 | }); | ||
| 38 | cases.addBuildFile("test/standalone/global_linkage/build.zig", .{}); | ||
| 39 | cases.addBuildFile("test/standalone/static_c_lib/build.zig", .{}); | ||
| 40 | cases.addBuildFile("test/standalone/issue_339/build.zig", .{}); | ||
| 41 | cases.addBuildFile("test/standalone/issue_8550/build.zig", .{}); | ||
| 42 | cases.addBuildFile("test/standalone/issue_794/build.zig", .{}); | ||
| 43 | cases.addBuildFile("test/standalone/issue_5825/build.zig", .{}); | ||
| 44 | cases.addBuildFile("test/standalone/pkg_import/build.zig", .{}); | ||
| 45 | cases.addBuildFile("test/standalone/use_alias/build.zig", .{}); | ||
| 46 | cases.addBuildFile("test/standalone/brace_expansion/build.zig", .{}); | ||
| 47 | if (builtin.os.tag != .windows or builtin.cpu.arch != .aarch64) { | ||
| 48 | // https://github.com/ziglang/zig/issues/13685 | ||
| 49 | cases.addBuildFile("test/standalone/empty_env/build.zig", .{}); | ||
| 50 | } | ||
| 51 | cases.addBuildFile("test/standalone/issue_7030/build.zig", .{}); | ||
| 52 | cases.addBuildFile("test/standalone/install_raw_hex/build.zig", .{}); | ||
| 53 | if (builtin.zig_backend == .stage1) { // https://github.com/ziglang/zig/issues/12194 | ||
| 54 | cases.addBuildFile("test/standalone/issue_9812/build.zig", .{}); | ||
| 55 | } | ||
| 56 | if (builtin.os.tag != .windows) { | ||
| 57 | // https://github.com/ziglang/zig/issues/12419 | ||
| 58 | cases.addBuildFile("test/standalone/issue_11595/build.zig", .{}); | ||
| 59 | } | ||
| 60 | |||
| 61 | if (builtin.os.tag != .wasi and | ||
| 62 | // https://github.com/ziglang/zig/issues/13550 | ||
| 63 | (builtin.os.tag != .macos or builtin.cpu.arch != .aarch64) and | ||
| 64 | // https://github.com/ziglang/zig/issues/13686 | ||
| 65 | (builtin.os.tag != .windows or builtin.cpu.arch != .aarch64)) | ||
| 66 | { | ||
| 67 | cases.addBuildFile("test/standalone/load_dynamic_library/build.zig", .{}); | ||
| 68 | } | ||
| 69 | 45 | ||
| 70 | if (builtin.os.tag == .windows) { | 46 | .{ .src_path = "test/standalone/issue_12471/main.zig" }, |
| 71 | cases.addBuildFile("test/standalone/windows_spawn/build.zig", .{}); | 47 | .{ .src_path = "test/standalone/guess_number/main.zig" }, |
| 72 | } | 48 | .{ .src_path = "test/standalone/main_return_error/error_u8.zig" }, |
| 49 | .{ .src_path = "test/standalone/main_return_error/error_u8_non_zero.zig" }, | ||
| 50 | .{ .src_path = "test/standalone/noreturn_call/inline.zig" }, | ||
| 51 | .{ .src_path = "test/standalone/noreturn_call/as_arg.zig" }, | ||
| 73 | 52 | ||
| 74 | cases.addBuildFile("test/standalone/c_compiler/build.zig", .{ | 53 | .{ |
| 75 | .build_modes = true, | 54 | .src_path = "test/standalone/issue_9402/main.zig", |
| 76 | .cross_targets = true, | 55 | .os_filter = .windows, |
| 77 | }); | 56 | .link_libc = true, |
| 78 | 57 | }, | |
| 79 | if (builtin.os.tag == .windows) { | ||
| 80 | cases.addC("test/standalone/issue_9402/main.zig"); | ||
| 81 | } | ||
| 82 | // Try to build and run a PIE executable. | ||
| 83 | if (builtin.os.tag == .linux) { | ||
| 84 | cases.addBuildFile("test/standalone/pie/build.zig", .{}); | ||
| 85 | } | ||
| 86 | cases.addBuildFile("test/standalone/issue_12706/build.zig", .{}); | ||
| 87 | if (std.os.have_sigpipe_support) { | ||
| 88 | cases.addBuildFile("test/standalone/sigpipe/build.zig", .{}); | ||
| 89 | } | ||
| 90 | 58 | ||
| 91 | // Ensure the development tools are buildable. Alphabetically sorted. | 59 | // Ensure the development tools are buildable. Alphabetically sorted. |
| 92 | // No need to build `tools/spirv/grammar.zig`. | 60 | // No need to build `tools/spirv/grammar.zig`. |
| 93 | cases.add("tools/extract-grammar.zig"); | 61 | .{ .src_path = "tools/extract-grammar.zig" }, |
| 94 | cases.add("tools/gen_outline_atomics.zig"); | 62 | .{ .src_path = "tools/gen_outline_atomics.zig" }, |
| 95 | cases.add("tools/gen_spirv_spec.zig"); | 63 | .{ .src_path = "tools/gen_spirv_spec.zig" }, |
| 96 | cases.add("tools/gen_stubs.zig"); | 64 | .{ .src_path = "tools/gen_stubs.zig" }, |
| 97 | cases.add("tools/generate_linux_syscalls.zig"); | 65 | .{ .src_path = "tools/generate_linux_syscalls.zig" }, |
| 98 | cases.add("tools/process_headers.zig"); | 66 | .{ .src_path = "tools/process_headers.zig" }, |
| 99 | cases.add("tools/update-license-headers.zig"); | 67 | .{ .src_path = "tools/update-license-headers.zig" }, |
| 100 | cases.add("tools/update-linux-headers.zig"); | 68 | .{ .src_path = "tools/update-linux-headers.zig" }, |
| 101 | cases.add("tools/update_clang_options.zig"); | 69 | .{ .src_path = "tools/update_clang_options.zig" }, |
| 102 | cases.add("tools/update_cpu_features.zig"); | 70 | .{ .src_path = "tools/update_cpu_features.zig" }, |
| 103 | cases.add("tools/update_glibc.zig"); | 71 | .{ .src_path = "tools/update_glibc.zig" }, |
| 104 | cases.add("tools/update_spirv_features.zig"); | 72 | .{ .src_path = "tools/update_spirv_features.zig" }, |
| 73 | }; | ||
| 105 | 74 | ||
| 106 | cases.addBuildFile("test/standalone/issue_13030/build.zig", .{ .build_modes = true }); | 75 | pub const build_cases = [_]BuildCase{ |
| 107 | cases.addBuildFile("test/standalone/emit_asm_and_bin/build.zig", .{}); | 76 | .{ |
| 108 | cases.addBuildFile("test/standalone/issue_12588/build.zig", .{}); | 77 | .build_root = "test/standalone/test_runner_path", |
| 109 | cases.addBuildFile("test/standalone/embed_generated_file/build.zig", .{}); | 78 | .import = @import("standalone/test_runner_path/build.zig"), |
| 110 | cases.addBuildFile("test/standalone/extern/build.zig", .{}); | 79 | }, |
| 80 | .{ | ||
| 81 | .build_root = "test/standalone/issue_13970", | ||
| 82 | .import = @import("standalone/issue_13970/build.zig"), | ||
| 83 | }, | ||
| 84 | .{ | ||
| 85 | .build_root = "test/standalone/main_pkg_path", | ||
| 86 | .import = @import("standalone/main_pkg_path/build.zig"), | ||
| 87 | }, | ||
| 88 | .{ | ||
| 89 | .build_root = "test/standalone/shared_library", | ||
| 90 | .import = @import("standalone/shared_library/build.zig"), | ||
| 91 | }, | ||
| 92 | .{ | ||
| 93 | .build_root = "test/standalone/mix_o_files", | ||
| 94 | .import = @import("standalone/mix_o_files/build.zig"), | ||
| 95 | }, | ||
| 96 | .{ | ||
| 97 | .build_root = "test/standalone/mix_c_files", | ||
| 98 | .import = @import("standalone/mix_c_files/build.zig"), | ||
| 99 | }, | ||
| 100 | .{ | ||
| 101 | .build_root = "test/standalone/global_linkage", | ||
| 102 | .import = @import("standalone/global_linkage/build.zig"), | ||
| 103 | }, | ||
| 104 | .{ | ||
| 105 | .build_root = "test/standalone/static_c_lib", | ||
| 106 | .import = @import("standalone/static_c_lib/build.zig"), | ||
| 107 | }, | ||
| 108 | .{ | ||
| 109 | .build_root = "test/standalone/issue_339", | ||
| 110 | .import = @import("standalone/issue_339/build.zig"), | ||
| 111 | }, | ||
| 112 | .{ | ||
| 113 | .build_root = "test/standalone/issue_8550", | ||
| 114 | .import = @import("standalone/issue_8550/build.zig"), | ||
| 115 | }, | ||
| 116 | .{ | ||
| 117 | .build_root = "test/standalone/issue_794", | ||
| 118 | .import = @import("standalone/issue_794/build.zig"), | ||
| 119 | }, | ||
| 120 | .{ | ||
| 121 | .build_root = "test/standalone/issue_5825", | ||
| 122 | .import = @import("standalone/issue_5825/build.zig"), | ||
| 123 | }, | ||
| 124 | .{ | ||
| 125 | .build_root = "test/standalone/pkg_import", | ||
| 126 | .import = @import("standalone/pkg_import/build.zig"), | ||
| 127 | }, | ||
| 128 | .{ | ||
| 129 | .build_root = "test/standalone/use_alias", | ||
| 130 | .import = @import("standalone/use_alias/build.zig"), | ||
| 131 | }, | ||
| 132 | .{ | ||
| 133 | .build_root = "test/standalone/install_raw_hex", | ||
| 134 | .import = @import("standalone/install_raw_hex/build.zig"), | ||
| 135 | }, | ||
| 136 | // TODO take away EmitOption.emit_to option and make it give a FileSource | ||
| 137 | //.{ | ||
| 138 | // .build_root = "test/standalone/emit_asm_and_bin", | ||
| 139 | // .import = @import("standalone/emit_asm_and_bin/build.zig"), | ||
| 140 | //}, | ||
| 141 | // TODO take away EmitOption.emit_to option and make it give a FileSource | ||
| 142 | //.{ | ||
| 143 | // .build_root = "test/standalone/issue_12588", | ||
| 144 | // .import = @import("standalone/issue_12588/build.zig"), | ||
| 145 | //}, | ||
| 146 | .{ | ||
| 147 | .build_root = "test/standalone/embed_generated_file", | ||
| 148 | .import = @import("standalone/embed_generated_file/build.zig"), | ||
| 149 | }, | ||
| 150 | .{ | ||
| 151 | .build_root = "test/standalone/extern", | ||
| 152 | .import = @import("standalone/extern/build.zig"), | ||
| 153 | }, | ||
| 154 | .{ | ||
| 155 | .build_root = "test/standalone/dep_diamond", | ||
| 156 | .import = @import("standalone/dep_diamond/build.zig"), | ||
| 157 | }, | ||
| 158 | .{ | ||
| 159 | .build_root = "test/standalone/dep_triangle", | ||
| 160 | .import = @import("standalone/dep_triangle/build.zig"), | ||
| 161 | }, | ||
| 162 | .{ | ||
| 163 | .build_root = "test/standalone/dep_recursive", | ||
| 164 | .import = @import("standalone/dep_recursive/build.zig"), | ||
| 165 | }, | ||
| 166 | .{ | ||
| 167 | .build_root = "test/standalone/dep_mutually_recursive", | ||
| 168 | .import = @import("standalone/dep_mutually_recursive/build.zig"), | ||
| 169 | }, | ||
| 170 | .{ | ||
| 171 | .build_root = "test/standalone/dep_shared_builtin", | ||
| 172 | .import = @import("standalone/dep_shared_builtin/build.zig"), | ||
| 173 | }, | ||
| 174 | .{ | ||
| 175 | .build_root = "test/standalone/empty_env", | ||
| 176 | .import = @import("standalone/empty_env/build.zig"), | ||
| 177 | }, | ||
| 178 | .{ | ||
| 179 | .build_root = "test/standalone/issue_9812", | ||
| 180 | .import = @import("standalone/issue_9812/build.zig"), | ||
| 181 | }, | ||
| 182 | .{ | ||
| 183 | .build_root = "test/standalone/issue_11595", | ||
| 184 | .import = @import("standalone/issue_11595/build.zig"), | ||
| 185 | }, | ||
| 186 | .{ | ||
| 187 | .build_root = "test/standalone/load_dynamic_library", | ||
| 188 | .import = @import("standalone/load_dynamic_library/build.zig"), | ||
| 189 | }, | ||
| 190 | .{ | ||
| 191 | .build_root = "test/standalone/windows_spawn", | ||
| 192 | .import = @import("standalone/windows_spawn/build.zig"), | ||
| 193 | }, | ||
| 194 | .{ | ||
| 195 | .build_root = "test/standalone/c_compiler", | ||
| 196 | .import = @import("standalone/c_compiler/build.zig"), | ||
| 197 | }, | ||
| 198 | .{ | ||
| 199 | .build_root = "test/standalone/pie", | ||
| 200 | .import = @import("standalone/pie/build.zig"), | ||
| 201 | }, | ||
| 202 | .{ | ||
| 203 | .build_root = "test/standalone/issue_12706", | ||
| 204 | .import = @import("standalone/issue_12706/build.zig"), | ||
| 205 | }, | ||
| 206 | // TODO This test is disabled for doing naughty things in the build script. | ||
| 207 | // The logic needs to get moved to a child process instead of build.zig. | ||
| 208 | //.{ | ||
| 209 | // .build_root = "test/standalone/sigpipe", | ||
| 210 | // .import = @import("standalone/sigpipe/build.zig"), | ||
| 211 | //}, | ||
| 212 | .{ | ||
| 213 | .build_root = "test/standalone/issue_13030", | ||
| 214 | .import = @import("standalone/issue_13030/build.zig"), | ||
| 215 | }, | ||
| 216 | }; | ||
| 111 | 217 | ||
| 112 | cases.addBuildFile("test/standalone/dep_diamond/build.zig", .{}); | 218 | const std = @import("std"); |
| 113 | cases.addBuildFile("test/standalone/dep_triangle/build.zig", .{}); | ||
| 114 | cases.addBuildFile("test/standalone/dep_recursive/build.zig", .{}); | ||
| 115 | cases.addBuildFile("test/standalone/dep_mutually_recursive/build.zig", .{}); | ||
| 116 | cases.addBuildFile("test/standalone/dep_shared_builtin/build.zig", .{}); | ||
| 117 | } |
test/standalone/brace_expansion.zig created+292| ... | @@ -0,0 +1,292 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const io = std.io; | ||
| 3 | const mem = std.mem; | ||
| 4 | const debug = std.debug; | ||
| 5 | const assert = debug.assert; | ||
| 6 | const testing = std.testing; | ||
| 7 | const ArrayList = std.ArrayList; | ||
| 8 | const maxInt = std.math.maxInt; | ||
| 9 | |||
| 10 | const Token = union(enum) { | ||
| 11 | Word: []const u8, | ||
| 12 | OpenBrace, | ||
| 13 | CloseBrace, | ||
| 14 | Comma, | ||
| 15 | Eof, | ||
| 16 | }; | ||
| 17 | |||
| 18 | var gpa = std.heap.GeneralPurposeAllocator(.{}){}; | ||
| 19 | var global_allocator = gpa.allocator(); | ||
| 20 | |||
| 21 | fn tokenize(input: []const u8) !ArrayList(Token) { | ||
| 22 | const State = enum { | ||
| 23 | Start, | ||
| 24 | Word, | ||
| 25 | }; | ||
| 26 | |||
| 27 | var token_list = ArrayList(Token).init(global_allocator); | ||
| 28 | errdefer token_list.deinit(); | ||
| 29 | var tok_begin: usize = undefined; | ||
| 30 | var state = State.Start; | ||
| 31 | |||
| 32 | for (input, 0..) |b, i| { | ||
| 33 | switch (state) { | ||
| 34 | .Start => switch (b) { | ||
| 35 | 'a'...'z', 'A'...'Z' => { | ||
| 36 | state = State.Word; | ||
| 37 | tok_begin = i; | ||
| 38 | }, | ||
| 39 | '{' => try token_list.append(Token.OpenBrace), | ||
| 40 | '}' => try token_list.append(Token.CloseBrace), | ||
| 41 | ',' => try token_list.append(Token.Comma), | ||
| 42 | else => return error.InvalidInput, | ||
| 43 | }, | ||
| 44 | .Word => switch (b) { | ||
| 45 | 'a'...'z', 'A'...'Z' => {}, | ||
| 46 | '{', '}', ',' => { | ||
| 47 | try token_list.append(Token{ .Word = input[tok_begin..i] }); | ||
| 48 | switch (b) { | ||
| 49 | '{' => try token_list.append(Token.OpenBrace), | ||
| 50 | '}' => try token_list.append(Token.CloseBrace), | ||
| 51 | ',' => try token_list.append(Token.Comma), | ||
| 52 | else => unreachable, | ||
| 53 | } | ||
| 54 | state = State.Start; | ||
| 55 | }, | ||
| 56 | else => return error.InvalidInput, | ||
| 57 | }, | ||
| 58 | } | ||
| 59 | } | ||
| 60 | switch (state) { | ||
| 61 | State.Start => {}, | ||
| 62 | State.Word => try token_list.append(Token{ .Word = input[tok_begin..] }), | ||
| 63 | } | ||
| 64 | try token_list.append(Token.Eof); | ||
| 65 | return token_list; | ||
| 66 | } | ||
| 67 | |||
| 68 | const Node = union(enum) { | ||
| 69 | Scalar: []const u8, | ||
| 70 | List: ArrayList(Node), | ||
| 71 | Combine: []Node, | ||
| 72 | |||
| 73 | fn deinit(self: Node) void { | ||
| 74 | switch (self) { | ||
| 75 | .Scalar => {}, | ||
| 76 | .Combine => |pair| { | ||
| 77 | pair[0].deinit(); | ||
| 78 | pair[1].deinit(); | ||
| 79 | global_allocator.free(pair); | ||
| 80 | }, | ||
| 81 | .List => |list| { | ||
| 82 | for (list.items) |item| { | ||
| 83 | item.deinit(); | ||
| 84 | } | ||
| 85 | list.deinit(); | ||
| 86 | }, | ||
| 87 | } | ||
| 88 | } | ||
| 89 | }; | ||
| 90 | |||
| 91 | const ParseError = error{ | ||
| 92 | InvalidInput, | ||
| 93 | OutOfMemory, | ||
| 94 | }; | ||
| 95 | |||
| 96 | fn parse(tokens: *const ArrayList(Token), token_index: *usize) ParseError!Node { | ||
| 97 | const first_token = tokens.items[token_index.*]; | ||
| 98 | token_index.* += 1; | ||
| 99 | |||
| 100 | const result_node = switch (first_token) { | ||
| 101 | .Word => |word| Node{ .Scalar = word }, | ||
| 102 | .OpenBrace => blk: { | ||
| 103 | var list = ArrayList(Node).init(global_allocator); | ||
| 104 | errdefer { | ||
| 105 | for (list.items) |node| node.deinit(); | ||
| 106 | list.deinit(); | ||
| 107 | } | ||
| 108 | while (true) { | ||
| 109 | try list.append(try parse(tokens, token_index)); | ||
| 110 | |||
| 111 | const token = tokens.items[token_index.*]; | ||
| 112 | token_index.* += 1; | ||
| 113 | |||
| 114 | switch (token) { | ||
| 115 | .CloseBrace => break, | ||
| 116 | .Comma => continue, | ||
| 117 | else => return error.InvalidInput, | ||
| 118 | } | ||
| 119 | } | ||
| 120 | break :blk Node{ .List = list }; | ||
| 121 | }, | ||
| 122 | else => return error.InvalidInput, | ||
| 123 | }; | ||
| 124 | |||
| 125 | switch (tokens.items[token_index.*]) { | ||
| 126 | .Word, .OpenBrace => { | ||
| 127 | const pair = try global_allocator.alloc(Node, 2); | ||
| 128 | errdefer global_allocator.free(pair); | ||
| 129 | pair[0] = result_node; | ||
| 130 | pair[1] = try parse(tokens, token_index); | ||
| 131 | return Node{ .Combine = pair }; | ||
| 132 | }, | ||
| 133 | else => return result_node, | ||
| 134 | } | ||
| 135 | } | ||
| 136 | |||
| 137 | fn expandString(input: []const u8, output: *ArrayList(u8)) !void { | ||
| 138 | const tokens = try tokenize(input); | ||
| 139 | defer tokens.deinit(); | ||
| 140 | if (tokens.items.len == 1) { | ||
| 141 | return output.resize(0); | ||
| 142 | } | ||
| 143 | |||
| 144 | var token_index: usize = 0; | ||
| 145 | const root = try parse(&tokens, &token_index); | ||
| 146 | defer root.deinit(); | ||
| 147 | const last_token = tokens.items[token_index]; | ||
| 148 | switch (last_token) { | ||
| 149 | Token.Eof => {}, | ||
| 150 | else => return error.InvalidInput, | ||
| 151 | } | ||
| 152 | |||
| 153 | var result_list = ArrayList(ArrayList(u8)).init(global_allocator); | ||
| 154 | defer { | ||
| 155 | for (result_list.items) |*buf| buf.deinit(); | ||
| 156 | result_list.deinit(); | ||
| 157 | } | ||
| 158 | |||
| 159 | try expandNode(root, &result_list); | ||
| 160 | |||
| 161 | try output.resize(0); | ||
| 162 | for (result_list.items, 0..) |buf, i| { | ||
| 163 | if (i != 0) { | ||
| 164 | try output.append(' '); | ||
| 165 | } | ||
| 166 | try output.appendSlice(buf.items); | ||
| 167 | } | ||
| 168 | } | ||
| 169 | |||
| 170 | const ExpandNodeError = error{OutOfMemory}; | ||
| 171 | |||
| 172 | fn expandNode(node: Node, output: *ArrayList(ArrayList(u8))) ExpandNodeError!void { | ||
| 173 | assert(output.items.len == 0); | ||
| 174 | switch (node) { | ||
| 175 | .Scalar => |scalar| { | ||
| 176 | var list = ArrayList(u8).init(global_allocator); | ||
| 177 | errdefer list.deinit(); | ||
| 178 | try list.appendSlice(scalar); | ||
| 179 | try output.append(list); | ||
| 180 | }, | ||
| 181 | .Combine => |pair| { | ||
| 182 | const a_node = pair[0]; | ||
| 183 | const b_node = pair[1]; | ||
| 184 | |||
| 185 | var child_list_a = ArrayList(ArrayList(u8)).init(global_allocator); | ||
| 186 | defer { | ||
| 187 | for (child_list_a.items) |*buf| buf.deinit(); | ||
| 188 | child_list_a.deinit(); | ||
| 189 | } | ||
| 190 | try expandNode(a_node, &child_list_a); | ||
| 191 | |||
| 192 | var child_list_b = ArrayList(ArrayList(u8)).init(global_allocator); | ||
| 193 | defer { | ||
| 194 | for (child_list_b.items) |*buf| buf.deinit(); | ||
| 195 | child_list_b.deinit(); | ||
| 196 | } | ||
| 197 | try expandNode(b_node, &child_list_b); | ||
| 198 | |||
| 199 | for (child_list_a.items) |buf_a| { | ||
| 200 | for (child_list_b.items) |buf_b| { | ||
| 201 | var combined_buf = ArrayList(u8).init(global_allocator); | ||
| 202 | errdefer combined_buf.deinit(); | ||
| 203 | |||
| 204 | try combined_buf.appendSlice(buf_a.items); | ||
| 205 | try combined_buf.appendSlice(buf_b.items); | ||
| 206 | try output.append(combined_buf); | ||
| 207 | } | ||
| 208 | } | ||
| 209 | }, | ||
| 210 | .List => |list| { | ||
| 211 | for (list.items) |child_node| { | ||
| 212 | var child_list = ArrayList(ArrayList(u8)).init(global_allocator); | ||
| 213 | errdefer for (child_list.items) |*buf| buf.deinit(); | ||
| 214 | defer child_list.deinit(); | ||
| 215 | |||
| 216 | try expandNode(child_node, &child_list); | ||
| 217 | |||
| 218 | for (child_list.items) |buf| { | ||
| 219 | try output.append(buf); | ||
| 220 | } | ||
| 221 | } | ||
| 222 | }, | ||
| 223 | } | ||
| 224 | } | ||
| 225 | |||
| 226 | pub fn main() !void { | ||
| 227 | defer _ = gpa.deinit(); | ||
| 228 | const stdin_file = io.getStdIn(); | ||
| 229 | const stdout_file = io.getStdOut(); | ||
| 230 | |||
| 231 | const stdin = try stdin_file.reader().readAllAlloc(global_allocator, std.math.maxInt(usize)); | ||
| 232 | defer global_allocator.free(stdin); | ||
| 233 | |||
| 234 | var result_buf = ArrayList(u8).init(global_allocator); | ||
| 235 | defer result_buf.deinit(); | ||
| 236 | |||
| 237 | try expandString(stdin, &result_buf); | ||
| 238 | try stdout_file.writeAll(result_buf.items); | ||
| 239 | } | ||
| 240 | |||
| 241 | test "invalid inputs" { | ||
| 242 | global_allocator = std.testing.allocator; | ||
| 243 | |||
| 244 | try expectError("}ABC", error.InvalidInput); | ||
| 245 | try expectError("{ABC", error.InvalidInput); | ||
| 246 | try expectError("}{", error.InvalidInput); | ||
| 247 | try expectError("{}", error.InvalidInput); | ||
| 248 | try expectError("A,B,C", error.InvalidInput); | ||
| 249 | try expectError("{A{B,C}", error.InvalidInput); | ||
| 250 | try expectError("{A,}", error.InvalidInput); | ||
| 251 | |||
| 252 | try expectError("\n", error.InvalidInput); | ||
| 253 | } | ||
| 254 | |||
| 255 | fn expectError(test_input: []const u8, expected_err: anyerror) !void { | ||
| 256 | var output_buf = ArrayList(u8).init(global_allocator); | ||
| 257 | defer output_buf.deinit(); | ||
| 258 | |||
| 259 | try testing.expectError(expected_err, expandString(test_input, &output_buf)); | ||
| 260 | } | ||
| 261 | |||
| 262 | test "valid inputs" { | ||
| 263 | global_allocator = std.testing.allocator; | ||
| 264 | |||
| 265 | try expectExpansion("{x,y,z}", "x y z"); | ||
| 266 | try expectExpansion("{A,B}{x,y}", "Ax Ay Bx By"); | ||
| 267 | try expectExpansion("{A,B{x,y}}", "A Bx By"); | ||
| 268 | |||
| 269 | try expectExpansion("{ABC}", "ABC"); | ||
| 270 | try expectExpansion("{A,B,C}", "A B C"); | ||
| 271 | try expectExpansion("ABC", "ABC"); | ||
| 272 | |||
| 273 | try expectExpansion("", ""); | ||
| 274 | try expectExpansion("{A,B}{C,{x,y}}{g,h}", "ACg ACh Axg Axh Ayg Ayh BCg BCh Bxg Bxh Byg Byh"); | ||
| 275 | try expectExpansion("{A,B}{C,C{x,y}}{g,h}", "ACg ACh ACxg ACxh ACyg ACyh BCg BCh BCxg BCxh BCyg BCyh"); | ||
| 276 | try expectExpansion("{A,B}a", "Aa Ba"); | ||
| 277 | try expectExpansion("{C,{x,y}}", "C x y"); | ||
| 278 | try expectExpansion("z{C,{x,y}}", "zC zx zy"); | ||
| 279 | try expectExpansion("a{b,c{d,e{f,g}}}", "ab acd acef aceg"); | ||
| 280 | try expectExpansion("a{x,y}b", "axb ayb"); | ||
| 281 | try expectExpansion("z{{a,b}}", "za zb"); | ||
| 282 | try expectExpansion("a{b}", "ab"); | ||
| 283 | } | ||
| 284 | |||
| 285 | fn expectExpansion(test_input: []const u8, expected_result: []const u8) !void { | ||
| 286 | var result = ArrayList(u8).init(global_allocator); | ||
| 287 | defer result.deinit(); | ||
| 288 | |||
| 289 | expandString(test_input, &result) catch unreachable; | ||
| 290 | |||
| 291 | try testing.expectEqualSlices(u8, expected_result, result.items); | ||
| 292 | } | ||
test/standalone/brace_expansion/build.zig deleted-11| ... | @@ -1,11 +0,0 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | |||
| 3 | pub fn build(b: *std.Build) void { | ||
| 4 | const main = b.addTest(.{ | ||
| 5 | .root_source_file = .{ .path = "main.zig" }, | ||
| 6 | .optimize = b.standardOptimizeOption(.{}), | ||
| 7 | }); | ||
| 8 | |||
| 9 | const test_step = b.step("test", "Test it"); | ||
| 10 | test_step.dependOn(&main.step); | ||
| 11 | } | ||
test/standalone/brace_expansion/main.zig deleted-292| ... | @@ -1,292 +0,0 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const io = std.io; | ||
| 3 | const mem = std.mem; | ||
| 4 | const debug = std.debug; | ||
| 5 | const assert = debug.assert; | ||
| 6 | const testing = std.testing; | ||
| 7 | const ArrayList = std.ArrayList; | ||
| 8 | const maxInt = std.math.maxInt; | ||
| 9 | |||
| 10 | const Token = union(enum) { | ||
| 11 | Word: []const u8, | ||
| 12 | OpenBrace, | ||
| 13 | CloseBrace, | ||
| 14 | Comma, | ||
| 15 | Eof, | ||
| 16 | }; | ||
| 17 | |||
| 18 | var gpa = std.heap.GeneralPurposeAllocator(.{}){}; | ||
| 19 | var global_allocator = gpa.allocator(); | ||
| 20 | |||
| 21 | fn tokenize(input: []const u8) !ArrayList(Token) { | ||
| 22 | const State = enum { | ||
| 23 | Start, | ||
| 24 | Word, | ||
| 25 | }; | ||
| 26 | |||
| 27 | var token_list = ArrayList(Token).init(global_allocator); | ||
| 28 | errdefer token_list.deinit(); | ||
| 29 | var tok_begin: usize = undefined; | ||
| 30 | var state = State.Start; | ||
| 31 | |||
| 32 | for (input, 0..) |b, i| { | ||
| 33 | switch (state) { | ||
| 34 | .Start => switch (b) { | ||
| 35 | 'a'...'z', 'A'...'Z' => { | ||
| 36 | state = State.Word; | ||
| 37 | tok_begin = i; | ||
| 38 | }, | ||
| 39 | '{' => try token_list.append(Token.OpenBrace), | ||
| 40 | '}' => try token_list.append(Token.CloseBrace), | ||
| 41 | ',' => try token_list.append(Token.Comma), | ||
| 42 | else => return error.InvalidInput, | ||
| 43 | }, | ||
| 44 | .Word => switch (b) { | ||
| 45 | 'a'...'z', 'A'...'Z' => {}, | ||
| 46 | '{', '}', ',' => { | ||
| 47 | try token_list.append(Token{ .Word = input[tok_begin..i] }); | ||
| 48 | switch (b) { | ||
| 49 | '{' => try token_list.append(Token.OpenBrace), | ||
| 50 | '}' => try token_list.append(Token.CloseBrace), | ||
| 51 | ',' => try token_list.append(Token.Comma), | ||
| 52 | else => unreachable, | ||
| 53 | } | ||
| 54 | state = State.Start; | ||
| 55 | }, | ||
| 56 | else => return error.InvalidInput, | ||
| 57 | }, | ||
| 58 | } | ||
| 59 | } | ||
| 60 | switch (state) { | ||
| 61 | State.Start => {}, | ||
| 62 | State.Word => try token_list.append(Token{ .Word = input[tok_begin..] }), | ||
| 63 | } | ||
| 64 | try token_list.append(Token.Eof); | ||
| 65 | return token_list; | ||
| 66 | } | ||
| 67 | |||
| 68 | const Node = union(enum) { | ||
| 69 | Scalar: []const u8, | ||
| 70 | List: ArrayList(Node), | ||
| 71 | Combine: []Node, | ||
| 72 | |||
| 73 | fn deinit(self: Node) void { | ||
| 74 | switch (self) { | ||
| 75 | .Scalar => {}, | ||
| 76 | .Combine => |pair| { | ||
| 77 | pair[0].deinit(); | ||
| 78 | pair[1].deinit(); | ||
| 79 | global_allocator.free(pair); | ||
| 80 | }, | ||
| 81 | .List => |list| { | ||
| 82 | for (list.items) |item| { | ||
| 83 | item.deinit(); | ||
| 84 | } | ||
| 85 | list.deinit(); | ||
| 86 | }, | ||
| 87 | } | ||
| 88 | } | ||
| 89 | }; | ||
| 90 | |||
| 91 | const ParseError = error{ | ||
| 92 | InvalidInput, | ||
| 93 | OutOfMemory, | ||
| 94 | }; | ||
| 95 | |||
| 96 | fn parse(tokens: *const ArrayList(Token), token_index: *usize) ParseError!Node { | ||
| 97 | const first_token = tokens.items[token_index.*]; | ||
| 98 | token_index.* += 1; | ||
| 99 | |||
| 100 | const result_node = switch (first_token) { | ||
| 101 | .Word => |word| Node{ .Scalar = word }, | ||
| 102 | .OpenBrace => blk: { | ||
| 103 | var list = ArrayList(Node).init(global_allocator); | ||
| 104 | errdefer { | ||
| 105 | for (list.items) |node| node.deinit(); | ||
| 106 | list.deinit(); | ||
| 107 | } | ||
| 108 | while (true) { | ||
| 109 | try list.append(try parse(tokens, token_index)); | ||
| 110 | |||
| 111 | const token = tokens.items[token_index.*]; | ||
| 112 | token_index.* += 1; | ||
| 113 | |||
| 114 | switch (token) { | ||
| 115 | .CloseBrace => break, | ||
| 116 | .Comma => continue, | ||
| 117 | else => return error.InvalidInput, | ||
| 118 | } | ||
| 119 | } | ||
| 120 | break :blk Node{ .List = list }; | ||
| 121 | }, | ||
| 122 | else => return error.InvalidInput, | ||
| 123 | }; | ||
| 124 | |||
| 125 | switch (tokens.items[token_index.*]) { | ||
| 126 | .Word, .OpenBrace => { | ||
| 127 | const pair = try global_allocator.alloc(Node, 2); | ||
| 128 | errdefer global_allocator.free(pair); | ||
| 129 | pair[0] = result_node; | ||
| 130 | pair[1] = try parse(tokens, token_index); | ||
| 131 | return Node{ .Combine = pair }; | ||
| 132 | }, | ||
| 133 | else => return result_node, | ||
| 134 | } | ||
| 135 | } | ||
| 136 | |||
| 137 | fn expandString(input: []const u8, output: *ArrayList(u8)) !void { | ||
| 138 | const tokens = try tokenize(input); | ||
| 139 | defer tokens.deinit(); | ||
| 140 | if (tokens.items.len == 1) { | ||
| 141 | return output.resize(0); | ||
| 142 | } | ||
| 143 | |||
| 144 | var token_index: usize = 0; | ||
| 145 | const root = try parse(&tokens, &token_index); | ||
| 146 | defer root.deinit(); | ||
| 147 | const last_token = tokens.items[token_index]; | ||
| 148 | switch (last_token) { | ||
| 149 | Token.Eof => {}, | ||
| 150 | else => return error.InvalidInput, | ||
| 151 | } | ||
| 152 | |||
| 153 | var result_list = ArrayList(ArrayList(u8)).init(global_allocator); | ||
| 154 | defer { | ||
| 155 | for (result_list.items) |*buf| buf.deinit(); | ||
| 156 | result_list.deinit(); | ||
| 157 | } | ||
| 158 | |||
| 159 | try expandNode(root, &result_list); | ||
| 160 | |||
| 161 | try output.resize(0); | ||
| 162 | for (result_list.items, 0..) |buf, i| { | ||
| 163 | if (i != 0) { | ||
| 164 | try output.append(' '); | ||
| 165 | } | ||
| 166 | try output.appendSlice(buf.items); | ||
| 167 | } | ||
| 168 | } | ||
| 169 | |||
| 170 | const ExpandNodeError = error{OutOfMemory}; | ||
| 171 | |||
| 172 | fn expandNode(node: Node, output: *ArrayList(ArrayList(u8))) ExpandNodeError!void { | ||
| 173 | assert(output.items.len == 0); | ||
| 174 | switch (node) { | ||
| 175 | .Scalar => |scalar| { | ||
| 176 | var list = ArrayList(u8).init(global_allocator); | ||
| 177 | errdefer list.deinit(); | ||
| 178 | try list.appendSlice(scalar); | ||
| 179 | try output.append(list); | ||
| 180 | }, | ||
| 181 | .Combine => |pair| { | ||
| 182 | const a_node = pair[0]; | ||
| 183 | const b_node = pair[1]; | ||
| 184 | |||
| 185 | var child_list_a = ArrayList(ArrayList(u8)).init(global_allocator); | ||
| 186 | defer { | ||
| 187 | for (child_list_a.items) |*buf| buf.deinit(); | ||
| 188 | child_list_a.deinit(); | ||
| 189 | } | ||
| 190 | try expandNode(a_node, &child_list_a); | ||
| 191 | |||
| 192 | var child_list_b = ArrayList(ArrayList(u8)).init(global_allocator); | ||
| 193 | defer { | ||
| 194 | for (child_list_b.items) |*buf| buf.deinit(); | ||
| 195 | child_list_b.deinit(); | ||
| 196 | } | ||
| 197 | try expandNode(b_node, &child_list_b); | ||
| 198 | |||
| 199 | for (child_list_a.items) |buf_a| { | ||
| 200 | for (child_list_b.items) |buf_b| { | ||
| 201 | var combined_buf = ArrayList(u8).init(global_allocator); | ||
| 202 | errdefer combined_buf.deinit(); | ||
| 203 | |||
| 204 | try combined_buf.appendSlice(buf_a.items); | ||
| 205 | try combined_buf.appendSlice(buf_b.items); | ||
| 206 | try output.append(combined_buf); | ||
| 207 | } | ||
| 208 | } | ||
| 209 | }, | ||
| 210 | .List => |list| { | ||
| 211 | for (list.items) |child_node| { | ||
| 212 | var child_list = ArrayList(ArrayList(u8)).init(global_allocator); | ||
| 213 | errdefer for (child_list.items) |*buf| buf.deinit(); | ||
| 214 | defer child_list.deinit(); | ||
| 215 | |||
| 216 | try expandNode(child_node, &child_list); | ||
| 217 | |||
| 218 | for (child_list.items) |buf| { | ||
| 219 | try output.append(buf); | ||
| 220 | } | ||
| 221 | } | ||
| 222 | }, | ||
| 223 | } | ||
| 224 | } | ||
| 225 | |||
| 226 | pub fn main() !void { | ||
| 227 | defer _ = gpa.deinit(); | ||
| 228 | const stdin_file = io.getStdIn(); | ||
| 229 | const stdout_file = io.getStdOut(); | ||
| 230 | |||
| 231 | const stdin = try stdin_file.reader().readAllAlloc(global_allocator, std.math.maxInt(usize)); | ||
| 232 | defer global_allocator.free(stdin); | ||
| 233 | |||
| 234 | var result_buf = ArrayList(u8).init(global_allocator); | ||
| 235 | defer result_buf.deinit(); | ||
| 236 | |||
| 237 | try expandString(stdin.items, &result_buf); | ||
| 238 | try stdout_file.write(result_buf.items); | ||
| 239 | } | ||
| 240 | |||
| 241 | test "invalid inputs" { | ||
| 242 | global_allocator = std.testing.allocator; | ||
| 243 | |||
| 244 | try expectError("}ABC", error.InvalidInput); | ||
| 245 | try expectError("{ABC", error.InvalidInput); | ||
| 246 | try expectError("}{", error.InvalidInput); | ||
| 247 | try expectError("{}", error.InvalidInput); | ||
| 248 | try expectError("A,B,C", error.InvalidInput); | ||
| 249 | try expectError("{A{B,C}", error.InvalidInput); | ||
| 250 | try expectError("{A,}", error.InvalidInput); | ||
| 251 | |||
| 252 | try expectError("\n", error.InvalidInput); | ||
| 253 | } | ||
| 254 | |||
| 255 | fn expectError(test_input: []const u8, expected_err: anyerror) !void { | ||
| 256 | var output_buf = ArrayList(u8).init(global_allocator); | ||
| 257 | defer output_buf.deinit(); | ||
| 258 | |||
| 259 | try testing.expectError(expected_err, expandString(test_input, &output_buf)); | ||
| 260 | } | ||
| 261 | |||
| 262 | test "valid inputs" { | ||
| 263 | global_allocator = std.testing.allocator; | ||
| 264 | |||
| 265 | try expectExpansion("{x,y,z}", "x y z"); | ||
| 266 | try expectExpansion("{A,B}{x,y}", "Ax Ay Bx By"); | ||
| 267 | try expectExpansion("{A,B{x,y}}", "A Bx By"); | ||
| 268 | |||
| 269 | try expectExpansion("{ABC}", "ABC"); | ||
| 270 | try expectExpansion("{A,B,C}", "A B C"); | ||
| 271 | try expectExpansion("ABC", "ABC"); | ||
| 272 | |||
| 273 | try expectExpansion("", ""); | ||
| 274 | try expectExpansion("{A,B}{C,{x,y}}{g,h}", "ACg ACh Axg Axh Ayg Ayh BCg BCh Bxg Bxh Byg Byh"); | ||
| 275 | try expectExpansion("{A,B}{C,C{x,y}}{g,h}", "ACg ACh ACxg ACxh ACyg ACyh BCg BCh BCxg BCxh BCyg BCyh"); | ||
| 276 | try expectExpansion("{A,B}a", "Aa Ba"); | ||
| 277 | try expectExpansion("{C,{x,y}}", "C x y"); | ||
| 278 | try expectExpansion("z{C,{x,y}}", "zC zx zy"); | ||
| 279 | try expectExpansion("a{b,c{d,e{f,g}}}", "ab acd acef aceg"); | ||
| 280 | try expectExpansion("a{x,y}b", "axb ayb"); | ||
| 281 | try expectExpansion("z{{a,b}}", "za zb"); | ||
| 282 | try expectExpansion("a{b}", "ab"); | ||
| 283 | } | ||
| 284 | |||
| 285 | fn expectExpansion(test_input: []const u8, expected_result: []const u8) !void { | ||
| 286 | var result = ArrayList(u8).init(global_allocator); | ||
| 287 | defer result.deinit(); | ||
| 288 | |||
| 289 | expandString(test_input, &result) catch unreachable; | ||
| 290 | |||
| 291 | try testing.expectEqualSlices(u8, expected_result, result.items); | ||
| 292 | } | ||
test/standalone/c_compiler/build.zig+18-21| ... | @@ -1,27 +1,24 @@ | ... | @@ -1,27 +1,24 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const CrossTarget = std.zig.CrossTarget; | ||
| 4 | 3 | ||
| 5 | // TODO integrate this with the std.Build executor API | 4 | pub fn build(b: *std.Build) void { |
| 6 | fn isRunnableTarget(t: CrossTarget) bool { | 5 | const test_step = b.step("test", "Test it"); |
| 7 | if (t.isNative()) return true; | 6 | b.default_step = test_step; |
| 8 | 7 | ||
| 9 | return (t.getOsTag() == builtin.os.tag and | 8 | add(b, test_step, .Debug); |
| 10 | t.getCpuArch() == builtin.cpu.arch); | 9 | add(b, test_step, .ReleaseFast); |
| 10 | add(b, test_step, .ReleaseSmall); | ||
| 11 | add(b, test_step, .ReleaseSafe); | ||
| 11 | } | 12 | } |
| 12 | 13 | ||
| 13 | pub fn build(b: *std.Build) void { | 14 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { |
| 14 | const optimize = b.standardOptimizeOption(.{}); | 15 | const target: std.zig.CrossTarget = .{}; |
| 15 | const target = b.standardTargetOptions(.{}); | ||
| 16 | |||
| 17 | const test_step = b.step("test", "Test the program"); | ||
| 18 | 16 | ||
| 19 | const exe_c = b.addExecutable(.{ | 17 | const exe_c = b.addExecutable(.{ |
| 20 | .name = "test_c", | 18 | .name = "test_c", |
| 21 | .optimize = optimize, | 19 | .optimize = optimize, |
| 22 | .target = target, | 20 | .target = target, |
| 23 | }); | 21 | }); |
| 24 | b.default_step.dependOn(&exe_c.step); | ||
| 25 | exe_c.addCSourceFile("test.c", &[0][]const u8{}); | 22 | exe_c.addCSourceFile("test.c", &[0][]const u8{}); |
| 26 | exe_c.linkLibC(); | 23 | exe_c.linkLibC(); |
| 27 | 24 | ||
| ... | @@ -47,13 +44,13 @@ pub fn build(b: *std.Build) void { | ... | @@ -47,13 +44,13 @@ pub fn build(b: *std.Build) void { |
| 47 | else => {}, | 44 | else => {}, |
| 48 | } | 45 | } |
| 49 | 46 | ||
| 50 | if (isRunnableTarget(target)) { | 47 | const run_c_cmd = b.addRunArtifact(exe_c); |
| 51 | const run_c_cmd = exe_c.run(); | 48 | run_c_cmd.expectExitCode(0); |
| 52 | test_step.dependOn(&run_c_cmd.step); | 49 | run_c_cmd.skip_foreign_checks = true; |
| 53 | const run_cpp_cmd = exe_cpp.run(); | 50 | test_step.dependOn(&run_c_cmd.step); |
| 54 | test_step.dependOn(&run_cpp_cmd.step); | 51 | |
| 55 | } else { | 52 | const run_cpp_cmd = b.addRunArtifact(exe_cpp); |
| 56 | test_step.dependOn(&exe_c.step); | 53 | run_cpp_cmd.expectExitCode(0); |
| 57 | test_step.dependOn(&exe_cpp.step); | 54 | run_cpp_cmd.skip_foreign_checks = true; |
| 58 | } | 55 | test_step.dependOn(&run_cpp_cmd.step); |
| 59 | } | 56 | } |
test/standalone/dep_diamond/build.zig+4-2| ... | @@ -1,7 +1,10 @@ | ... | @@ -1,7 +1,10 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn build(b: *std.Build) void { | 3 | pub fn build(b: *std.Build) void { |
| 4 | const optimize = b.standardOptimizeOption(.{}); | 4 | const test_step = b.step("test", "Test it"); |
| 5 | b.default_step = test_step; | ||
| 6 | |||
| 7 | const optimize: std.builtin.OptimizeMode = .Debug; | ||
| 5 | 8 | ||
| 6 | const shared = b.createModule(.{ | 9 | const shared = b.createModule(.{ |
| 7 | .source_file = .{ .path = "shared.zig" }, | 10 | .source_file = .{ .path = "shared.zig" }, |
| ... | @@ -23,6 +26,5 @@ pub fn build(b: *std.Build) void { | ... | @@ -23,6 +26,5 @@ pub fn build(b: *std.Build) void { |
| 23 | 26 | ||
| 24 | const run = exe.run(); | 27 | const run = exe.run(); |
| 25 | 28 | ||
| 26 | const test_step = b.step("test", "Test it"); | ||
| 27 | test_step.dependOn(&run.step); | 29 | test_step.dependOn(&run.step); |
| 28 | } | 30 | } |
test/standalone/dep_mutually_recursive/build.zig+4-2| ... | @@ -1,7 +1,10 @@ | ... | @@ -1,7 +1,10 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn build(b: *std.Build) void { | 3 | pub fn build(b: *std.Build) void { |
| 4 | const optimize = b.standardOptimizeOption(.{}); | 4 | const test_step = b.step("test", "Test it"); |
| 5 | b.default_step = test_step; | ||
| 6 | |||
| 7 | const optimize: std.builtin.OptimizeMode = .Debug; | ||
| 5 | 8 | ||
| 6 | const foo = b.createModule(.{ | 9 | const foo = b.createModule(.{ |
| 7 | .source_file = .{ .path = "foo.zig" }, | 10 | .source_file = .{ .path = "foo.zig" }, |
| ... | @@ -21,6 +24,5 @@ pub fn build(b: *std.Build) void { | ... | @@ -21,6 +24,5 @@ pub fn build(b: *std.Build) void { |
| 21 | 24 | ||
| 22 | const run = exe.run(); | 25 | const run = exe.run(); |
| 23 | 26 | ||
| 24 | const test_step = b.step("test", "Test it"); | ||
| 25 | test_step.dependOn(&run.step); | 27 | test_step.dependOn(&run.step); |
| 26 | } | 28 | } |
test/standalone/dep_recursive/build.zig+4-2| ... | @@ -1,7 +1,10 @@ | ... | @@ -1,7 +1,10 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn build(b: *std.Build) void { | 3 | pub fn build(b: *std.Build) void { |
| 4 | const optimize = b.standardOptimizeOption(.{}); | 4 | const test_step = b.step("test", "Test it"); |
| 5 | b.default_step = test_step; | ||
| 6 | |||
| 7 | const optimize: std.builtin.OptimizeMode = .Debug; | ||
| 5 | 8 | ||
| 6 | const foo = b.createModule(.{ | 9 | const foo = b.createModule(.{ |
| 7 | .source_file = .{ .path = "foo.zig" }, | 10 | .source_file = .{ .path = "foo.zig" }, |
| ... | @@ -17,6 +20,5 @@ pub fn build(b: *std.Build) void { | ... | @@ -17,6 +20,5 @@ pub fn build(b: *std.Build) void { |
| 17 | 20 | ||
| 18 | const run = exe.run(); | 21 | const run = exe.run(); |
| 19 | 22 | ||
| 20 | const test_step = b.step("test", "Test it"); | ||
| 21 | test_step.dependOn(&run.step); | 23 | test_step.dependOn(&run.step); |
| 22 | } | 24 | } |
test/standalone/dep_shared_builtin/build.zig+4-2| ... | @@ -1,7 +1,10 @@ | ... | @@ -1,7 +1,10 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn build(b: *std.Build) void { | 3 | pub fn build(b: *std.Build) void { |
| 4 | const optimize = b.standardOptimizeOption(.{}); | 4 | const test_step = b.step("test", "Test it"); |
| 5 | b.default_step = test_step; | ||
| 6 | |||
| 7 | const optimize: std.builtin.OptimizeMode = .Debug; | ||
| 5 | 8 | ||
| 6 | const exe = b.addExecutable(.{ | 9 | const exe = b.addExecutable(.{ |
| 7 | .name = "test", | 10 | .name = "test", |
| ... | @@ -14,6 +17,5 @@ pub fn build(b: *std.Build) void { | ... | @@ -14,6 +17,5 @@ pub fn build(b: *std.Build) void { |
| 14 | 17 | ||
| 15 | const run = exe.run(); | 18 | const run = exe.run(); |
| 16 | 19 | ||
| 17 | const test_step = b.step("test", "Test it"); | ||
| 18 | test_step.dependOn(&run.step); | 20 | test_step.dependOn(&run.step); |
| 19 | } | 21 | } |
test/standalone/dep_triangle/build.zig+4-2| ... | @@ -1,7 +1,10 @@ | ... | @@ -1,7 +1,10 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn build(b: *std.Build) void { | 3 | pub fn build(b: *std.Build) void { |
| 4 | const optimize = b.standardOptimizeOption(.{}); | 4 | const test_step = b.step("test", "Test it"); |
| 5 | b.default_step = test_step; | ||
| 6 | |||
| 7 | const optimize: std.builtin.OptimizeMode = .Debug; | ||
| 5 | 8 | ||
| 6 | const shared = b.createModule(.{ | 9 | const shared = b.createModule(.{ |
| 7 | .source_file = .{ .path = "shared.zig" }, | 10 | .source_file = .{ .path = "shared.zig" }, |
| ... | @@ -20,6 +23,5 @@ pub fn build(b: *std.Build) void { | ... | @@ -20,6 +23,5 @@ pub fn build(b: *std.Build) void { |
| 20 | 23 | ||
| 21 | const run = exe.run(); | 24 | const run = exe.run(); |
| 22 | 25 | ||
| 23 | const test_step = b.step("test", "Test it"); | ||
| 24 | test_step.dependOn(&run.step); | 26 | test_step.dependOn(&run.step); |
| 25 | } | 27 | } |
test/standalone/embed_generated_file/build.zig+3-5| ... | @@ -1,8 +1,8 @@ | ... | @@ -1,8 +1,8 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn build(b: *std.Build) void { | 3 | pub fn build(b: *std.Build) void { |
| 4 | const target = b.standardTargetOptions(.{}); | 4 | const test_step = b.step("test", "Test it"); |
| 5 | const optimize = b.standardOptimizeOption(.{}); | 5 | b.default_step = test_step; |
| 6 | 6 | ||
| 7 | const bootloader = b.addExecutable(.{ | 7 | const bootloader = b.addExecutable(.{ |
| 8 | .name = "bootloader", | 8 | .name = "bootloader", |
| ... | @@ -16,13 +16,11 @@ pub fn build(b: *std.Build) void { | ... | @@ -16,13 +16,11 @@ pub fn build(b: *std.Build) void { |
| 16 | 16 | ||
| 17 | const exe = b.addTest(.{ | 17 | const exe = b.addTest(.{ |
| 18 | .root_source_file = .{ .path = "main.zig" }, | 18 | .root_source_file = .{ .path = "main.zig" }, |
| 19 | .target = target, | 19 | .optimize = .Debug, |
| 20 | .optimize = optimize, | ||
| 21 | }); | 20 | }); |
| 22 | exe.addAnonymousModule("bootloader.elf", .{ | 21 | exe.addAnonymousModule("bootloader.elf", .{ |
| 23 | .source_file = bootloader.getOutputSource(), | 22 | .source_file = bootloader.getOutputSource(), |
| 24 | }); | 23 | }); |
| 25 | 24 | ||
| 26 | const test_step = b.step("test", "Test the program"); | ||
| 27 | test_step.dependOn(&exe.step); | 25 | test_step.dependOn(&exe.step); |
| 28 | } | 26 | } |
test/standalone/emit_asm_and_bin/build.zig+4-2| ... | @@ -1,6 +1,9 @@ | ... | @@ -1,6 +1,9 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn build(b: *std.Build) void { | 3 | pub fn build(b: *std.Build) void { |
| 4 | const test_step = b.step("test", "Test it"); | ||
| 5 | b.default_step = test_step; | ||
| 6 | |||
| 4 | const main = b.addTest(.{ | 7 | const main = b.addTest(.{ |
| 5 | .root_source_file = .{ .path = "main.zig" }, | 8 | .root_source_file = .{ .path = "main.zig" }, |
| 6 | .optimize = b.standardOptimizeOption(.{}), | 9 | .optimize = b.standardOptimizeOption(.{}), |
| ... | @@ -8,6 +11,5 @@ pub fn build(b: *std.Build) void { | ... | @@ -8,6 +11,5 @@ pub fn build(b: *std.Build) void { |
| 8 | main.emit_asm = .{ .emit_to = b.pathFromRoot("main.s") }; | 11 | main.emit_asm = .{ .emit_to = b.pathFromRoot("main.s") }; |
| 9 | main.emit_bin = .{ .emit_to = b.pathFromRoot("main") }; | 12 | main.emit_bin = .{ .emit_to = b.pathFromRoot("main") }; |
| 10 | 13 | ||
| 11 | const test_step = b.step("test", "Run test"); | 14 | test_step.dependOn(&main.run().step); |
| 12 | test_step.dependOn(&main.step); | ||
| 13 | } | 15 | } |
test/standalone/empty_env/build.zig+13-3| ... | @@ -1,15 +1,25 @@ | ... | @@ -1,15 +1,25 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | ||
| 2 | 3 | ||
| 3 | pub fn build(b: *std.Build) void { | 4 | pub fn build(b: *std.Build) void { |
| 5 | const test_step = b.step("test", "Test it"); | ||
| 6 | b.default_step = test_step; | ||
| 7 | |||
| 8 | const optimize: std.builtin.OptimizeMode = .Debug; | ||
| 9 | |||
| 10 | if (builtin.os.tag == .windows and builtin.cpu.arch == .aarch64) { | ||
| 11 | // https://github.com/ziglang/zig/issues/13685 | ||
| 12 | return; | ||
| 13 | } | ||
| 14 | |||
| 4 | const main = b.addExecutable(.{ | 15 | const main = b.addExecutable(.{ |
| 5 | .name = "main", | 16 | .name = "main", |
| 6 | .root_source_file = .{ .path = "main.zig" }, | 17 | .root_source_file = .{ .path = "main.zig" }, |
| 7 | .optimize = b.standardOptimizeOption(.{}), | 18 | .optimize = optimize, |
| 8 | }); | 19 | }); |
| 9 | 20 | ||
| 10 | const run = main.run(); | 21 | const run = b.addRunArtifact(main); |
| 11 | run.clearEnvironment(); | 22 | run.clearEnvironment(); |
| 12 | 23 | ||
| 13 | const test_step = b.step("test", "Test it"); | ||
| 14 | test_step.dependOn(&run.step); | 24 | test_step.dependOn(&run.step); |
| 15 | } | 25 | } |
test/standalone/extern/build.zig+1-1| ... | @@ -1,7 +1,7 @@ | ... | @@ -1,7 +1,7 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn build(b: *std.Build) void { | 3 | pub fn build(b: *std.Build) void { |
| 4 | const optimize = b.standardOptimizeOption(.{}); | 4 | const optimize: std.builtin.OptimizeMode = .Debug; |
| 5 | 5 | ||
| 6 | const obj = b.addObject(.{ | 6 | const obj = b.addObject(.{ |
| 7 | .name = "exports", | 7 | .name = "exports", |
test/standalone/global_linkage/build.zig+8-5| ... | @@ -1,20 +1,24 @@ | ... | @@ -1,20 +1,24 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn build(b: *std.Build) void { | 3 | pub fn build(b: *std.Build) void { |
| 4 | const optimize = b.standardOptimizeOption(.{}); | 4 | const test_step = b.step("test", "Test the program"); |
| 5 | b.default_step = test_step; | ||
| 6 | |||
| 7 | const optimize: std.builtin.OptimizeMode = .Debug; | ||
| 8 | const target: std.zig.CrossTarget = .{}; | ||
| 5 | 9 | ||
| 6 | const obj1 = b.addStaticLibrary(.{ | 10 | const obj1 = b.addStaticLibrary(.{ |
| 7 | .name = "obj1", | 11 | .name = "obj1", |
| 8 | .root_source_file = .{ .path = "obj1.zig" }, | 12 | .root_source_file = .{ .path = "obj1.zig" }, |
| 9 | .optimize = optimize, | 13 | .optimize = optimize, |
| 10 | .target = .{}, | 14 | .target = target, |
| 11 | }); | 15 | }); |
| 12 | 16 | ||
| 13 | const obj2 = b.addStaticLibrary(.{ | 17 | const obj2 = b.addStaticLibrary(.{ |
| 14 | .name = "obj2", | 18 | .name = "obj2", |
| 15 | .root_source_file = .{ .path = "obj2.zig" }, | 19 | .root_source_file = .{ .path = "obj2.zig" }, |
| 16 | .optimize = optimize, | 20 | .optimize = optimize, |
| 17 | .target = .{}, | 21 | .target = target, |
| 18 | }); | 22 | }); |
| 19 | 23 | ||
| 20 | const main = b.addTest(.{ | 24 | const main = b.addTest(.{ |
| ... | @@ -24,6 +28,5 @@ pub fn build(b: *std.Build) void { | ... | @@ -24,6 +28,5 @@ pub fn build(b: *std.Build) void { |
| 24 | main.linkLibrary(obj1); | 28 | main.linkLibrary(obj1); |
| 25 | main.linkLibrary(obj2); | 29 | main.linkLibrary(obj2); |
| 26 | 30 | ||
| 27 | const test_step = b.step("test", "Test it"); | 31 | test_step.dependOn(&main.run().step); |
| 28 | test_step.dependOn(&main.step); | ||
| 29 | } | 32 | } |
test/standalone/install_raw_hex/build.zig+3-3| ... | @@ -3,8 +3,8 @@ const std = @import("std"); | ... | @@ -3,8 +3,8 @@ const std = @import("std"); |
| 3 | const CheckFileStep = std.Build.CheckFileStep; | 3 | const CheckFileStep = std.Build.CheckFileStep; |
| 4 | 4 | ||
| 5 | pub fn build(b: *std.Build) void { | 5 | pub fn build(b: *std.Build) void { |
| 6 | const test_step = b.step("test", "Test the program"); | 6 | const test_step = b.step("test", "Test it"); |
| 7 | b.default_step.dependOn(test_step); | 7 | b.default_step = test_step; |
| 8 | 8 | ||
| 9 | const target = .{ | 9 | const target = .{ |
| 10 | .cpu_arch = .thumb, | 10 | .cpu_arch = .thumb, |
| ... | @@ -13,7 +13,7 @@ pub fn build(b: *std.Build) void { | ... | @@ -13,7 +13,7 @@ pub fn build(b: *std.Build) void { |
| 13 | .abi = .gnueabihf, | 13 | .abi = .gnueabihf, |
| 14 | }; | 14 | }; |
| 15 | 15 | ||
| 16 | const optimize = b.standardOptimizeOption(.{}); | 16 | const optimize: std.builtin.OptimizeMode = .Debug; |
| 17 | 17 | ||
| 18 | const elf = b.addExecutable(.{ | 18 | const elf = b.addExecutable(.{ |
| 19 | .name = "zig-nrf52-blink.elf", | 19 | .name = "zig-nrf52-blink.elf", |
test/standalone/issue_11595/build.zig+14-17| ... | @@ -1,18 +1,17 @@ | ... | @@ -1,18 +1,17 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const CrossTarget = std.zig.CrossTarget; | ||
| 4 | 3 | ||
| 5 | // TODO integrate this with the std.Build executor API | 4 | pub fn build(b: *std.Build) void { |
| 6 | fn isRunnableTarget(t: CrossTarget) bool { | 5 | const test_step = b.step("test", "Test it"); |
| 7 | if (t.isNative()) return true; | 6 | b.default_step = test_step; |
| 8 | 7 | ||
| 9 | return (t.getOsTag() == builtin.os.tag and | 8 | const optimize: std.builtin.OptimizeMode = .Debug; |
| 10 | t.getCpuArch() == builtin.cpu.arch); | 9 | const target: std.zig.CrossTarget = .{}; |
| 11 | } | ||
| 12 | 10 | ||
| 13 | pub fn build(b: *std.Build) void { | 11 | if (builtin.os.tag == .windows) { |
| 14 | const optimize = b.standardOptimizeOption(.{}); | 12 | // https://github.com/ziglang/zig/issues/12419 |
| 15 | const target = b.standardTargetOptions(.{}); | 13 | return; |
| 14 | } | ||
| 16 | 15 | ||
| 17 | const exe = b.addExecutable(.{ | 16 | const exe = b.addExecutable(.{ |
| 18 | .name = "zigtest", | 17 | .name = "zigtest", |
| ... | @@ -44,11 +43,9 @@ pub fn build(b: *std.Build) void { | ... | @@ -44,11 +43,9 @@ pub fn build(b: *std.Build) void { |
| 44 | 43 | ||
| 45 | b.default_step.dependOn(&exe.step); | 44 | b.default_step.dependOn(&exe.step); |
| 46 | 45 | ||
| 47 | const test_step = b.step("test", "Test the program"); | 46 | const run_cmd = b.addRunArtifact(exe); |
| 48 | if (isRunnableTarget(target)) { | 47 | run_cmd.skip_foreign_checks = true; |
| 49 | const run_cmd = exe.run(); | 48 | run_cmd.expectExitCode(0); |
| 50 | test_step.dependOn(&run_cmd.step); | 49 | |
| 51 | } else { | 50 | test_step.dependOn(&run_cmd.step); |
| 52 | test_step.dependOn(&exe.step); | ||
| 53 | } | ||
| 54 | } | 51 | } |
test/standalone/issue_12588/build.zig+5-3| ... | @@ -1,8 +1,11 @@ | ... | @@ -1,8 +1,11 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn build(b: *std.Build) void { | 3 | pub fn build(b: *std.Build) void { |
| 4 | const optimize = b.standardOptimizeOption(.{}); | 4 | const test_step = b.step("test", "Test it"); |
| 5 | const target = b.standardTargetOptions(.{}); | 5 | b.default_step = test_step; |
| 6 | |||
| 7 | const optimize: std.builtin.OptimizeMode = .Debug; | ||
| 8 | const target: std.zig.CrossTarget = .{}; | ||
| 6 | 9 | ||
| 7 | const obj = b.addObject(.{ | 10 | const obj = b.addObject(.{ |
| 8 | .name = "main", | 11 | .name = "main", |
| ... | @@ -15,6 +18,5 @@ pub fn build(b: *std.Build) void { | ... | @@ -15,6 +18,5 @@ pub fn build(b: *std.Build) void { |
| 15 | obj.emit_bin = .no_emit; | 18 | obj.emit_bin = .no_emit; |
| 16 | b.default_step.dependOn(&obj.step); | 19 | b.default_step.dependOn(&obj.step); |
| 17 | 20 | ||
| 18 | const test_step = b.step("test", "Test the program"); | ||
| 19 | test_step.dependOn(&obj.step); | 21 | test_step.dependOn(&obj.step); |
| 20 | } | 22 | } |
test/standalone/issue_12706/build.zig+9-21| ... | @@ -2,17 +2,12 @@ const std = @import("std"); | ... | @@ -2,17 +2,12 @@ const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const CrossTarget = std.zig.CrossTarget; | 3 | const CrossTarget = std.zig.CrossTarget; |
| 4 | 4 | ||
| 5 | // TODO integrate this with the std.Build executor API | ||
| 6 | fn isRunnableTarget(t: CrossTarget) bool { | ||
| 7 | if (t.isNative()) return true; | ||
| 8 | |||
| 9 | return (t.getOsTag() == builtin.os.tag and | ||
| 10 | t.getCpuArch() == builtin.cpu.arch); | ||
| 11 | } | ||
| 12 | |||
| 13 | pub fn build(b: *std.Build) void { | 5 | pub fn build(b: *std.Build) void { |
| 14 | const optimize = b.standardOptimizeOption(.{}); | 6 | const test_step = b.step("test", "Test it"); |
| 15 | const target = b.standardTargetOptions(.{}); | 7 | b.default_step = test_step; |
| 8 | |||
| 9 | const optimize: std.builtin.OptimizeMode = .Debug; | ||
| 10 | const target: std.zig.CrossTarget = .{}; | ||
| 16 | 11 | ||
| 17 | const exe = b.addExecutable(.{ | 12 | const exe = b.addExecutable(.{ |
| 18 | .name = "main", | 13 | .name = "main", |
| ... | @@ -20,22 +15,15 @@ pub fn build(b: *std.Build) void { | ... | @@ -20,22 +15,15 @@ pub fn build(b: *std.Build) void { |
| 20 | .optimize = optimize, | 15 | .optimize = optimize, |
| 21 | .target = target, | 16 | .target = target, |
| 22 | }); | 17 | }); |
| 23 | exe.install(); | ||
| 24 | 18 | ||
| 25 | const c_sources = [_][]const u8{ | 19 | const c_sources = [_][]const u8{ |
| 26 | "test.c", | 20 | "test.c", |
| 27 | }; | 21 | }; |
| 28 | |||
| 29 | exe.addCSourceFiles(&c_sources, &.{}); | 22 | exe.addCSourceFiles(&c_sources, &.{}); |
| 30 | exe.linkLibC(); | 23 | exe.linkLibC(); |
| 31 | 24 | ||
| 32 | b.default_step.dependOn(&exe.step); | 25 | const run_cmd = b.addRunArtifact(exe); |
| 33 | 26 | run_cmd.expectExitCode(0); | |
| 34 | const test_step = b.step("test", "Test the program"); | 27 | run_cmd.skip_foreign_checks = true; |
| 35 | if (isRunnableTarget(target)) { | 28 | test_step.dependOn(&run_cmd.step); |
| 36 | const run_cmd = exe.run(); | ||
| 37 | test_step.dependOn(&run_cmd.step); | ||
| 38 | } else { | ||
| 39 | test_step.dependOn(&exe.step); | ||
| 40 | } | ||
| 41 | } | 29 | } |
test/standalone/issue_13030/build.zig+10-5| ... | @@ -3,17 +3,22 @@ const builtin = @import("builtin"); | ... | @@ -3,17 +3,22 @@ const builtin = @import("builtin"); |
| 3 | const CrossTarget = std.zig.CrossTarget; | 3 | const CrossTarget = std.zig.CrossTarget; |
| 4 | 4 | ||
| 5 | pub fn build(b: *std.Build) void { | 5 | pub fn build(b: *std.Build) void { |
| 6 | const optimize = b.standardOptimizeOption(.{}); | 6 | const test_step = b.step("test", "Test it"); |
| 7 | const target = b.standardTargetOptions(.{}); | 7 | b.default_step = test_step; |
| 8 | 8 | ||
| 9 | add(b, test_step, .Debug); | ||
| 10 | add(b, test_step, .ReleaseFast); | ||
| 11 | add(b, test_step, .ReleaseSmall); | ||
| 12 | add(b, test_step, .ReleaseSafe); | ||
| 13 | } | ||
| 14 | |||
| 15 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 9 | const obj = b.addObject(.{ | 16 | const obj = b.addObject(.{ |
| 10 | .name = "main", | 17 | .name = "main", |
| 11 | .root_source_file = .{ .path = "main.zig" }, | 18 | .root_source_file = .{ .path = "main.zig" }, |
| 12 | .optimize = optimize, | 19 | .optimize = optimize, |
| 13 | .target = target, | 20 | .target = .{}, |
| 14 | }); | 21 | }); |
| 15 | b.default_step.dependOn(&obj.step); | ||
| 16 | 22 | ||
| 17 | const test_step = b.step("test", "Test the program"); | ||
| 18 | test_step.dependOn(&obj.step); | 23 | test_step.dependOn(&obj.step); |
| 19 | } | 24 | } |
test/standalone/issue_13970/build.zig+6-4| ... | @@ -1,6 +1,9 @@ | ... | @@ -1,6 +1,9 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn build(b: *std.Build) void { | 3 | pub fn build(b: *std.Build) void { |
| 4 | const test_step = b.step("test", "Test it"); | ||
| 5 | b.default_step = test_step; | ||
| 6 | |||
| 4 | const test1 = b.addTest(.{ | 7 | const test1 = b.addTest(.{ |
| 5 | .root_source_file = .{ .path = "test_root/empty.zig" }, | 8 | .root_source_file = .{ .path = "test_root/empty.zig" }, |
| 6 | }); | 9 | }); |
| ... | @@ -14,8 +17,7 @@ pub fn build(b: *std.Build) void { | ... | @@ -14,8 +17,7 @@ pub fn build(b: *std.Build) void { |
| 14 | test2.setTestRunner("src/main.zig"); | 17 | test2.setTestRunner("src/main.zig"); |
| 15 | test3.setTestRunner("src/main.zig"); | 18 | test3.setTestRunner("src/main.zig"); |
| 16 | 19 | ||
| 17 | const test_step = b.step("test", "Test package path resolution of custom test runner"); | 20 | test_step.dependOn(&test1.run().step); |
| 18 | test_step.dependOn(&test1.step); | 21 | test_step.dependOn(&test2.run().step); |
| 19 | test_step.dependOn(&test2.step); | 22 | test_step.dependOn(&test3.run().step); |
| 20 | test_step.dependOn(&test3.step); | ||
| 21 | } | 23 | } |
test/standalone/issue_339/build.zig+8-3| ... | @@ -1,13 +1,18 @@ | ... | @@ -1,13 +1,18 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn build(b: *std.Build) void { | 3 | pub fn build(b: *std.Build) void { |
| 4 | const test_step = b.step("test", "Test it"); | ||
| 5 | b.default_step = test_step; | ||
| 6 | |||
| 7 | const optimize: std.builtin.OptimizeMode = .Debug; | ||
| 8 | const target: std.zig.CrossTarget = .{}; | ||
| 9 | |||
| 4 | const obj = b.addObject(.{ | 10 | const obj = b.addObject(.{ |
| 5 | .name = "test", | 11 | .name = "test", |
| 6 | .root_source_file = .{ .path = "test.zig" }, | 12 | .root_source_file = .{ .path = "test.zig" }, |
| 7 | .target = b.standardTargetOptions(.{}), | 13 | .target = target, |
| 8 | .optimize = b.standardOptimizeOption(.{}), | 14 | .optimize = optimize, |
| 9 | }); | 15 | }); |
| 10 | 16 | ||
| 11 | const test_step = b.step("test", "Test the program"); | ||
| 12 | test_step.dependOn(&obj.step); | 17 | test_step.dependOn(&obj.step); |
| 13 | } | 18 | } |
test/standalone/issue_5825/build.zig+4-2| ... | @@ -1,12 +1,15 @@ | ... | @@ -1,12 +1,15 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn build(b: *std.Build) void { | 3 | pub fn build(b: *std.Build) void { |
| 4 | const test_step = b.step("test", "Test it"); | ||
| 5 | b.default_step = test_step; | ||
| 6 | |||
| 4 | const target = .{ | 7 | const target = .{ |
| 5 | .cpu_arch = .x86_64, | 8 | .cpu_arch = .x86_64, |
| 6 | .os_tag = .windows, | 9 | .os_tag = .windows, |
| 7 | .abi = .msvc, | 10 | .abi = .msvc, |
| 8 | }; | 11 | }; |
| 9 | const optimize = b.standardOptimizeOption(.{}); | 12 | const optimize: std.builtin.OptimizeMode = .Debug; |
| 10 | const obj = b.addObject(.{ | 13 | const obj = b.addObject(.{ |
| 11 | .name = "issue_5825", | 14 | .name = "issue_5825", |
| 12 | .root_source_file = .{ .path = "main.zig" }, | 15 | .root_source_file = .{ .path = "main.zig" }, |
| ... | @@ -24,6 +27,5 @@ pub fn build(b: *std.Build) void { | ... | @@ -24,6 +27,5 @@ pub fn build(b: *std.Build) void { |
| 24 | exe.linkSystemLibrary("ntdll"); | 27 | exe.linkSystemLibrary("ntdll"); |
| 25 | exe.addObject(obj); | 28 | exe.addObject(obj); |
| 26 | 29 | ||
| 27 | const test_step = b.step("test", "Test the program"); | ||
| 28 | test_step.dependOn(&exe.step); | 30 | test_step.dependOn(&exe.step); |
| 29 | } | 31 | } |
test/standalone/issue_7030.zig created+21| ... | @@ -0,0 +1,21 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | |||
| 3 | pub const std_options = struct { | ||
| 4 | pub const logFn = log; | ||
| 5 | }; | ||
| 6 | |||
| 7 | pub fn log( | ||
| 8 | comptime message_level: std.log.Level, | ||
| 9 | comptime scope: @Type(.EnumLiteral), | ||
| 10 | comptime format: []const u8, | ||
| 11 | args: anytype, | ||
| 12 | ) void { | ||
| 13 | _ = message_level; | ||
| 14 | _ = scope; | ||
| 15 | _ = format; | ||
| 16 | _ = args; | ||
| 17 | } | ||
| 18 | |||
| 19 | pub fn main() anyerror!void { | ||
| 20 | std.log.info("All your codebase are belong to us.", .{}); | ||
| 21 | } | ||
test/standalone/issue_7030/build.zig deleted-17| ... | @@ -1,17 +0,0 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | |||
| 3 | pub fn build(b: *std.Build) void { | ||
| 4 | const exe = b.addExecutable(.{ | ||
| 5 | .name = "issue_7030", | ||
| 6 | .root_source_file = .{ .path = "main.zig" }, | ||
| 7 | .target = .{ | ||
| 8 | .cpu_arch = .wasm32, | ||
| 9 | .os_tag = .freestanding, | ||
| 10 | }, | ||
| 11 | }); | ||
| 12 | exe.install(); | ||
| 13 | b.default_step.dependOn(&exe.step); | ||
| 14 | |||
| 15 | const test_step = b.step("test", "Test the program"); | ||
| 16 | test_step.dependOn(&exe.step); | ||
| 17 | } | ||
test/standalone/issue_7030/main.zig deleted-21| ... | @@ -1,21 +0,0 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | |||
| 3 | pub const std_options = struct { | ||
| 4 | pub const logFn = log; | ||
| 5 | }; | ||
| 6 | |||
| 7 | pub fn log( | ||
| 8 | comptime message_level: std.log.Level, | ||
| 9 | comptime scope: @Type(.EnumLiteral), | ||
| 10 | comptime format: []const u8, | ||
| 11 | args: anytype, | ||
| 12 | ) void { | ||
| 13 | _ = message_level; | ||
| 14 | _ = scope; | ||
| 15 | _ = format; | ||
| 16 | _ = args; | ||
| 17 | } | ||
| 18 | |||
| 19 | pub fn main() anyerror!void { | ||
| 20 | std.log.info("All your codebase are belong to us.", .{}); | ||
| 21 | } | ||
test/standalone/issue_794/build.zig+3-3| ... | @@ -1,13 +1,13 @@ | ... | @@ -1,13 +1,13 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn build(b: *std.Build) void { | 3 | pub fn build(b: *std.Build) void { |
| 4 | const test_step = b.step("test", "Test it"); | ||
| 5 | b.default_step = test_step; | ||
| 6 | |||
| 4 | const test_artifact = b.addTest(.{ | 7 | const test_artifact = b.addTest(.{ |
| 5 | .root_source_file = .{ .path = "main.zig" }, | 8 | .root_source_file = .{ .path = "main.zig" }, |
| 6 | }); | 9 | }); |
| 7 | test_artifact.addIncludePath("a_directory"); | 10 | test_artifact.addIncludePath("a_directory"); |
| 8 | 11 | ||
| 9 | b.default_step.dependOn(&test_artifact.step); | ||
| 10 | |||
| 11 | const test_step = b.step("test", "Test the program"); | ||
| 12 | test_step.dependOn(&test_artifact.step); | 12 | test_step.dependOn(&test_artifact.step); |
| 13 | } | 13 | } |
test/standalone/issue_8550/build.zig+5-2| ... | @@ -1,6 +1,10 @@ | ... | @@ -1,6 +1,10 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn build(b: *std.Build) !void { | 3 | pub fn build(b: *std.Build) !void { |
| 4 | const test_step = b.step("test", "Test it"); | ||
| 5 | b.default_step = test_step; | ||
| 6 | |||
| 7 | const optimize: std.builtin.OptimizeMode = .Debug; | ||
| 4 | const target = std.zig.CrossTarget{ | 8 | const target = std.zig.CrossTarget{ |
| 5 | .os_tag = .freestanding, | 9 | .os_tag = .freestanding, |
| 6 | .cpu_arch = .arm, | 10 | .cpu_arch = .arm, |
| ... | @@ -8,7 +12,7 @@ pub fn build(b: *std.Build) !void { | ... | @@ -8,7 +12,7 @@ pub fn build(b: *std.Build) !void { |
| 8 | .explicit = &std.Target.arm.cpu.arm1176jz_s, | 12 | .explicit = &std.Target.arm.cpu.arm1176jz_s, |
| 9 | }, | 13 | }, |
| 10 | }; | 14 | }; |
| 11 | const optimize = b.standardOptimizeOption(.{}); | 15 | |
| 12 | const kernel = b.addExecutable(.{ | 16 | const kernel = b.addExecutable(.{ |
| 13 | .name = "kernel", | 17 | .name = "kernel", |
| 14 | .root_source_file = .{ .path = "./main.zig" }, | 18 | .root_source_file = .{ .path = "./main.zig" }, |
| ... | @@ -19,6 +23,5 @@ pub fn build(b: *std.Build) !void { | ... | @@ -19,6 +23,5 @@ pub fn build(b: *std.Build) !void { |
| 19 | kernel.setLinkerScriptPath(.{ .path = "./linker.ld" }); | 23 | kernel.setLinkerScriptPath(.{ .path = "./linker.ld" }); |
| 20 | kernel.install(); | 24 | kernel.install(); |
| 21 | 25 | ||
| 22 | const test_step = b.step("test", "Test it"); | ||
| 23 | test_step.dependOn(&kernel.step); | 26 | test_step.dependOn(&kernel.step); |
| 24 | } | 27 | } |
test/standalone/issue_9812/build.zig+5-2| ... | @@ -1,7 +1,11 @@ | ... | @@ -1,7 +1,11 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn build(b: *std.Build) !void { | 3 | pub fn build(b: *std.Build) !void { |
| 4 | const optimize = b.standardOptimizeOption(.{}); | 4 | const test_step = b.step("test", "Test it"); |
| 5 | b.default_step = test_step; | ||
| 6 | |||
| 7 | const optimize: std.builtin.OptimizeMode = .Debug; | ||
| 8 | |||
| 5 | const zip_add = b.addTest(.{ | 9 | const zip_add = b.addTest(.{ |
| 6 | .root_source_file = .{ .path = "main.zig" }, | 10 | .root_source_file = .{ .path = "main.zig" }, |
| 7 | .optimize = optimize, | 11 | .optimize = optimize, |
| ... | @@ -13,6 +17,5 @@ pub fn build(b: *std.Build) !void { | ... | @@ -13,6 +17,5 @@ pub fn build(b: *std.Build) !void { |
| 13 | zip_add.addIncludePath("vendor/kuba-zip"); | 17 | zip_add.addIncludePath("vendor/kuba-zip"); |
| 14 | zip_add.linkLibC(); | 18 | zip_add.linkLibC(); |
| 15 | 19 | ||
| 16 | const test_step = b.step("test", "Test it"); | ||
| 17 | test_step.dependOn(&zip_add.step); | 20 | test_step.dependOn(&zip_add.step); |
| 18 | } | 21 | } |
test/standalone/load_dynamic_library/build.zig+16-4| ... | @@ -1,8 +1,19 @@ | ... | @@ -1,8 +1,19 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | ||
| 2 | 3 | ||
| 3 | pub fn build(b: *std.Build) void { | 4 | pub fn build(b: *std.Build) void { |
| 4 | const target = b.standardTargetOptions(.{}); | 5 | const test_step = b.step("test", "Test it"); |
| 5 | const optimize = b.standardOptimizeOption(.{}); | 6 | b.default_step = test_step; |
| 7 | |||
| 8 | const optimize: std.builtin.OptimizeMode = .Debug; | ||
| 9 | const target: std.zig.CrossTarget = .{}; | ||
| 10 | |||
| 11 | const ok = (builtin.os.tag != .wasi and | ||
| 12 | // https://github.com/ziglang/zig/issues/13550 | ||
| 13 | (builtin.os.tag != .macos or builtin.cpu.arch != .aarch64) and | ||
| 14 | // https://github.com/ziglang/zig/issues/13686 | ||
| 15 | (builtin.os.tag != .windows or builtin.cpu.arch != .aarch64)); | ||
| 16 | if (!ok) return; | ||
| 6 | 17 | ||
| 7 | const lib = b.addSharedLibrary(.{ | 18 | const lib = b.addSharedLibrary(.{ |
| 8 | .name = "add", | 19 | .name = "add", |
| ... | @@ -19,9 +30,10 @@ pub fn build(b: *std.Build) void { | ... | @@ -19,9 +30,10 @@ pub fn build(b: *std.Build) void { |
| 19 | .target = target, | 30 | .target = target, |
| 20 | }); | 31 | }); |
| 21 | 32 | ||
| 22 | const run = main.run(); | 33 | const run = b.addRunArtifact(main); |
| 23 | run.addArtifactArg(lib); | 34 | run.addArtifactArg(lib); |
| 35 | run.skip_foreign_checks = true; | ||
| 36 | run.expectExitCode(0); | ||
| 24 | 37 | ||
| 25 | const test_step = b.step("test", "Test the program"); | ||
| 26 | test_step.dependOn(&run.step); | 38 | test_step.dependOn(&run.step); |
| 27 | } | 39 | } |
test/standalone/load_dynamic_library/main.zig+1-5| ... | @@ -11,11 +11,7 @@ pub fn main() !void { | ... | @@ -11,11 +11,7 @@ pub fn main() !void { |
| 11 | var lib = try std.DynLib.open(dynlib_name); | 11 | var lib = try std.DynLib.open(dynlib_name); |
| 12 | defer lib.close(); | 12 | defer lib.close(); |
| 13 | 13 | ||
| 14 | const Add = switch (@import("builtin").zig_backend) { | 14 | const Add = *const fn (i32, i32) callconv(.C) i32; |
| 15 | .stage1 => fn (i32, i32) callconv(.C) i32, | ||
| 16 | else => *const fn (i32, i32) callconv(.C) i32, | ||
| 17 | }; | ||
| 18 | |||
| 19 | const addFn = lib.lookup(Add, "add") orelse return error.SymbolNotFound; | 15 | const addFn = lib.lookup(Add, "add") orelse return error.SymbolNotFound; |
| 20 | 16 | ||
| 21 | const result = addFn(12, 34); | 17 | const result = addFn(12, 34); |
test/standalone/main_pkg_path/build.zig+4-2| ... | @@ -1,11 +1,13 @@ | ... | @@ -1,11 +1,13 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn build(b: *std.Build) void { | 3 | pub fn build(b: *std.Build) void { |
| 4 | const test_step = b.step("test", "Test it"); | ||
| 5 | b.default_step = test_step; | ||
| 6 | |||
| 4 | const test_exe = b.addTest(.{ | 7 | const test_exe = b.addTest(.{ |
| 5 | .root_source_file = .{ .path = "a/test.zig" }, | 8 | .root_source_file = .{ .path = "a/test.zig" }, |
| 6 | }); | 9 | }); |
| 7 | test_exe.setMainPkgPath("."); | 10 | test_exe.setMainPkgPath("."); |
| 8 | 11 | ||
| 9 | const test_step = b.step("test", "Test the program"); | 12 | test_step.dependOn(&test_exe.run().step); |
| 10 | test_step.dependOn(&test_exe.step); | ||
| 11 | } | 13 | } |
test/standalone/mix_c_files/build.zig+13-19| ... | @@ -1,34 +1,28 @@ | ... | @@ -1,34 +1,28 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | ||
| 3 | const CrossTarget = std.zig.CrossTarget; | ||
| 4 | 2 | ||
| 5 | // TODO integrate this with the std.Build executor API | 3 | pub fn build(b: *std.Build) void { |
| 6 | fn isRunnableTarget(t: CrossTarget) bool { | 4 | const test_step = b.step("test", "Test it"); |
| 7 | if (t.isNative()) return true; | 5 | b.default_step = test_step; |
| 8 | 6 | ||
| 9 | return (t.getOsTag() == builtin.os.tag and | 7 | add(b, test_step, .Debug); |
| 10 | t.getCpuArch() == builtin.cpu.arch); | 8 | add(b, test_step, .ReleaseFast); |
| 9 | add(b, test_step, .ReleaseSmall); | ||
| 10 | add(b, test_step, .ReleaseSafe); | ||
| 11 | } | 11 | } |
| 12 | 12 | ||
| 13 | pub fn build(b: *std.Build) void { | 13 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { |
| 14 | const optimize = b.standardOptimizeOption(.{}); | ||
| 15 | const target = b.standardTargetOptions(.{}); | ||
| 16 | |||
| 17 | const exe = b.addExecutable(.{ | 14 | const exe = b.addExecutable(.{ |
| 18 | .name = "test", | 15 | .name = "test", |
| 19 | .root_source_file = .{ .path = "main.zig" }, | 16 | .root_source_file = .{ .path = "main.zig" }, |
| 20 | .optimize = optimize, | 17 | .optimize = optimize, |
| 21 | .target = target, | ||
| 22 | }); | 18 | }); |
| 23 | exe.addCSourceFile("test.c", &[_][]const u8{"-std=c11"}); | 19 | exe.addCSourceFile("test.c", &[_][]const u8{"-std=c11"}); |
| 24 | exe.linkLibC(); | 20 | exe.linkLibC(); |
| 25 | b.default_step.dependOn(&exe.step); | 21 | b.default_step.dependOn(&exe.step); |
| 26 | 22 | ||
| 27 | const test_step = b.step("test", "Test the program"); | 23 | const run_cmd = b.addRunArtifact(exe); |
| 28 | if (isRunnableTarget(target)) { | 24 | run_cmd.skip_foreign_checks = true; |
| 29 | const run_cmd = exe.run(); | 25 | run_cmd.expectExitCode(0); |
| 30 | test_step.dependOn(&run_cmd.step); | 26 | |
| 31 | } else { | 27 | test_step.dependOn(&run_cmd.step); |
| 32 | test_step.dependOn(&exe.step); | ||
| 33 | } | ||
| 34 | } | 28 | } |
test/standalone/mix_o_files/build.zig+7-3| ... | @@ -1,18 +1,23 @@ | ... | @@ -1,18 +1,23 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn build(b: *std.Build) void { | 3 | pub fn build(b: *std.Build) void { |
| 4 | const optimize = b.standardOptimizeOption(.{}); | 4 | const test_step = b.step("test", "Test it"); |
| 5 | b.default_step = test_step; | ||
| 6 | |||
| 7 | const optimize: std.builtin.OptimizeMode = .Debug; | ||
| 8 | const target: std.zig.CrossTarget = .{}; | ||
| 5 | 9 | ||
| 6 | const obj = b.addObject(.{ | 10 | const obj = b.addObject(.{ |
| 7 | .name = "base64", | 11 | .name = "base64", |
| 8 | .root_source_file = .{ .path = "base64.zig" }, | 12 | .root_source_file = .{ .path = "base64.zig" }, |
| 9 | .optimize = optimize, | 13 | .optimize = optimize, |
| 10 | .target = .{}, | 14 | .target = target, |
| 11 | }); | 15 | }); |
| 12 | 16 | ||
| 13 | const exe = b.addExecutable(.{ | 17 | const exe = b.addExecutable(.{ |
| 14 | .name = "test", | 18 | .name = "test", |
| 15 | .optimize = optimize, | 19 | .optimize = optimize, |
| 20 | .target = target, | ||
| 16 | }); | 21 | }); |
| 17 | exe.addCSourceFile("test.c", &[_][]const u8{"-std=c99"}); | 22 | exe.addCSourceFile("test.c", &[_][]const u8{"-std=c99"}); |
| 18 | exe.addObject(obj); | 23 | exe.addObject(obj); |
| ... | @@ -22,6 +27,5 @@ pub fn build(b: *std.Build) void { | ... | @@ -22,6 +27,5 @@ pub fn build(b: *std.Build) void { |
| 22 | 27 | ||
| 23 | const run_cmd = exe.run(); | 28 | const run_cmd = exe.run(); |
| 24 | 29 | ||
| 25 | const test_step = b.step("test", "Test the program"); | ||
| 26 | test_step.dependOn(&run_cmd.step); | 30 | test_step.dependOn(&run_cmd.step); |
| 27 | } | 31 | } |
test/standalone/options/build.zig+1-1| ... | @@ -20,5 +20,5 @@ pub fn build(b: *std.Build) void { | ... | @@ -20,5 +20,5 @@ pub fn build(b: *std.Build) void { |
| 20 | options.addOption([]const u8, "string", b.option([]const u8, "string", "s").?); | 20 | options.addOption([]const u8, "string", b.option([]const u8, "string", "s").?); |
| 21 | 21 | ||
| 22 | const test_step = b.step("test", "Run unit tests"); | 22 | const test_step = b.step("test", "Run unit tests"); |
| 23 | test_step.dependOn(&main.step); | 23 | test_step.dependOn(&main.run().step); |
| 24 | } | 24 | } |
test/standalone/pie/build.zig+14-4| ... | @@ -1,14 +1,24 @@ | ... | @@ -1,14 +1,24 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn build(b: *std.Build) void { | 3 | pub fn build(b: *std.Build) void { |
| 4 | const test_step = b.step("test", "Test it"); | ||
| 5 | b.default_step = test_step; | ||
| 6 | |||
| 7 | const optimize: std.builtin.OptimizeMode = .Debug; | ||
| 8 | const target: std.zig.CrossTarget = .{ | ||
| 9 | .os_tag = .linux, | ||
| 10 | .cpu_arch = .x86_64, | ||
| 11 | }; | ||
| 12 | |||
| 4 | const main = b.addTest(.{ | 13 | const main = b.addTest(.{ |
| 5 | .root_source_file = .{ .path = "main.zig" }, | 14 | .root_source_file = .{ .path = "main.zig" }, |
| 6 | .optimize = b.standardOptimizeOption(.{}), | 15 | .optimize = optimize, |
| 16 | .target = target, | ||
| 7 | }); | 17 | }); |
| 8 | main.pie = true; | 18 | main.pie = true; |
| 9 | 19 | ||
| 10 | const test_step = b.step("test", "Test the program"); | 20 | const run = main.run(); |
| 11 | test_step.dependOn(&main.step); | 21 | run.skip_foreign_checks = true; |
| 12 | 22 | ||
| 13 | b.default_step.dependOn(test_step); | 23 | test_step.dependOn(&run.step); |
| 14 | } | 24 | } |
test/standalone/pkg_import/build.zig+4-2| ... | @@ -1,7 +1,10 @@ | ... | @@ -1,7 +1,10 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn build(b: *std.Build) void { | 3 | pub fn build(b: *std.Build) void { |
| 4 | const optimize = b.standardOptimizeOption(.{}); | 4 | const test_step = b.step("test", "Test it"); |
| 5 | b.default_step = test_step; | ||
| 6 | |||
| 7 | const optimize: std.builtin.OptimizeMode = .Debug; | ||
| 5 | 8 | ||
| 6 | const exe = b.addExecutable(.{ | 9 | const exe = b.addExecutable(.{ |
| 7 | .name = "test", | 10 | .name = "test", |
| ... | @@ -12,6 +15,5 @@ pub fn build(b: *std.Build) void { | ... | @@ -12,6 +15,5 @@ pub fn build(b: *std.Build) void { |
| 12 | 15 | ||
| 13 | const run = exe.run(); | 16 | const run = exe.run(); |
| 14 | 17 | ||
| 15 | const test_step = b.step("test", "Test it"); | ||
| 16 | test_step.dependOn(&run.step); | 18 | test_step.dependOn(&run.step); |
| 17 | } | 19 | } |
test/standalone/shared_library/build.zig+5-5| ... | @@ -1,8 +1,11 @@ | ... | @@ -1,8 +1,11 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn build(b: *std.Build) void { | 3 | pub fn build(b: *std.Build) void { |
| 4 | const optimize = b.standardOptimizeOption(.{}); | 4 | const test_step = b.step("test", "Test it"); |
| 5 | const target = b.standardTargetOptions(.{}); | 5 | b.default_step = test_step; |
| 6 | |||
| 7 | const optimize: std.builtin.OptimizeMode = .Debug; | ||
| 8 | const target: std.zig.CrossTarget = .{}; | ||
| 6 | const lib = b.addSharedLibrary(.{ | 9 | const lib = b.addSharedLibrary(.{ |
| 7 | .name = "mathtest", | 10 | .name = "mathtest", |
| 8 | .root_source_file = .{ .path = "mathtest.zig" }, | 11 | .root_source_file = .{ .path = "mathtest.zig" }, |
| ... | @@ -20,10 +23,7 @@ pub fn build(b: *std.Build) void { | ... | @@ -20,10 +23,7 @@ pub fn build(b: *std.Build) void { |
| 20 | exe.linkLibrary(lib); | 23 | exe.linkLibrary(lib); |
| 21 | exe.linkSystemLibrary("c"); | 24 | exe.linkSystemLibrary("c"); |
| 22 | 25 | ||
| 23 | b.default_step.dependOn(&exe.step); | ||
| 24 | |||
| 25 | const run_cmd = exe.run(); | 26 | const run_cmd = exe.run(); |
| 26 | 27 | ||
| 27 | const test_step = b.step("test", "Test the program"); | ||
| 28 | test_step.dependOn(&run_cmd.step); | 28 | test_step.dependOn(&run_cmd.step); |
| 29 | } | 29 | } |
test/standalone/sigpipe/build.zig+14-5| ... | @@ -2,7 +2,16 @@ const std = @import("std"); | ... | @@ -2,7 +2,16 @@ const std = @import("std"); |
| 2 | const os = std.os; | 2 | const os = std.os; |
| 3 | 3 | ||
| 4 | pub fn build(b: *std.build.Builder) !void { | 4 | pub fn build(b: *std.build.Builder) !void { |
| 5 | const test_step = b.step("test", "Run the tests"); | 5 | const test_step = b.step("test", "Test it"); |
| 6 | b.default_step = test_step; | ||
| 7 | |||
| 8 | // TODO signal handling code has no business being in a build script. | ||
| 9 | // this logic needs to move to a file called parent.zig which is | ||
| 10 | // added as an executable. | ||
| 11 | |||
| 12 | //if (!std.os.have_sigpipe_support) { | ||
| 13 | // return; | ||
| 14 | //} | ||
| 6 | 15 | ||
| 7 | // This test runs "breakpipe" as a child process and that process | 16 | // This test runs "breakpipe" as a child process and that process |
| 8 | // depends on inheriting a SIGPIPE disposition of "default". | 17 | // depends on inheriting a SIGPIPE disposition of "default". |
| ... | @@ -23,12 +32,12 @@ pub fn build(b: *std.build.Builder) !void { | ... | @@ -23,12 +32,12 @@ pub fn build(b: *std.build.Builder) !void { |
| 23 | .root_source_file = .{ .path = "breakpipe.zig" }, | 32 | .root_source_file = .{ .path = "breakpipe.zig" }, |
| 24 | }); | 33 | }); |
| 25 | exe.addOptions("build_options", options); | 34 | exe.addOptions("build_options", options); |
| 26 | const run = exe.run(); | 35 | const run = b.addRunArtifact(exe); |
| 27 | if (keep_sigpipe) { | 36 | if (keep_sigpipe) { |
| 28 | run.expected_term = .{ .Signal = std.os.SIG.PIPE }; | 37 | run.addCheck(.{ .expect_term = .{ .Signal = std.os.SIG.PIPE } }); |
| 29 | } else { | 38 | } else { |
| 30 | run.stdout_action = .{ .expect_exact = "BrokenPipe\n" }; | 39 | run.addCheck(.{ .expect_stdout_exact = "BrokenPipe\n" }); |
| 31 | run.expected_term = .{ .Exited = 123 }; | 40 | run.addCheck(.{ .expect_term = .{ .Exited = 123 } }); |
| 32 | } | 41 | } |
| 33 | test_step.dependOn(&run.step); | 42 | test_step.dependOn(&run.step); |
| 34 | } | 43 | } |
test/standalone/static_c_lib/build.zig+5-3| ... | @@ -1,7 +1,10 @@ | ... | @@ -1,7 +1,10 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn build(b: *std.Build) void { | 3 | pub fn build(b: *std.Build) void { |
| 4 | const optimize = b.standardOptimizeOption(.{}); | 4 | const test_step = b.step("test", "Test it"); |
| 5 | b.default_step = test_step; | ||
| 6 | |||
| 7 | const optimize: std.builtin.OptimizeMode = .Debug; | ||
| 5 | 8 | ||
| 6 | const foo = b.addStaticLibrary(.{ | 9 | const foo = b.addStaticLibrary(.{ |
| 7 | .name = "foo", | 10 | .name = "foo", |
| ... | @@ -18,6 +21,5 @@ pub fn build(b: *std.Build) void { | ... | @@ -18,6 +21,5 @@ pub fn build(b: *std.Build) void { |
| 18 | test_exe.linkLibrary(foo); | 21 | test_exe.linkLibrary(foo); |
| 19 | test_exe.addIncludePath("."); | 22 | test_exe.addIncludePath("."); |
| 20 | 23 | ||
| 21 | const test_step = b.step("test", "Test it"); | 24 | test_step.dependOn(&test_exe.run().step); |
| 22 | test_step.dependOn(&test_exe.step); | ||
| 23 | } | 25 | } |
test/standalone/test_runner_module_imports/build.zig+1-1| ... | @@ -15,5 +15,5 @@ pub fn build(b: *std.Build) void { | ... | @@ -15,5 +15,5 @@ pub fn build(b: *std.Build) void { |
| 15 | t.addModule("module2", module2); | 15 | t.addModule("module2", module2); |
| 16 | 16 | ||
| 17 | const test_step = b.step("test", "Run unit tests"); | 17 | const test_step = b.step("test", "Run unit tests"); |
| 18 | test_step.dependOn(&t.step); | 18 | test_step.dependOn(&t.run().step); |
| 19 | } | 19 | } |
test/standalone/test_runner_path/build.zig+5-2| ... | @@ -1,14 +1,17 @@ | ... | @@ -1,14 +1,17 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_stage2 = true; | ||
| 4 | |||
| 3 | pub fn build(b: *std.Build) void { | 5 | pub fn build(b: *std.Build) void { |
| 6 | const test_step = b.step("test", "Test the program"); | ||
| 7 | b.default_step = test_step; | ||
| 8 | |||
| 4 | const test_exe = b.addTest(.{ | 9 | const test_exe = b.addTest(.{ |
| 5 | .root_source_file = .{ .path = "test.zig" }, | 10 | .root_source_file = .{ .path = "test.zig" }, |
| 6 | .kind = .test_exe, | ||
| 7 | }); | 11 | }); |
| 8 | test_exe.test_runner = "test_runner.zig"; | 12 | test_exe.test_runner = "test_runner.zig"; |
| 9 | 13 | ||
| 10 | const test_run = test_exe.run(); | 14 | const test_run = test_exe.run(); |
| 11 | 15 | ||
| 12 | const test_step = b.step("test", "Test the program"); | ||
| 13 | test_step.dependOn(&test_run.step); | 16 | test_step.dependOn(&test_run.step); |
| 14 | } | 17 | } |
test/standalone/test_runner_path/test_runner.zig+4-36| ... | @@ -1,51 +1,19 @@ | ... | @@ -1,51 +1,19 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const io = std.io; | ||
| 3 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 4 | 3 | ||
| 5 | pub const io_mode: io.Mode = builtin.test_io_mode; | ||
| 6 | |||
| 7 | pub fn main() void { | 4 | pub fn main() void { |
| 8 | const test_fn_list = builtin.test_functions; | ||
| 9 | var ok_count: usize = 0; | 5 | var ok_count: usize = 0; |
| 10 | var skip_count: usize = 0; | 6 | var skip_count: usize = 0; |
| 11 | var fail_count: usize = 0; | 7 | var fail_count: usize = 0; |
| 12 | 8 | ||
| 13 | var async_frame_buffer: []align(std.Target.stack_align) u8 = undefined; | 9 | for (builtin.test_functions) |test_fn| { |
| 14 | // TODO this is on the next line (using `undefined` above) because otherwise zig incorrectly | 10 | if (test_fn.func()) |_| { |
| 15 | // ignores the alignment of the slice. | ||
| 16 | async_frame_buffer = &[_]u8{}; | ||
| 17 | |||
| 18 | for (test_fn_list) |test_fn| { | ||
| 19 | const result = if (test_fn.async_frame_size) |size| switch (io_mode) { | ||
| 20 | .evented => blk: { | ||
| 21 | if (async_frame_buffer.len < size) { | ||
| 22 | std.heap.page_allocator.free(async_frame_buffer); | ||
| 23 | async_frame_buffer = std.heap.page_allocator.alignedAlloc(u8, std.Target.stack_align, size) catch @panic("out of memory"); | ||
| 24 | } | ||
| 25 | const casted_fn = @ptrCast(fn () callconv(.Async) anyerror!void, test_fn.func); | ||
| 26 | break :blk await @asyncCall(async_frame_buffer, {}, casted_fn, .{}); | ||
| 27 | }, | ||
| 28 | .blocking => { | ||
| 29 | skip_count += 1; | ||
| 30 | continue; | ||
| 31 | }, | ||
| 32 | } else test_fn.func(); | ||
| 33 | if (result) |_| { | ||
| 34 | ok_count += 1; | 11 | ok_count += 1; |
| 35 | } else |err| switch (err) { | 12 | } else |err| switch (err) { |
| 36 | error.SkipZigTest => { | 13 | error.SkipZigTest => skip_count += 1, |
| 37 | skip_count += 1; | 14 | else => fail_count += 1, |
| 38 | }, | ||
| 39 | else => { | ||
| 40 | fail_count += 1; | ||
| 41 | }, | ||
| 42 | } | 15 | } |
| 43 | } | 16 | } |
| 44 | if (ok_count == test_fn_list.len) { | ||
| 45 | std.debug.print("All {d} tests passed.\n", .{ok_count}); | ||
| 46 | } else { | ||
| 47 | std.debug.print("{d} passed; {d} skipped; {d} failed.\n", .{ ok_count, skip_count, fail_count }); | ||
| 48 | } | ||
| 49 | if (ok_count != 1 or skip_count != 1 or fail_count != 1) { | 17 | if (ok_count != 1 or skip_count != 1 or fail_count != 1) { |
| 50 | std.process.exit(1); | 18 | std.process.exit(1); |
| 51 | } | 19 | } |
test/standalone/use_alias/build.zig+7-3| ... | @@ -1,12 +1,16 @@ | ... | @@ -1,12 +1,16 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn build(b: *std.Build) void { | 3 | pub fn build(b: *std.Build) void { |
| 4 | const test_step = b.step("test", "Test it"); | ||
| 5 | b.default_step = test_step; | ||
| 6 | |||
| 7 | const optimize: std.builtin.OptimizeMode = .Debug; | ||
| 8 | |||
| 4 | const main = b.addTest(.{ | 9 | const main = b.addTest(.{ |
| 5 | .root_source_file = .{ .path = "main.zig" }, | 10 | .root_source_file = .{ .path = "main.zig" }, |
| 6 | .optimize = b.standardOptimizeOption(.{}), | 11 | .optimize = optimize, |
| 7 | }); | 12 | }); |
| 8 | main.addIncludePath("."); | 13 | main.addIncludePath("."); |
| 9 | 14 | ||
| 10 | const test_step = b.step("test", "Test it"); | 15 | test_step.dependOn(&main.run().step); |
| 11 | test_step.dependOn(&main.step); | ||
| 12 | } | 16 | } |
test/standalone/windows_spawn/build.zig+13-3| ... | @@ -1,23 +1,33 @@ | ... | @@ -1,23 +1,33 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | ||
| 2 | 3 | ||
| 3 | pub fn build(b: *std.Build) void { | 4 | pub fn build(b: *std.Build) void { |
| 4 | const optimize = b.standardOptimizeOption(.{}); | 5 | const test_step = b.step("test", "Test it"); |
| 6 | b.default_step = test_step; | ||
| 7 | |||
| 8 | const optimize: std.builtin.OptimizeMode = .Debug; | ||
| 9 | const target: std.zig.CrossTarget = .{}; | ||
| 10 | |||
| 11 | if (builtin.os.tag != .windows) return; | ||
| 5 | 12 | ||
| 6 | const hello = b.addExecutable(.{ | 13 | const hello = b.addExecutable(.{ |
| 7 | .name = "hello", | 14 | .name = "hello", |
| 8 | .root_source_file = .{ .path = "hello.zig" }, | 15 | .root_source_file = .{ .path = "hello.zig" }, |
| 9 | .optimize = optimize, | 16 | .optimize = optimize, |
| 17 | .target = target, | ||
| 10 | }); | 18 | }); |
| 11 | 19 | ||
| 12 | const main = b.addExecutable(.{ | 20 | const main = b.addExecutable(.{ |
| 13 | .name = "main", | 21 | .name = "main", |
| 14 | .root_source_file = .{ .path = "main.zig" }, | 22 | .root_source_file = .{ .path = "main.zig" }, |
| 15 | .optimize = optimize, | 23 | .optimize = optimize, |
| 24 | .target = target, | ||
| 16 | }); | 25 | }); |
| 17 | 26 | ||
| 18 | const run = main.run(); | 27 | const run = b.addRunArtifact(main); |
| 19 | run.addArtifactArg(hello); | 28 | run.addArtifactArg(hello); |
| 29 | run.expectExitCode(0); | ||
| 30 | run.skip_foreign_checks = true; | ||
| 20 | 31 | ||
| 21 | const test_step = b.step("test", "Test it"); | ||
| 22 | test_step.dependOn(&run.step); | 32 | test_step.dependOn(&run.step); |
| 23 | } | 33 | } |
test/tests.zig+449-745| ... | @@ -1,16 +1,9 @@ | ... | @@ -1,16 +1,9 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const debug = std.debug; | 3 | const assert = std.debug.assert; |
| 4 | const CrossTarget = std.zig.CrossTarget; | 4 | const CrossTarget = std.zig.CrossTarget; |
| 5 | const io = std.io; | ||
| 6 | const fs = std.fs; | ||
| 7 | const mem = std.mem; | 5 | const mem = std.mem; |
| 8 | const fmt = std.fmt; | ||
| 9 | const ArrayList = std.ArrayList; | ||
| 10 | const OptimizeMode = std.builtin.OptimizeMode; | 6 | const OptimizeMode = std.builtin.OptimizeMode; |
| 11 | const CompileStep = std.Build.CompileStep; | ||
| 12 | const Allocator = mem.Allocator; | ||
| 13 | const ExecError = std.Build.ExecError; | ||
| 14 | const Step = std.Build.Step; | 7 | const Step = std.Build.Step; |
| 15 | 8 | ||
| 16 | // Cases | 9 | // Cases |
| ... | @@ -20,13 +13,13 @@ const stack_traces = @import("stack_traces.zig"); | ... | @@ -20,13 +13,13 @@ const stack_traces = @import("stack_traces.zig"); |
| 20 | const assemble_and_link = @import("assemble_and_link.zig"); | 13 | const assemble_and_link = @import("assemble_and_link.zig"); |
| 21 | const translate_c = @import("translate_c.zig"); | 14 | const translate_c = @import("translate_c.zig"); |
| 22 | const run_translated_c = @import("run_translated_c.zig"); | 15 | const run_translated_c = @import("run_translated_c.zig"); |
| 23 | const gen_h = @import("gen_h.zig"); | ||
| 24 | const link = @import("link.zig"); | 16 | const link = @import("link.zig"); |
| 25 | 17 | ||
| 26 | // Implementations | 18 | // Implementations |
| 27 | pub const TranslateCContext = @import("src/translate_c.zig").TranslateCContext; | 19 | pub const TranslateCContext = @import("src/translate_c.zig").TranslateCContext; |
| 28 | pub const RunTranslatedCContext = @import("src/run_translated_c.zig").RunTranslatedCContext; | 20 | pub const RunTranslatedCContext = @import("src/run_translated_c.zig").RunTranslatedCContext; |
| 29 | pub const CompareOutputContext = @import("src/compare_output.zig").CompareOutputContext; | 21 | pub const CompareOutputContext = @import("src/CompareOutput.zig"); |
| 22 | pub const StackTracesContext = @import("src/StackTrace.zig"); | ||
| 30 | 23 | ||
| 31 | const TestTarget = struct { | 24 | const TestTarget = struct { |
| 32 | target: CrossTarget = @as(CrossTarget, .{}), | 25 | target: CrossTarget = @as(CrossTarget, .{}), |
| ... | @@ -460,10 +453,71 @@ const test_targets = blk: { | ... | @@ -460,10 +453,71 @@ const test_targets = blk: { |
| 460 | }; | 453 | }; |
| 461 | }; | 454 | }; |
| 462 | 455 | ||
| 463 | const max_stdout_size = 1 * 1024 * 1024; // 1 MB | 456 | const c_abi_targets = [_]CrossTarget{ |
| 457 | .{}, | ||
| 458 | .{ | ||
| 459 | .cpu_arch = .x86_64, | ||
| 460 | .os_tag = .linux, | ||
| 461 | .abi = .musl, | ||
| 462 | }, | ||
| 463 | .{ | ||
| 464 | .cpu_arch = .x86, | ||
| 465 | .os_tag = .linux, | ||
| 466 | .abi = .musl, | ||
| 467 | }, | ||
| 468 | .{ | ||
| 469 | .cpu_arch = .aarch64, | ||
| 470 | .os_tag = .linux, | ||
| 471 | .abi = .musl, | ||
| 472 | }, | ||
| 473 | .{ | ||
| 474 | .cpu_arch = .arm, | ||
| 475 | .os_tag = .linux, | ||
| 476 | .abi = .musleabihf, | ||
| 477 | }, | ||
| 478 | .{ | ||
| 479 | .cpu_arch = .mips, | ||
| 480 | .os_tag = .linux, | ||
| 481 | .abi = .musl, | ||
| 482 | }, | ||
| 483 | .{ | ||
| 484 | .cpu_arch = .riscv64, | ||
| 485 | .os_tag = .linux, | ||
| 486 | .abi = .musl, | ||
| 487 | }, | ||
| 488 | .{ | ||
| 489 | .cpu_arch = .wasm32, | ||
| 490 | .os_tag = .wasi, | ||
| 491 | .abi = .musl, | ||
| 492 | }, | ||
| 493 | .{ | ||
| 494 | .cpu_arch = .powerpc, | ||
| 495 | .os_tag = .linux, | ||
| 496 | .abi = .musl, | ||
| 497 | }, | ||
| 498 | .{ | ||
| 499 | .cpu_arch = .powerpc64le, | ||
| 500 | .os_tag = .linux, | ||
| 501 | .abi = .musl, | ||
| 502 | }, | ||
| 503 | .{ | ||
| 504 | .cpu_arch = .x86, | ||
| 505 | .os_tag = .windows, | ||
| 506 | .abi = .gnu, | ||
| 507 | }, | ||
| 508 | .{ | ||
| 509 | .cpu_arch = .x86_64, | ||
| 510 | .os_tag = .windows, | ||
| 511 | .abi = .gnu, | ||
| 512 | }, | ||
| 513 | }; | ||
| 464 | 514 | ||
| 465 | pub fn addCompareOutputTests(b: *std.Build, test_filter: ?[]const u8, optimize_modes: []const OptimizeMode) *Step { | 515 | pub fn addCompareOutputTests( |
| 466 | const cases = b.allocator.create(CompareOutputContext) catch unreachable; | 516 | b: *std.Build, |
| 517 | test_filter: ?[]const u8, | ||
| 518 | optimize_modes: []const OptimizeMode, | ||
| 519 | ) *Step { | ||
| 520 | const cases = b.allocator.create(CompareOutputContext) catch @panic("OOM"); | ||
| 467 | cases.* = CompareOutputContext{ | 521 | cases.* = CompareOutputContext{ |
| 468 | .b = b, | 522 | .b = b, |
| 469 | .step = b.step("test-compare-output", "Run the compare output tests"), | 523 | .step = b.step("test-compare-output", "Run the compare output tests"), |
| ... | @@ -477,14 +531,26 @@ pub fn addCompareOutputTests(b: *std.Build, test_filter: ?[]const u8, optimize_m | ... | @@ -477,14 +531,26 @@ pub fn addCompareOutputTests(b: *std.Build, test_filter: ?[]const u8, optimize_m |
| 477 | return cases.step; | 531 | return cases.step; |
| 478 | } | 532 | } |
| 479 | 533 | ||
| 480 | pub fn addStackTraceTests(b: *std.Build, test_filter: ?[]const u8, optimize_modes: []const OptimizeMode) *Step { | 534 | pub fn addStackTraceTests( |
| 481 | const cases = b.allocator.create(StackTracesContext) catch unreachable; | 535 | b: *std.Build, |
| 482 | cases.* = StackTracesContext{ | 536 | test_filter: ?[]const u8, |
| 537 | optimize_modes: []const OptimizeMode, | ||
| 538 | ) *Step { | ||
| 539 | const check_exe = b.addExecutable(.{ | ||
| 540 | .name = "check-stack-trace", | ||
| 541 | .root_source_file = .{ .path = "test/src/check-stack-trace.zig" }, | ||
| 542 | .target = .{}, | ||
| 543 | .optimize = .Debug, | ||
| 544 | }); | ||
| 545 | |||
| 546 | const cases = b.allocator.create(StackTracesContext) catch @panic("OOM"); | ||
| 547 | cases.* = .{ | ||
| 483 | .b = b, | 548 | .b = b, |
| 484 | .step = b.step("test-stack-traces", "Run the stack trace tests"), | 549 | .step = b.step("test-stack-traces", "Run the stack trace tests"), |
| 485 | .test_index = 0, | 550 | .test_index = 0, |
| 486 | .test_filter = test_filter, | 551 | .test_filter = test_filter, |
| 487 | .optimize_modes = optimize_modes, | 552 | .optimize_modes = optimize_modes, |
| 553 | .check_exe = check_exe, | ||
| 488 | }; | 554 | }; |
| 489 | 555 | ||
| 490 | stack_traces.addCases(cases); | 556 | stack_traces.addCases(cases); |
| ... | @@ -494,91 +560,302 @@ pub fn addStackTraceTests(b: *std.Build, test_filter: ?[]const u8, optimize_mode | ... | @@ -494,91 +560,302 @@ pub fn addStackTraceTests(b: *std.Build, test_filter: ?[]const u8, optimize_mode |
| 494 | 560 | ||
| 495 | pub fn addStandaloneTests( | 561 | pub fn addStandaloneTests( |
| 496 | b: *std.Build, | 562 | b: *std.Build, |
| 497 | test_filter: ?[]const u8, | ||
| 498 | optimize_modes: []const OptimizeMode, | 563 | optimize_modes: []const OptimizeMode, |
| 499 | skip_non_native: bool, | ||
| 500 | enable_macos_sdk: bool, | 564 | enable_macos_sdk: bool, |
| 501 | target: std.zig.CrossTarget, | ||
| 502 | omit_stage2: bool, | 565 | omit_stage2: bool, |
| 503 | enable_darling: bool, | ||
| 504 | enable_qemu: bool, | ||
| 505 | enable_rosetta: bool, | ||
| 506 | enable_wasmtime: bool, | ||
| 507 | enable_wine: bool, | ||
| 508 | enable_symlinks_windows: bool, | 566 | enable_symlinks_windows: bool, |
| 509 | ) *Step { | 567 | ) *Step { |
| 510 | const cases = b.allocator.create(StandaloneContext) catch unreachable; | 568 | const step = b.step("test-standalone", "Run the standalone tests"); |
| 511 | cases.* = StandaloneContext{ | 569 | const omit_symlinks = builtin.os.tag == .windows and !enable_symlinks_windows; |
| 512 | .b = b, | 570 | |
| 513 | .step = b.step("test-standalone", "Run the standalone tests"), | 571 | for (standalone.simple_cases) |case| { |
| 514 | .test_index = 0, | 572 | for (optimize_modes) |optimize| { |
| 515 | .test_filter = test_filter, | 573 | if (!case.all_modes and optimize != .Debug) continue; |
| 516 | .optimize_modes = optimize_modes, | 574 | if (case.os_filter) |os_tag| { |
| 517 | .skip_non_native = skip_non_native, | 575 | if (os_tag != builtin.os.tag) continue; |
| 518 | .enable_macos_sdk = enable_macos_sdk, | 576 | } |
| 519 | .target = target, | ||
| 520 | .omit_stage2 = omit_stage2, | ||
| 521 | .enable_darling = enable_darling, | ||
| 522 | .enable_qemu = enable_qemu, | ||
| 523 | .enable_rosetta = enable_rosetta, | ||
| 524 | .enable_wasmtime = enable_wasmtime, | ||
| 525 | .enable_wine = enable_wine, | ||
| 526 | .enable_symlinks_windows = enable_symlinks_windows, | ||
| 527 | }; | ||
| 528 | 577 | ||
| 529 | standalone.addCases(cases); | 578 | if (case.is_exe) { |
| 579 | const exe = b.addExecutable(.{ | ||
| 580 | .name = std.fs.path.stem(case.src_path), | ||
| 581 | .root_source_file = .{ .path = case.src_path }, | ||
| 582 | .optimize = optimize, | ||
| 583 | .target = case.target, | ||
| 584 | }); | ||
| 585 | if (case.link_libc) exe.linkLibC(); | ||
| 530 | 586 | ||
| 531 | return cases.step; | 587 | step.dependOn(&exe.step); |
| 588 | } | ||
| 589 | |||
| 590 | if (case.is_test) { | ||
| 591 | const exe = b.addTest(.{ | ||
| 592 | .name = std.fs.path.stem(case.src_path), | ||
| 593 | .root_source_file = .{ .path = case.src_path }, | ||
| 594 | .optimize = optimize, | ||
| 595 | .target = case.target, | ||
| 596 | }); | ||
| 597 | if (case.link_libc) exe.linkLibC(); | ||
| 598 | |||
| 599 | step.dependOn(&exe.run().step); | ||
| 600 | } | ||
| 601 | } | ||
| 602 | } | ||
| 603 | |||
| 604 | inline for (standalone.build_cases) |case| { | ||
| 605 | const requires_stage2 = @hasDecl(case.import, "requires_stage2") and | ||
| 606 | case.import.requires_stage2; | ||
| 607 | const requires_symlinks = @hasDecl(case.import, "requires_symlinks") and | ||
| 608 | case.import.requires_symlinks; | ||
| 609 | const requires_macos_sdk = @hasDecl(case.import, "requires_macos_sdk") and | ||
| 610 | case.import.requires_macos_sdk; | ||
| 611 | const bad = | ||
| 612 | (requires_stage2 and omit_stage2) or | ||
| 613 | (requires_symlinks and omit_symlinks) or | ||
| 614 | (requires_macos_sdk and !enable_macos_sdk); | ||
| 615 | if (!bad) { | ||
| 616 | const dep = b.anonymousDependency(case.build_root, case.import, .{}); | ||
| 617 | const dep_step = dep.builder.default_step; | ||
| 618 | assert(mem.startsWith(u8, dep.builder.dep_prefix, "test.")); | ||
| 619 | const dep_prefix_adjusted = dep.builder.dep_prefix["test.".len..]; | ||
| 620 | dep_step.name = b.fmt("{s}{s}", .{ dep_prefix_adjusted, dep_step.name }); | ||
| 621 | step.dependOn(dep_step); | ||
| 622 | } | ||
| 623 | } | ||
| 624 | |||
| 625 | return step; | ||
| 532 | } | 626 | } |
| 533 | 627 | ||
| 534 | pub fn addLinkTests( | 628 | pub fn addLinkTests( |
| 535 | b: *std.Build, | 629 | b: *std.Build, |
| 536 | test_filter: ?[]const u8, | ||
| 537 | optimize_modes: []const OptimizeMode, | ||
| 538 | enable_macos_sdk: bool, | 630 | enable_macos_sdk: bool, |
| 539 | omit_stage2: bool, | 631 | omit_stage2: bool, |
| 540 | enable_symlinks_windows: bool, | 632 | enable_symlinks_windows: bool, |
| 541 | ) *Step { | 633 | ) *Step { |
| 542 | const cases = b.allocator.create(StandaloneContext) catch unreachable; | 634 | const step = b.step("test-link", "Run the linker tests"); |
| 543 | cases.* = StandaloneContext{ | 635 | const omit_symlinks = builtin.os.tag == .windows and !enable_symlinks_windows; |
| 544 | .b = b, | 636 | |
| 545 | .step = b.step("test-link", "Run the linker tests"), | 637 | inline for (link.cases) |case| { |
| 546 | .test_index = 0, | 638 | const requires_stage2 = @hasDecl(case.import, "requires_stage2") and |
| 547 | .test_filter = test_filter, | 639 | case.import.requires_stage2; |
| 548 | .optimize_modes = optimize_modes, | 640 | const requires_symlinks = @hasDecl(case.import, "requires_symlinks") and |
| 549 | .skip_non_native = true, | 641 | case.import.requires_symlinks; |
| 550 | .enable_macos_sdk = enable_macos_sdk, | 642 | const requires_macos_sdk = @hasDecl(case.import, "requires_macos_sdk") and |
| 551 | .target = .{}, | 643 | case.import.requires_macos_sdk; |
| 552 | .omit_stage2 = omit_stage2, | 644 | const bad = |
| 553 | .enable_symlinks_windows = enable_symlinks_windows, | 645 | (requires_stage2 and omit_stage2) or |
| 554 | }; | 646 | (requires_symlinks and omit_symlinks) or |
| 555 | link.addCases(cases); | 647 | (requires_macos_sdk and !enable_macos_sdk); |
| 556 | return cases.step; | 648 | if (!bad) { |
| 649 | const dep = b.anonymousDependency(case.build_root, case.import, .{}); | ||
| 650 | const dep_step = dep.builder.default_step; | ||
| 651 | assert(mem.startsWith(u8, dep.builder.dep_prefix, "test.")); | ||
| 652 | const dep_prefix_adjusted = dep.builder.dep_prefix["test.".len..]; | ||
| 653 | dep_step.name = b.fmt("{s}{s}", .{ dep_prefix_adjusted, dep_step.name }); | ||
| 654 | step.dependOn(dep_step); | ||
| 655 | } | ||
| 656 | } | ||
| 657 | |||
| 658 | return step; | ||
| 557 | } | 659 | } |
| 558 | 660 | ||
| 559 | pub fn addCliTests(b: *std.Build, test_filter: ?[]const u8, optimize_modes: []const OptimizeMode) *Step { | 661 | pub fn addCliTests(b: *std.Build) *Step { |
| 560 | _ = test_filter; | ||
| 561 | _ = optimize_modes; | ||
| 562 | const step = b.step("test-cli", "Test the command line interface"); | 662 | const step = b.step("test-cli", "Test the command line interface"); |
| 663 | const s = std.fs.path.sep_str; | ||
| 664 | |||
| 665 | { | ||
| 666 | |||
| 667 | // Test `zig init-lib`. | ||
| 668 | const tmp_path = b.makeTempPath(); | ||
| 669 | const init_lib = b.addSystemCommand(&.{ b.zig_exe, "init-lib" }); | ||
| 670 | init_lib.cwd = tmp_path; | ||
| 671 | init_lib.setName("zig init-lib"); | ||
| 672 | init_lib.expectStdOutEqual(""); | ||
| 673 | init_lib.expectStdErrEqual("info: Created build.zig\n" ++ | ||
| 674 | "info: Created src" ++ s ++ "main.zig\n" ++ | ||
| 675 | "info: Next, try `zig build --help` or `zig build test`\n"); | ||
| 676 | |||
| 677 | const run_test = b.addSystemCommand(&.{ b.zig_exe, "build", "test" }); | ||
| 678 | run_test.cwd = tmp_path; | ||
| 679 | run_test.setName("zig build test"); | ||
| 680 | run_test.expectStdOutEqual(""); | ||
| 681 | run_test.step.dependOn(&init_lib.step); | ||
| 682 | |||
| 683 | const cleanup = b.addRemoveDirTree(tmp_path); | ||
| 684 | cleanup.step.dependOn(&run_test.step); | ||
| 685 | |||
| 686 | step.dependOn(&cleanup.step); | ||
| 687 | } | ||
| 563 | 688 | ||
| 564 | const exe = b.addExecutable(.{ | 689 | { |
| 565 | .name = "test-cli", | 690 | // Test `zig init-exe`. |
| 566 | .root_source_file = .{ .path = "test/cli.zig" }, | 691 | const tmp_path = b.makeTempPath(); |
| 567 | .target = .{}, | 692 | const init_exe = b.addSystemCommand(&.{ b.zig_exe, "init-exe" }); |
| 568 | .optimize = .Debug, | 693 | init_exe.cwd = tmp_path; |
| 569 | }); | 694 | init_exe.setName("zig init-exe"); |
| 570 | const run_cmd = exe.run(); | 695 | init_exe.expectStdOutEqual(""); |
| 571 | run_cmd.addArgs(&[_][]const u8{ | 696 | init_exe.expectStdErrEqual("info: Created build.zig\n" ++ |
| 572 | fs.realpathAlloc(b.allocator, b.zig_exe) catch unreachable, | 697 | "info: Created src" ++ s ++ "main.zig\n" ++ |
| 573 | b.pathFromRoot(b.cache_root.path orelse "."), | 698 | "info: Next, try `zig build --help` or `zig build run`\n"); |
| 574 | }); | 699 | |
| 700 | // Test missing output path. | ||
| 701 | const bad_out_arg = "-femit-bin=does" ++ s ++ "not" ++ s ++ "exist" ++ s ++ "foo.exe"; | ||
| 702 | const ok_src_arg = "src" ++ s ++ "main.zig"; | ||
| 703 | const expected = "error: unable to open output directory 'does" ++ s ++ "not" ++ s ++ "exist': FileNotFound\n"; | ||
| 704 | const run_bad = b.addSystemCommand(&.{ b.zig_exe, "build-exe", ok_src_arg, bad_out_arg }); | ||
| 705 | run_bad.setName("zig build-exe error message for bad -femit-bin arg"); | ||
| 706 | run_bad.expectExitCode(1); | ||
| 707 | run_bad.expectStdErrEqual(expected); | ||
| 708 | run_bad.expectStdOutEqual(""); | ||
| 709 | run_bad.step.dependOn(&init_exe.step); | ||
| 710 | |||
| 711 | const run_test = b.addSystemCommand(&.{ b.zig_exe, "build", "test" }); | ||
| 712 | run_test.cwd = tmp_path; | ||
| 713 | run_test.setName("zig build test"); | ||
| 714 | run_test.expectStdOutEqual(""); | ||
| 715 | run_test.step.dependOn(&init_exe.step); | ||
| 716 | |||
| 717 | const run_run = b.addSystemCommand(&.{ b.zig_exe, "build", "run" }); | ||
| 718 | run_run.cwd = tmp_path; | ||
| 719 | run_run.setName("zig build run"); | ||
| 720 | run_run.expectStdOutEqual("Run `zig build test` to run the tests.\n"); | ||
| 721 | run_run.expectStdErrEqual("All your codebase are belong to us.\n"); | ||
| 722 | run_run.step.dependOn(&init_exe.step); | ||
| 723 | |||
| 724 | const cleanup = b.addRemoveDirTree(tmp_path); | ||
| 725 | cleanup.step.dependOn(&run_test.step); | ||
| 726 | cleanup.step.dependOn(&run_run.step); | ||
| 727 | cleanup.step.dependOn(&run_bad.step); | ||
| 728 | |||
| 729 | step.dependOn(&cleanup.step); | ||
| 730 | } | ||
| 731 | |||
| 732 | // Test Godbolt API | ||
| 733 | if (builtin.os.tag == .linux and builtin.cpu.arch == .x86_64) { | ||
| 734 | const tmp_path = b.makeTempPath(); | ||
| 735 | |||
| 736 | const writefile = b.addWriteFile("example.zig", | ||
| 737 | \\// Type your code here, or load an example. | ||
| 738 | \\export fn square(num: i32) i32 { | ||
| 739 | \\ return num * num; | ||
| 740 | \\} | ||
| 741 | \\extern fn zig_panic() noreturn; | ||
| 742 | \\pub fn panic(msg: []const u8, error_return_trace: ?*@import("std").builtin.StackTrace, _: ?usize) noreturn { | ||
| 743 | \\ _ = msg; | ||
| 744 | \\ _ = error_return_trace; | ||
| 745 | \\ zig_panic(); | ||
| 746 | \\} | ||
| 747 | ); | ||
| 748 | |||
| 749 | // This is intended to be the exact CLI usage used by godbolt.org. | ||
| 750 | const run = b.addSystemCommand(&.{ | ||
| 751 | b.zig_exe, "build-obj", | ||
| 752 | "--cache-dir", tmp_path, | ||
| 753 | "--name", "example", | ||
| 754 | "-fno-emit-bin", "-fno-emit-h", | ||
| 755 | "-fstrip", "-OReleaseFast", | ||
| 756 | }); | ||
| 757 | run.addFileSourceArg(writefile.getFileSource("example.zig").?); | ||
| 758 | const example_s = run.addPrefixedOutputFileArg("-femit-asm=", "example.s"); | ||
| 759 | |||
| 760 | const checkfile = b.addCheckFile(example_s, .{ | ||
| 761 | .expected_matches = &.{ | ||
| 762 | "square:", | ||
| 763 | "mov\teax, edi", | ||
| 764 | "imul\teax, edi", | ||
| 765 | }, | ||
| 766 | }); | ||
| 767 | checkfile.setName("check godbolt.org CLI usage generating valid asm"); | ||
| 768 | |||
| 769 | const cleanup = b.addRemoveDirTree(tmp_path); | ||
| 770 | cleanup.step.dependOn(&checkfile.step); | ||
| 771 | |||
| 772 | step.dependOn(&cleanup.step); | ||
| 773 | } | ||
| 774 | |||
| 775 | { | ||
| 776 | // Test `zig fmt`. | ||
| 777 | // This test must use a temporary directory rather than a cache | ||
| 778 | // directory because this test will be mutating the files. The cache | ||
| 779 | // system relies on cache directories being mutated only by their | ||
| 780 | // owners. | ||
| 781 | const tmp_path = b.makeTempPath(); | ||
| 782 | const unformatted_code = " // no reason for indent"; | ||
| 783 | |||
| 784 | var dir = std.fs.cwd().openDir(tmp_path, .{}) catch @panic("unhandled"); | ||
| 785 | defer dir.close(); | ||
| 786 | dir.writeFile("fmt1.zig", unformatted_code) catch @panic("unhandled"); | ||
| 787 | dir.writeFile("fmt2.zig", unformatted_code) catch @panic("unhandled"); | ||
| 788 | |||
| 789 | // Test zig fmt affecting only the appropriate files. | ||
| 790 | const run1 = b.addSystemCommand(&.{ b.zig_exe, "fmt", "fmt1.zig" }); | ||
| 791 | run1.setName("run zig fmt one file"); | ||
| 792 | run1.cwd = tmp_path; | ||
| 793 | run1.has_side_effects = true; | ||
| 794 | // stdout should be file path + \n | ||
| 795 | run1.expectStdOutEqual("fmt1.zig\n"); | ||
| 796 | |||
| 797 | // running it on the dir, only the new file should be changed | ||
| 798 | const run2 = b.addSystemCommand(&.{ b.zig_exe, "fmt", "." }); | ||
| 799 | run2.setName("run zig fmt the directory"); | ||
| 800 | run2.cwd = tmp_path; | ||
| 801 | run2.has_side_effects = true; | ||
| 802 | run2.expectStdOutEqual("." ++ s ++ "fmt2.zig\n"); | ||
| 803 | run2.step.dependOn(&run1.step); | ||
| 804 | |||
| 805 | // both files have been formatted, nothing should change now | ||
| 806 | const run3 = b.addSystemCommand(&.{ b.zig_exe, "fmt", "." }); | ||
| 807 | run3.setName("run zig fmt with nothing to do"); | ||
| 808 | run3.cwd = tmp_path; | ||
| 809 | run3.has_side_effects = true; | ||
| 810 | run3.expectStdOutEqual(""); | ||
| 811 | run3.step.dependOn(&run2.step); | ||
| 812 | |||
| 813 | const unformatted_code_utf16 = "\xff\xfe \x00 \x00 \x00 \x00/\x00/\x00 \x00n\x00o\x00 \x00r\x00e\x00a\x00s\x00o\x00n\x00"; | ||
| 814 | const fmt4_path = std.fs.path.join(b.allocator, &.{ tmp_path, "fmt4.zig" }) catch @panic("OOM"); | ||
| 815 | const write4 = b.addWriteFiles(); | ||
| 816 | write4.addBytesToSource(unformatted_code_utf16, fmt4_path); | ||
| 817 | write4.step.dependOn(&run3.step); | ||
| 818 | |||
| 819 | // Test `zig fmt` handling UTF-16 decoding. | ||
| 820 | const run4 = b.addSystemCommand(&.{ b.zig_exe, "fmt", "." }); | ||
| 821 | run4.setName("run zig fmt convert UTF-16 to UTF-8"); | ||
| 822 | run4.cwd = tmp_path; | ||
| 823 | run4.has_side_effects = true; | ||
| 824 | run4.expectStdOutEqual("." ++ s ++ "fmt4.zig\n"); | ||
| 825 | run4.step.dependOn(&write4.step); | ||
| 826 | |||
| 827 | // TODO change this to an exact match | ||
| 828 | const check4 = b.addCheckFile(.{ .path = fmt4_path }, .{ | ||
| 829 | .expected_matches = &.{ | ||
| 830 | "// no reason", | ||
| 831 | }, | ||
| 832 | }); | ||
| 833 | check4.step.dependOn(&run4.step); | ||
| 834 | |||
| 835 | const cleanup = b.addRemoveDirTree(tmp_path); | ||
| 836 | cleanup.step.dependOn(&check4.step); | ||
| 837 | |||
| 838 | step.dependOn(&cleanup.step); | ||
| 839 | } | ||
| 840 | |||
| 841 | { | ||
| 842 | // TODO this should move to become a CLI test rather than standalone | ||
| 843 | // cases.addBuildFile("test/standalone/options/build.zig", .{ | ||
| 844 | // .extra_argv = &.{ | ||
| 845 | // "-Dbool_true", | ||
| 846 | // "-Dbool_false=false", | ||
| 847 | // "-Dint=1234", | ||
| 848 | // "-De=two", | ||
| 849 | // "-Dstring=hello", | ||
| 850 | // }, | ||
| 851 | // }); | ||
| 852 | } | ||
| 575 | 853 | ||
| 576 | step.dependOn(&run_cmd.step); | ||
| 577 | return step; | 854 | return step; |
| 578 | } | 855 | } |
| 579 | 856 | ||
| 580 | pub fn addAssembleAndLinkTests(b: *std.Build, test_filter: ?[]const u8, optimize_modes: []const OptimizeMode) *Step { | 857 | pub fn addAssembleAndLinkTests(b: *std.Build, test_filter: ?[]const u8, optimize_modes: []const OptimizeMode) *Step { |
| 581 | const cases = b.allocator.create(CompareOutputContext) catch unreachable; | 858 | const cases = b.allocator.create(CompareOutputContext) catch @panic("OOM"); |
| 582 | cases.* = CompareOutputContext{ | 859 | cases.* = CompareOutputContext{ |
| 583 | .b = b, | 860 | .b = b, |
| 584 | .step = b.step("test-asm-link", "Run the assemble and link tests"), | 861 | .step = b.step("test-asm-link", "Run the assemble and link tests"), |
| ... | @@ -593,7 +870,7 @@ pub fn addAssembleAndLinkTests(b: *std.Build, test_filter: ?[]const u8, optimize | ... | @@ -593,7 +870,7 @@ pub fn addAssembleAndLinkTests(b: *std.Build, test_filter: ?[]const u8, optimize |
| 593 | } | 870 | } |
| 594 | 871 | ||
| 595 | pub fn addTranslateCTests(b: *std.Build, test_filter: ?[]const u8) *Step { | 872 | pub fn addTranslateCTests(b: *std.Build, test_filter: ?[]const u8) *Step { |
| 596 | const cases = b.allocator.create(TranslateCContext) catch unreachable; | 873 | const cases = b.allocator.create(TranslateCContext) catch @panic("OOM"); |
| 597 | cases.* = TranslateCContext{ | 874 | cases.* = TranslateCContext{ |
| 598 | .b = b, | 875 | .b = b, |
| 599 | .step = b.step("test-translate-c", "Run the C translation tests"), | 876 | .step = b.step("test-translate-c", "Run the C translation tests"), |
| ... | @@ -611,7 +888,7 @@ pub fn addRunTranslatedCTests( | ... | @@ -611,7 +888,7 @@ pub fn addRunTranslatedCTests( |
| 611 | test_filter: ?[]const u8, | 888 | test_filter: ?[]const u8, |
| 612 | target: std.zig.CrossTarget, | 889 | target: std.zig.CrossTarget, |
| 613 | ) *Step { | 890 | ) *Step { |
| 614 | const cases = b.allocator.create(RunTranslatedCContext) catch unreachable; | 891 | const cases = b.allocator.create(RunTranslatedCContext) catch @panic("OOM"); |
| 615 | cases.* = .{ | 892 | cases.* = .{ |
| 616 | .b = b, | 893 | .b = b, |
| 617 | .step = b.step("test-run-translated-c", "Run the Run-Translated-C tests"), | 894 | .step = b.step("test-run-translated-c", "Run the Run-Translated-C tests"), |
| ... | @@ -625,22 +902,7 @@ pub fn addRunTranslatedCTests( | ... | @@ -625,22 +902,7 @@ pub fn addRunTranslatedCTests( |
| 625 | return cases.step; | 902 | return cases.step; |
| 626 | } | 903 | } |
| 627 | 904 | ||
| 628 | pub fn addGenHTests(b: *std.Build, test_filter: ?[]const u8) *Step { | 905 | const ModuleTestOptions = struct { |
| 629 | const cases = b.allocator.create(GenHContext) catch unreachable; | ||
| 630 | cases.* = GenHContext{ | ||
| 631 | .b = b, | ||
| 632 | .step = b.step("test-gen-h", "Run the C header file generation tests"), | ||
| 633 | .test_index = 0, | ||
| 634 | .test_filter = test_filter, | ||
| 635 | }; | ||
| 636 | |||
| 637 | gen_h.addCases(cases); | ||
| 638 | |||
| 639 | return cases.step; | ||
| 640 | } | ||
| 641 | |||
| 642 | pub fn addPkgTests( | ||
| 643 | b: *std.Build, | ||
| 644 | test_filter: ?[]const u8, | 906 | test_filter: ?[]const u8, |
| 645 | root_src: []const u8, | 907 | root_src: []const u8, |
| 646 | name: []const u8, | 908 | name: []const u8, |
| ... | @@ -651,14 +913,17 @@ pub fn addPkgTests( | ... | @@ -651,14 +913,17 @@ pub fn addPkgTests( |
| 651 | skip_libc: bool, | 913 | skip_libc: bool, |
| 652 | skip_stage1: bool, | 914 | skip_stage1: bool, |
| 653 | skip_stage2: bool, | 915 | skip_stage2: bool, |
| 654 | ) *Step { | 916 | max_rss: usize = 0, |
| 655 | const step = b.step(b.fmt("test-{s}", .{name}), desc); | 917 | }; |
| 918 | |||
| 919 | pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step { | ||
| 920 | const step = b.step(b.fmt("test-{s}", .{options.name}), options.desc); | ||
| 656 | 921 | ||
| 657 | for (test_targets) |test_target| { | 922 | for (test_targets) |test_target| { |
| 658 | if (skip_non_native and !test_target.target.isNative()) | 923 | if (options.skip_non_native and !test_target.target.isNative()) |
| 659 | continue; | 924 | continue; |
| 660 | 925 | ||
| 661 | if (skip_libc and test_target.link_libc) | 926 | if (options.skip_libc and test_target.link_libc) |
| 662 | continue; | 927 | continue; |
| 663 | 928 | ||
| 664 | if (test_target.link_libc and test_target.target.getOs().requiresLibC()) { | 929 | if (test_target.link_libc and test_target.target.getOs().requiresLibC()) { |
| ... | @@ -666,7 +931,7 @@ pub fn addPkgTests( | ... | @@ -666,7 +931,7 @@ pub fn addPkgTests( |
| 666 | continue; | 931 | continue; |
| 667 | } | 932 | } |
| 668 | 933 | ||
| 669 | if (skip_single_threaded and test_target.single_threaded) | 934 | if (options.skip_single_threaded and test_target.single_threaded) |
| 670 | continue; | 935 | continue; |
| 671 | 936 | ||
| 672 | if (test_target.disable_native and | 937 | if (test_target.disable_native and |
| ... | @@ -677,12 +942,12 @@ pub fn addPkgTests( | ... | @@ -677,12 +942,12 @@ pub fn addPkgTests( |
| 677 | } | 942 | } |
| 678 | 943 | ||
| 679 | if (test_target.backend) |backend| switch (backend) { | 944 | if (test_target.backend) |backend| switch (backend) { |
| 680 | .stage1 => if (skip_stage1) continue, | 945 | .stage1 => if (options.skip_stage1) continue, |
| 681 | .stage2_llvm => {}, | 946 | .stage2_llvm => {}, |
| 682 | else => if (skip_stage2) continue, | 947 | else => if (options.skip_stage2) continue, |
| 683 | }; | 948 | }; |
| 684 | 949 | ||
| 685 | const want_this_mode = for (optimize_modes) |m| { | 950 | const want_this_mode = for (options.optimize_modes) |m| { |
| 686 | if (m == test_target.optimize_mode) break true; | 951 | if (m == test_target.optimize_mode) break true; |
| 687 | } else false; | 952 | } else false; |
| 688 | if (!want_this_mode) continue; | 953 | if (!want_this_mode) continue; |
| ... | @@ -694,25 +959,24 @@ pub fn addPkgTests( | ... | @@ -694,25 +959,24 @@ pub fn addPkgTests( |
| 694 | else | 959 | else |
| 695 | "bare"; | 960 | "bare"; |
| 696 | 961 | ||
| 697 | const triple_prefix = test_target.target.zigTriple(b.allocator) catch unreachable; | 962 | const triple_prefix = test_target.target.zigTriple(b.allocator) catch @panic("OOM"); |
| 963 | |||
| 964 | // wasm32-wasi builds need more RAM, idk why | ||
| 965 | const max_rss = if (test_target.target.getOs().tag == .wasi) | ||
| 966 | options.max_rss * 2 | ||
| 967 | else | ||
| 968 | options.max_rss; | ||
| 698 | 969 | ||
| 699 | const these_tests = b.addTest(.{ | 970 | const these_tests = b.addTest(.{ |
| 700 | .root_source_file = .{ .path = root_src }, | 971 | .root_source_file = .{ .path = options.root_src }, |
| 701 | .optimize = test_target.optimize_mode, | 972 | .optimize = test_target.optimize_mode, |
| 702 | .target = test_target.target, | 973 | .target = test_target.target, |
| 974 | .max_rss = max_rss, | ||
| 703 | }); | 975 | }); |
| 704 | const single_threaded_txt = if (test_target.single_threaded) "single" else "multi"; | 976 | const single_threaded_txt = if (test_target.single_threaded) "single" else "multi"; |
| 705 | const backend_txt = if (test_target.backend) |backend| @tagName(backend) else "default"; | 977 | const backend_txt = if (test_target.backend) |backend| @tagName(backend) else "default"; |
| 706 | these_tests.setNamePrefix(b.fmt("{s}-{s}-{s}-{s}-{s}-{s} ", .{ | ||
| 707 | name, | ||
| 708 | triple_prefix, | ||
| 709 | @tagName(test_target.optimize_mode), | ||
| 710 | libc_prefix, | ||
| 711 | single_threaded_txt, | ||
| 712 | backend_txt, | ||
| 713 | })); | ||
| 714 | these_tests.single_threaded = test_target.single_threaded; | 978 | these_tests.single_threaded = test_target.single_threaded; |
| 715 | these_tests.setFilter(test_filter); | 979 | these_tests.setFilter(options.test_filter); |
| 716 | if (test_target.link_libc) { | 980 | if (test_target.link_libc) { |
| 717 | these_tests.linkSystemLibrary("c"); | 981 | these_tests.linkSystemLibrary("c"); |
| 718 | } | 982 | } |
| ... | @@ -736,654 +1000,94 @@ pub fn addPkgTests( | ... | @@ -736,654 +1000,94 @@ pub fn addPkgTests( |
| 736 | }, | 1000 | }, |
| 737 | }; | 1001 | }; |
| 738 | 1002 | ||
| 739 | step.dependOn(&these_tests.step); | 1003 | const run = these_tests.run(); |
| 1004 | run.skip_foreign_checks = true; | ||
| 1005 | run.setName(b.fmt("run test {s}-{s}-{s}-{s}-{s}-{s}", .{ | ||
| 1006 | options.name, | ||
| 1007 | triple_prefix, | ||
| 1008 | @tagName(test_target.optimize_mode), | ||
| 1009 | libc_prefix, | ||
| 1010 | single_threaded_txt, | ||
| 1011 | backend_txt, | ||
| 1012 | })); | ||
| 1013 | |||
| 1014 | step.dependOn(&run.step); | ||
| 740 | } | 1015 | } |
| 741 | return step; | 1016 | return step; |
| 742 | } | 1017 | } |
| 743 | 1018 | ||
| 744 | pub const StackTracesContext = struct { | 1019 | pub fn addCAbiTests(b: *std.Build, skip_non_native: bool, skip_release: bool) *Step { |
| 745 | b: *std.Build, | 1020 | const step = b.step("test-c-abi", "Run the C ABI tests"); |
| 746 | step: *Step, | ||
| 747 | test_index: usize, | ||
| 748 | test_filter: ?[]const u8, | ||
| 749 | optimize_modes: []const OptimizeMode, | ||
| 750 | |||
| 751 | const Expect = [@typeInfo(OptimizeMode).Enum.fields.len][]const u8; | ||
| 752 | |||
| 753 | pub fn addCase(self: *StackTracesContext, config: anytype) void { | ||
| 754 | if (@hasField(@TypeOf(config), "exclude")) { | ||
| 755 | if (config.exclude.exclude()) return; | ||
| 756 | } | ||
| 757 | if (@hasField(@TypeOf(config), "exclude_arch")) { | ||
| 758 | const exclude_arch: []const std.Target.Cpu.Arch = &config.exclude_arch; | ||
| 759 | for (exclude_arch) |arch| if (arch == builtin.cpu.arch) return; | ||
| 760 | } | ||
| 761 | if (@hasField(@TypeOf(config), "exclude_os")) { | ||
| 762 | const exclude_os: []const std.Target.Os.Tag = &config.exclude_os; | ||
| 763 | for (exclude_os) |os| if (os == builtin.os.tag) return; | ||
| 764 | } | ||
| 765 | for (self.optimize_modes) |optimize_mode| { | ||
| 766 | switch (optimize_mode) { | ||
| 767 | .Debug => { | ||
| 768 | if (@hasField(@TypeOf(config), "Debug")) { | ||
| 769 | self.addExpect(config.name, config.source, optimize_mode, config.Debug); | ||
| 770 | } | ||
| 771 | }, | ||
| 772 | .ReleaseSafe => { | ||
| 773 | if (@hasField(@TypeOf(config), "ReleaseSafe")) { | ||
| 774 | self.addExpect(config.name, config.source, optimize_mode, config.ReleaseSafe); | ||
| 775 | } | ||
| 776 | }, | ||
| 777 | .ReleaseFast => { | ||
| 778 | if (@hasField(@TypeOf(config), "ReleaseFast")) { | ||
| 779 | self.addExpect(config.name, config.source, optimize_mode, config.ReleaseFast); | ||
| 780 | } | ||
| 781 | }, | ||
| 782 | .ReleaseSmall => { | ||
| 783 | if (@hasField(@TypeOf(config), "ReleaseSmall")) { | ||
| 784 | self.addExpect(config.name, config.source, optimize_mode, config.ReleaseSmall); | ||
| 785 | } | ||
| 786 | }, | ||
| 787 | } | ||
| 788 | } | ||
| 789 | } | ||
| 790 | |||
| 791 | fn addExpect( | ||
| 792 | self: *StackTracesContext, | ||
| 793 | name: []const u8, | ||
| 794 | source: []const u8, | ||
| 795 | optimize_mode: OptimizeMode, | ||
| 796 | mode_config: anytype, | ||
| 797 | ) void { | ||
| 798 | if (@hasField(@TypeOf(mode_config), "exclude")) { | ||
| 799 | if (mode_config.exclude.exclude()) return; | ||
| 800 | } | ||
| 801 | if (@hasField(@TypeOf(mode_config), "exclude_arch")) { | ||
| 802 | const exclude_arch: []const std.Target.Cpu.Arch = &mode_config.exclude_arch; | ||
| 803 | for (exclude_arch) |arch| if (arch == builtin.cpu.arch) return; | ||
| 804 | } | ||
| 805 | if (@hasField(@TypeOf(mode_config), "exclude_os")) { | ||
| 806 | const exclude_os: []const std.Target.Os.Tag = &mode_config.exclude_os; | ||
| 807 | for (exclude_os) |os| if (os == builtin.os.tag) return; | ||
| 808 | } | ||
| 809 | |||
| 810 | const annotated_case_name = fmt.allocPrint(self.b.allocator, "{s} {s} ({s})", .{ | ||
| 811 | "stack-trace", | ||
| 812 | name, | ||
| 813 | @tagName(optimize_mode), | ||
| 814 | }) catch unreachable; | ||
| 815 | if (self.test_filter) |filter| { | ||
| 816 | if (mem.indexOf(u8, annotated_case_name, filter) == null) return; | ||
| 817 | } | ||
| 818 | |||
| 819 | const b = self.b; | ||
| 820 | const src_basename = "source.zig"; | ||
| 821 | const write_src = b.addWriteFile(src_basename, source); | ||
| 822 | const exe = b.addExecutable(.{ | ||
| 823 | .name = "test", | ||
| 824 | .root_source_file = write_src.getFileSource(src_basename).?, | ||
| 825 | .optimize = optimize_mode, | ||
| 826 | .target = .{}, | ||
| 827 | }); | ||
| 828 | |||
| 829 | const run_and_compare = RunAndCompareStep.create( | ||
| 830 | self, | ||
| 831 | exe, | ||
| 832 | annotated_case_name, | ||
| 833 | optimize_mode, | ||
| 834 | mode_config.expect, | ||
| 835 | ); | ||
| 836 | |||
| 837 | self.step.dependOn(&run_and_compare.step); | ||
| 838 | } | ||
| 839 | |||
| 840 | const RunAndCompareStep = struct { | ||
| 841 | pub const base_id = .custom; | ||
| 842 | |||
| 843 | step: Step, | ||
| 844 | context: *StackTracesContext, | ||
| 845 | exe: *CompileStep, | ||
| 846 | name: []const u8, | ||
| 847 | optimize_mode: OptimizeMode, | ||
| 848 | expect_output: []const u8, | ||
| 849 | test_index: usize, | ||
| 850 | |||
| 851 | pub fn create( | ||
| 852 | context: *StackTracesContext, | ||
| 853 | exe: *CompileStep, | ||
| 854 | name: []const u8, | ||
| 855 | optimize_mode: OptimizeMode, | ||
| 856 | expect_output: []const u8, | ||
| 857 | ) *RunAndCompareStep { | ||
| 858 | const allocator = context.b.allocator; | ||
| 859 | const ptr = allocator.create(RunAndCompareStep) catch unreachable; | ||
| 860 | ptr.* = RunAndCompareStep{ | ||
| 861 | .step = Step.init(.custom, "StackTraceCompareOutputStep", allocator, make), | ||
| 862 | .context = context, | ||
| 863 | .exe = exe, | ||
| 864 | .name = name, | ||
| 865 | .optimize_mode = optimize_mode, | ||
| 866 | .expect_output = expect_output, | ||
| 867 | .test_index = context.test_index, | ||
| 868 | }; | ||
| 869 | ptr.step.dependOn(&exe.step); | ||
| 870 | context.test_index += 1; | ||
| 871 | return ptr; | ||
| 872 | } | ||
| 873 | |||
| 874 | fn make(step: *Step) !void { | ||
| 875 | const self = @fieldParentPtr(RunAndCompareStep, "step", step); | ||
| 876 | const b = self.context.b; | ||
| 877 | |||
| 878 | const full_exe_path = self.exe.getOutputSource().getPath(b); | ||
| 879 | var args = ArrayList([]const u8).init(b.allocator); | ||
| 880 | defer args.deinit(); | ||
| 881 | args.append(full_exe_path) catch unreachable; | ||
| 882 | |||
| 883 | std.debug.print("Test {d}/{d} {s}...", .{ self.test_index + 1, self.context.test_index, self.name }); | ||
| 884 | |||
| 885 | if (!std.process.can_spawn) { | ||
| 886 | const cmd = try std.mem.join(b.allocator, " ", args.items); | ||
| 887 | std.debug.print("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{ @tagName(builtin.os.tag), cmd }); | ||
| 888 | b.allocator.free(cmd); | ||
| 889 | return ExecError.ExecNotSupported; | ||
| 890 | } | ||
| 891 | |||
| 892 | var child = std.ChildProcess.init(args.items, b.allocator); | ||
| 893 | child.stdin_behavior = .Ignore; | ||
| 894 | child.stdout_behavior = .Pipe; | ||
| 895 | child.stderr_behavior = .Pipe; | ||
| 896 | child.env_map = b.env_map; | ||
| 897 | |||
| 898 | if (b.verbose) { | ||
| 899 | printInvocation(args.items); | ||
| 900 | } | ||
| 901 | child.spawn() catch |err| debug.panic("Unable to spawn {s}: {s}\n", .{ full_exe_path, @errorName(err) }); | ||
| 902 | |||
| 903 | const stdout = child.stdout.?.reader().readAllAlloc(b.allocator, max_stdout_size) catch unreachable; | ||
| 904 | defer b.allocator.free(stdout); | ||
| 905 | const stderrFull = child.stderr.?.reader().readAllAlloc(b.allocator, max_stdout_size) catch unreachable; | ||
| 906 | defer b.allocator.free(stderrFull); | ||
| 907 | var stderr = stderrFull; | ||
| 908 | |||
| 909 | const term = child.wait() catch |err| { | ||
| 910 | debug.panic("Unable to spawn {s}: {s}\n", .{ full_exe_path, @errorName(err) }); | ||
| 911 | }; | ||
| 912 | |||
| 913 | switch (term) { | ||
| 914 | .Exited => |code| { | ||
| 915 | const expect_code: u32 = 1; | ||
| 916 | if (code != expect_code) { | ||
| 917 | std.debug.print("Process {s} exited with error code {d} but expected code {d}\n", .{ | ||
| 918 | full_exe_path, | ||
| 919 | code, | ||
| 920 | expect_code, | ||
| 921 | }); | ||
| 922 | printInvocation(args.items); | ||
| 923 | return error.TestFailed; | ||
| 924 | } | ||
| 925 | }, | ||
| 926 | .Signal => |signum| { | ||
| 927 | std.debug.print("Process {s} terminated on signal {d}\n", .{ full_exe_path, signum }); | ||
| 928 | printInvocation(args.items); | ||
| 929 | return error.TestFailed; | ||
| 930 | }, | ||
| 931 | .Stopped => |signum| { | ||
| 932 | std.debug.print("Process {s} stopped on signal {d}\n", .{ full_exe_path, signum }); | ||
| 933 | printInvocation(args.items); | ||
| 934 | return error.TestFailed; | ||
| 935 | }, | ||
| 936 | .Unknown => |code| { | ||
| 937 | std.debug.print("Process {s} terminated unexpectedly with error code {d}\n", .{ full_exe_path, code }); | ||
| 938 | printInvocation(args.items); | ||
| 939 | return error.TestFailed; | ||
| 940 | }, | ||
| 941 | } | ||
| 942 | |||
| 943 | // process result | ||
| 944 | // - keep only basename of source file path | ||
| 945 | // - replace address with symbolic string | ||
| 946 | // - replace function name with symbolic string when optimize_mode != .Debug | ||
| 947 | // - skip empty lines | ||
| 948 | const got: []const u8 = got_result: { | ||
| 949 | var buf = ArrayList(u8).init(b.allocator); | ||
| 950 | defer buf.deinit(); | ||
| 951 | if (stderr.len != 0 and stderr[stderr.len - 1] == '\n') stderr = stderr[0 .. stderr.len - 1]; | ||
| 952 | var it = mem.split(u8, stderr, "\n"); | ||
| 953 | process_lines: while (it.next()) |line| { | ||
| 954 | if (line.len == 0) continue; | ||
| 955 | |||
| 956 | // offset search past `[drive]:` on windows | ||
| 957 | var pos: usize = if (builtin.os.tag == .windows) 2 else 0; | ||
| 958 | // locate delims/anchor | ||
| 959 | const delims = [_][]const u8{ ":", ":", ":", " in ", "(", ")" }; | ||
| 960 | var marks = [_]usize{0} ** delims.len; | ||
| 961 | for (delims, 0..) |delim, i| { | ||
| 962 | marks[i] = mem.indexOfPos(u8, line, pos, delim) orelse { | ||
| 963 | // unexpected pattern: emit raw line and cont | ||
| 964 | try buf.appendSlice(line); | ||
| 965 | try buf.appendSlice("\n"); | ||
| 966 | continue :process_lines; | ||
| 967 | }; | ||
| 968 | pos = marks[i] + delim.len; | ||
| 969 | } | ||
| 970 | // locate source basename | ||
| 971 | pos = mem.lastIndexOfScalar(u8, line[0..marks[0]], fs.path.sep) orelse { | ||
| 972 | // unexpected pattern: emit raw line and cont | ||
| 973 | try buf.appendSlice(line); | ||
| 974 | try buf.appendSlice("\n"); | ||
| 975 | continue :process_lines; | ||
| 976 | }; | ||
| 977 | // end processing if source basename changes | ||
| 978 | if (!mem.eql(u8, "source.zig", line[pos + 1 .. marks[0]])) break; | ||
| 979 | // emit substituted line | ||
| 980 | try buf.appendSlice(line[pos + 1 .. marks[2] + delims[2].len]); | ||
| 981 | try buf.appendSlice(" [address]"); | ||
| 982 | if (self.optimize_mode == .Debug) { | ||
| 983 | // On certain platforms (windows) or possibly depending on how we choose to link main | ||
| 984 | // the object file extension may be present so we simply strip any extension. | ||
| 985 | if (mem.indexOfScalar(u8, line[marks[4]..marks[5]], '.')) |idot| { | ||
| 986 | try buf.appendSlice(line[marks[3] .. marks[4] + idot]); | ||
| 987 | try buf.appendSlice(line[marks[5]..]); | ||
| 988 | } else { | ||
| 989 | try buf.appendSlice(line[marks[3]..]); | ||
| 990 | } | ||
| 991 | } else { | ||
| 992 | try buf.appendSlice(line[marks[3] .. marks[3] + delims[3].len]); | ||
| 993 | try buf.appendSlice("[function]"); | ||
| 994 | } | ||
| 995 | try buf.appendSlice("\n"); | ||
| 996 | } | ||
| 997 | break :got_result try buf.toOwnedSlice(); | ||
| 998 | }; | ||
| 999 | |||
| 1000 | if (!mem.eql(u8, self.expect_output, got)) { | ||
| 1001 | std.debug.print( | ||
| 1002 | \\ | ||
| 1003 | \\========= Expected this output: ========= | ||
| 1004 | \\{s} | ||
| 1005 | \\================================================ | ||
| 1006 | \\{s} | ||
| 1007 | \\ | ||
| 1008 | , .{ self.expect_output, got }); | ||
| 1009 | return error.TestFailed; | ||
| 1010 | } | ||
| 1011 | std.debug.print("OK\n", .{}); | ||
| 1012 | } | ||
| 1013 | }; | ||
| 1014 | }; | ||
| 1015 | |||
| 1016 | pub const StandaloneContext = struct { | ||
| 1017 | b: *std.Build, | ||
| 1018 | step: *Step, | ||
| 1019 | test_index: usize, | ||
| 1020 | test_filter: ?[]const u8, | ||
| 1021 | optimize_modes: []const OptimizeMode, | ||
| 1022 | skip_non_native: bool, | ||
| 1023 | enable_macos_sdk: bool, | ||
| 1024 | target: std.zig.CrossTarget, | ||
| 1025 | omit_stage2: bool, | ||
| 1026 | enable_darling: bool = false, | ||
| 1027 | enable_qemu: bool = false, | ||
| 1028 | enable_rosetta: bool = false, | ||
| 1029 | enable_wasmtime: bool = false, | ||
| 1030 | enable_wine: bool = false, | ||
| 1031 | enable_symlinks_windows: bool, | ||
| 1032 | |||
| 1033 | pub fn addC(self: *StandaloneContext, root_src: []const u8) void { | ||
| 1034 | self.addAllArgs(root_src, true); | ||
| 1035 | } | ||
| 1036 | |||
| 1037 | pub fn add(self: *StandaloneContext, root_src: []const u8) void { | ||
| 1038 | self.addAllArgs(root_src, false); | ||
| 1039 | } | ||
| 1040 | |||
| 1041 | pub fn addBuildFile(self: *StandaloneContext, build_file: []const u8, features: struct { | ||
| 1042 | build_modes: bool = false, | ||
| 1043 | cross_targets: bool = false, | ||
| 1044 | requires_macos_sdk: bool = false, | ||
| 1045 | requires_stage2: bool = false, | ||
| 1046 | use_emulation: bool = false, | ||
| 1047 | requires_symlinks: bool = false, | ||
| 1048 | extra_argv: []const []const u8 = &.{}, | ||
| 1049 | }) void { | ||
| 1050 | const b = self.b; | ||
| 1051 | |||
| 1052 | if (features.requires_macos_sdk and !self.enable_macos_sdk) return; | ||
| 1053 | if (features.requires_stage2 and self.omit_stage2) return; | ||
| 1054 | if (features.requires_symlinks and !self.enable_symlinks_windows and builtin.os.tag == .windows) return; | ||
| 1055 | |||
| 1056 | const annotated_case_name = b.fmt("build {s}", .{build_file}); | ||
| 1057 | if (self.test_filter) |filter| { | ||
| 1058 | if (mem.indexOf(u8, annotated_case_name, filter) == null) return; | ||
| 1059 | } | ||
| 1060 | |||
| 1061 | var zig_args = ArrayList([]const u8).init(b.allocator); | ||
| 1062 | const rel_zig_exe = fs.path.relative(b.allocator, b.build_root.path orelse ".", b.zig_exe) catch unreachable; | ||
| 1063 | zig_args.append(rel_zig_exe) catch unreachable; | ||
| 1064 | zig_args.append("build") catch unreachable; | ||
| 1065 | |||
| 1066 | zig_args.append("--build-file") catch unreachable; | ||
| 1067 | zig_args.append(b.pathFromRoot(build_file)) catch unreachable; | ||
| 1068 | |||
| 1069 | zig_args.appendSlice(features.extra_argv) catch unreachable; | ||
| 1070 | |||
| 1071 | zig_args.append("test") catch unreachable; | ||
| 1072 | |||
| 1073 | if (b.verbose) { | ||
| 1074 | zig_args.append("--verbose") catch unreachable; | ||
| 1075 | } | ||
| 1076 | |||
| 1077 | if (features.cross_targets and !self.target.isNative()) { | ||
| 1078 | const target_triple = self.target.zigTriple(b.allocator) catch unreachable; | ||
| 1079 | const target_arg = fmt.allocPrint(b.allocator, "-Dtarget={s}", .{target_triple}) catch unreachable; | ||
| 1080 | zig_args.append(target_arg) catch unreachable; | ||
| 1081 | } | ||
| 1082 | 1021 | ||
| 1083 | if (features.use_emulation) { | 1022 | const optimize_modes: [2]OptimizeMode = .{ .Debug, .ReleaseFast }; |
| 1084 | if (self.enable_darling) { | ||
| 1085 | zig_args.append("-fdarling") catch unreachable; | ||
| 1086 | } | ||
| 1087 | if (self.enable_qemu) { | ||
| 1088 | zig_args.append("-fqemu") catch unreachable; | ||
| 1089 | } | ||
| 1090 | if (self.enable_rosetta) { | ||
| 1091 | zig_args.append("-frosetta") catch unreachable; | ||
| 1092 | } | ||
| 1093 | if (self.enable_wasmtime) { | ||
| 1094 | zig_args.append("-fwasmtime") catch unreachable; | ||
| 1095 | } | ||
| 1096 | if (self.enable_wine) { | ||
| 1097 | zig_args.append("-fwine") catch unreachable; | ||
| 1098 | } | ||
| 1099 | } | ||
| 1100 | 1023 | ||
| 1101 | const optimize_modes = if (features.build_modes) self.optimize_modes else &[1]OptimizeMode{.Debug}; | 1024 | for (optimize_modes) |optimize_mode| { |
| 1102 | for (optimize_modes) |optimize_mode| { | 1025 | if (optimize_mode != .Debug and skip_release) continue; |
| 1103 | const arg = switch (optimize_mode) { | ||
| 1104 | .Debug => "", | ||
| 1105 | .ReleaseFast => "-Doptimize=ReleaseFast", | ||
| 1106 | .ReleaseSafe => "-Doptimize=ReleaseSafe", | ||
| 1107 | .ReleaseSmall => "-Doptimize=ReleaseSmall", | ||
| 1108 | }; | ||
| 1109 | const zig_args_base_len = zig_args.items.len; | ||
| 1110 | if (arg.len > 0) | ||
| 1111 | zig_args.append(arg) catch unreachable; | ||
| 1112 | defer zig_args.resize(zig_args_base_len) catch unreachable; | ||
| 1113 | |||
| 1114 | const run_cmd = b.addSystemCommand(zig_args.items); | ||
| 1115 | const log_step = b.addLog("PASS {s} ({s})", .{ annotated_case_name, @tagName(optimize_mode) }); | ||
| 1116 | log_step.step.dependOn(&run_cmd.step); | ||
| 1117 | |||
| 1118 | self.step.dependOn(&log_step.step); | ||
| 1119 | } | ||
| 1120 | } | ||
| 1121 | 1026 | ||
| 1122 | pub fn addAllArgs(self: *StandaloneContext, root_src: []const u8, link_libc: bool) void { | 1027 | for (c_abi_targets) |c_abi_target| { |
| 1123 | const b = self.b; | 1028 | if (skip_non_native and !c_abi_target.isNative()) continue; |
| 1124 | 1029 | ||
| 1125 | for (self.optimize_modes) |optimize| { | 1030 | if (c_abi_target.isWindows() and c_abi_target.getCpuArch() == .aarch64) { |
| 1126 | const annotated_case_name = fmt.allocPrint(self.b.allocator, "build {s} ({s})", .{ | 1031 | // https://github.com/ziglang/zig/issues/14908 |
| 1127 | root_src, | 1032 | continue; |
| 1128 | @tagName(optimize), | ||
| 1129 | }) catch unreachable; | ||
| 1130 | if (self.test_filter) |filter| { | ||
| 1131 | if (mem.indexOf(u8, annotated_case_name, filter) == null) continue; | ||
| 1132 | } | 1033 | } |
| 1133 | 1034 | ||
| 1134 | const exe = b.addExecutable(.{ | 1035 | const test_step = b.addTest(.{ |
| 1135 | .name = "test", | 1036 | .root_source_file = .{ .path = "test/c_abi/main.zig" }, |
| 1136 | .root_source_file = .{ .path = root_src }, | 1037 | .optimize = optimize_mode, |
| 1137 | .optimize = optimize, | 1038 | .target = c_abi_target, |
| 1138 | .target = .{}, | ||
| 1139 | }); | 1039 | }); |
| 1140 | if (link_libc) { | 1040 | if (c_abi_target.abi != null and c_abi_target.abi.?.isMusl()) { |
| 1141 | exe.linkSystemLibrary("c"); | 1041 | // TODO NativeTargetInfo insists on dynamically linking musl |
| 1042 | // for some reason? | ||
| 1043 | test_step.target_info.dynamic_linker.max_byte = null; | ||
| 1142 | } | 1044 | } |
| 1143 | 1045 | test_step.linkLibC(); | |
| 1144 | const log_step = b.addLog("PASS {s}", .{annotated_case_name}); | 1046 | test_step.addCSourceFile("test/c_abi/cfuncs.c", &.{"-std=c99"}); |
| 1145 | log_step.step.dependOn(&exe.step); | 1047 | |
| 1146 | 1048 | // test-c-abi should test both with LTO on and with LTO off. Only | |
| 1147 | self.step.dependOn(&log_step.step); | 1049 | // some combinations are passing currently: |
| 1148 | } | 1050 | // https://github.com/ziglang/zig/issues/14908 |
| 1149 | } | 1051 | if (c_abi_target.isWindows()) { |
| 1150 | }; | 1052 | test_step.want_lto = false; |
| 1151 | |||
| 1152 | pub const GenHContext = struct { | ||
| 1153 | b: *std.Build, | ||
| 1154 | step: *Step, | ||
| 1155 | test_index: usize, | ||
| 1156 | test_filter: ?[]const u8, | ||
| 1157 | |||
| 1158 | const TestCase = struct { | ||
| 1159 | name: []const u8, | ||
| 1160 | sources: ArrayList(SourceFile), | ||
| 1161 | expected_lines: ArrayList([]const u8), | ||
| 1162 | |||
| 1163 | const SourceFile = struct { | ||
| 1164 | filename: []const u8, | ||
| 1165 | source: []const u8, | ||
| 1166 | }; | ||
| 1167 | |||
| 1168 | pub fn addSourceFile(self: *TestCase, filename: []const u8, source: []const u8) void { | ||
| 1169 | self.sources.append(SourceFile{ | ||
| 1170 | .filename = filename, | ||
| 1171 | .source = source, | ||
| 1172 | }) catch unreachable; | ||
| 1173 | } | ||
| 1174 | |||
| 1175 | pub fn addExpectedLine(self: *TestCase, text: []const u8) void { | ||
| 1176 | self.expected_lines.append(text) catch unreachable; | ||
| 1177 | } | ||
| 1178 | }; | ||
| 1179 | |||
| 1180 | const GenHCmpOutputStep = struct { | ||
| 1181 | step: Step, | ||
| 1182 | context: *GenHContext, | ||
| 1183 | obj: *CompileStep, | ||
| 1184 | name: []const u8, | ||
| 1185 | test_index: usize, | ||
| 1186 | case: *const TestCase, | ||
| 1187 | |||
| 1188 | pub fn create( | ||
| 1189 | context: *GenHContext, | ||
| 1190 | obj: *CompileStep, | ||
| 1191 | name: []const u8, | ||
| 1192 | case: *const TestCase, | ||
| 1193 | ) *GenHCmpOutputStep { | ||
| 1194 | const allocator = context.b.allocator; | ||
| 1195 | const ptr = allocator.create(GenHCmpOutputStep) catch unreachable; | ||
| 1196 | ptr.* = GenHCmpOutputStep{ | ||
| 1197 | .step = Step.init(.Custom, "ParseCCmpOutput", allocator, make), | ||
| 1198 | .context = context, | ||
| 1199 | .obj = obj, | ||
| 1200 | .name = name, | ||
| 1201 | .test_index = context.test_index, | ||
| 1202 | .case = case, | ||
| 1203 | }; | ||
| 1204 | ptr.step.dependOn(&obj.step); | ||
| 1205 | context.test_index += 1; | ||
| 1206 | return ptr; | ||
| 1207 | } | ||
| 1208 | |||
| 1209 | fn make(step: *Step) !void { | ||
| 1210 | const self = @fieldParentPtr(GenHCmpOutputStep, "step", step); | ||
| 1211 | const b = self.context.b; | ||
| 1212 | |||
| 1213 | std.debug.print("Test {d}/{d} {s}...", .{ self.test_index + 1, self.context.test_index, self.name }); | ||
| 1214 | |||
| 1215 | const full_h_path = self.obj.getOutputHPath(); | ||
| 1216 | const actual_h = try io.readFileAlloc(b.allocator, full_h_path); | ||
| 1217 | |||
| 1218 | for (self.case.expected_lines.items) |expected_line| { | ||
| 1219 | if (mem.indexOf(u8, actual_h, expected_line) == null) { | ||
| 1220 | std.debug.print( | ||
| 1221 | \\ | ||
| 1222 | \\========= Expected this output: ================ | ||
| 1223 | \\{s} | ||
| 1224 | \\========= But found: =========================== | ||
| 1225 | \\{s} | ||
| 1226 | \\ | ||
| 1227 | , .{ expected_line, actual_h }); | ||
| 1228 | return error.TestFailed; | ||
| 1229 | } | ||
| 1230 | } | 1053 | } |
| 1231 | std.debug.print("OK\n", .{}); | ||
| 1232 | } | ||
| 1233 | }; | ||
| 1234 | |||
| 1235 | pub fn create( | ||
| 1236 | self: *GenHContext, | ||
| 1237 | filename: []const u8, | ||
| 1238 | name: []const u8, | ||
| 1239 | source: []const u8, | ||
| 1240 | expected_lines: []const []const u8, | ||
| 1241 | ) *TestCase { | ||
| 1242 | const tc = self.b.allocator.create(TestCase) catch unreachable; | ||
| 1243 | tc.* = TestCase{ | ||
| 1244 | .name = name, | ||
| 1245 | .sources = ArrayList(TestCase.SourceFile).init(self.b.allocator), | ||
| 1246 | .expected_lines = ArrayList([]const u8).init(self.b.allocator), | ||
| 1247 | }; | ||
| 1248 | |||
| 1249 | tc.addSourceFile(filename, source); | ||
| 1250 | var arg_i: usize = 0; | ||
| 1251 | while (arg_i < expected_lines.len) : (arg_i += 1) { | ||
| 1252 | tc.addExpectedLine(expected_lines[arg_i]); | ||
| 1253 | } | ||
| 1254 | return tc; | ||
| 1255 | } | ||
| 1256 | |||
| 1257 | pub fn add(self: *GenHContext, name: []const u8, source: []const u8, expected_lines: []const []const u8) void { | ||
| 1258 | const tc = self.create("test.zig", name, source, expected_lines); | ||
| 1259 | self.addCase(tc); | ||
| 1260 | } | ||
| 1261 | 1054 | ||
| 1262 | pub fn addCase(self: *GenHContext, case: *const TestCase) void { | 1055 | const triple_prefix = c_abi_target.zigTriple(b.allocator) catch @panic("OOM"); |
| 1263 | const b = self.b; | 1056 | test_step.setName(b.fmt("test-c-abi-{s}-{s} ", .{ |
| 1057 | triple_prefix, @tagName(optimize_mode), | ||
| 1058 | })); | ||
| 1264 | 1059 | ||
| 1265 | const optimize_mode = std.builtin.OptimizeMode.Debug; | 1060 | const run = test_step.run(); |
| 1266 | const annotated_case_name = fmt.allocPrint(self.b.allocator, "gen-h {s} ({s})", .{ case.name, @tagName(optimize_mode) }) catch unreachable; | 1061 | run.skip_foreign_checks = true; |
| 1267 | if (self.test_filter) |filter| { | 1062 | step.dependOn(&run.step); |
| 1268 | if (mem.indexOf(u8, annotated_case_name, filter) == null) return; | ||
| 1269 | } | 1063 | } |
| 1270 | |||
| 1271 | const write_src = b.addWriteFiles(); | ||
| 1272 | for (case.sources.items) |src_file| { | ||
| 1273 | write_src.add(src_file.filename, src_file.source); | ||
| 1274 | } | ||
| 1275 | |||
| 1276 | const obj = b.addObjectFromWriteFileStep("test", write_src, case.sources.items[0].filename); | ||
| 1277 | obj.setBuildMode(optimize_mode); | ||
| 1278 | |||
| 1279 | const cmp_h = GenHCmpOutputStep.create(self, obj, annotated_case_name, case); | ||
| 1280 | |||
| 1281 | self.step.dependOn(&cmp_h.step); | ||
| 1282 | } | 1064 | } |
| 1283 | }; | 1065 | return step; |
| 1284 | |||
| 1285 | fn printInvocation(args: []const []const u8) void { | ||
| 1286 | for (args) |arg| { | ||
| 1287 | std.debug.print("{s} ", .{arg}); | ||
| 1288 | } | ||
| 1289 | std.debug.print("\n", .{}); | ||
| 1290 | } | 1066 | } |
| 1291 | 1067 | ||
| 1292 | const c_abi_targets = [_]CrossTarget{ | 1068 | pub fn addCases( |
| 1293 | .{}, | 1069 | b: *std.Build, |
| 1294 | .{ | 1070 | parent_step: *Step, |
| 1295 | .cpu_arch = .x86_64, | 1071 | opt_test_filter: ?[]const u8, |
| 1296 | .os_tag = .linux, | 1072 | check_case_exe: *std.Build.CompileStep, |
| 1297 | .abi = .musl, | 1073 | ) !void { |
| 1298 | }, | 1074 | const arena = b.allocator; |
| 1299 | .{ | 1075 | const gpa = b.allocator; |
| 1300 | .cpu_arch = .x86, | 1076 | |
| 1301 | .os_tag = .linux, | 1077 | var cases = @import("src/Cases.zig").init(gpa, arena); |
| 1302 | .abi = .musl, | 1078 | |
| 1303 | }, | 1079 | var dir = try b.build_root.handle.openIterableDir("test/cases", .{}); |
| 1304 | .{ | 1080 | defer dir.close(); |
| 1305 | .cpu_arch = .aarch64, | 1081 | |
| 1306 | .os_tag = .linux, | 1082 | cases.addFromDir(dir); |
| 1307 | .abi = .musl, | 1083 | try @import("cases.zig").addCases(&cases); |
| 1308 | }, | 1084 | |
| 1309 | .{ | 1085 | const cases_dir_path = try b.build_root.join(b.allocator, &.{ "test", "cases" }); |
| 1310 | .cpu_arch = .arm, | 1086 | cases.lowerToBuildSteps( |
| 1311 | .os_tag = .linux, | 1087 | b, |
| 1312 | .abi = .musleabihf, | 1088 | parent_step, |
| 1313 | }, | 1089 | opt_test_filter, |
| 1314 | .{ | 1090 | cases_dir_path, |
| 1315 | .cpu_arch = .mips, | 1091 | check_case_exe, |
| 1316 | .os_tag = .linux, | 1092 | ); |
| 1317 | .abi = .musl, | ||
| 1318 | }, | ||
| 1319 | .{ | ||
| 1320 | .cpu_arch = .riscv64, | ||
| 1321 | .os_tag = .linux, | ||
| 1322 | .abi = .musl, | ||
| 1323 | }, | ||
| 1324 | .{ | ||
| 1325 | .cpu_arch = .wasm32, | ||
| 1326 | .os_tag = .wasi, | ||
| 1327 | .abi = .musl, | ||
| 1328 | }, | ||
| 1329 | .{ | ||
| 1330 | .cpu_arch = .powerpc, | ||
| 1331 | .os_tag = .linux, | ||
| 1332 | .abi = .musl, | ||
| 1333 | }, | ||
| 1334 | .{ | ||
| 1335 | .cpu_arch = .powerpc64le, | ||
| 1336 | .os_tag = .linux, | ||
| 1337 | .abi = .musl, | ||
| 1338 | }, | ||
| 1339 | .{ | ||
| 1340 | .cpu_arch = .x86, | ||
| 1341 | .os_tag = .windows, | ||
| 1342 | .abi = .gnu, | ||
| 1343 | }, | ||
| 1344 | .{ | ||
| 1345 | .cpu_arch = .x86_64, | ||
| 1346 | .os_tag = .windows, | ||
| 1347 | .abi = .gnu, | ||
| 1348 | }, | ||
| 1349 | }; | ||
| 1350 | |||
| 1351 | pub fn addCAbiTests(b: *std.Build, skip_non_native: bool, skip_release: bool) *Step { | ||
| 1352 | const step = b.step("test-c-abi", "Run the C ABI tests"); | ||
| 1353 | |||
| 1354 | const optimize_modes: [2]OptimizeMode = .{ .Debug, .ReleaseFast }; | ||
| 1355 | |||
| 1356 | for (optimize_modes[0 .. @as(u8, 1) + @boolToInt(!skip_release)]) |optimize_mode| for (c_abi_targets) |c_abi_target| { | ||
| 1357 | if (skip_non_native and !c_abi_target.isNative()) | ||
| 1358 | continue; | ||
| 1359 | |||
| 1360 | const test_step = b.addTest(.{ | ||
| 1361 | .root_source_file = .{ .path = "test/c_abi/main.zig" }, | ||
| 1362 | .optimize = optimize_mode, | ||
| 1363 | .target = c_abi_target, | ||
| 1364 | }); | ||
| 1365 | if (c_abi_target.abi != null and c_abi_target.abi.?.isMusl()) { | ||
| 1366 | // TODO NativeTargetInfo insists on dynamically linking musl | ||
| 1367 | // for some reason? | ||
| 1368 | test_step.target_info.dynamic_linker.max_byte = null; | ||
| 1369 | } | ||
| 1370 | test_step.linkLibC(); | ||
| 1371 | test_step.addCSourceFile("test/c_abi/cfuncs.c", &.{"-std=c99"}); | ||
| 1372 | |||
| 1373 | if (c_abi_target.isWindows() and (c_abi_target.getCpuArch() == .x86 or builtin.target.os.tag == .linux)) { | ||
| 1374 | // LTO currently incorrectly strips stdcall name-mangled functions | ||
| 1375 | // LLD crashes in LTO here when cross compiling for windows on linux | ||
| 1376 | test_step.want_lto = false; | ||
| 1377 | } | ||
| 1378 | |||
| 1379 | const triple_prefix = c_abi_target.zigTriple(b.allocator) catch unreachable; | ||
| 1380 | test_step.setNamePrefix(b.fmt("{s}-{s}-{s} ", .{ | ||
| 1381 | "test-c-abi", | ||
| 1382 | triple_prefix, | ||
| 1383 | @tagName(optimize_mode), | ||
| 1384 | })); | ||
| 1385 | |||
| 1386 | step.dependOn(&test_step.step); | ||
| 1387 | }; | ||
| 1388 | return step; | ||
| 1389 | } | 1093 | } |