Crafter.Build/tests/StandardArgs/main.cpp

116 lines
4.8 KiB
C++
Raw Normal View History

2026-07-23 01:24:42 +02:00
// SPDX-License-Identifier: LGPL-3.0-only
// SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
2026-07-22 18:25:39 +02:00
import std;
import Crafter.Build;
using namespace Crafter;
namespace {
2026-07-23 01:24:42 +02:00
std::int32_t Failures = 0;
void Check(bool cond, std::string_view msg) {
if (!cond) {
std::println(std::cerr, "FAIL: {}", msg);
2026-07-23 01:24:42 +02:00
++Failures;
}
}
}
// Pure-data tests for ApplyStandardArgs and ArgQuery — no build/run, just
// confirms the documented arg parsing produces the documented mutations.
int main() {
// --debug + --target= + --march= + --mtune=
{
Configuration cfg;
cfg.target = "ignored";
std::array<std::string_view, 4> raw = {
"--debug", "--target=aarch64-linux-gnu", "--march=armv8-a", "--mtune=cortex-a72",
};
ApplyStandardArgs(cfg, raw);
Check(cfg.debug, "--debug should set cfg.debug");
Check(cfg.target == "aarch64-linux-gnu", "--target= should overwrite cfg.target");
Check(cfg.march == "armv8-a", "--march= should overwrite cfg.march");
Check(cfg.mtune == "cortex-a72", "--mtune= should overwrite cfg.mtune");
}
2026-07-22 18:25:39 +02:00
// --sysroot= sets cfg.sysroot (cross-compiles against a foreign root,
// e.g. an Alpine aarch64 tree; also picked up by the std.cppm lookup
// and the qemu test-runner's QEMU_LD_PREFIX).
{
Configuration cfg;
std::array<std::string_view, 2> raw = {
"--target=aarch64-alpine-linux-musl", "--sysroot=/opt/alpine-aarch64",
};
ApplyStandardArgs(cfg, raw);
Check(cfg.sysroot == "/opt/alpine-aarch64", "--sysroot= should set cfg.sysroot");
Check(cfg.target == "aarch64-alpine-linux-musl", "--target= combines with --sysroot=");
}
// --lib promotes Executable -> LibraryStatic; --shared then promotes to Dynamic.
{
Configuration cfg;
cfg.type = ConfigurationType::Executable;
std::array<std::string_view, 1> raw = { "--lib" };
ApplyStandardArgs(cfg, raw);
Check(cfg.type == ConfigurationType::LibraryStatic, "--lib promotes Exe -> Static");
}
{
Configuration cfg;
cfg.type = ConfigurationType::Executable;
// Order shouldn't matter — promotions chain in priority order.
std::array<std::string_view, 2> raw = { "--shared", "--lib" };
ApplyStandardArgs(cfg, raw);
Check(cfg.type == ConfigurationType::LibraryDynamic, "--lib + --shared lands on Dynamic regardless of order");
}
{
Configuration cfg;
cfg.type = ConfigurationType::Executable;
std::array<std::string_view, 1> raw = { "--shared" };
ApplyStandardArgs(cfg, raw);
Check(cfg.type == ConfigurationType::Executable, "--shared alone on Executable is a no-op");
}
// ArgQuery is returned over the same args span and exposes Has/Get.
{
Configuration cfg;
std::array<std::string_view, 3> raw = { "--timing", "--prefix=/opt/x", "extra" };
ArgQuery q = ApplyStandardArgs(cfg, raw);
Check(q.Has("--timing"), "ArgQuery::Has true for present flag");
Check(!q.Has("--missing"), "ArgQuery::Has false for absent flag");
auto prefix = q.Get("--prefix=");
Check(prefix.has_value(), "ArgQuery::Get returns a value for known prefix");
Check(prefix.value_or("") == "/opt/x", "ArgQuery::Get returns the substring after =");
Check(!q.Get("--other=").has_value(), "ArgQuery::Get returns nullopt for absent prefix");
}
fix: key the host PCM cache on source content, add clean, hash project args Three follow-ons to the stale-build report, all cases of an identity not capturing something that changes the output. The host PCM cache under <cache>/crafter.build/<target>-<march>/ is shared by every crafter-build on the machine, and freshness was a per-file mtime comparison. That cannot tell "this PCM is newer than my source" from "this PCM was built from different sources that happen to be newer", so a package install and a working checkout — or two checkouts of different versions — silently compiled their project.cpp against each other's declarations. Invalidation now keys on a stamp over the bytes of every module source, which also covers the case one file's mtime never could: the cached PCMs import each other, so a change to :Interface invalidates :Clang's PCM with Crafter.Build-Clang.cppm untouched. Project args ApplyStandardArgs does not itself interpret are now folded into VariantId. Such a flag typically decides what gets compiled or bundled — the report's example is --no-webgpu dropping entries from cfg.files — and without it both settings shared one bin dir and interleaved their outputs there, leaving a bundle matching neither. Sorted and deduplicated so flag order doesn't split the cache, and inherited by test Configurations. `crafter-build clean` removes the project's bin/ and build/ trees. It deliberately does not load project.cpp: cleaning is most often reached when something is already wrong, and a clean that first needs the project to compile is useless exactly then.
2026-07-30 17:43:09 +00:00
// Flags ApplyStandardArgs doesn't itself interpret are collected into
// cfg.projectArgs, which feeds VariantId — a project flag that changes what
// gets compiled has to move the output directory with it.
{
Configuration cfg;
std::array<std::string_view, 5> raw = {
"--debug", "--no-webgpu", "--target=aarch64-linux-gnu", "--feature=fancy", "bare",
};
ApplyStandardArgs(cfg, raw);
std::vector<std::string> expected = { "--feature=fancy", "--no-webgpu", "bare" };
Check(cfg.projectArgs == expected, std::format("only unrecognised args are collected, sorted; got [{}]", std::format("{}", std::views::join_with(cfg.projectArgs, std::string(", ")) | std::ranges::to<std::string>())));
}
// Order and repetition must not perturb the set — otherwise the same build
// spelled two ways would land in two directories.
{
Configuration a;
Configuration b;
std::array<std::string_view, 2> forward = { "--alpha", "--beta" };
std::array<std::string_view, 3> reversed = { "--beta", "--alpha", "--beta" };
ApplyStandardArgs(a, forward);
ApplyStandardArgs(b, reversed);
Check(a.projectArgs == b.projectArgs, "projectArgs is order- and duplicate-insensitive");
}
2026-07-23 01:24:42 +02:00
if (Failures > 0) {
std::println(std::cerr, "{} assertions failed", Failures);
return 1;
}
return 0;
}