The fixture puts a #warning in each kind of source the build compiles — module interface, module implementation, C, consumer — plus an unknown -z value so ld.lld warns on the link step too, and asserts the build succeeds cold (every unit compiling, every warning emitted), succeeds with nothing to do, and succeeds again after each source is touched back into staleness. The staged copy lives outside the repo because the whole test depends on units compiling for the first time. The last pass breaks a source on purpose: "never fails" would satisfy everything above just as well, so the error path is asserted too — both that it fails and that it still reports the compiler's diagnostic.
142 lines
7.1 KiB
C++
142 lines
7.1 KiB
C++
// SPDX-License-Identifier: LGPL-3.0-only
|
|
// SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
|
|
|
import std;
|
|
import Crafter.Build;
|
|
#include "lint-rules.h"
|
|
namespace fs = std::filesystem;
|
|
using namespace Crafter;
|
|
|
|
extern "C" Configuration CrafterBuildProject(std::span<const std::string_view> args) {
|
|
std::vector<std::string> depArgs(args.begin(), args.end());
|
|
Configuration* math = GitProject({
|
|
.source = { "https://forgejo.catcrafts.net/Catcrafts/Crafter.Math.git" },
|
|
.args = depArgs,
|
|
});
|
|
Configuration* asset = GitProject({
|
|
.source = { "https://forgejo.catcrafts.net/Catcrafts/Crafter.Asset.git" },
|
|
.args = depArgs,
|
|
});
|
|
|
|
static auto CrafterBuildLib = std::make_unique<Configuration>();
|
|
CrafterBuildLib->path = "./";
|
|
CrafterBuildLib->name = "crafter.build-lib";
|
|
CrafterBuildLib->outputName = "crafter-build";
|
|
ApplyStandardArgs(*CrafterBuildLib, args);
|
|
// Windows builds (native msvc via build.cmd or cross-compiled mingw from
|
|
// Linux) need a DLL + import lib + launcher exe so LoadProject can
|
|
// compile project.cpp against a stable ABI boundary. Linux is monolithic.
|
|
CrafterBuildLib->type = (CrafterBuildLib->target == "x86_64-w64-mingw32" || CrafterBuildLib->target == "x86_64-pc-windows-msvc")
|
|
? ConfigurationType::LibraryDynamic
|
|
: ConfigurationType::LibraryStatic;
|
|
|
|
CrafterBuildLib->dependencies = { math, asset };
|
|
CrafterBuildLib->defines.push_back({"CRAFTER_BUILD_HAS_ASSET", ""});
|
|
{
|
|
std::array<fs::path, 11> interfaces = {
|
|
"interfaces/Crafter.Build",
|
|
"interfaces/Crafter.Build-Shader",
|
|
"interfaces/Crafter.Build-Platform",
|
|
"interfaces/Crafter.Build-Interface",
|
|
"interfaces/Crafter.Build-Implementation",
|
|
"interfaces/Crafter.Build-External",
|
|
"interfaces/Crafter.Build-Clang",
|
|
"interfaces/Crafter.Build-Test",
|
|
"interfaces/Crafter.Build-Lint",
|
|
"interfaces/Crafter.Build-Progress",
|
|
"interfaces/Crafter.Build-Asset",
|
|
};
|
|
std::array<fs::path, 10> implementations = {
|
|
"implementations/Crafter.Build-Shader",
|
|
"implementations/Crafter.Build-Platform",
|
|
"implementations/Crafter.Build-Interface",
|
|
"implementations/Crafter.Build-Implementation",
|
|
"implementations/Crafter.Build-External",
|
|
"implementations/Crafter.Build-Clang",
|
|
"implementations/Crafter.Build-Test",
|
|
"implementations/Crafter.Build-Lint",
|
|
"implementations/Crafter.Build-Progress",
|
|
"implementations/Crafter.Build-Asset",
|
|
};
|
|
CrafterBuildLib->GetInterfacesAndImplementations(interfaces, implementations);
|
|
}
|
|
ExternalDependency& glslang = CrafterBuildLib->externalDependencies.emplace_back();
|
|
glslang.name = "glslang";
|
|
glslang.source.url = "https://github.com/KhronosGroup/glslang.git";
|
|
glslang.source.branch = "main";
|
|
glslang.builder = ExternalBuilder::CMake;
|
|
glslang.options = { "-DENABLE_OPT=OFF" };
|
|
// mingw cross-build: skip the standalone executable. We only consume the
|
|
// libraries, and glslang.exe pulls in libgcc_eh which needs pthread that
|
|
// mingw-w64 doesn't link by default.
|
|
if (CrafterBuildLib->target == "x86_64-w64-mingw32") {
|
|
glslang.options.push_back("-DENABLE_GLSLANG_BINARIES=OFF");
|
|
}
|
|
glslang.includeDirs = { "" };
|
|
glslang.libs = { "SPIRV", "GenericCodeGen", "glslang", "OSDependent", "MachineIndependent", "glslang-default-resource-limits" };
|
|
|
|
Configuration cfg;
|
|
cfg.path = "./";
|
|
cfg.name = "crafter.build-exe";
|
|
cfg.outputName = "crafter-build";
|
|
ApplyStandardArgs(cfg, args);
|
|
cfg.type = ConfigurationType::Executable;
|
|
cfg.dependencies = { CrafterBuildLib.get() };
|
|
{
|
|
std::array<fs::path, 0> interfaces = {};
|
|
std::array<fs::path, 1> implementations = { "implementations/main" };
|
|
cfg.GetInterfacesAndImplementations(interfaces, implementations);
|
|
}
|
|
if (cfg.target == "x86_64-pc-linux-gnu") {
|
|
cfg.linkFlags.push_back("-Wl,--export-dynamic");
|
|
cfg.linkFlags.push_back("-ldl");
|
|
}
|
|
if (cfg.target == "x86_64-w64-mingw32" || cfg.target == "x86_64-pc-windows-msvc") {
|
|
// winsock for the -r wasm port probe (bind/WSAStartup).
|
|
CrafterBuildLib->linkFlags.push_back("-lws2_32");
|
|
}
|
|
|
|
// Self-tests link the local crafter-build library and exercise it in
|
|
// process. The harness (whichever crafter-build invokes `test`) compiles
|
|
// these against the *installed* share/crafter-build .cppm files, then
|
|
// links each test exe against CrafterBuildLib built from the local
|
|
// sources — so the code under test is whatever's in this checkout.
|
|
// Mirrors how downstream consumers link their own libraries into tests.
|
|
if (cfg.target == "x86_64-pc-linux-gnu") {
|
|
cfg.AddTest("HelloWorld").Dependencies({ CrafterBuildLib.get() });
|
|
cfg.AddTest("StaticLib").Dependencies({ CrafterBuildLib.get() });
|
|
cfg.AddTest("ModuleInterface").Dependencies({ CrafterBuildLib.get() });
|
|
cfg.AddTest("DependencyLink").Dependencies({ CrafterBuildLib.get() });
|
|
cfg.AddTest("IncrementalInterfaceChange").Dependencies({ CrafterBuildLib.get() });
|
|
cfg.AddTest("TransitiveInterfaceChange").Dependencies({ CrafterBuildLib.get() });
|
|
cfg.AddTest("IncrementalHeaderChange").Dependencies({ CrafterBuildLib.get() });
|
|
cfg.AddTest("CompilerWarning").Dependencies({ CrafterBuildLib.get() });
|
|
cfg.AddTest("CleanProject").Dependencies({ CrafterBuildLib.get() });
|
|
cfg.AddTest("ShaderCompile").Dependencies({ CrafterBuildLib.get() });
|
|
cfg.AddTest("StandardArgs").Dependencies({ CrafterBuildLib.get() });
|
|
cfg.AddTest("TestRunnerSpec").Dependencies({ CrafterBuildLib.get() });
|
|
cfg.AddTest("VariantId").Dependencies({ CrafterBuildLib.get() });
|
|
cfg.AddTest("WasiBrowserRuntime").Dependencies({ CrafterBuildLib.get() });
|
|
cfg.AddTest("WasmVariants").Dependencies({ CrafterBuildLib.get() });
|
|
cfg.AddTest("RunSingleTestExit").Dependencies({ CrafterBuildLib.get() });
|
|
// LoadProject dlopens the synthesized project.so, which references
|
|
// Crafter:: symbols (HostTarget, Configuration ctors) that have to be
|
|
// visible from the test exe — same wiring crafter-build itself uses
|
|
// for project.so.
|
|
cfg.AddTest("ConcurrentCacheRace").Dependencies({ CrafterBuildLib.get() })
|
|
.LinkFlag("-Wl,--export-dynamic").LinkFlag("-ldl");
|
|
// Same LoadProject wiring as ConcurrentCacheRace above.
|
|
cfg.AddTest("HostCacheSourceStamp").Dependencies({ CrafterBuildLib.get() })
|
|
.LinkFlag("-Wl,--export-dynamic").LinkFlag("-ldl");
|
|
cfg.AddTest("ConcurrentDependencyReset").Dependencies({ CrafterBuildLib.get() });
|
|
cfg.AddTest("Lint").Dependencies({ CrafterBuildLib.get() });
|
|
cfg.AddTest("HouseRules").Dependencies({ CrafterBuildLib.get() });
|
|
}
|
|
|
|
// Dogfood: this repo's house-style rules (see lint-rules.h). Report
|
|
// rules gate `crafter-build lint`; transform rules also auto-fix under
|
|
// `crafter-build format`.
|
|
ProjectLint::AddProjectLintRules(cfg);
|
|
|
|
return cfg;
|
|
}
|