1const std = @import("std");
2const builtin = @import("builtin");
3
4// To run executables linked against a specific glibc version, the
5// run-time glibc version needs to be new enough. Check the host's glibc
6// version. Note that this does not allow for translation/vm/emulation
7// services to run these tests.
8const running_glibc_ver = builtin.os.versionRange().gnuLibCVersion();
9
10pub fn build(b: *std.Build) void {
11 const test_step = b.step("test", "Test");
12 b.default_step = test_step;
13
14 for ([_][]const u8{ "aarch64-linux-gnu.2.27", "aarch64-linux-gnu.2.34" }) |t| {
15 const exe = b.addExecutable(.{
16 .name = t,
17 .root_module = b.createModule(.{
18 .root_source_file = null,
19 .target = b.resolveTargetQuery(std.Target.Query.parse(
20 .{ .arch_os_abi = t },
21 ) catch unreachable),
22 .link_libc = true,
23 }),
24 });
25 // We disable UBSAN for these tests as the libc being tested here is
26 // so old, it doesn't even support compiling our UBSAN implementation.
27 exe.bundle_ubsan_rt = false;
28 exe.root_module.sanitize_c = .off;
29 exe.root_module.addCSourceFile(.{ .file = b.path("main.c") });
30 // TODO: actually test the output
31 _ = exe.getEmittedBin();
32 test_step.dependOn(&exe.step);
33 }
34
35 // Build & run a C test case against a sampling of supported glibc versions
36 versions: for ([_][]const u8{
37 // "native-linux-gnu.2.0", // fails with a pile of missing symbols.
38 "native-linux-gnu.2.2.5",
39 "native-linux-gnu.2.4",
40 "native-linux-gnu.2.12",
41 "native-linux-gnu.2.16",
42 "native-linux-gnu.2.22",
43 "native-linux-gnu.2.28",
44 "native-linux-gnu.2.33",
45 "native-linux-gnu.2.38",
46 "native-linux-gnu",
47 }) |t| {
48 const target = b.resolveTargetQuery(std.Target.Query.parse(
49 .{ .arch_os_abi = t },
50 ) catch unreachable);
51
52 const glibc_ver = target.result.os.version_range.linux.glibc;
53
54 // only build test if glibc version supports the architecture
55 for (std.zig.target.available_libcs) |libc| {
56 if (libc.arch != target.result.cpu.arch or
57 libc.os != target.result.os.tag or
58 libc.abi != target.result.abi)
59 continue;
60
61 if (libc.glibc_min) |min| {
62 if (glibc_ver.order(min) == .lt) continue :versions;
63 }
64 }
65
66 const exe = b.addExecutable(.{
67 .name = t,
68 .root_module = b.createModule(.{
69 .root_source_file = null,
70 .target = target,
71 .link_libc = true,
72 }),
73 });
74 // We disable UBSAN for these tests as the libc being tested here is
75 // so old, it doesn't even support compiling our UBSAN implementation.
76 exe.bundle_ubsan_rt = false;
77 exe.root_module.sanitize_c = .off;
78 exe.root_module.addCSourceFile(.{ .file = b.path("glibc_runtime_check.c") });
79
80 // Only try running the test if the host glibc is known to be good enough. Ideally, the Zig
81 // test runner would be able to check this, but see https://github.com/ziglang/zig/pull/17702#issuecomment-1831310453
82 if (running_glibc_ver) |running_ver| {
83 if (glibc_ver.order(running_ver) == .lt) {
84 const run_cmd = b.addRunArtifact(exe);
85 run_cmd.skip_foreign_checks = true;
86 run_cmd.expectExitCode(0);
87
88 test_step.dependOn(&run_cmd.step);
89 }
90 }
91 }
92
93 // Build & run a Zig test case against a sampling of supported glibc versions
94 versions: for ([_][]const u8{
95 "native-linux-gnu.2.17", // Currently oldest supported, see #17769
96 "native-linux-gnu.2.23",
97 "native-linux-gnu.2.28",
98 "native-linux-gnu.2.33",
99 "native-linux-gnu.2.38",
100 "native-linux-gnu",
101 }) |t| {
102 const target = b.resolveTargetQuery(std.Target.Query.parse(
103 .{ .arch_os_abi = t },
104 ) catch unreachable);
105
106 if (target.result.cpu.arch.isLoongArch()) continue; // https://github.com/Vexu/arocc/issues/1096
107
108 const glibc_ver = target.result.os.version_range.linux.glibc;
109
110 // only build test if glibc version supports the architecture
111 for (std.zig.target.available_libcs) |libc| {
112 if (libc.arch != target.result.cpu.arch or
113 libc.os != target.result.os.tag or
114 libc.abi != target.result.abi)
115 continue;
116
117 if (libc.glibc_min) |min| {
118 if (glibc_ver.order(min) == .lt) continue :versions;
119 }
120 }
121
122 const malloc_translation = b.addTranslateC(.{
123 .root_source_file = b.path("include_malloc.h"),
124 .target = target,
125 .optimize = .debug,
126 .link_libc = true,
127 });
128 const stdlib_translation = b.addTranslateC(.{
129 .root_source_file = b.path("include_stdlib.h"),
130 .target = target,
131 .optimize = .debug,
132 .link_libc = true,
133 });
134 const string_translation = b.addTranslateC(.{
135 .root_source_file = b.path("include_string.h"),
136 .target = target,
137 .optimize = .debug,
138 .link_libc = true,
139 });
140 const exe = b.addExecutable(.{
141 .name = t,
142 .root_module = b.createModule(.{
143 .root_source_file = b.path("glibc_runtime_check.zig"),
144 .target = target,
145 .link_libc = true,
146 .imports = &.{
147 .{
148 .name = "malloc.h",
149 .module = malloc_translation.createModule(),
150 },
151 .{
152 .name = "stdlib.h",
153 .module = stdlib_translation.createModule(),
154 },
155 .{
156 .name = "string.h",
157 .module = string_translation.createModule(),
158 },
159 },
160 }),
161 });
162 // We disable UBSAN for these tests as the libc being tested here is
163 // so old, it doesn't even support compiling our UBSAN implementation.
164 exe.bundle_ubsan_rt = false;
165 exe.root_module.sanitize_c = .off;
166
167 // Only try running the test if the host glibc is known to be good enough. Ideally, the Zig
168 // test runner would be able to check this, but see https://github.com/ziglang/zig/pull/17702#issuecomment-1831310453
169 if (running_glibc_ver) |running_ver| {
170 if (glibc_ver.order(running_ver) == .lt) {
171 const run_cmd = b.addRunArtifact(exe);
172 run_cmd.skip_foreign_checks = true;
173 run_cmd.expectExitCode(0);
174
175 test_step.dependOn(&run_cmd.step);
176 }
177 }
178 }
179}