authorgravatar for rpkak@noreply.codeberg.orgrpkak <rpkak@noreply.codeberg.org> 2025-08-26 21:47:59+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-09-24 16:05:18-07:00
log9b3b7aa911424c70b02987934a56000f06d79e9d
tree9d903b33e6db26324af0fd0f81183e842c2ba2cb
parent4fb08986cba65623c6dbabcbb83f0aa570e06a65

Integrate libc-test cases into the build system

zig build test-libc -Dlibc-test-path=/path/to/libc-test

4 files changed, 457 insertions(+), 0 deletions(-)

build.zig+6
......@@ -632,6 +632,12 @@ pub fn build(b: *std.Build) !void {
632632 const test_incremental_step = b.step("test-incremental", "Run the incremental compilation test cases");
633633 try tests.addIncrementalTests(b, test_incremental_step);
634634 test_step.dependOn(test_incremental_step);
635
636 if (tests.addLibcTests(b, .{
637 .optimize_modes = optimization_modes,
638 .test_filters = test_filters,
639 .test_target_filters = test_target_filters,
640 })) |test_libc_step| test_step.dependOn(test_libc_step);
635641}
636642
637643fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void {
test/libc.zig created+143
......@@ -0,0 +1,143 @@
1pub fn addCases(cases: *tests.LibcContext) void {
2 cases.addLibcTestCase("api/main.c", true, .{});
3
4 cases.addLibcTestCase("functional/argv.c", true, .{});
5 cases.addLibcTestCase("functional/basename.c", true, .{});
6 cases.addLibcTestCase("functional/clocale_mbfuncs.c", true, .{});
7 cases.addLibcTestCase("functional/clock_gettime.c", true, .{});
8 cases.addLibcTestCase("functional/crypt.c", true, .{});
9 cases.addLibcTestCase("functional/dirname.c", true, .{});
10 cases.addLibcTestCase("functional/env.c", true, .{});
11 cases.addLibcTestCase("functional/fcntl.c", false, .{});
12 cases.addLibcTestCase("functional/fdopen.c", false, .{});
13 cases.addLibcTestCase("functional/fnmatch.c", true, .{});
14 cases.addLibcTestCase("functional/fscanf.c", false, .{});
15 cases.addLibcTestCase("functional/fwscanf.c", false, .{});
16 cases.addLibcTestCase("functional/iconv_open.c", true, .{});
17 cases.addLibcTestCase("functional/inet_pton.c", false, .{});
18 // "functional/ipc_msg.c": Probably a bug in qemu
19 // "functional/ipc_sem.c": Probably a bug in qemu
20 // "functional/ipc_shm.c": Probably a bug in qemu
21 cases.addLibcTestCase("functional/mbc.c", true, .{});
22 cases.addLibcTestCase("functional/memstream.c", true, .{});
23 // "functional/mntent.c": https://www.openwall.com/lists/musl/2024/10/22/1
24 cases.addLibcTestCase("functional/popen.c", false, .{});
25 cases.addLibcTestCase("functional/pthread_cancel-points.c", false, .{});
26 cases.addLibcTestCase("functional/pthread_cancel.c", false, .{});
27 cases.addLibcTestCase("functional/pthread_cond.c", false, .{});
28 cases.addLibcTestCase("functional/pthread_mutex.c", false, .{});
29 // "functional/pthread_mutex_pi.c": Probably a bug in qemu (big/little endian FUTEX_LOCK_PI)
30 // "functional/pthread_robust.c": https://gitlab.com/qemu-project/qemu/-/issues/2424
31 cases.addLibcTestCase("functional/pthread_tsd.c", false, .{});
32 cases.addLibcTestCase("functional/qsort.c", true, .{});
33 cases.addLibcTestCase("functional/random.c", true, .{});
34 cases.addLibcTestCase("functional/search_hsearch.c", false, .{}); // The test suite of wasi-libc runs this test case
35 cases.addLibcTestCase("functional/search_insque.c", true, .{});
36 cases.addLibcTestCase("functional/search_lsearch.c", true, .{});
37 cases.addLibcTestCase("functional/search_tsearch.c", true, .{});
38 cases.addLibcTestCase("functional/sem_init.c", false, .{});
39 cases.addLibcTestCase("functional/sem_open.c", false, .{});
40 cases.addLibcTestCase("functional/setjmp.c", false, .{});
41 cases.addLibcTestCase("functional/snprintf.c", true, .{});
42 cases.addLibcTestCase("functional/socket.c", false, .{});
43 cases.addLibcTestCase("functional/spawn.c", false, .{});
44 cases.addLibcTestCase("functional/sscanf.c", true, .{});
45 cases.addLibcTestCase("functional/sscanf_long.c", false, .{});
46 cases.addLibcTestCase("functional/stat.c", false, .{});
47 cases.addLibcTestCase("functional/strftime.c", true, .{});
48 cases.addLibcTestCase("functional/string.c", true, .{});
49 cases.addLibcTestCase("functional/string_memcpy.c", true, .{});
50 cases.addLibcTestCase("functional/string_memmem.c", true, .{});
51 cases.addLibcTestCase("functional/string_memset.c", true, .{});
52 cases.addLibcTestCase("functional/string_strchr.c", true, .{});
53 cases.addLibcTestCase("functional/string_strcspn.c", true, .{});
54 cases.addLibcTestCase("functional/string_strstr.c", true, .{});
55 cases.addLibcTestCase("functional/strtod.c", true, .{});
56 cases.addLibcTestCase("functional/strtod_long.c", true, .{});
57 cases.addLibcTestCase("functional/strtod_simple.c", true, .{});
58 cases.addLibcTestCase("functional/strtof.c", true, .{});
59 cases.addLibcTestCase("functional/strtol.c", true, .{});
60 cases.addLibcTestCase("functional/strtold.c", true, .{});
61 cases.addLibcTestCase("functional/swprintf.c", true, .{});
62 cases.addLibcTestCase("functional/tgmath.c", true, .{});
63 cases.addLibcTestCase("functional/time.c", false, .{});
64 cases.addLibcTestCase("functional/tls_align.c", true, .{ .additional_src_file = "functional/tls_align_dso.c" });
65 cases.addLibcTestCase("functional/tls_init.c", false, .{});
66 cases.addLibcTestCase("functional/tls_local_exec.c", false, .{});
67 cases.addLibcTestCase("functional/udiv.c", true, .{});
68 cases.addLibcTestCase("functional/ungetc.c", false, .{});
69 cases.addLibcTestCase("functional/utime.c", false, .{});
70 cases.addLibcTestCase("functional/vfork.c", false, .{});
71 cases.addLibcTestCase("functional/wcsstr.c", true, .{});
72 cases.addLibcTestCase("functional/wcstol.c", true, .{});
73
74 cases.addLibcTestCase("regression/daemon-failure.c", false, .{});
75 cases.addLibcTestCase("regression/dn_expand-empty.c", false, .{});
76 cases.addLibcTestCase("regression/dn_expand-ptr-0.c", false, .{});
77 cases.addLibcTestCase("regression/execle-env.c", false, .{});
78 cases.addLibcTestCase("regression/fflush-exit.c", false, .{});
79 cases.addLibcTestCase("regression/fgets-eof.c", true, .{});
80 cases.addLibcTestCase("regression/fgetwc-buffering.c", false, .{});
81 cases.addLibcTestCase("regression/flockfile-list.c", false, .{});
82 cases.addLibcTestCase("regression/fpclassify-invalid-ld80.c", true, .{});
83 cases.addLibcTestCase("regression/ftello-unflushed-append.c", false, .{});
84 cases.addLibcTestCase("regression/getpwnam_r-crash.c", false, .{});
85 cases.addLibcTestCase("regression/getpwnam_r-errno.c", false, .{});
86 cases.addLibcTestCase("regression/iconv-roundtrips.c", true, .{});
87 cases.addLibcTestCase("regression/inet_ntop-v4mapped.c", true, .{});
88 cases.addLibcTestCase("regression/inet_pton-empty-last-field.c", true, .{});
89 cases.addLibcTestCase("regression/iswspace-null.c", true, .{});
90 cases.addLibcTestCase("regression/lrand48-signextend.c", true, .{});
91 cases.addLibcTestCase("regression/lseek-large.c", false, .{});
92 cases.addLibcTestCase("regression/malloc-0.c", true, .{});
93 // "regression/malloc-brk-fail.c": QEMU OOM
94 cases.addLibcTestCase("regression/malloc-oom.c", false, .{}); // wasi-libc: requires t_memfill
95 cases.addLibcTestCase("regression/mbsrtowcs-overflow.c", true, .{});
96 cases.addLibcTestCase("regression/memmem-oob-read.c", true, .{});
97 cases.addLibcTestCase("regression/memmem-oob.c", true, .{});
98 cases.addLibcTestCase("regression/mkdtemp-failure.c", false, .{});
99 cases.addLibcTestCase("regression/mkstemp-failure.c", false, .{});
100 cases.addLibcTestCase("regression/printf-1e9-oob.c", true, .{});
101 cases.addLibcTestCase("regression/printf-fmt-g-round.c", true, .{});
102 cases.addLibcTestCase("regression/printf-fmt-g-zeros.c", true, .{});
103 cases.addLibcTestCase("regression/printf-fmt-n.c", true, .{});
104 // "regression/pthread-robust-detach.c": https://gitlab.com/qemu-project/qemu/-/issues/2424
105 cases.addLibcTestCase("regression/pthread_atfork-errno-clobber.c", false, .{});
106 cases.addLibcTestCase("regression/pthread_cancel-sem_wait.c", false, .{});
107 cases.addLibcTestCase("regression/pthread_cond-smasher.c", false, .{});
108 cases.addLibcTestCase("regression/pthread_cond_wait-cancel_ignored.c", false, .{});
109 cases.addLibcTestCase("regression/pthread_condattr_setclock.c", false, .{});
110 // "regression/pthread_create-oom.c": QEMU OOM
111 cases.addLibcTestCase("regression/pthread_exit-cancel.c", false, .{});
112 cases.addLibcTestCase("regression/pthread_exit-dtor.c", false, .{});
113 cases.addLibcTestCase("regression/pthread_once-deadlock.c", false, .{});
114 cases.addLibcTestCase("regression/pthread_rwlock-ebusy.c", false, .{});
115 cases.addLibcTestCase("regression/putenv-doublefree.c", true, .{});
116 cases.addLibcTestCase("regression/raise-race.c", false, .{});
117 cases.addLibcTestCase("regression/regex-backref-0.c", true, .{});
118 cases.addLibcTestCase("regression/regex-bracket-icase.c", true, .{});
119 cases.addLibcTestCase("regression/regex-ere-backref.c", true, .{});
120 cases.addLibcTestCase("regression/regex-escaped-high-byte.c", true, .{});
121 cases.addLibcTestCase("regression/regex-negated-range.c", true, .{});
122 cases.addLibcTestCase("regression/regexec-nosub.c", true, .{});
123 cases.addLibcTestCase("regression/rewind-clear-error.c", false, .{});
124 cases.addLibcTestCase("regression/rlimit-open-files.c", false, .{});
125 cases.addLibcTestCase("regression/scanf-bytes-consumed.c", true, .{});
126 cases.addLibcTestCase("regression/scanf-match-literal-eof.c", true, .{});
127 cases.addLibcTestCase("regression/scanf-nullbyte-char.c", true, .{});
128 cases.addLibcTestCase("regression/sem_close-unmap.c", false, .{});
129 // "regression/setenv-oom.c": QEMU OOM
130 cases.addLibcTestCase("regression/setvbuf-unget.c", true, .{});
131 cases.addLibcTestCase("regression/sigaltstack.c", false, .{});
132 cases.addLibcTestCase("regression/sigprocmask-internal.c", false, .{});
133 cases.addLibcTestCase("regression/sigreturn.c", true, .{});
134 cases.addLibcTestCase("regression/sscanf-eof.c", true, .{});
135 cases.addLibcTestCase("regression/strverscmp.c", true, .{});
136 cases.addLibcTestCase("regression/syscall-sign-extend.c", false, .{});
137 cases.addLibcTestCase("regression/uselocale-0.c", true, .{});
138 cases.addLibcTestCase("regression/wcsncpy-read-overflow.c", true, .{});
139 cases.addLibcTestCase("regression/wcsstr-false-negative.c", true, .{});
140}
141
142const std = @import("std");
143const tests = @import("tests.zig");
test/src/Libc.zig created+118
......@@ -0,0 +1,118 @@
1b: *std.Build,
2options: Options,
3root_step: *std.Build.Step,
4
5libc_test_src_path: std.Build.LazyPath,
6
7test_cases: std.ArrayList(TestCase) = .empty,
8
9pub const Options = struct {
10 optimize_modes: []const std.builtin.OptimizeMode,
11 test_filters: []const []const u8,
12 test_target_filters: []const []const u8,
13};
14
15const TestCase = struct {
16 name: []const u8,
17 src_file: std.Build.LazyPath,
18 additional_src_file: ?std.Build.LazyPath,
19 supports_wasi_libc: bool,
20};
21
22pub const LibcTestCaseOption = struct {
23 additional_src_file: ?[]const u8 = null,
24};
25
26pub fn addLibcTestCase(
27 libc: *Libc,
28 path: []const u8,
29 supports_wasi_libc: bool,
30 options: LibcTestCaseOption,
31) void {
32 const name = libc.b.dupe(path[0 .. path.len - std.fs.path.extension(path).len]);
33 std.mem.replaceScalar(u8, name, '/', '.');
34 libc.test_cases.append(libc.b.allocator, .{
35 .name = name,
36 .src_file = libc.libc_test_src_path.path(libc.b, path),
37 .additional_src_file = if (options.additional_src_file) |additional_src_file| libc.libc_test_src_path.path(libc.b, additional_src_file) else null,
38 .supports_wasi_libc = supports_wasi_libc,
39 }) catch @panic("OOM");
40}
41
42pub fn addTarget(libc: *const Libc, target: std.Build.ResolvedTarget) void {
43 if (libc.options.test_target_filters.len > 0) {
44 const triple_txt = target.query.zigTriple(libc.b.allocator) catch @panic("OOM");
45 for (libc.options.test_target_filters) |filter| {
46 if (std.mem.indexOf(u8, triple_txt, filter)) |_| break;
47 } else return;
48 }
49
50 const common = libc.libc_test_src_path.path(libc.b, "common");
51
52 for (libc.options.optimize_modes) |optimize| {
53 const libtest_mod = libc.b.createModule(.{
54 .target = target,
55 .optimize = optimize,
56 .link_libc = true,
57 });
58
59 var libtest_c_source_files: []const []const u8 = &.{ "print.c", "rand.c", "setrlim.c", "memfill.c", "vmfill.c", "fdfill.c", "utf8.c" };
60 libtest_mod.addCSourceFiles(.{
61 .root = common,
62 .files = libtest_c_source_files[0..if (target.result.isMuslLibC()) 7 else 2],
63 .flags = &.{"-fno-builtin"},
64 });
65
66 const libtest = libc.b.addLibrary(.{
67 .name = "test",
68 .root_module = libtest_mod,
69 });
70
71 for (libc.test_cases.items) |*test_case| {
72 if (target.result.isWasiLibC() and !test_case.supports_wasi_libc)
73 continue;
74
75 const annotated_case_name = libc.b.fmt("run libc-test {s} ({t})", .{ test_case.name, optimize });
76 for (libc.options.test_filters) |test_filter| {
77 if (std.mem.indexOf(u8, annotated_case_name, test_filter)) |_| break;
78 } else if (libc.options.test_filters.len > 0) continue;
79
80 const mod = libc.b.createModule(.{
81 .target = target,
82 .optimize = optimize,
83 .link_libc = true,
84 });
85 mod.addIncludePath(common);
86 if (target.result.isWasiLibC())
87 mod.addCMacro("_WASI_EMULATED_SIGNAL", "");
88 mod.addCSourceFile(.{
89 .file = test_case.src_file,
90 .flags = &.{"-fno-builtin"},
91 });
92 if (test_case.additional_src_file) |additional_src_file| {
93 mod.addCSourceFile(.{
94 .file = additional_src_file,
95 .flags = &.{"-fno-builtin"},
96 });
97 }
98 mod.linkLibrary(libtest);
99
100 const exe = libc.b.addExecutable(.{
101 .name = test_case.name,
102 .root_module = mod,
103 });
104
105 const run = libc.b.addRunArtifact(exe);
106 run.setName(annotated_case_name);
107 run.skip_foreign_checks = true;
108 run.expectStdErrEqual("");
109 run.expectStdOutEqual("");
110 run.expectExitCode(0);
111
112 libc.root_step.dependOn(&run.step);
113 }
114 }
115}
116
117const Libc = @This();
118const std = @import("std");
test/tests.zig+190
......@@ -10,6 +10,7 @@ const stack_traces = @import("stack_traces.zig");
1010const translate_c = @import("translate_c.zig");
1111const run_translated_c = @import("run_translated_c.zig");
1212const llvm_ir = @import("llvm_ir.zig");
13const libc = @import("libc.zig");
1314
1415// Implementations
1516pub const TranslateCContext = @import("src/TranslateC.zig");
......@@ -17,6 +18,7 @@ pub const RunTranslatedCContext = @import("src/RunTranslatedC.zig");
1718pub const StackTracesContext = @import("src/StackTrace.zig");
1819pub const DebuggerContext = @import("src/Debugger.zig");
1920pub const LlvmIrContext = @import("src/LlvmIr.zig");
21pub const LibcContext = @import("src/Libc.zig");
2022
2123const TestTarget = struct {
2224 linkage: ?std.builtin.LinkMode = null,
......@@ -2669,3 +2671,191 @@ pub fn addLlvmIrTests(b: *std.Build, options: LlvmIrContext.Options) ?*Step {
26692671
26702672 return step;
26712673}
2674
2675const libc_targets: []const std.Target.Query = &.{
2676 .{
2677 .cpu_arch = .arm,
2678 .os_tag = .linux,
2679 .abi = .musleabi,
2680 },
2681 .{
2682 .cpu_arch = .arm,
2683 .os_tag = .linux,
2684 .abi = .musleabihf,
2685 },
2686 .{
2687 .cpu_arch = .armeb,
2688 .os_tag = .linux,
2689 .abi = .musleabi,
2690 },
2691 .{
2692 .cpu_arch = .armeb,
2693 .os_tag = .linux,
2694 .abi = .musleabihf,
2695 },
2696 .{
2697 .cpu_arch = .thumb,
2698 .os_tag = .linux,
2699 .abi = .musleabi,
2700 },
2701 .{
2702 .cpu_arch = .thumb,
2703 .os_tag = .linux,
2704 .abi = .musleabihf,
2705 },
2706 .{
2707 .cpu_arch = .thumbeb,
2708 .os_tag = .linux,
2709 .abi = .musleabi,
2710 },
2711 .{
2712 .cpu_arch = .thumbeb,
2713 .os_tag = .linux,
2714 .abi = .musleabihf,
2715 },
2716 .{
2717 .cpu_arch = .aarch64,
2718 .os_tag = .linux,
2719 .abi = .musl,
2720 },
2721 .{
2722 .cpu_arch = .aarch64_be,
2723 .os_tag = .linux,
2724 .abi = .musl,
2725 },
2726 // .{
2727 // .cpu_arch = .hexagon,
2728 // .os_tag = .linux,
2729 // .abi = .musl,
2730 // },
2731 .{
2732 .cpu_arch = .loongarch64,
2733 .os_tag = .linux,
2734 .abi = .musl,
2735 },
2736 .{
2737 .cpu_arch = .loongarch64,
2738 .os_tag = .linux,
2739 .abi = .muslsf,
2740 },
2741 // .{
2742 // .cpu_arch = .mips,
2743 // .os_tag = .linux,
2744 // .abi = .musleabi,
2745 // },
2746 // .{
2747 // .cpu_arch = .mips,
2748 // .os_tag = .linux,
2749 // .abi = .musleabihf,
2750 // },
2751 // .{
2752 // .cpu_arch = .mipsel,
2753 // .os_tag = .linux,
2754 // .abi = .musleabi,
2755 // },
2756 // .{
2757 // .cpu_arch = .mipsel,
2758 // .os_tag = .linux,
2759 // .abi = .musleabihf,
2760 // },
2761 // .{
2762 // .cpu_arch = .mips64,
2763 // .os_tag = .linux,
2764 // .abi = .muslabi64,
2765 // },
2766 // .{
2767 // .cpu_arch = .mips64,
2768 // .os_tag = .linux,
2769 // .abi = .muslabin32,
2770 // },
2771 // .{
2772 // .cpu_arch = .mips64el,
2773 // .os_tag = .linux,
2774 // .abi = .muslabi64,
2775 // },
2776 // .{
2777 // .cpu_arch = .mips64el,
2778 // .os_tag = .linux,
2779 // .abi = .muslabin32,
2780 // },
2781 .{
2782 .cpu_arch = .powerpc,
2783 .os_tag = .linux,
2784 .abi = .musleabi,
2785 },
2786 .{
2787 .cpu_arch = .powerpc,
2788 .os_tag = .linux,
2789 .abi = .musleabihf,
2790 },
2791 .{
2792 .cpu_arch = .powerpc64,
2793 .os_tag = .linux,
2794 .abi = .musl,
2795 },
2796 .{
2797 .cpu_arch = .powerpc64le,
2798 .os_tag = .linux,
2799 .abi = .musl,
2800 },
2801 .{
2802 .cpu_arch = .riscv32,
2803 .os_tag = .linux,
2804 .abi = .musl,
2805 },
2806 .{
2807 .cpu_arch = .riscv64,
2808 .os_tag = .linux,
2809 .abi = .musl,
2810 },
2811 .{
2812 .cpu_arch = .s390x,
2813 .os_tag = .linux,
2814 .abi = .musl,
2815 },
2816 .{
2817 .cpu_arch = .wasm32,
2818 .os_tag = .wasi,
2819 .abi = .musl,
2820 },
2821 .{
2822 .cpu_arch = .x86,
2823 .os_tag = .linux,
2824 .abi = .musl,
2825 },
2826 .{
2827 .cpu_arch = .x86_64,
2828 .os_tag = .linux,
2829 .abi = .musl,
2830 },
2831 .{
2832 .cpu_arch = .x86_64,
2833 .os_tag = .linux,
2834 .abi = .muslx32,
2835 },
2836};
2837
2838pub fn addLibcTests(b: *std.Build, options: LibcContext.Options) ?*Step {
2839 const step = b.step("test-libc", "Run libc-test test cases");
2840 const opt_libc_test_path = b.option(std.Build.LazyPath, "libc-test-path", "path to libc-test source directory");
2841 if (opt_libc_test_path) |libc_test_path| {
2842 var context: LibcContext = .{
2843 .b = b,
2844 .options = options,
2845 .root_step = step,
2846 .libc_test_src_path = libc_test_path.path(b, "src"),
2847 };
2848
2849 libc.addCases(&context);
2850
2851 for (libc_targets) |target_query| {
2852 const target = b.resolveTargetQuery(target_query);
2853 context.addTarget(target);
2854 }
2855
2856 return step;
2857 } else {
2858 step.dependOn(&b.addFail("The -Dlibc-test-path=... option is required for this step").step);
2859 return null;
2860 }
2861}