Two toolchains that differ only in the clang release, as the Flatpak's do across an update, must give one Configuration two variants; so must the same release installed under another prefix. The no-argument form is checked to be the variant under the running toolchain, and BuildDir to be named for it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
169 lines
6.9 KiB
C++
169 lines
6.9 KiB
C++
// SPDX-License-Identifier: LGPL-3.0-only
|
|
// SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
|
|
|
import std;
|
|
import Crafter.Build;
|
|
using namespace Crafter;
|
|
|
|
namespace {
|
|
std::int32_t Failures = 0;
|
|
|
|
void Check(bool cond, std::string_view msg) {
|
|
if (!cond) {
|
|
std::println(std::cerr, "FAIL: {}", msg);
|
|
++Failures;
|
|
}
|
|
}
|
|
|
|
Configuration MakeBase() {
|
|
Configuration cfg;
|
|
cfg.path = "/tmp/variant-id-test";
|
|
cfg.name = "vt";
|
|
cfg.outputName = "vt";
|
|
cfg.target = "x86_64-pc-linux-gnu";
|
|
cfg.march = "native";
|
|
cfg.mtune = "native";
|
|
cfg.type = ConfigurationType::Executable;
|
|
return cfg;
|
|
}
|
|
}
|
|
|
|
// VariantId is the cache key for build outputs. Two Configurations that
|
|
// differ only in something that affects codegen must produce different
|
|
// VariantId / BuildDir / BinDir, otherwise their .o files collide.
|
|
int main() {
|
|
{
|
|
// Baseline against itself: VariantId is deterministic.
|
|
Configuration a = MakeBase();
|
|
Configuration b = MakeBase();
|
|
Check(a.VariantId() == b.VariantId(), "identical configs have identical VariantId");
|
|
Check(a.BuildDir() == b.BuildDir(), "identical configs have identical BuildDir");
|
|
}
|
|
{
|
|
// type differs -> VariantId differs.
|
|
Configuration a = MakeBase();
|
|
Configuration b = MakeBase();
|
|
b.type = ConfigurationType::LibraryStatic;
|
|
Check(a.VariantId() != b.VariantId(), "type change perturbs VariantId");
|
|
}
|
|
{
|
|
// debug differs -> VariantId differs.
|
|
Configuration a = MakeBase();
|
|
Configuration b = MakeBase();
|
|
b.debug = true;
|
|
Check(a.VariantId() != b.VariantId(), "debug change perturbs VariantId");
|
|
}
|
|
{
|
|
// sysroot differs -> VariantId differs.
|
|
Configuration a = MakeBase();
|
|
Configuration b = MakeBase();
|
|
b.sysroot = "/opt/sysroot";
|
|
Check(a.VariantId() != b.VariantId(), "sysroot change perturbs VariantId");
|
|
}
|
|
{
|
|
// defines differ -> VariantId differs.
|
|
Configuration a = MakeBase();
|
|
Configuration b = MakeBase();
|
|
b.defines.push_back({"FOO", "1"});
|
|
Check(a.VariantId() != b.VariantId(), "define added perturbs VariantId");
|
|
}
|
|
{
|
|
// Same define name, different value -> VariantId differs.
|
|
Configuration a = MakeBase();
|
|
a.defines.push_back({"FOO", "1"});
|
|
Configuration b = MakeBase();
|
|
b.defines.push_back({"FOO", "2"});
|
|
Check(a.VariantId() != b.VariantId(), "define value change perturbs VariantId");
|
|
}
|
|
{
|
|
// compileFlags differ -> VariantId differs.
|
|
Configuration a = MakeBase();
|
|
Configuration b = MakeBase();
|
|
b.compileFlags.push_back("-fno-omit-frame-pointer");
|
|
Check(a.VariantId() != b.VariantId(), "compile flag added perturbs VariantId");
|
|
}
|
|
{
|
|
// target differs -> VariantId differs.
|
|
Configuration a = MakeBase();
|
|
Configuration b = MakeBase();
|
|
b.target = "aarch64-linux-gnu";
|
|
Check(a.VariantId() != b.VariantId(), "target change perturbs VariantId");
|
|
}
|
|
{
|
|
// march differs -> VariantId differs.
|
|
Configuration a = MakeBase();
|
|
Configuration b = MakeBase();
|
|
b.march = "x86-64-v3";
|
|
Check(a.VariantId() != b.VariantId(), "march change perturbs VariantId");
|
|
}
|
|
{
|
|
// A project flag the framework can't interpret still decides what gets
|
|
// compiled and bundled, so it has to key into VariantId. Otherwise both
|
|
// settings share one bin dir and interleave their outputs there — an
|
|
// index.html from one alongside a script from the other.
|
|
Configuration a = MakeBase();
|
|
Configuration b = MakeBase();
|
|
b.projectArgs.push_back("--no-webgpu");
|
|
Check(a.VariantId() != b.VariantId(), "project arg perturbs VariantId");
|
|
Check(a.BinDir() != b.BinDir(), "project arg yields a distinct BinDir");
|
|
}
|
|
{
|
|
// Distinct project args must not collide with each other either.
|
|
Configuration a = MakeBase();
|
|
a.projectArgs.push_back("--no-webgpu");
|
|
Configuration b = MakeBase();
|
|
b.projectArgs.push_back("--no-audio");
|
|
Check(a.VariantId() != b.VariantId(), "different project args yield different VariantId");
|
|
}
|
|
{
|
|
// PcmDir() differs between Executable (in BuildDir) and Library
|
|
// (in BinDir) — Library PCMs land in the installable bin dir so
|
|
// downstream consumers can find them.
|
|
Configuration exe = MakeBase();
|
|
Configuration lib = MakeBase();
|
|
lib.type = ConfigurationType::LibraryStatic;
|
|
Check(exe.PcmDir() == exe.BuildDir(), "Executable PcmDir == BuildDir");
|
|
Check(lib.PcmDir() == lib.BinDir(), "Library PcmDir == BinDir");
|
|
}
|
|
{
|
|
// VariantId is embedded in the path so distinct ids produce distinct dirs.
|
|
Configuration a = MakeBase();
|
|
Configuration b = MakeBase();
|
|
b.debug = true;
|
|
Check(a.BuildDir() != b.BuildDir(), "different VariantId yields different BuildDir");
|
|
Check(a.BinDir() != b.BinDir(), "different VariantId yields different BinDir");
|
|
}
|
|
{
|
|
// The compiler is compile state as much as any flag: a PCM can only
|
|
// be read by the clang release that wrote it, and nothing in a
|
|
// project's sources changes when the compiler does. With the
|
|
// toolchain left out of the id, a project built with clang 22 kept
|
|
// its PCMs after crafter-build moved to clang 23; the build after
|
|
// the update found nothing stale, and the first edit failed with
|
|
// "module file ... uses an older format that is no longer supported"
|
|
// until the project was cleaned by hand.
|
|
Configuration cfg = MakeBase();
|
|
Toolchain older;
|
|
older.clangxx = "/app/bin/clang++";
|
|
older.clangVersion = "clang version 22.1.8";
|
|
Toolchain newer = older;
|
|
newer.clangVersion = "clang version 23.1.2";
|
|
Check(cfg.VariantId(older) != cfg.VariantId(newer), "a compiler update at the same path perturbs VariantId");
|
|
Check(cfg.VariantId(older) == cfg.VariantId(older), "VariantId under one toolchain is deterministic");
|
|
|
|
// Two installs of one release are still two toolchains: their
|
|
// libc++ need not be the same build.
|
|
Toolchain elsewhere = older;
|
|
elsewhere.clangxx = "/usr/bin/clang++";
|
|
Check(cfg.VariantId(older) != cfg.VariantId(elsewhere), "the same release installed elsewhere perturbs VariantId");
|
|
|
|
Check(cfg.VariantId() == cfg.VariantId(GetToolchain()), "VariantId() is the variant under the running toolchain");
|
|
Check(cfg.BuildDir().filename() == cfg.VariantId(GetToolchain()), "BuildDir is named for the running toolchain's variant");
|
|
}
|
|
|
|
if (Failures > 0) {
|
|
std::println(std::cerr, "{} assertions failed", Failures);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|