This commit is contained in:
parent
fb2f6079cc
commit
934c94cb5c
50 changed files with 10464 additions and 758 deletions
159
project.cpp
159
project.cpp
|
|
@ -11,7 +11,134 @@ import Crafter.Build;
|
|||
namespace fs = std::filesystem;
|
||||
using namespace Crafter;
|
||||
|
||||
// Two products come out of this repo, selected with `--product=`:
|
||||
//
|
||||
// web (default) — the wasm32-wasip1 browser bundle. Crafter.Graphics for
|
||||
// the DOM, Catcrafts.Shared for the page renderers.
|
||||
// crafter-build
|
||||
//
|
||||
// server — the native HTTP server that will render the same pages
|
||||
// server-side for crawlers and no-JS clients, and later
|
||||
// host the shop API. No Crafter.Graphics.
|
||||
// crafter-build -- --product=server
|
||||
//
|
||||
// One project file rather than three, following the imsd convention: a single
|
||||
// CrafterBuildProject returns a single Configuration, so the products are
|
||||
// branches and the shared library is a `static unique_ptr<Configuration>`
|
||||
// both branches can depend on.
|
||||
|
||||
// Catcrafts.Shared — target-neutral page renderers, built as a static library
|
||||
// for whichever target the selected product is using. `static` because the
|
||||
// Configuration must outlive this function: cfg.dependencies holds a raw
|
||||
// pointer to it.
|
||||
//
|
||||
// `args` MUST carry the consumer's --target=. ApplyStandardArgs is what reads
|
||||
// it, and without it the library silently builds for the host triple while a
|
||||
// wasm consumer links against it — which currently "works" only because there
|
||||
// are no implementation units, so the static archive is empty. The moment a
|
||||
// .cpp lands here that would be a wrong-architecture link.
|
||||
static Configuration* SharedLibrary(std::span<const std::string_view> args) {
|
||||
static auto shared = std::make_unique<Configuration>();
|
||||
shared->path = "./";
|
||||
shared->name = "Catcrafts.Shared";
|
||||
shared->outputName = "Catcrafts.Shared";
|
||||
ApplyStandardArgs(*shared, args); // inherits --target / --debug from the parent
|
||||
shared->type = ConfigurationType::LibraryStatic;
|
||||
|
||||
std::array<fs::path, 9> ifaces = {
|
||||
"shared/interfaces/Catcrafts.Shared",
|
||||
"shared/interfaces/Catcrafts.Shared-Html",
|
||||
"shared/interfaces/Catcrafts.Shared-Form",
|
||||
"shared/interfaces/Catcrafts.Shared-Json",
|
||||
"shared/interfaces/Catcrafts.Shared-Model",
|
||||
"shared/interfaces/Catcrafts.Shared-Content",
|
||||
"shared/interfaces/Catcrafts.Shared-Money",
|
||||
"shared/interfaces/Catcrafts.Shared-Route",
|
||||
"shared/interfaces/Catcrafts.Shared-Views",
|
||||
};
|
||||
std::array<fs::path, 0> impls = {};
|
||||
shared->GetInterfacesAndImplementations(ifaces, impls);
|
||||
return shared.get();
|
||||
}
|
||||
|
||||
// Payload trimming, release only.
|
||||
//
|
||||
// The wasi-libc / wasi-libc++ static libs ship with DWARF and wasm-ld keeps
|
||||
// debug sections by default, so a build that never passed -g still ended up
|
||||
// ~84% debug info (3.3 MB of 3.9 MB). Stripping takes the bundle from
|
||||
// 3.9 MB / 1.05 MB gzip to ~700 KB / ~195 KB gzip.
|
||||
static void ApplyReleaseTrimming(Configuration& cfg) {
|
||||
if (cfg.debug) return; // --debug builds want their symbols
|
||||
cfg.compileFlags.push_back("-ffunction-sections");
|
||||
cfg.compileFlags.push_back("-fdata-sections");
|
||||
cfg.linkFlags.push_back("-Wl,--gc-sections");
|
||||
cfg.linkFlags.push_back("-Wl,--strip-debug");
|
||||
}
|
||||
|
||||
extern "C" Configuration CrafterBuildProject(std::span<const std::string_view> args) {
|
||||
bool wantServer = false;
|
||||
for (std::string_view a : args) {
|
||||
if (a == "--product=server") wantServer = true;
|
||||
}
|
||||
|
||||
// ── server ────────────────────────────────────────────────────────
|
||||
if (wantServer) {
|
||||
// Crafter.Network supplies the HTTP layer. Notably NOT cpp-httplib or
|
||||
// libcurl: ListenerHTTP1 covers the inbound side (TLS is Caddy's job —
|
||||
// it terminates and reverse-proxies plaintext to localhost, which is
|
||||
// exactly why HTTP/1.1 rather than the HTTP/3 listener, since Caddy
|
||||
// cannot proxy to an h3 upstream), and ClientHTTP1 with
|
||||
// TLSClientCredentials covers outbound HTTPS for the payment and
|
||||
// shipping APIs later — it verifies certificate chain and hostname by
|
||||
// default.
|
||||
std::vector<std::string> netArgs(args.begin(), args.end());
|
||||
bool useLocalNet = false;
|
||||
for (std::string_view a : args) {
|
||||
if (a == "--local") { useLocalNet = true; break; }
|
||||
}
|
||||
Configuration* network = useLocalNet
|
||||
? LocalProject({
|
||||
.projectFile = "../Crafter/Crafter.Network/project.cpp",
|
||||
.args = netArgs,
|
||||
})
|
||||
: GitProject({
|
||||
.source = { .url = "https://forgejo.catcrafts.net/Catcrafts/Crafter.Network.git" },
|
||||
.args = netArgs,
|
||||
});
|
||||
|
||||
Configuration cfg;
|
||||
cfg.path = "./";
|
||||
cfg.name = "Catcrafts.Server";
|
||||
cfg.outputName = "catcrafts-server";
|
||||
cfg.type = ConfigurationType::Executable;
|
||||
ApplyStandardArgs(cfg, args);
|
||||
cfg.dependencies = { SharedLibrary(args), network };
|
||||
|
||||
std::array<fs::path, 1> ifaces = {
|
||||
"server/interfaces/Catcrafts.Server",
|
||||
};
|
||||
std::array<fs::path, 7> impls = {
|
||||
"server/implementations/main",
|
||||
"server/implementations/Catcrafts.Server-Http",
|
||||
"server/implementations/Catcrafts.Server-Orders",
|
||||
"server/implementations/Catcrafts.Server-Mollie",
|
||||
"server/implementations/Catcrafts.Server-Invoice",
|
||||
"server/implementations/Catcrafts.Server-Bunq",
|
||||
"server/implementations/Catcrafts.Server-Shipping",
|
||||
};
|
||||
cfg.GetInterfacesAndImplementations(ifaces, impls);
|
||||
|
||||
// The bunq client signs requests with an RSA key (OpenSSL EVP). The
|
||||
// TLS transport already links libssl through Crafter.Network; libcrypto
|
||||
// is named explicitly because the signing code calls it directly.
|
||||
cfg.linkFlags.push_back("-lssl");
|
||||
cfg.linkFlags.push_back("-lcrypto");
|
||||
|
||||
ApplyReleaseTrimming(cfg);
|
||||
return cfg;
|
||||
}
|
||||
|
||||
// ── web (default) ─────────────────────────────────────────────────
|
||||
std::vector<std::string> depArgs(args.begin(), args.end());
|
||||
depArgs.push_back("--target=wasm32-wasip1");
|
||||
|
||||
|
|
@ -44,18 +171,21 @@ extern "C" Configuration CrafterBuildProject(std::span<const std::string_view> a
|
|||
cfg.type = ConfigurationType::Executable;
|
||||
cfg.target = "wasm32-wasip1";
|
||||
ApplyStandardArgs(cfg, args);
|
||||
cfg.dependencies = { graphics };
|
||||
|
||||
std::array<fs::path, 5> ifaces = {
|
||||
// Catcrafts.Shared has to be built for wasm here, not the host. `args` on
|
||||
// its own does not carry --target= (the caller just runs `crafter-build`),
|
||||
// so hand it the same augmented list the Crafter.Graphics dep gets.
|
||||
std::vector<std::string_view> sharedArgs(depArgs.begin(), depArgs.end());
|
||||
cfg.dependencies = { graphics, SharedLibrary(sharedArgs) };
|
||||
|
||||
std::array<fs::path, 4> ifaces = {
|
||||
"interfaces/Catcrafts",
|
||||
"interfaces/Catcrafts-Views",
|
||||
"interfaces/Catcrafts-Blog",
|
||||
"interfaces/Catcrafts-Root",
|
||||
"interfaces/Catcrafts-Demo",
|
||||
};
|
||||
std::array<fs::path, 5> impls = {
|
||||
std::array<fs::path, 4> impls = {
|
||||
"implementations/main",
|
||||
"implementations/Catcrafts-Blog",
|
||||
"implementations/Catcrafts-Root",
|
||||
"implementations/Catcrafts-Views",
|
||||
"implementations/Catcrafts-Demo",
|
||||
|
|
@ -64,7 +194,11 @@ extern "C" Configuration CrafterBuildProject(std::span<const std::string_view> a
|
|||
|
||||
cfg.files.emplace_back(fs::path("styles/styles.css"));
|
||||
cfg.files.emplace_back(fs::path("robots.txt"));
|
||||
// sitemap.xml and feed.xml are GENERATED by the server product before this
|
||||
// build runs (see .forgejo/workflows/deploy.yaml), from the same route
|
||||
// table and Post model the pages use. Checked-in copies would drift.
|
||||
cfg.files.emplace_back(fs::path("sitemap.xml"));
|
||||
cfg.files.emplace_back(fs::path("feed.xml"));
|
||||
cfg.files.emplace_back(fs::path("favicon.svg"));
|
||||
// WGSL for the ray-traced WebGPU demo embedded in the blog (see
|
||||
// interfaces/Catcrafts-Demo.cppm). Fetched at runtime by WebGPUShader.
|
||||
|
|
@ -76,7 +210,22 @@ extern "C" Configuration CrafterBuildProject(std::span<const std::string_view> a
|
|||
// EnableWasiBrowserRuntime — sets <title>/<link> tags since the
|
||||
// Dom partition has no head-element access.
|
||||
cfg.files.emplace_back(fs::path("catcrafts-head.js"));
|
||||
// Product photography. CC BY-SA 4.0, © Fairphone (official render via
|
||||
// Wikimedia Commons) — the attribution lives on /legal/imprint. NOT in
|
||||
// cfg.assets: that pipeline transcodes to .ctex, which <img> cannot decode.
|
||||
cfg.files.emplace_back(fs::path("images/fp6-pmos.jpg"));
|
||||
// ECB rates ride with the content so the wasm-rendered shop (backend-down
|
||||
// fallback) can emit the same indicative prices the server does.
|
||||
cfg.files.emplace_back(fs::path("content/rates.json"));
|
||||
// Site content. Loaded from the VFS at startup rather than compiled in, so
|
||||
// editing a project blurb or refreshing the post list is not a recompile.
|
||||
// NOTE: cfg.files flattens to the bundle root (copied by filename), so
|
||||
// this is read back as "posts.json". Products, projects, legal and demos
|
||||
// are COMPILED IN (Catcrafts.Shared:Content) — only pipeline-generated
|
||||
// data still travels as files.
|
||||
cfg.files.emplace_back(fs::path("content/posts.json"));
|
||||
|
||||
ApplyReleaseTrimming(cfg);
|
||||
EnableWasiBrowserRuntime(cfg);
|
||||
|
||||
return cfg;
|
||||
|
|
|
|||
Loading…
Reference in a new issue