zig0

my attempts at zig bootstrapping in C
Log | Files | Refs | README | LICENSE

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 }