penpot/render-wasm/scripts/cargo-build.cjs
Miguel de Benito Delgado 43b5e473cf 🚧 WIP 26, A/B tests and a bunch of fixes
AI-assisted-by: deepseek-flash, gpt-6.1-sol
2026-10-28 19:17:39 +01:00

154 lines
4.7 KiB
JavaScript

// Usage: cargo build --message-format=json-render-diagnostics ... |
// node scripts/cargo-build.cjs <build-info.json> <root Cargo.toml>
const fs = require("node:fs");
const path = require("node:path");
const readline = require("node:readline");
async function main() {
const [infoFile, manifestFile] = process.argv.slice(2);
if (!infoFile || !manifestFile) {
throw new Error(
"Usage: cargo-build.cjs <build-info.json> <root Cargo.toml>",
);
}
const manifest = path.resolve(manifestFile);
// A failed invocation must not leave facts from an earlier build.
fs.rmSync(infoFile, { force: true });
// This helper inherits the same shell setup as Cargo. Keep only build inputs,
// excluding credentials, invocation identities and artifact destinations.
const environmentKeys = new Set([
"RUSTFLAGS",
"RUSTC_WRAPPER",
"CARGO_TARGET_DIR",
"CARGO_HOME",
"CC",
"CFLAGS",
"CXXFLAGS",
"LDFLAGS",
"PENPOT_WASM_FUNCTION_NAMES",
"CARGO_ENCODED_RUSTFLAGS",
"CARGO_BUILD_TARGET",
"BUILD_MODE",
"EMCC_CFLAGS",
"EM_INITIAL_HEAP",
"EM_MEMORY_GROWTH_GEOMETRIC_STEP",
"EM_MALLOC",
"EM_CACHE",
"NODE_ENV",
"CARGO_NET_OFFLINE",
]);
const environment = Object.fromEntries(
Object.entries(process.env).filter(
([key]) =>
environmentKeys.has(key) ||
key.startsWith("CARGO_PROFILE_") ||
key.startsWith("EMSDK"),
),
);
const artifacts = [];
const buildScripts = new Map();
let succeeded = false;
const lines = readline.createInterface({
input: process.stdin,
crlfDelay: Infinity,
});
for await (const line of lines) {
if (!line.trim()) continue;
const message = JSON.parse(line);
if (!message || typeof message !== "object" || Array.isArray(message)) {
throw new Error("Invalid Cargo JSON record");
}
switch (message.reason) {
case "compiler-message":
if (typeof message.message?.rendered === "string") {
process.stderr.write(message.message.rendered);
}
break;
case "compiler-artifact":
if (
message.target?.name === "render_wasm" &&
typeof message.manifest_path === "string" &&
path.resolve(message.manifest_path) === manifest
) {
artifacts.push(message);
}
break;
case "build-script-executed": {
const outDirs = buildScripts.get(message.package_id) || new Set();
outDirs.add(message.out_dir);
buildScripts.set(message.package_id, outDirs);
break;
}
case "build-finished":
succeeded = message.success === true;
break;
}
}
if (!succeeded) throw new Error("Cargo build did not finish successfully");
if (artifacts.length !== 1) {
throw new Error("Expected one root render_wasm compiler-artifact");
}
const artifact = artifacts[0];
if (
typeof artifact.package_id !== "string" ||
!artifact.package_id ||
!Array.isArray(artifact.features) ||
!artifact.features.every((feature) => typeof feature === "string")
) {
throw new Error("Missing root renderer package_id or actual features");
}
if (
!artifact.profile ||
typeof artifact.profile !== "object" ||
Array.isArray(artifact.profile) ||
typeof artifact.profile.opt_level !== "string" ||
typeof artifact.profile.debug_assertions !== "boolean"
) {
throw new Error("Missing or malformed root renderer Cargo profile");
}
const outDirs = [...(buildScripts.get(artifact.package_id) || [])];
if (outDirs.length !== 1 || typeof outDirs[0] !== "string" || !outDirs[0]) {
throw new Error(
"Expected one matching root renderer build-script-executed out_dir",
);
}
const jsFiles = (
Array.isArray(artifact.filenames) ? artifact.filenames : []
).filter(
(file) =>
typeof file === "string" && path.basename(file) === "render_wasm.js",
);
if (jsFiles.length !== 1)
throw new Error("Missing root render_wasm.js artifact");
const outDir = path.resolve(outDirs[0]);
const sharedFile = path.join(outDir, "render_wasm_shared.js");
fs.accessSync(sharedFile, fs.constants.R_OK);
const info = {
package_id: artifact.package_id,
features: artifact.features,
profile: artifact.profile,
environment,
out_dir: outDir,
artifact_dir: path.dirname(path.resolve(jsFiles[0])),
shared_file: sharedFile,
};
fs.mkdirSync(path.dirname(infoFile), { recursive: true });
const temporary = `${infoFile}.tmp`;
try {
fs.writeFileSync(temporary, JSON.stringify(info, null, 2) + "\n");
fs.renameSync(temporary, infoFile);
} finally {
fs.rmSync(temporary, { force: true });
}
}
main().catch((error) => {
process.stderr.write(`ERROR: ${error.message}\n`);
process.exitCode = 1;
process.stdin.destroy();
});