flake.nix fetches the release tarballs and carries their SRI hashes in a generated block. The cut tool builds the release locally with the toolchain gates.yml pins, in a normalized nine-variable environment, writes the hashes into flake.nix, and commits it with build.zig.zon as the single bump commit. The package job verifies the pins on the bump commit and the publish job verifies them again on the tag, before anything is uploaded. The tarballs are written by dist_stage (std.tar.Writer, flate gzip) instead of the runner's tar and gzip, and -ffile-prefix-map keeps checkout paths out of the C objects; two checkouts at different absolute paths produce byte-identical archives. nxdns version, /api/version and the admin footer report the version only: the bump commit cannot know its own sha.
984 lines
44 KiB
Zig
984 lines
44 KiB
Zig
const std = @import("std");
|
|
const builtin = @import("builtin");
|
|
|
|
comptime {
|
|
if (builtin.zig_version.major != 0 or builtin.zig_version.minor != 16) {
|
|
@compileError("nxdns requires Zig 0.16.x, found " ++ builtin.zig_version_string);
|
|
}
|
|
}
|
|
|
|
const cross_targets = [_][]const u8{
|
|
"x86_64-linux-musl",
|
|
"aarch64-linux-musl",
|
|
};
|
|
|
|
/// The deploy target, read out of `cross_targets` so `test-aarch64` and `dist`
|
|
/// cannot describe different machines. Reordering the array is caught here
|
|
/// rather than by a qemu job that quietly ran the wrong architecture.
|
|
const aarch64_triple = cross_targets[1];
|
|
comptime {
|
|
if (!std.mem.startsWith(u8, aarch64_triple, "aarch64-")) {
|
|
@compileError("cross_targets[1] must be the aarch64 triple, found " ++ aarch64_triple);
|
|
}
|
|
}
|
|
|
|
pub fn build(b: *std.Build) void {
|
|
const target = b.standardTargetOptions(.{});
|
|
const optimize = b.standardOptimizeOption(.{});
|
|
|
|
const integration = b.option(bool, "integration", "Run hermetic integration tests (loopback sockets only)") orelse false;
|
|
const live = b.option(bool, "live", "Run tests that reach external network hosts") orelse false;
|
|
const fuzz = b.option(bool, "fuzz", "Build the fuzz targets with the LLVM backend (required for --fuzz)") orelse false;
|
|
// Milestone-17 ruling 5. The embedded golden carries no source-tree path,
|
|
// so regeneration names the destination explicitly:
|
|
// zig build test -Dintegration \
|
|
// -Dcontract-samples-out="$PWD/admin/src/lib/contractSamples.gen.ts"
|
|
const contract_samples_out = b.option(
|
|
[]const u8,
|
|
"contract-samples-out",
|
|
"Absolute path the contract-sample generator writes instead of comparing",
|
|
) orelse "";
|
|
// `dist` requires this option (milestone-14 ruling 4): a release must carry
|
|
// the tag's version, never a development default. Every other step keeps
|
|
// the default, so `zig build` and `zig build test` need no flag.
|
|
const version_option = b.option([]const u8, "version-string", "Version reported by `nxdns version` (required by `dist`)");
|
|
const version_string = version_option orelse "0.1.0-dev";
|
|
const admin_dist = b.option(
|
|
[]const u8,
|
|
"admin-dist",
|
|
"Built web UI directory to embed (default: the placeholder page). " ++
|
|
"Only the exact value `admin/dist` gets the freshness check; " ++
|
|
"the placeholder and any other path skip it.",
|
|
) orelse "admin/dist-placeholder";
|
|
|
|
// `b.path` panics on absolute paths, and a CI artifact directory is one.
|
|
const admin_dist_path: std.Build.LazyPath = if (std.fs.path.isAbsolute(admin_dist))
|
|
.{ .cwd_relative = admin_dist }
|
|
else
|
|
b.path(admin_dist);
|
|
|
|
// A stale `admin/dist` shipped a crashing settings page once (milestone-15
|
|
// ruling 5). The stamp is checked only for the real dist tree: the
|
|
// placeholder has no sources to be stale against, and an explicit path is a
|
|
// CI artifact that was built elsewhere.
|
|
const admin_dist_check: ?*std.Build.Step = if (std.mem.eql(u8, admin_dist, "admin/dist")) check: {
|
|
const run_check = b.addSystemCommand(&.{"node"});
|
|
// The script path goes through `b.path` so it resolves against the
|
|
// build root: `zig build` run from any other directory would not find
|
|
// a cwd-relative one.
|
|
run_check.addFileArg(b.path("admin/scripts/stamp-dist.mjs"));
|
|
run_check.addArg("--check");
|
|
break :check &run_check.step;
|
|
} else null;
|
|
const admin_assets = adminAssetsIndex(b, admin_dist_path, admin_dist_check);
|
|
|
|
const options = b.addOptions();
|
|
options.addOption(bool, "integration", integration);
|
|
options.addOption(bool, "live", live);
|
|
options.addOption([]const u8, "version_string", version_string);
|
|
options.addOption([]const u8, "zig_version_string", builtin.zig_version_string);
|
|
options.addOption([]const u8, "contract_samples_out", contract_samples_out);
|
|
|
|
const exe = addExecutable(b, target, optimize, options, admin_assets);
|
|
b.installArtifact(exe);
|
|
|
|
const run = b.addRunArtifact(exe);
|
|
run.step.dependOn(b.getInstallStep());
|
|
if (b.args) |args| run.addArgs(args);
|
|
b.step("run", "Run nxdns").dependOn(&run.step);
|
|
|
|
// Zig collects tests only from the root module, so a file missing from
|
|
// src/tests.zig silently contributes no tests. The list stays hand-written
|
|
// (generating it from a staged copy would point diagnostics at cache
|
|
// paths); this makes it complete by construction instead.
|
|
checkTestImports(b);
|
|
|
|
// A successful `zig build test` still prints `failed command: .../test
|
|
// ... --listen=-` as its last line. This is an upstream zig 0.16.0
|
|
// build-runner labelling defect, not a failure here, and not something
|
|
// this build script can suppress without hiding real failures.
|
|
//
|
|
// Mechanism, verified against the 0.16.0 sources on 2026-08-07:
|
|
// - std/Build/Step/Run.zig:1540 sets `result_failed_command` for every
|
|
// spawn, unconditionally ("if an error occurs, it's caused by this
|
|
// command"). Nothing clears it when the child succeeds.
|
|
// - compiler/build_runner.zig:1381 prints a step's diagnostics whenever
|
|
// `result_stderr` is non-empty, explicitly "no matter the result".
|
|
// - compiler/build_runner.zig:1515, reached from there, emits the
|
|
// `failed command: ` line because `result_failed_command` is non-null.
|
|
// So any Run step that both succeeds and writes one byte to stderr gets
|
|
// the label. Our suite writes plenty: the tests that exercise the warning
|
|
// paths log through the real sink.
|
|
//
|
|
// Minimal reproducer, no mbedTLS and no C: one passing test whose body is
|
|
// `std.debug.print` plus `try expect(true)`, in a build.zig with nothing
|
|
// but `addTest` + `addRunArtifact`. It prints the label and reports
|
|
// "3/3 steps succeeded; 1/1 tests passed". Deleting the print removes the
|
|
// label. The test child does not crash and does not abort in teardown.
|
|
// (Running the cached test binary by hand with `--listen=-` does abort,
|
|
// but only because stdin is then closed and the IPC runner panics on
|
|
// `EndOfStream`; that is an artifact of the manual invocation.)
|
|
//
|
|
// No upstream issue matched a search of ziglang/zig for this behaviour;
|
|
// the reference is the 0.16.0 source lines above. See AGENTS.md.
|
|
const licenses_files = licensesFilesRoot(b);
|
|
|
|
const tests = addTestSuite(b, target, optimize, options, admin_assets, licenses_files);
|
|
const test_step = b.step("test", "Run the test suite");
|
|
test_step.dependOn(&b.addRunArtifact(tests).step);
|
|
|
|
// Fuzz targets compile as a second test artifact with `dns` as a named module
|
|
// (a file belongs to one module per compilation; the aggregator keeps owning
|
|
// the in-file tests). `-Dfuzz` opts into the LLVM backend, which `--fuzz`
|
|
// needs for sanitizer coverage; stock 0.16.0 also requires a patched
|
|
// test_runner.zig for fuzz mode — see specs/milestone-2.md.
|
|
addFuzzSuite(b, target, optimize, fuzz, test_step, .{
|
|
.name = "fuzz",
|
|
.root = "tests/fuzz/dns_fuzz.zig",
|
|
.import_name = "dns",
|
|
.import_module = sourceModule(b, target, optimize, "src/dns/dns.zig"),
|
|
});
|
|
|
|
addFuzzSuite(b, target, optimize, fuzz, test_step, .{
|
|
.name = "blocklist-fuzz",
|
|
.root = "tests/fuzz/blocklist_fuzz.zig",
|
|
.import_name = "parsers",
|
|
.import_module = sourceModule(b, target, optimize, "src/filter/parsers.zig"),
|
|
});
|
|
|
|
// `src/web/http_util.zig` imports only std, so its fuzz module roots
|
|
// directly at the file — no aggregator needed (milestone-15 ruling 6c).
|
|
addFuzzSuite(b, target, optimize, fuzz, test_step, .{
|
|
.name = "http-util-fuzz",
|
|
.root = "tests/fuzz/http_util_fuzz.zig",
|
|
.import_name = "http_util",
|
|
.import_module = sourceModule(b, target, optimize, "src/web/http_util.zig"),
|
|
});
|
|
|
|
// `src/filter/regex.zig` imports only std (milestone-21 ruling 5), so its
|
|
// fuzz module roots directly at the file as well.
|
|
addFuzzSuite(b, target, optimize, fuzz, test_step, .{
|
|
.name = "regex-fuzz",
|
|
.root = "tests/fuzz/regex_fuzz.zig",
|
|
.import_name = "regex",
|
|
.import_module = sourceModule(b, target, optimize, "src/filter/regex.zig"),
|
|
});
|
|
|
|
// The bench harness (milestone-12 ruling 1). The measured roots
|
|
// (matcher.zig, dns_cache.zig, compiler.zig) share files in their relative
|
|
// import closures (model.zig, types.zig, ...), and a file may belong to
|
|
// only one module per compilation — separate modules per root cannot link
|
|
// into one executable. So one staged module: a copy of src/ plus a
|
|
// generated aggregator root, imported by the bench as `core`. No sqlite,
|
|
// no mbedTLS: the closure is pure Zig.
|
|
//
|
|
// The compiler fuzz target reuses the same staged tree (milestone-15
|
|
// ruling 6b): `compiler.zig` imports `../dns/`, so a module rooted under
|
|
// `src/filter/` fails with ImportOutsideModulePath.
|
|
const bench_stage = b.addWriteFiles();
|
|
_ = bench_stage.addCopyDirectory(b.path("src"), "src", .{});
|
|
const bench_core = bench_stage.add("bench_core.zig",
|
|
\\pub const matcher = @import("src/filter/matcher.zig");
|
|
\\pub const dns_cache = @import("src/cache/dns_cache.zig");
|
|
\\pub const compiler = @import("src/filter/compiler.zig");
|
|
\\pub const model = @import("src/config/model.zig");
|
|
\\pub const dns_name = @import("src/dns/name.zig");
|
|
\\pub const dns_types = @import("src/dns/types.zig");
|
|
\\pub const packet = @import("src/dns/packet.zig");
|
|
\\
|
|
);
|
|
const bench_core_mod = b.createModule(.{
|
|
.root_source_file = bench_core,
|
|
.target = target,
|
|
.optimize = optimize,
|
|
});
|
|
addFuzzSuite(b, target, optimize, fuzz, test_step, .{
|
|
.name = "compiler-fuzz",
|
|
.root = "tests/fuzz/compiler_fuzz.zig",
|
|
.import_name = "core",
|
|
.import_module = bench_core_mod,
|
|
});
|
|
|
|
const bench_mod = b.createModule(.{
|
|
.root_source_file = b.path("tools/bench.zig"),
|
|
.target = target,
|
|
.optimize = optimize,
|
|
});
|
|
bench_mod.addImport("core", bench_core_mod);
|
|
const bench_exe = b.addExecutable(.{ .name = "bench", .root_module = bench_mod });
|
|
const bench_run = b.addRunArtifact(bench_exe);
|
|
if (b.args) |args| bench_run.addArgs(args);
|
|
b.step("bench", "Run the performance benchmarks (PLAN §18)").dependOn(&bench_run.step);
|
|
|
|
// aarch64 test execution (milestone-12 ruling 6): the plain suite
|
|
// cross-built for the deploy target and run under qemu-user
|
|
// (`zig build test-aarch64 -fqemu`). Fuzz artifacts stay native-only, and
|
|
// -Dintegration stays out (ruling 7): qemu-user's slowdown makes the
|
|
// wall-clock-budgeted loopback TLS tests a flake source.
|
|
const aarch64_target = b.resolveTargetQuery(
|
|
std.Target.Query.parse(.{ .arch_os_abi = aarch64_triple }) catch unreachable,
|
|
);
|
|
const aarch64_tests = addTestSuite(b, aarch64_target, optimize, options, admin_assets, licenses_files);
|
|
aarch64_tests.linkage = .static;
|
|
const aarch64_run = b.addRunArtifact(aarch64_tests);
|
|
aarch64_run.skip_foreign_checks = true;
|
|
b.step("test-aarch64", "Run the test suite for aarch64-linux-musl (use -fqemu)")
|
|
.dependOn(&aarch64_run.step);
|
|
|
|
// The release publication tool (milestone-14 deviation 24). It is a host
|
|
// tool like `dist_stage` and `verify_dist`, and it is installed rather than
|
|
// run from the build graph: the workflow invokes it once per phase with the
|
|
// secrets in its environment, and a Run step would have to carry them.
|
|
const release_tool = hostTool(b, "release");
|
|
b.step("release-tool", "Install the release publication tool into zig-out/bin")
|
|
.dependOn(&b.addInstallArtifact(release_tool, .{}).step);
|
|
|
|
// Its pure decisions — semver ordering, VALIDSIG field selection, changelog
|
|
// extraction, the releases-payload shape guard — are the reason it exists,
|
|
// so they run in the same `zig build test` as everything else.
|
|
const release_tests = b.addTest(.{
|
|
.name = "release-tool",
|
|
.root_module = b.createModule(.{
|
|
.root_source_file = b.path("tools/release.zig"),
|
|
.target = b.graph.host,
|
|
.optimize = optimize,
|
|
}),
|
|
});
|
|
test_step.dependOn(&b.addRunArtifact(release_tests).step);
|
|
|
|
// The container acceptance gate. Installed rather than run from the build
|
|
// graph for the same reason as the release tool: it needs a live docker
|
|
// daemon and the workflow's environment, neither of which a Run step in this
|
|
// graph can supply.
|
|
const container_check_tool = hostTool(b, "container_check");
|
|
b.step("container-check-tool", "Install the container gate tool into zig-out/bin")
|
|
.dependOn(&b.addInstallArtifact(container_check_tool, .{}).step);
|
|
|
|
// Its pure decisions — object naming, the ownership label, the inspect-JSON
|
|
// topology read, the probe deadline, the build.zig.zon version parse — are
|
|
// what the shell it replaced could never be tested on.
|
|
const container_check_tests = b.addTest(.{
|
|
.name = "container-check-tool",
|
|
.root_module = b.createModule(.{
|
|
.root_source_file = b.path("tools/container_check.zig"),
|
|
.target = b.graph.host,
|
|
.optimize = optimize,
|
|
}),
|
|
});
|
|
test_step.dependOn(&b.addRunArtifact(container_check_tests).step);
|
|
|
|
// The release cut (specs/release-cut.md). A host tool like the two above,
|
|
// but run from the build graph rather than installed: it takes a bump kind
|
|
// on the command line (`zig build cut -- patch`), reads its own token, and
|
|
// needs the operator's terminal so `git commit -S` can reach pinentry —
|
|
// none of which a workflow supplies and all of which a Run step passes
|
|
// through.
|
|
// The cut's schema gate compares the querylog fingerprint of the previous
|
|
// release against this tree's. It must read that number from the file the
|
|
// server uses, never from a copy: a duplicated DDL or a duplicated hash
|
|
// would let the gate pass a schema change it no longer describes. Only
|
|
// `fingerprint` and `fingerprintOf` are referenced, both of which are
|
|
// comptime-computable text hashing, so no SQLite symbol is pulled in and
|
|
// the host tool needs no library.
|
|
const querylog_schema_mod = b.createModule(.{
|
|
.root_source_file = b.path("src/storage/querylog_schema.zig"),
|
|
.target = b.graph.host,
|
|
.optimize = optimize,
|
|
});
|
|
|
|
const cut_tool = hostTool(b, "cut");
|
|
cut_tool.root_module.addImport("querylog_schema", querylog_schema_mod);
|
|
const cut_run = b.addRunArtifact(cut_tool);
|
|
// It pushes commits and tags, so it must never be answered from the run
|
|
// cache, and it must run at the build root whatever directory `zig build`
|
|
// was invoked from.
|
|
cut_run.has_side_effects = true;
|
|
cut_run.stdio = .inherit;
|
|
cut_run.setCwd(b.path("."));
|
|
if (b.args) |args| cut_run.addArgs(args);
|
|
b.step("cut", "Cut a release: preflight, bump, push, wait for CI, signed tag, watch the run")
|
|
.dependOn(&cut_run.step);
|
|
|
|
// Its pure decisions — semver strictness, the zon rewrite, the changelog
|
|
// section check, the runs-payload read and the tea-config token lookup —
|
|
// are the reason it is a program rather than a shell script.
|
|
const cut_tests = b.addTest(.{
|
|
.name = "cut-tool",
|
|
.root_module = b.createModule(.{
|
|
.root_source_file = b.path("tools/cut.zig"),
|
|
.target = b.graph.host,
|
|
.optimize = optimize,
|
|
}),
|
|
});
|
|
cut_tests.root_module.addImport("querylog_schema", querylog_schema_mod);
|
|
const cut_tests_run = b.addRunArtifact(cut_tests);
|
|
// The schema-gate round trip reads `src/storage/querylog_schema.zig` off
|
|
// disk, so the test binary has to run at the build root.
|
|
cut_tests_run.setCwd(b.path("."));
|
|
test_step.dependOn(&cut_tests_run.step);
|
|
|
|
// `dist_stage` owns the release archive bytes, and its reproducibility is
|
|
// the property the flake pins depend on, so it is tested like any other
|
|
// decision this build makes.
|
|
const dist_stage_tests = b.addTest(.{
|
|
.name = "dist-stage-tool",
|
|
.root_module = b.createModule(.{
|
|
.root_source_file = b.path("tools/dist_stage.zig"),
|
|
.target = b.graph.host,
|
|
.optimize = optimize,
|
|
}),
|
|
});
|
|
test_step.dependOn(&b.addRunArtifact(dist_stage_tests).step);
|
|
|
|
addDist(b, options, admin_assets, .{
|
|
.version = version_option,
|
|
.version_string = version_string,
|
|
.admin_dist = admin_dist,
|
|
});
|
|
}
|
|
|
|
/// Byte budgets from PLAN §18, asserted by `verify-dist` rather than by shell.
|
|
const max_binary_bytes = 15_728_640;
|
|
const max_asset_free_binary_bytes = 10_485_760;
|
|
|
|
/// The default of `-Dadmin-dist`. A release built without the flag would ship the
|
|
/// placeholder admin page, so `dist` refuses it (milestone-14 ruling 4). There
|
|
/// is deliberately no override.
|
|
const placeholder_admin_dist = "admin/dist-placeholder";
|
|
|
|
/// Repository files that go into the tarball verbatim. `nxdns.conf` is
|
|
/// `sysusers.conf` under the name it is installed with, so nothing renames a
|
|
/// file during install (milestone-14 ruling 4).
|
|
const service_unit_path = "deploy/systemd/nxdns.service";
|
|
const sysusers_path = "deploy/systemd/nxdns.conf";
|
|
const license_path = "LICENSE";
|
|
const install_md_path = "INSTALL.md";
|
|
const dockerfile_path = "deploy/docker/Dockerfile";
|
|
|
|
/// The reviewed third-party inventory `dist` turns into `THIRD-PARTY-NOTICES`
|
|
/// (milestone-14 ruling 3). It is committed and audited, never scraped.
|
|
const licenses_dir = "licenses";
|
|
const inventory_path = "licenses/inventory.zon";
|
|
|
|
const DistOptions = struct {
|
|
/// `-Dversion-string` exactly as given, so a missing one is distinguishable
|
|
/// from one that happens to equal the default.
|
|
version: ?[]const u8,
|
|
version_string: []const u8,
|
|
admin_dist: []const u8,
|
|
};
|
|
|
|
/// `dist` builds everything releasable; `verify-dist` asserts the result;
|
|
/// `pin-flake` writes the resulting hashes into `flake.nix` and `verify-pins`
|
|
/// asserts that they still describe the bytes under `zig-out/dist`.
|
|
///
|
|
/// All four run on a laptop exactly as they run on the runner, which is the
|
|
/// point: release checks that only exist in CI shell are the brittleness
|
|
/// milestone 14 set out to remove. The pins depend on it twice over — the cut
|
|
/// writes them here and CI recomputes them there, and the two only agree
|
|
/// because it is one build graph rather than two scripts.
|
|
fn addDist(
|
|
b: *std.Build,
|
|
options: *std.Build.Step.Options,
|
|
admin_assets: std.Build.LazyPath,
|
|
dist_options: DistOptions,
|
|
) void {
|
|
const dist_step = b.step("dist", "Build the release tarballs, checksums and staged payloads");
|
|
const verify_step = b.step("verify-dist", "Verify the release artifacts under zig-out/dist");
|
|
const pin_step = b.step("pin-flake", "Write the release hashes under zig-out/dist into flake.nix");
|
|
const verify_pins_step = b.step("verify-pins", "Check flake.nix pins the hashes of the release under zig-out/dist");
|
|
|
|
if (distPreflight(b, dist_options)) |problem| {
|
|
const fail = b.addFail(problem);
|
|
dist_step.dependOn(&fail.step);
|
|
verify_step.dependOn(&fail.step);
|
|
pin_step.dependOn(&fail.step);
|
|
verify_pins_step.dependOn(&fail.step);
|
|
return;
|
|
}
|
|
|
|
const stage_tool = hostTool(b, "dist_stage");
|
|
|
|
// A Run step hashes only the resolved path string of a directory argument,
|
|
// not its contents, so the inventory is staged through WriteFiles first:
|
|
// the staged copy lives at a content-hashed path, and editing a licence
|
|
// text re-runs the staging tool instead of replaying a stale cache.
|
|
const licenses_stage = b.addWriteFiles();
|
|
const staged_licenses = licenses_stage.addCopyDirectory(b.path(licenses_dir), ".", .{});
|
|
|
|
const sums_run = b.addRunArtifact(stage_tool);
|
|
sums_run.addArg("sums");
|
|
sums_run.addArg("--out");
|
|
const sums_file = sums_run.addOutputFileArg("SHA256SUMS");
|
|
|
|
const verify_tool = hostTool(b, "verify_dist");
|
|
const verify_run = b.addRunArtifact(verify_tool);
|
|
// A verifier that a cache can replay is not a verifier. This also makes the
|
|
// tool's report reach the terminal instead of a captured pipe.
|
|
verify_run.has_side_effects = true;
|
|
verify_run.addArgs(&.{ "--dist-dir", b.getInstallPath(.prefix, "dist") });
|
|
verify_run.addArgs(&.{ "--work-dir", b.getInstallPath(.prefix, "dist-verify") });
|
|
verify_run.addArgs(&.{ "--version", dist_options.version_string });
|
|
verify_run.addArg("--zon");
|
|
verify_run.addFileArg(b.path("build.zig.zon"));
|
|
verify_run.addArgs(&.{ "--max-bytes", b.fmt("{d}", .{max_binary_bytes}) });
|
|
verify_run.addArgs(&.{ "--asset-free-max-bytes", b.fmt("{d}", .{max_asset_free_binary_bytes}) });
|
|
verify_run.addArgs(&.{ "--host-arch", @tagName(b.graph.host.result.cpu.arch) });
|
|
if (b.enable_qemu) verify_run.addArg("--qemu");
|
|
|
|
// The asset-free budget gets its own build against a generated empty assets
|
|
// directory (milestone-14 ruling 5). Not the placeholder: ruling 4 makes
|
|
// that unbuildable, and re-admitting it for one size check through a back
|
|
// door would defeat the point of the refusal.
|
|
const empty_admin_assets = adminAssetsIndex(b, b.addWriteFiles().getDirectory(), null);
|
|
|
|
for (cross_targets) |triple| {
|
|
const query = std.Target.Query.parse(.{ .arch_os_abi = triple }) catch |err| {
|
|
std.debug.panic("invalid cross target '{s}': {t}", .{ triple, err });
|
|
};
|
|
const target = b.resolveTargetQuery(query);
|
|
const name = b.fmt("nxdns-{s}-{s}", .{ dist_options.version_string, triple });
|
|
|
|
// ReleaseSafe is not read from `-Doptimize`: the release artifact must
|
|
// not change shape because a flag was forgotten. `.strip` is
|
|
// `std.Build.Module.strip`, which emits `-fstrip` (Module.zig:545), so
|
|
// no objcopy and no binutils-aarch64-linux-gnu on the runner.
|
|
const exe = addExecutable(b, target, .ReleaseSafe, options, admin_assets);
|
|
exe.linkage = .static;
|
|
exe.root_module.strip = true;
|
|
|
|
const stage_run = b.addRunArtifact(stage_tool);
|
|
stage_run.addArg("stage");
|
|
stage_run.addArg("--out");
|
|
const staged = stage_run.addOutputDirectoryArg(name);
|
|
stage_run.addArg("--binary");
|
|
stage_run.addFileArg(exe.getEmittedBin());
|
|
stage_run.addArg("--service");
|
|
stage_run.addFileArg(b.path(service_unit_path));
|
|
stage_run.addArg("--sysusers");
|
|
stage_run.addFileArg(b.path(sysusers_path));
|
|
stage_run.addArg("--license");
|
|
stage_run.addFileArg(b.path(license_path));
|
|
stage_run.addArg("--install-md");
|
|
stage_run.addFileArg(b.path(install_md_path));
|
|
stage_run.addArg("--licenses");
|
|
stage_run.addDirectoryArg(staged_licenses);
|
|
|
|
// The tarball is written by our own tool rather than by the runner's
|
|
// `tar` and `gzip`: the release hashes are pinned in `flake.nix` before
|
|
// CI rebuilds them, so the bytes may depend on the staged tree and on
|
|
// nothing else the host supplies.
|
|
const archive_run = b.addRunArtifact(stage_tool);
|
|
archive_run.addArg("archive");
|
|
archive_run.addArg("--root");
|
|
// The staged payload is the sole entry of its cache directory, so its
|
|
// parent is what `--root` needs and declaring it declares the payload.
|
|
archive_run.addDirectoryArg(staged.dirname());
|
|
archive_run.addArgs(&.{ "--payload", name });
|
|
archive_run.addArg("--out");
|
|
const tarball = archive_run.addOutputFileArg(b.fmt("{s}.tar.gz", .{name}));
|
|
|
|
const install_binary = b.addInstallFile(
|
|
staged.path(b, "nxdns"),
|
|
b.fmt("dist/bin/{s}/nxdns", .{triple}),
|
|
);
|
|
const install_stage = b.addInstallDirectory(.{
|
|
.source_dir = staged,
|
|
.install_dir = .prefix,
|
|
.install_subdir = b.fmt("dist/stage/{s}", .{name}),
|
|
});
|
|
const install_tarball = b.addInstallFile(tarball, b.fmt("dist/{s}.tar.gz", .{name}));
|
|
dist_step.dependOn(&install_binary.step);
|
|
dist_step.dependOn(&install_stage.step);
|
|
dist_step.dependOn(&install_tarball.step);
|
|
|
|
// The checksum file covers the two tarballs and nothing else. It cannot
|
|
// cover the container image: that digest does not exist until buildx
|
|
// has pushed, which happens later and elsewhere.
|
|
sums_run.addArg("--entry");
|
|
sums_run.addArg(b.fmt("{s}.tar.gz", .{name}));
|
|
sums_run.addFileArg(tarball);
|
|
|
|
const asset_free_exe = addExecutable(b, target, .ReleaseSafe, options, empty_admin_assets);
|
|
asset_free_exe.linkage = .static;
|
|
asset_free_exe.root_module.strip = true;
|
|
|
|
verify_run.addArgs(&.{ "--archive", triple, b.fmt("{s}.tar.gz", .{name}) });
|
|
verify_run.addArgs(&.{ "--asset-free", triple });
|
|
verify_run.addFileArg(asset_free_exe.getEmittedBin());
|
|
}
|
|
|
|
const install_sums = b.addInstallFile(sums_file, "dist/SHA256SUMS");
|
|
dist_step.dependOn(&install_sums.step);
|
|
|
|
verify_run.step.dependOn(dist_step);
|
|
verify_step.dependOn(&verify_run.step);
|
|
|
|
// The pins in `flake.nix` are written before CI ever builds the release, so
|
|
// the run that rebuilds it has to prove they describe its own bytes.
|
|
//
|
|
// It is a step of its own and NOT part of `verify-dist`. An ordinary commit
|
|
// between two cuts builds the `build.zig.zon` version from a tree that
|
|
// differs from the released one, so its bytes never match the pins and
|
|
// checking them there would fail every such build. The two CI jobs that may
|
|
// not skip it call it by name: the package job on the bump commit, and the
|
|
// publish job unconditionally before any upload.
|
|
const pin_check_run = b.addRunArtifact(stage_tool);
|
|
pin_check_run.has_side_effects = true;
|
|
pin_check_run.addArg("pin-check");
|
|
pin_check_run.addArgs(&.{ "--sums", b.getInstallPath(.prefix, "dist/SHA256SUMS") });
|
|
pin_check_run.addArg("--flake");
|
|
pin_check_run.addFileArg(b.path("flake.nix"));
|
|
pin_check_run.addArgs(&.{ "--version", dist_options.version_string });
|
|
pin_check_run.step.dependOn(dist_step);
|
|
verify_pins_step.dependOn(&pin_check_run.step);
|
|
|
|
// The write half, run by the cut and by nothing else. `flake.nix` is named
|
|
// as a plain path rather than a `LazyPath`: this run edits the source file
|
|
// in place, and a file argument would declare it an input of a step that is
|
|
// in fact its author.
|
|
const pin_run = b.addRunArtifact(stage_tool);
|
|
pin_run.has_side_effects = true;
|
|
pin_run.addArg("pin");
|
|
pin_run.addArgs(&.{ "--sums", b.getInstallPath(.prefix, "dist/SHA256SUMS") });
|
|
pin_run.addArgs(&.{ "--flake", b.pathFromRoot("flake.nix") });
|
|
pin_run.addArgs(&.{ "--version", dist_options.version_string });
|
|
pin_run.step.dependOn(dist_step);
|
|
pin_step.dependOn(&pin_run.step);
|
|
}
|
|
|
|
/// The one message `dist` and `verify-dist` fail with when the release inputs
|
|
/// are not there, or null when they are. Returning it rather than calling
|
|
/// `std.process.fatal` keeps `zig build` and `zig build test` working: the
|
|
/// checks gate only the two release steps.
|
|
fn distPreflight(b: *std.Build, dist_options: DistOptions) ?[]const u8 {
|
|
var problems: std.ArrayList([]const u8) = .empty;
|
|
|
|
if (dist_options.version == null) {
|
|
problems.append(b.allocator, "-Dversion-string=<version> is required by `dist` " ++
|
|
"(the release tag without its leading `v`); it has no default here.") catch @panic("OOM");
|
|
}
|
|
|
|
if (std.mem.eql(u8, b.pathFromRoot(dist_options.admin_dist), b.pathFromRoot(placeholder_admin_dist))) {
|
|
problems.append(b.allocator, "-Dadmin-dist resolves to " ++ placeholder_admin_dist ++
|
|
", which a release must never ship. Build the real admin UI " ++
|
|
"(cd admin && npm ci && npm run build) and pass -Dadmin-dist=admin/dist.") catch @panic("OOM");
|
|
}
|
|
|
|
for ([_][]const u8{
|
|
service_unit_path,
|
|
sysusers_path,
|
|
license_path,
|
|
install_md_path,
|
|
inventory_path,
|
|
}) |path| {
|
|
_ = b.build_root.handle.statFile(b.graph.io, path, .{}) catch |err| {
|
|
problems.append(b.allocator, b.fmt(
|
|
"`dist` needs '{s}', which is not readable: {t}",
|
|
.{ path, err },
|
|
)) catch @panic("OOM");
|
|
};
|
|
}
|
|
|
|
if (problems.items.len == 0) return null;
|
|
return std.mem.join(b.allocator, "\n", problems.items) catch @panic("OOM");
|
|
}
|
|
|
|
/// A build-time helper compiled for the host: `tools/<name>.zig`.
|
|
fn hostTool(b: *std.Build, name: []const u8) *std.Build.Step.Compile {
|
|
return b.addExecutable(.{
|
|
.name = name,
|
|
.root_module = b.createModule(.{
|
|
.root_source_file = b.path(b.fmt("tools/{s}.zig", .{name})),
|
|
.target = b.graph.host,
|
|
.optimize = .ReleaseSafe,
|
|
}),
|
|
});
|
|
}
|
|
|
|
/// Milestone-15 ruling 4: every `*.zig` under `src/` must appear in
|
|
/// `src/tests.zig` as a line that trims to exactly `_ = @import("<path>");`,
|
|
/// where `<path>` is relative to `src/`. Whole-line equality, not a substring
|
|
/// search: a commented-out import trims to a line starting with `//` and does
|
|
/// not match, and `db.zig` cannot satisfy the requirement for `db2.zig`.
|
|
/// Duplicate lines are an error too — they hide a botched merge. No allowlist:
|
|
/// a file with no tests still gets imported, because the import is free and an
|
|
/// exception is the thing that lets a real gap through.
|
|
fn checkTestImports(b: *std.Build) void {
|
|
const gpa = b.allocator;
|
|
const io = b.graph.io;
|
|
const root = b.build_root.handle;
|
|
|
|
const tests_src = root.readFileAlloc(io, "src/tests.zig", gpa, .limited(4 << 20)) catch |err| {
|
|
std.process.fatal("cannot read src/tests.zig: {t}", .{err});
|
|
};
|
|
|
|
var src_dir = root.openDir(io, "src", .{ .iterate = true }) catch |err| {
|
|
std.process.fatal("cannot open src/: {t}", .{err});
|
|
};
|
|
defer src_dir.close(io);
|
|
|
|
var walker = src_dir.walk(gpa) catch @panic("OOM");
|
|
defer walker.deinit();
|
|
|
|
while (walker.next(io) catch |err| {
|
|
std.process.fatal("cannot walk src/: {t}", .{err});
|
|
}) |entry| {
|
|
if (entry.kind != .file) continue;
|
|
if (!std.mem.endsWith(u8, entry.path, ".zig")) continue;
|
|
if (std.mem.eql(u8, entry.path, "tests.zig")) continue;
|
|
|
|
const needle = b.fmt("_ = @import(\"{s}\");", .{entry.path});
|
|
var matches: usize = 0;
|
|
var lines = std.mem.splitScalar(u8, tests_src, '\n');
|
|
while (lines.next()) |line| {
|
|
if (std.mem.eql(u8, std.mem.trim(u8, line, " \t\r"), needle)) matches += 1;
|
|
}
|
|
if (matches == 0) {
|
|
std.process.fatal("src/tests.zig is missing `{s}`", .{needle});
|
|
}
|
|
if (matches > 1) {
|
|
std.process.fatal("src/tests.zig repeats `{s}` {d} times", .{ needle, matches });
|
|
}
|
|
}
|
|
}
|
|
|
|
/// The `src/tests.zig` suite, wired for one target. The host build and the
|
|
/// aarch64 build take the same artifact; their only differences (`linkage` and
|
|
/// `skip_foreign_checks`) stay at the call sites.
|
|
fn addTestSuite(
|
|
b: *std.Build,
|
|
target: std.Build.ResolvedTarget,
|
|
optimize: std.builtin.OptimizeMode,
|
|
options: *std.Build.Step.Options,
|
|
admin_assets: std.Build.LazyPath,
|
|
licenses_files: std.Build.LazyPath,
|
|
) *std.Build.Step.Compile {
|
|
const tests = b.addTest(.{
|
|
.root_module = b.createModule(.{
|
|
.root_source_file = b.path("src/tests.zig"),
|
|
.target = target,
|
|
.optimize = optimize,
|
|
.link_libc = true,
|
|
}),
|
|
});
|
|
tests.root_module.addOptions("build_options", options);
|
|
tests.root_module.linkLibrary(sqliteLibrary(b, target, optimize));
|
|
tests.root_module.linkLibrary(mbedtlsLibrary(b, target, optimize));
|
|
tests.root_module.addCSourceFile(.{ .file = b.path("src/platform/mbedtls_shim.c") });
|
|
addMbedtlsThreadingMacros(tests.root_module);
|
|
tests.root_module.addAnonymousImport("test_fixtures", .{
|
|
.root_source_file = b.path("tests/fixtures/fixtures.zig"),
|
|
});
|
|
tests.root_module.addAnonymousImport("docs_files", .{
|
|
.root_source_file = b.path("docs/docs.zig"),
|
|
});
|
|
// An anonymous-import root must be Zig, so the committed TypeScript golden
|
|
// is reached through a one-decl wrapper beside it.
|
|
tests.root_module.addAnonymousImport("contract_samples", .{
|
|
.root_source_file = b.path("admin/src/lib/contract_samples.zig"),
|
|
});
|
|
tests.root_module.addAnonymousImport("admin_assets", .{ .root_source_file = admin_assets });
|
|
tests.root_module.addAnonymousImport("licenses_files", .{ .root_source_file = licenses_files });
|
|
return tests;
|
|
}
|
|
|
|
/// The root of the `licenses_files` module (milestone-14 ruling 3), through
|
|
/// which `src/licenses_drift_test.zig` embeds the licence inventory and the two
|
|
/// dependency manifests whose identity it guards.
|
|
///
|
|
/// A module cannot embed anything above its own root directory, so the trees
|
|
/// are merged into one WriteFiles directory: `licenses/` at the root, plus
|
|
/// copies of `build.zig.zon`, `admin/package-lock.json` and the image Dockerfile
|
|
/// beside it. The module root is the copy of `licenses/licenses.zig`, which is
|
|
/// why that file's `@embedFile` paths name files that do not sit beside it in
|
|
/// the repository.
|
|
fn licensesFilesRoot(b: *std.Build) std.Build.LazyPath {
|
|
const stage = b.addWriteFiles();
|
|
_ = stage.addCopyDirectory(b.path("licenses"), ".", .{});
|
|
_ = stage.addCopyFile(b.path("build.zig.zon"), "build.zig.zon");
|
|
_ = stage.addCopyFile(b.path("admin/package-lock.json"), "package-lock.json");
|
|
_ = stage.addCopyFile(b.path(dockerfile_path), "Dockerfile");
|
|
return stage.getDirectory().path(b, "licenses.zig");
|
|
}
|
|
|
|
/// One fuzz test artifact: a module rooted at a `tests/fuzz/` file plus the one
|
|
/// named import through which that target reaches the code under test. The fuzz
|
|
/// suites cannot share `addTestSuite` — they take no `build_options`, no
|
|
/// anonymous imports and no C libraries, and they carry `use_llvm`, which the
|
|
/// main suite must not set.
|
|
fn addFuzzSuite(
|
|
b: *std.Build,
|
|
target: std.Build.ResolvedTarget,
|
|
optimize: std.builtin.OptimizeMode,
|
|
fuzz: bool,
|
|
test_step: *std.Build.Step,
|
|
spec: struct {
|
|
name: []const u8,
|
|
root: []const u8,
|
|
import_name: []const u8,
|
|
import_module: *std.Build.Module,
|
|
},
|
|
) void {
|
|
const mod = b.createModule(.{
|
|
.root_source_file = b.path(spec.root),
|
|
.target = target,
|
|
.optimize = optimize,
|
|
});
|
|
mod.addImport(spec.import_name, spec.import_module);
|
|
const tests = b.addTest(.{
|
|
.name = spec.name,
|
|
.use_llvm = if (fuzz) true else null,
|
|
.root_module = mod,
|
|
});
|
|
test_step.dependOn(&b.addRunArtifact(tests).step);
|
|
}
|
|
|
|
/// A plain module over one source file, with no imports of its own: what a fuzz
|
|
/// target reaches its code under test through.
|
|
fn sourceModule(
|
|
b: *std.Build,
|
|
target: std.Build.ResolvedTarget,
|
|
optimize: std.builtin.OptimizeMode,
|
|
path: []const u8,
|
|
) *std.Build.Module {
|
|
return b.createModule(.{
|
|
.root_source_file = b.path(path),
|
|
.target = target,
|
|
.optimize = optimize,
|
|
});
|
|
}
|
|
|
|
fn addExecutable(
|
|
b: *std.Build,
|
|
target: std.Build.ResolvedTarget,
|
|
optimize: std.builtin.OptimizeMode,
|
|
options: *std.Build.Step.Options,
|
|
admin_assets: std.Build.LazyPath,
|
|
) *std.Build.Step.Compile {
|
|
const exe = b.addExecutable(.{
|
|
.name = "nxdns",
|
|
.root_module = b.createModule(.{
|
|
.root_source_file = b.path("src/main.zig"),
|
|
.target = target,
|
|
.optimize = optimize,
|
|
.link_libc = true,
|
|
}),
|
|
});
|
|
exe.root_module.addOptions("build_options", options);
|
|
exe.root_module.addAnonymousImport("admin_assets", .{ .root_source_file = admin_assets });
|
|
exe.root_module.linkLibrary(sqliteLibrary(b, target, optimize));
|
|
exe.root_module.linkLibrary(mbedtlsLibrary(b, target, optimize));
|
|
exe.root_module.addCSourceFile(.{
|
|
.file = b.path("src/platform/mbedtls_shim.c"),
|
|
.flags = &.{filePrefixMap(b, .build_root)},
|
|
});
|
|
addMbedtlsThreadingMacros(exe.root_module);
|
|
return exe;
|
|
}
|
|
|
|
/// The embedded web UI (milestone-8 ruling 24): the dist directory plus the
|
|
/// generated `assets.zig` index and build-time gzip siblings, merged into one
|
|
/// WriteFiles directory so every `@embedFile` path resolves inside the module
|
|
/// root. Returns the index file, the root of the `admin_assets` module.
|
|
///
|
|
/// The dist is staged through its own WriteFiles step before it reaches the
|
|
/// tool because a Run step hashes only the resolved path string of a directory
|
|
/// argument, not its contents; the staged copy lives at a content-hashed path,
|
|
/// so editing an asset re-runs the tool instead of replaying a stale cache.
|
|
fn adminAssetsIndex(
|
|
b: *std.Build,
|
|
dist: std.Build.LazyPath,
|
|
freshness_check: ?*std.Build.Step,
|
|
) std.Build.LazyPath {
|
|
const stage = b.addWriteFiles();
|
|
if (freshness_check) |check| stage.step.dependOn(check);
|
|
const staged = stage.addCopyDirectory(dist, ".", .{});
|
|
|
|
const tool = b.addExecutable(.{
|
|
.name = "gen_admin_assets",
|
|
.root_module = b.createModule(.{
|
|
.root_source_file = b.path("tools/gen_admin_assets.zig"),
|
|
.target = b.graph.host,
|
|
.optimize = .ReleaseSafe,
|
|
}),
|
|
});
|
|
|
|
const run = b.addRunArtifact(tool);
|
|
run.addDirectoryArg(staged);
|
|
const generated = run.addOutputDirectoryArg("admin_assets");
|
|
|
|
const merged = b.addWriteFiles();
|
|
_ = merged.addCopyDirectory(staged, ".", .{});
|
|
_ = merged.addCopyDirectory(generated, ".", .{});
|
|
return merged.getDirectory().path(b, "assets.zig");
|
|
}
|
|
|
|
/// SQLite 3.53.4 amalgamation (see build.zig.zon for the pinned URL and hash).
|
|
fn sqliteLibrary(
|
|
b: *std.Build,
|
|
target: std.Build.ResolvedTarget,
|
|
optimize: std.builtin.OptimizeMode,
|
|
) *std.Build.Step.Compile {
|
|
const dep = b.dependency("sqlite", .{});
|
|
const lib = b.addLibrary(.{
|
|
.name = "sqlite3",
|
|
.linkage = .static,
|
|
.root_module = b.createModule(.{
|
|
.target = target,
|
|
.optimize = optimize,
|
|
.link_libc = true,
|
|
}),
|
|
});
|
|
lib.root_module.addIncludePath(dep.path(""));
|
|
lib.root_module.addCSourceFile(.{
|
|
.file = dep.path("sqlite3.c"),
|
|
.flags = &.{
|
|
"-DSQLITE_ENABLE_FTS5",
|
|
"-DSQLITE_THREADSAFE=1",
|
|
"-DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1",
|
|
"-DSQLITE_OMIT_LOAD_EXTENSION",
|
|
filePrefixMap(b, .build_root),
|
|
filePrefixMap(b, .global_cache),
|
|
},
|
|
});
|
|
return lib;
|
|
}
|
|
|
|
/// Mbed TLS 3.6.7 LTS, stock `mbedtls_config.h` (see build.zig.zon for the pinned URL and hash).
|
|
fn mbedtlsLibrary(
|
|
b: *std.Build,
|
|
target: std.Build.ResolvedTarget,
|
|
optimize: std.builtin.OptimizeMode,
|
|
) *std.Build.Step.Compile {
|
|
const dep = b.dependency("mbedtls", .{});
|
|
const lib = b.addLibrary(.{
|
|
.name = "mbedtls",
|
|
.linkage = .static,
|
|
.root_module = b.createModule(.{
|
|
.target = target,
|
|
.optimize = optimize,
|
|
.link_libc = true,
|
|
}),
|
|
});
|
|
|
|
addMbedtlsThreadingMacros(lib.root_module);
|
|
|
|
for ([_][]const u8{
|
|
"include",
|
|
"library",
|
|
"3rdparty/everest/include",
|
|
"3rdparty/everest/include/everest",
|
|
"3rdparty/everest/include/everest/kremlib",
|
|
"3rdparty/p256-m/p256-m/include",
|
|
"3rdparty/p256-m/p256-m_driver_interface",
|
|
}) |include_dir| {
|
|
lib.root_module.addIncludePath(dep.path(include_dir));
|
|
}
|
|
|
|
const c_flags = [_][]const u8{ filePrefixMap(b, .build_root), filePrefixMap(b, .global_cache) };
|
|
lib.root_module.addCSourceFiles(.{
|
|
.root = dep.path("library"),
|
|
.files = &mbedtls_library_sources,
|
|
.flags = &c_flags,
|
|
});
|
|
lib.root_module.addCSourceFiles(.{
|
|
.root = dep.path("3rdparty"),
|
|
.files = &mbedtls_3rdparty_sources,
|
|
.flags = &c_flags,
|
|
});
|
|
lib.installHeadersDirectory(dep.path("include/mbedtls"), "mbedtls", .{});
|
|
lib.installHeadersDirectory(dep.path("include/psa"), "psa", .{});
|
|
return lib;
|
|
}
|
|
|
|
/// `__FILE__` in the C sources (mbedTLS debug and assertion macros) would
|
|
/// otherwise embed the absolute checkout path into the release binary, and the
|
|
/// flake pins require the bytes to be the same on every machine that builds
|
|
/// the tag. Both roots a dependency can be stored under are mapped.
|
|
fn filePrefixMap(b: *std.Build, root: enum { build_root, global_cache }) []const u8 {
|
|
const path = switch (root) {
|
|
.build_root => b.build_root.path orelse ".",
|
|
.global_cache => b.graph.global_cache_root.path orelse ".",
|
|
};
|
|
return b.fmt("-ffile-prefix-map={s}=.", .{path});
|
|
}
|
|
|
|
/// Context sizes change with threading enabled, so every compilation unit that
|
|
/// includes mbedTLS headers (the library itself and `mbedtls_shim.c`) must see
|
|
/// the same macros. Concurrent handshakes share `ssl_config`, the CTR-DRBG, and
|
|
/// global PSA state; without MBEDTLS_THREADING_C those race.
|
|
fn addMbedtlsThreadingMacros(m: *std.Build.Module) void {
|
|
m.addCMacro("MBEDTLS_THREADING_C", "1");
|
|
m.addCMacro("MBEDTLS_THREADING_PTHREAD", "1");
|
|
}
|
|
|
|
/// Every `library/*.c` of the release, matching `library/Makefile`.
|
|
const mbedtls_library_sources = [_][]const u8{
|
|
"aes.c", "aesce.c",
|
|
"aesni.c", "aria.c",
|
|
"asn1parse.c", "asn1write.c",
|
|
"base64.c", "bignum.c",
|
|
"bignum_core.c", "bignum_mod.c",
|
|
"bignum_mod_raw.c", "block_cipher.c",
|
|
"camellia.c", "ccm.c",
|
|
"chacha20.c", "chachapoly.c",
|
|
"cipher.c", "cipher_wrap.c",
|
|
"cmac.c", "constant_time.c",
|
|
"ctr_drbg.c", "debug.c",
|
|
"des.c", "dhm.c",
|
|
"ecdh.c", "ecdsa.c",
|
|
"ecjpake.c", "ecp.c",
|
|
"ecp_curves.c", "ecp_curves_new.c",
|
|
"entropy.c", "entropy_poll.c",
|
|
"error.c", "gcm.c",
|
|
"hkdf.c", "hmac_drbg.c",
|
|
"lmots.c", "lms.c",
|
|
"md.c", "md5.c",
|
|
"memory_buffer_alloc.c", "mps_reader.c",
|
|
"mps_trace.c", "net_sockets.c",
|
|
"nist_kw.c", "oid.c",
|
|
"padlock.c", "pem.c",
|
|
"pk.c", "pk_ecc.c",
|
|
"pk_wrap.c", "pkcs12.c",
|
|
"pkcs5.c", "pkcs7.c",
|
|
"pkparse.c", "pkwrite.c",
|
|
"platform.c", "platform_util.c",
|
|
"poly1305.c", "psa_crypto.c",
|
|
"psa_crypto_aead.c", "psa_crypto_cipher.c",
|
|
"psa_crypto_client.c", "psa_crypto_driver_wrappers_no_static.c",
|
|
"psa_crypto_ecp.c", "psa_crypto_ffdh.c",
|
|
"psa_crypto_hash.c", "psa_crypto_mac.c",
|
|
"psa_crypto_pake.c", "psa_crypto_random.c",
|
|
"psa_crypto_rsa.c", "psa_crypto_se.c",
|
|
"psa_crypto_slot_management.c", "psa_crypto_storage.c",
|
|
"psa_its_file.c", "psa_util.c",
|
|
"ripemd160.c", "rsa.c",
|
|
"rsa_alt_helpers.c", "sha1.c",
|
|
"sha256.c", "sha3.c",
|
|
"sha512.c", "ssl_cache.c",
|
|
"ssl_ciphersuites.c", "ssl_client.c",
|
|
"ssl_cookie.c", "ssl_debug_helpers_generated.c",
|
|
"ssl_msg.c", "ssl_ticket.c",
|
|
"ssl_tls.c", "ssl_tls12_client.c",
|
|
"ssl_tls12_server.c", "ssl_tls13_client.c",
|
|
"ssl_tls13_generic.c", "ssl_tls13_keys.c",
|
|
"ssl_tls13_server.c", "threading.c",
|
|
"timing.c", "version.c",
|
|
"version_features.c", "x509.c",
|
|
"x509_create.c", "x509_crl.c",
|
|
"x509_crt.c", "x509_csr.c",
|
|
"x509write.c", "x509write_crt.c",
|
|
"x509write_csr.c",
|
|
};
|
|
|
|
/// The object lists of `3rdparty/everest/Makefile.inc` and `3rdparty/p256-m/Makefile.inc`.
|
|
/// The stock config enables neither driver, so these compile to empty objects.
|
|
const mbedtls_3rdparty_sources = [_][]const u8{
|
|
"everest/library/everest.c",
|
|
"everest/library/x25519.c",
|
|
"everest/library/Hacl_Curve25519_joined.c",
|
|
"p256-m/p256-m_driver_entrypoints.c",
|
|
"p256-m/p256-m/p256-m.c",
|
|
};
|