Some checks failed
CI / build-test-release (pull_request) Failing after 6m5s
A primary module interface unit — `export module Widget;`, no partitions — recorded nothing about what it imported. GetInterfacesAndImplementations registered the Module and then erased the file from the scan list, so the import pass only ever saw partitions, and Module had no vectors to hold an edge anyway. Two consequences, both reported as issue #26: Module::Check consulted only its own .cppm and its partitions. A data member added to an imported module left Widget.pcm, Widget.o and every consumer object untouched while the imported library rebuilt and both binaries relinked — one executable holding two class layouts, no diagnostic, and a crash somewhere unrelated. Wiping build/ was the only cure, so `crafter-build test` could not be trusted straight after an interface edit. Module::Compile waited on nothing. Two modules in one Configuration compile on concurrent threads, so a primary interface importing a sibling was a coin flip between working and "module 'Base' not found". Partitions never had either problem — they carry the same three vectors and Check/Compile honour them — which is why the gap only surfaced on a module whose interface is one flat unit. Module now carries moduleDependencies, externalModuleDependencies and pendingImports with the same meanings as on ModulePartition; primary units stay in the scan list so their imports land there; Check sees through them; Compile orders itself behind a local sibling; and ResolvePendingImports sweeps them so an edge survives dependencies being wired up afterwards. Build() now Checks every interface before spawning any compile thread — the `compiled` flag a waiter blocks on is raised either by a Compile that runs or by the Check that decides none is needed, so a Check still pending while another module's thread waits would have hung the build. Resolves #26 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
140 lines
7 KiB
C++
140 lines
7 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("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;
|
|
}
|