build.zig (7998B) - Raw
1 const std = @import("std"); 2 const builtin = @import("builtin"); 3 4 const headers = &[_][]const u8{ 5 "common.h", 6 "ast.h", 7 "parser.h", 8 "zir.h", 9 "astgen.h", 10 }; 11 12 const c_lib_files = &[_][]const u8{ 13 "tokenizer.c", 14 "ast.c", 15 "zig0.c", 16 "parser.c", 17 "zir.c", 18 "astgen.c", 19 }; 20 21 const all_c_files = c_lib_files ++ &[_][]const u8{"main.c"}; 22 23 const cflags = &[_][]const u8{ 24 "-std=c11", 25 "-Wall", 26 "-Wvla", 27 "-Wextra", 28 "-Werror", 29 "-Wshadow", 30 "-Wswitch", 31 "-Walloca", 32 "-Wformat=2", 33 "-fno-common", 34 "-Wconversion", 35 "-Wuninitialized", 36 "-Wdouble-promotion", 37 "-fstack-protector-all", 38 "-Wimplicit-fallthrough", 39 "-Wno-unused-function", // TODO remove once refactoring is done 40 //"-D_FORTIFY_SOURCE=2", // consider when optimization flags are enabled 41 }; 42 43 const compilers = &[_][]const u8{ "zig", "clang", "gcc", "tcc" }; 44 45 pub fn build(b: *std.Build) !void { 46 const optimize = b.standardOptimizeOption(.{}); 47 48 const cc = b.option([]const u8, "cc", "C compiler") orelse "zig"; 49 const no_exec = b.option(bool, "no-exec", "Compile test binary without running it") orelse false; 50 const valgrind = b.option(bool, "valgrind", "Run tests under valgrind") orelse false; 51 const test_timeout = b.option([]const u8, "test-timeout", "Test execution timeout (default: 10s, none with valgrind)"); 52 53 const target = blk: { 54 var query = b.standardTargetOptionsQueryOnly(.{}); 55 if (valgrind) { 56 const arch = query.cpu_arch orelse builtin.cpu.arch; 57 if (arch == .x86_64) { 58 query.cpu_features_sub.addFeature(@intFromEnum(std.Target.x86.Feature.avx512f)); 59 } 60 } 61 break :blk b.resolveTargetQuery(query); 62 }; 63 64 const test_step = b.step("test", "Run unit tests"); 65 addTestStep(b, test_step, target, optimize, cc, no_exec, valgrind, test_timeout); 66 67 const fmt_step = b.step("fmt", "clang-format"); 68 const clang_format = b.addSystemCommand(&.{ "clang-format", "-i" }); 69 for (all_c_files ++ headers) |f| clang_format.addFileArg(b.path(f)); 70 fmt_step.dependOn(&clang_format.step); 71 72 const lint_step = b.step("lint", "Run linters"); 73 74 for (all_c_files) |cfile| { 75 const clang_analyze = b.addSystemCommand(&.{ 76 "clang", 77 "--analyze", 78 "--analyzer-output", 79 "text", 80 "-Wno-unused-command-line-argument", 81 "-Werror", 82 // false positive in astgen.c comptimeDecl: analyzer cannot track 83 // scratch_instructions ownership through pointer parameters. 84 "-Xclang", 85 "-analyzer-disable-checker", 86 "-Xclang", 87 "unix.Malloc", 88 }); 89 clang_analyze.addFileArg(b.path(cfile)); 90 clang_analyze.expectExitCode(0); 91 lint_step.dependOn(&clang_analyze.step); 92 93 // TODO(motiejus) re-enable once project 94 // nears completion. Takes too long for comfort. 95 //const gcc_analyze = b.addSystemCommand(&.{ 96 // "gcc", 97 // "-c", 98 // "--analyzer", 99 // "-Werror", 100 // "-o", 101 // "/dev/null", 102 //}); 103 //gcc_analyze.addFileArg(b.path(cfile)); 104 //gcc_analyze.expectExitCode(0); 105 //lint_step.dependOn(&gcc_analyze.step); 106 107 const cppcheck = b.addSystemCommand(&.{ 108 "cppcheck", 109 "--quiet", 110 "--error-exitcode=1", 111 "--check-level=exhaustive", 112 "--enable=all", 113 "--inline-suppr", 114 "--suppress=missingIncludeSystem", 115 "--suppress=checkersReport", 116 "--suppress=unusedFunction", // TODO remove after plumbing is done 117 "--suppress=unusedStructMember", // TODO remove after plumbing is done 118 "--suppress=unmatchedSuppression", 119 }); 120 cppcheck.addFileArg(b.path(cfile)); 121 cppcheck.expectExitCode(0); 122 lint_step.dependOn(&cppcheck.step); 123 } 124 125 const fmt_check = b.addSystemCommand(&.{ "clang-format", "--dry-run", "-Werror" }); 126 for (all_c_files ++ headers) |f| fmt_check.addFileArg(b.path(f)); 127 fmt_check.expectExitCode(0); 128 b.default_step.dependOn(&fmt_check.step); 129 130 for (compilers) |compiler| { 131 addTestStep(b, b.default_step, target, optimize, compiler, false, valgrind, test_timeout); 132 } 133 134 const all_step = b.step("all", "Run fmt check, lint, and tests with all compilers"); 135 all_step.dependOn(b.default_step); 136 all_step.dependOn(lint_step); 137 } 138 139 fn addTestStep( 140 b: *std.Build, 141 step: *std.Build.Step, 142 target: std.Build.ResolvedTarget, 143 optimize: std.builtin.OptimizeMode, 144 cc: []const u8, 145 no_exec: bool, 146 valgrind: bool, 147 test_timeout: ?[]const u8, 148 ) void { 149 const test_mod = b.createModule(.{ 150 .root_source_file = b.path("test_all.zig"), 151 .optimize = optimize, 152 .target = target, 153 }); 154 test_mod.addIncludePath(b.path(".")); 155 156 // TODO(zig 0.16+): remove this if block entirely; keep only the addLibrary branch. 157 // Also delete addCObjectsDirectly. 158 // Zig 0.15's ELF archive parser fails on archives containing odd-sized objects 159 // (off-by-one after 2-byte alignment). This is fixed on zig master/0.16. 160 if (comptime builtin.zig_version.order(.{ .major = 0, .minor = 16, .patch = 0 }) == .lt) { 161 addCObjectsDirectly(b, test_mod, cc, optimize); 162 } else { 163 const lib_mod = b.createModule(.{ 164 .optimize = optimize, 165 .target = target, 166 .link_libc = true, 167 }); 168 const lib = b.addLibrary(.{ 169 .name = b.fmt("zig0-{s}", .{cc}), 170 .root_module = lib_mod, 171 }); 172 addCSources(b, lib.root_module, cc, optimize); 173 test_mod.linkLibrary(lib); 174 } 175 176 const test_exe = b.addTest(.{ 177 .root_module = test_mod, 178 .use_llvm = false, 179 .use_lld = false, 180 }); 181 const timeout: ?[]const u8 = test_timeout orelse if (valgrind) null else "10"; 182 if (valgrind) { 183 if (timeout) |t| 184 test_exe.setExecCmd(&.{ 185 "timeout", 186 t, 187 "valgrind", 188 "--error-exitcode=2", 189 "--leak-check=full", 190 "--show-leak-kinds=all", 191 "--errors-for-leak-kinds=all", 192 "--track-fds=yes", 193 null, 194 }) 195 else 196 test_exe.setExecCmd(&.{ 197 "valgrind", 198 "--error-exitcode=2", 199 "--leak-check=full", 200 "--show-leak-kinds=all", 201 "--errors-for-leak-kinds=all", 202 "--track-fds=yes", 203 null, 204 }); 205 } else { 206 test_exe.setExecCmd(&.{ "timeout", timeout orelse "10", null }); 207 } 208 if (no_exec) { 209 const install = b.addInstallArtifact(test_exe, .{}); 210 step.dependOn(&install.step); 211 } else { 212 step.dependOn(&b.addRunArtifact(test_exe).step); 213 } 214 } 215 216 fn addCSources( 217 b: *std.Build, 218 mod: *std.Build.Module, 219 cc: []const u8, 220 optimize: std.builtin.OptimizeMode, 221 ) void { 222 if (std.mem.eql(u8, cc, "zig")) { 223 mod.addCSourceFiles(.{ .files = c_lib_files, .flags = cflags }); 224 } else for (c_lib_files) |cfile| { 225 const cc1 = b.addSystemCommand(&.{cc}); 226 cc1.addArgs(cflags ++ .{"-g"}); 227 cc1.addArg(switch (optimize) { 228 .Debug => "-O0", 229 .ReleaseFast, .ReleaseSafe => "-O3", 230 .ReleaseSmall => "-Os", 231 }); 232 cc1.addArg("-c"); 233 cc1.addFileArg(b.path(cfile)); 234 cc1.addArg("-o"); 235 mod.addObjectFile(cc1.addOutputFileArg(b.fmt("{s}.o", .{cfile[0 .. cfile.len - 2]}))); 236 } 237 } 238 239 // TODO(zig 0.16+): delete this function. 240 fn addCObjectsDirectly( 241 b: *std.Build, 242 mod: *std.Build.Module, 243 cc: []const u8, 244 optimize: std.builtin.OptimizeMode, 245 ) void { 246 addCSources(b, mod, cc, optimize); 247 mod.linkSystemLibrary("c", .{}); 248 }