2026-04-30 01:29:17 +02:00
|
|
|
import std;
|
|
|
|
|
import Crafter.Build;
|
|
|
|
|
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());
|
2026-05-12 00:24:48 +02:00
|
|
|
|
|
|
|
|
// --local resolves Crafter.* deps from sibling working trees instead of
|
|
|
|
|
// fetching them from forgejo. Use during cross-repo development so edits
|
|
|
|
|
// in ../Crafter.Asset are picked up without commit-and-pull. Add to
|
|
|
|
|
// depArgs too so transitive deps inherit the same mode.
|
|
|
|
|
bool useLocal = false;
|
|
|
|
|
for (std::string_view a : args) {
|
|
|
|
|
if (a == "--local") { useLocal = true; break; }
|
|
|
|
|
}
|
|
|
|
|
if (useLocal && std::find(depArgs.begin(), depArgs.end(), std::string("--local")) == depArgs.end()) {
|
|
|
|
|
depArgs.push_back("--local");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto resolveDep = [&](std::string_view name, std::string_view gitUrl) -> Configuration* {
|
|
|
|
|
if (useLocal) {
|
|
|
|
|
return LocalProject({
|
|
|
|
|
.projectFile = fs::path("../") / name / "project.cpp",
|
|
|
|
|
.args = depArgs,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
return GitProject({
|
|
|
|
|
.source = { .url = std::string(gitUrl) },
|
|
|
|
|
.args = depArgs,
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
2026-05-18 02:07:48 +02:00
|
|
|
// Sniff the requested target from args before any deps resolve — the
|
|
|
|
|
// Crafter.Asset dependency is heavy and not wasm-ready (uses `throw`
|
|
|
|
|
// under -fno-exceptions, references `_Float16`). The DOM build stubs
|
|
|
|
|
// the renderer entirely so the dep doesn't apply anyway.
|
|
|
|
|
bool isWasm = false;
|
|
|
|
|
for (std::string_view a : args) {
|
|
|
|
|
if (a.starts_with("--target=") && a.find("wasm") != std::string_view::npos) {
|
|
|
|
|
isWasm = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-12 00:24:48 +02:00
|
|
|
Configuration* event = resolveDep("Crafter.Event", "https://forgejo.catcrafts.net/Catcrafts/Crafter.Event.git");
|
|
|
|
|
Configuration* math = resolveDep("Crafter.Math", "https://forgejo.catcrafts.net/Catcrafts/Crafter.Math.git");
|
2026-05-18 02:07:48 +02:00
|
|
|
Configuration* asset = isWasm
|
|
|
|
|
? nullptr
|
|
|
|
|
: resolveDep("Crafter.Asset", "https://forgejo.catcrafts.net/Catcrafts/Crafter.Asset.git");
|
2026-04-30 01:29:17 +02:00
|
|
|
|
|
|
|
|
Configuration cfg;
|
|
|
|
|
cfg.path = "./";
|
|
|
|
|
cfg.name = "Crafter.Graphics";
|
|
|
|
|
cfg.outputName = "Crafter.Graphics";
|
|
|
|
|
cfg.type = ConfigurationType::LibraryStatic;
|
|
|
|
|
auto opts = ApplyStandardArgs(cfg, args);
|
2026-05-18 02:07:48 +02:00
|
|
|
if (asset) {
|
|
|
|
|
cfg.dependencies = { event, math, asset };
|
|
|
|
|
} else {
|
|
|
|
|
cfg.dependencies = { event, math };
|
|
|
|
|
}
|
2026-04-30 01:29:17 +02:00
|
|
|
|
|
|
|
|
// Window backend follows the target triple. V1 had separate lib-wayland /
|
|
|
|
|
// lib-win32 configurations; V2 picks the right one automatically based on
|
|
|
|
|
// where the build is going. Cross-compile (`--target=...`) flips the
|
2026-05-18 02:07:48 +02:00
|
|
|
// backend along with everything else. The DOM backend is reached by any
|
|
|
|
|
// wasm32-* target and produces a Vulkan-free build whose Window is wired
|
|
|
|
|
// to a custom JS env (see additional/dom-env.js).
|
|
|
|
|
bool dom = cfg.target.find("wasm") != std::string::npos;
|
|
|
|
|
bool windows = !dom
|
|
|
|
|
&& (cfg.target.find("windows") != std::string::npos
|
|
|
|
|
|| cfg.target.find("mingw") != std::string::npos);
|
|
|
|
|
if (dom) {
|
|
|
|
|
cfg.defines.push_back({"CRAFTER_GRAPHICS_WINDOW_DOM", ""});
|
|
|
|
|
// No native window libs, no Vulkan loader, no Wayland/X11. The JS
|
|
|
|
|
// bridge satisfies every dynamic symbol via wasm imports. Crafter.Build
|
|
|
|
|
// strips -march/-mtune from the clang command line for any wasm32-*
|
|
|
|
|
// triple, so cfg.march/mtune can stay at their defaults — keeping them
|
|
|
|
|
// matches the VariantId of dependency PCMs.
|
|
|
|
|
} else if (windows) {
|
2026-04-30 01:29:17 +02:00
|
|
|
cfg.defines.push_back({"CRAFTER_GRAPHICS_WINDOW_WIN32", ""});
|
|
|
|
|
cfg.linkFlags.push_back("-lkernel32");
|
|
|
|
|
cfg.linkFlags.push_back("-luser32");
|
|
|
|
|
cfg.linkFlags.push_back("-lgdi32");
|
2026-05-12 00:24:48 +02:00
|
|
|
// Windows.Gaming.Input (WGI) needs the WinRT activation runtime
|
|
|
|
|
// and combase for HSTRING / RoGetActivationFactory.
|
|
|
|
|
cfg.linkFlags.push_back("-lruntimeobject");
|
|
|
|
|
cfg.linkFlags.push_back("-lcombase");
|
2026-04-30 01:29:17 +02:00
|
|
|
} else {
|
|
|
|
|
cfg.defines.push_back({"CRAFTER_GRAPHICS_WINDOW_WAYLAND", ""});
|
|
|
|
|
cfg.linkFlags.push_back("-lwayland-client");
|
|
|
|
|
cfg.linkFlags.push_back("-lxkbcommon");
|
2026-05-12 00:24:48 +02:00
|
|
|
// Gamepad: libudev for hot-plug + device enumeration; libevdev
|
|
|
|
|
// for event parsing + axis calibration. libevdev ships its headers
|
|
|
|
|
// under a versioned dir (libevdev-1.0/) so the -I is mandatory.
|
|
|
|
|
cfg.linkFlags.push_back("-ludev");
|
|
|
|
|
cfg.linkFlags.push_back("-levdev");
|
|
|
|
|
cfg.compileFlags.push_back("-I/usr/include/libevdev-1.0");
|
2026-04-30 01:29:17 +02:00
|
|
|
cfg.cFiles.push_back("lib/xdg-shell-protocol");
|
|
|
|
|
cfg.cFiles.push_back("lib/wayland-xdg-decoration-unstable-v1-client-protocol");
|
|
|
|
|
cfg.cFiles.push_back("lib/fractional-scale-v1");
|
|
|
|
|
cfg.cFiles.push_back("lib/viewporter");
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-18 02:07:48 +02:00
|
|
|
// Vulkan is the only renderer on native targets. Software fallback is
|
|
|
|
|
// provided externally via the Vulkan loader (e.g. llvmpipe / lavapipe) —
|
|
|
|
|
// no separate code path. The DOM backend doesn't render in V1 (the
|
|
|
|
|
// UIRenderer and every Vulkan-typed module are excluded below); the
|
|
|
|
|
// WebGPU follow-up will gain its own headers/loader rather than reuse
|
|
|
|
|
// the Vulkan ones.
|
|
|
|
|
if (!dom) {
|
|
|
|
|
ExternalDependency& vkHeaders = cfg.externalDependencies.emplace_back();
|
|
|
|
|
vkHeaders.name = "Vulkan-Headers";
|
|
|
|
|
vkHeaders.source.url = "https://github.com/KhronosGroup/Vulkan-Headers.git";
|
|
|
|
|
vkHeaders.builder = ExternalBuilder::None;
|
|
|
|
|
vkHeaders.includeDirs = { "include" };
|
|
|
|
|
ExternalDependency& vkUtility = cfg.externalDependencies.emplace_back();
|
|
|
|
|
vkUtility.name = "Vulkan-Utility-Libraries";
|
|
|
|
|
vkUtility.source.url = "https://github.com/KhronosGroup/Vulkan-Utility-Libraries.git";
|
|
|
|
|
vkUtility.builder = ExternalBuilder::None;
|
|
|
|
|
vkUtility.includeDirs = { "include" };
|
|
|
|
|
cfg.linkFlags.push_back(windows ? "-lvulkan-1" : "-lvulkan");
|
|
|
|
|
}
|
2026-04-30 01:29:17 +02:00
|
|
|
|
|
|
|
|
if (opts.Has("--timing")) cfg.defines.push_back({"CRAFTER_TIMING", ""});
|
|
|
|
|
|
2026-05-18 02:07:48 +02:00
|
|
|
// One master interface list. Every partition exists on every target
|
|
|
|
|
// — Crafter.Build's dependency scanner doesn't respect `#ifdef` on
|
|
|
|
|
// `import :X` statements, so the partition file must be present even
|
|
|
|
|
// when its body is gated out. Vulkan-typed partitions stub to empty
|
|
|
|
|
// modules under CRAFTER_GRAPHICS_WINDOW_DOM; the Dom/DomEvents/Router
|
|
|
|
|
// partitions stub to empty modules in the opposite direction.
|
2026-05-18 04:58:52 +02:00
|
|
|
std::array<fs::path, 37> ifaces = {
|
2026-04-30 01:29:17 +02:00
|
|
|
"interfaces/Crafter.Graphics",
|
|
|
|
|
"interfaces/Crafter.Graphics-Animation",
|
2026-05-12 00:24:48 +02:00
|
|
|
"interfaces/Crafter.Graphics-Clipboard",
|
2026-05-02 21:08:20 +02:00
|
|
|
"interfaces/Crafter.Graphics-ComputeShader",
|
2026-05-12 00:24:48 +02:00
|
|
|
"interfaces/Crafter.Graphics-Decompress",
|
2026-04-30 01:29:17 +02:00
|
|
|
"interfaces/Crafter.Graphics-DescriptorHeapVulkan",
|
2026-05-18 04:58:52 +02:00
|
|
|
"interfaces/Crafter.Graphics-DescriptorHeapWebGPU",
|
2026-04-30 01:29:17 +02:00
|
|
|
"interfaces/Crafter.Graphics-Device",
|
2026-05-18 02:07:48 +02:00
|
|
|
"interfaces/Crafter.Graphics-Dom",
|
|
|
|
|
"interfaces/Crafter.Graphics-DomEvents",
|
2026-04-30 01:29:17 +02:00
|
|
|
"interfaces/Crafter.Graphics-Font",
|
2026-05-02 21:08:20 +02:00
|
|
|
"interfaces/Crafter.Graphics-FontAtlas",
|
2026-04-30 01:29:17 +02:00
|
|
|
"interfaces/Crafter.Graphics-ForwardDeclarations",
|
2026-05-12 00:24:48 +02:00
|
|
|
"interfaces/Crafter.Graphics-Gamepad",
|
2026-05-18 04:58:52 +02:00
|
|
|
"interfaces/Crafter.Graphics-GraphicsTypes",
|
2026-04-30 01:29:17 +02:00
|
|
|
"interfaces/Crafter.Graphics-ImageVulkan",
|
2026-05-12 00:24:48 +02:00
|
|
|
"interfaces/Crafter.Graphics-Input",
|
2026-05-03 02:45:38 +02:00
|
|
|
"interfaces/Crafter.Graphics-InputField",
|
2026-05-12 00:24:48 +02:00
|
|
|
"interfaces/Crafter.Graphics-Keys",
|
2026-04-30 01:29:17 +02:00
|
|
|
"interfaces/Crafter.Graphics-Mesh",
|
|
|
|
|
"interfaces/Crafter.Graphics-PipelineRTVulkan",
|
|
|
|
|
"interfaces/Crafter.Graphics-RenderingElement3D",
|
2026-05-01 23:35:37 +02:00
|
|
|
"interfaces/Crafter.Graphics-RenderPass",
|
2026-05-18 02:07:48 +02:00
|
|
|
"interfaces/Crafter.Graphics-Router",
|
2026-05-01 23:35:37 +02:00
|
|
|
"interfaces/Crafter.Graphics-RTPass",
|
2026-04-30 01:29:17 +02:00
|
|
|
"interfaces/Crafter.Graphics-SamplerVulkan",
|
|
|
|
|
"interfaces/Crafter.Graphics-ShaderBindingTableVulkan",
|
|
|
|
|
"interfaces/Crafter.Graphics-ShaderVulkan",
|
|
|
|
|
"interfaces/Crafter.Graphics-Types",
|
2026-05-01 23:35:37 +02:00
|
|
|
"interfaces/Crafter.Graphics-UI",
|
2026-05-02 21:08:20 +02:00
|
|
|
"interfaces/Crafter.Graphics-UIComponents",
|
2026-04-30 01:29:17 +02:00
|
|
|
"interfaces/Crafter.Graphics-VulkanBuffer",
|
|
|
|
|
"interfaces/Crafter.Graphics-VulkanTransition",
|
2026-05-18 04:58:52 +02:00
|
|
|
"interfaces/Crafter.Graphics-WebGPU",
|
|
|
|
|
"interfaces/Crafter.Graphics-WebGPUBuffer",
|
|
|
|
|
"interfaces/Crafter.Graphics-WebGPUComputeShader",
|
2026-04-30 01:29:17 +02:00
|
|
|
"interfaces/Crafter.Graphics-Window",
|
|
|
|
|
};
|
|
|
|
|
|
2026-05-18 02:07:48 +02:00
|
|
|
if (dom) {
|
2026-05-18 04:58:52 +02:00
|
|
|
// DOM impl set. UI-Shared.cpp is backend-agnostic; UI-WebGPU.cpp
|
|
|
|
|
// is the DOM-only implementation of UIRenderer's GPU-touching
|
|
|
|
|
// methods. Font / FontAtlas / UIComponents are now portable.
|
|
|
|
|
std::array<fs::path, 11> domImpls = {
|
2026-05-18 02:07:48 +02:00
|
|
|
"implementations/Crafter.Graphics-Clipboard",
|
|
|
|
|
"implementations/Crafter.Graphics-Dom",
|
2026-05-18 04:58:52 +02:00
|
|
|
"implementations/Crafter.Graphics-Font",
|
|
|
|
|
"implementations/Crafter.Graphics-FontAtlas",
|
2026-05-18 02:07:48 +02:00
|
|
|
"implementations/Crafter.Graphics-Gamepad",
|
|
|
|
|
"implementations/Crafter.Graphics-Input",
|
|
|
|
|
"implementations/Crafter.Graphics-Router",
|
2026-05-18 04:58:52 +02:00
|
|
|
"implementations/Crafter.Graphics-UI-Shared",
|
|
|
|
|
"implementations/Crafter.Graphics-UI-WebGPU",
|
|
|
|
|
"implementations/Crafter.Graphics-UIComponents",
|
2026-05-18 02:07:48 +02:00
|
|
|
"implementations/Crafter.Graphics-Window",
|
|
|
|
|
};
|
|
|
|
|
cfg.GetInterfacesAndImplementations(ifaces, domImpls);
|
|
|
|
|
// JS glue shipped alongside the .wasm so the loader has the
|
|
|
|
|
// env-import surface the Window/Dom bindings expect.
|
|
|
|
|
cfg.files.emplace_back(fs::path("additional/dom-env.js"));
|
2026-05-18 04:58:52 +02:00
|
|
|
cfg.files.emplace_back(fs::path("additional/dom-webgpu.js"));
|
2026-05-18 02:07:48 +02:00
|
|
|
} else {
|
2026-05-18 04:58:52 +02:00
|
|
|
std::array<fs::path, 14> impls = {
|
2026-05-18 02:07:48 +02:00
|
|
|
"implementations/Crafter.Graphics-Clipboard",
|
|
|
|
|
"implementations/Crafter.Graphics-ComputeShader",
|
|
|
|
|
"implementations/Crafter.Graphics-Device",
|
|
|
|
|
"implementations/Crafter.Graphics-Font",
|
|
|
|
|
"implementations/Crafter.Graphics-FontAtlas",
|
|
|
|
|
"implementations/Crafter.Graphics-Gamepad",
|
|
|
|
|
"implementations/Crafter.Graphics-Input",
|
|
|
|
|
"implementations/Crafter.Graphics-InputField",
|
|
|
|
|
"implementations/Crafter.Graphics-Mesh",
|
|
|
|
|
"implementations/Crafter.Graphics-RenderingElement3D",
|
|
|
|
|
"implementations/Crafter.Graphics-UI",
|
2026-05-18 04:58:52 +02:00
|
|
|
"implementations/Crafter.Graphics-UI-Shared",
|
2026-05-18 02:07:48 +02:00
|
|
|
"implementations/Crafter.Graphics-UIComponents",
|
|
|
|
|
"implementations/Crafter.Graphics-Window",
|
|
|
|
|
};
|
|
|
|
|
cfg.GetInterfacesAndImplementations(ifaces, impls);
|
|
|
|
|
|
|
|
|
|
cfg.shaders.emplace_back(fs::path("shaders/ui-quads.comp.glsl"), std::string("main"), ShaderType::Compute);
|
|
|
|
|
cfg.shaders.emplace_back(fs::path("shaders/ui-circles.comp.glsl"), std::string("main"), ShaderType::Compute);
|
|
|
|
|
cfg.shaders.emplace_back(fs::path("shaders/ui-images.comp.glsl"), std::string("main"), ShaderType::Compute);
|
|
|
|
|
cfg.shaders.emplace_back(fs::path("shaders/ui-text.comp.glsl"), std::string("main"), ShaderType::Compute);
|
|
|
|
|
cfg.buildFiles.emplace_back(fs::path("shaders/ui-shared.glsl"));
|
|
|
|
|
}
|
2026-05-01 23:35:37 +02:00
|
|
|
|
2026-04-30 01:29:17 +02:00
|
|
|
return cfg;
|
|
|
|
|
}
|