2026-03-13 12:21:09 -06:00
|
|
|
#!/usr/bin/env node
|
|
|
|
|
|
|
|
|
|
/**
|
2026-04-15 14:54:20 +02:00
|
|
|
* Build script for the SF native Rust addon.
|
2026-03-13 12:21:09 -06:00
|
|
|
*
|
|
|
|
|
* Usage:
|
|
|
|
|
* node native/scripts/build.js # release build
|
|
|
|
|
* node native/scripts/build.js --dev # debug build
|
|
|
|
|
*
|
|
|
|
|
* Runs `cargo build` in the engine crate directory and copies the resulting
|
|
|
|
|
* shared library to `native/addon/` with a `.node` extension so Node.js
|
|
|
|
|
* can load it via `require()`.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
import { execSync } from "node:child_process";
|
|
|
|
|
import * as fs from "node:fs";
|
|
|
|
|
import * as path from "node:path";
|
|
|
|
|
|
2026-05-02 06:18:50 +02:00
|
|
|
const nativeRoot = path.resolve(import.meta.dirname, "..");
|
2026-03-13 12:21:09 -06:00
|
|
|
const engineDir = path.join(nativeRoot, "crates", "engine");
|
|
|
|
|
const addonDir = path.join(nativeRoot, "addon");
|
|
|
|
|
|
|
|
|
|
const isDev = process.argv.includes("--dev");
|
|
|
|
|
const profile = isDev ? "debug" : "release";
|
|
|
|
|
const cargoArgs = ["build"];
|
|
|
|
|
if (!isDev) cargoArgs.push("--release");
|
|
|
|
|
|
2026-04-30 21:07:36 +02:00
|
|
|
function shellValue(command) {
|
|
|
|
|
try {
|
|
|
|
|
return execSync(command, {
|
|
|
|
|
stdio: ["ignore", "pipe", "ignore"],
|
|
|
|
|
env: process.env,
|
|
|
|
|
}).toString().trim() || undefined;
|
|
|
|
|
} catch {
|
|
|
|
|
return undefined;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getZlibLibDir() {
|
|
|
|
|
return (
|
|
|
|
|
process.env.ZLIB_LIB_DIR ||
|
|
|
|
|
shellValue("pkg-config --variable=libdir zlib")
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-15 18:33:47 +02:00
|
|
|
function getCargoEnvironment() {
|
2026-04-30 21:07:36 +02:00
|
|
|
const zlibLibDir = getZlibLibDir();
|
|
|
|
|
const defaultRustFlags = process.env.RUSTFLAGS || "-C target-cpu=native";
|
|
|
|
|
const rustFlags = zlibLibDir && !defaultRustFlags.includes(zlibLibDir)
|
|
|
|
|
? `${defaultRustFlags} -L native=${zlibLibDir}`
|
|
|
|
|
: defaultRustFlags;
|
|
|
|
|
const libraryPath = [zlibLibDir, process.env.LIBRARY_PATH]
|
|
|
|
|
.filter(Boolean)
|
|
|
|
|
.join(path.delimiter);
|
|
|
|
|
|
2026-04-15 18:33:47 +02:00
|
|
|
return {
|
|
|
|
|
...process.env,
|
|
|
|
|
// Optimize for native CPU when building locally
|
2026-04-30 21:07:36 +02:00
|
|
|
RUSTFLAGS: rustFlags,
|
|
|
|
|
...(libraryPath ? { LIBRARY_PATH: libraryPath } : {}),
|
2026-04-15 18:33:47 +02:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getCargoTargetDirectory() {
|
|
|
|
|
if (process.env.CARGO_TARGET_DIR) {
|
|
|
|
|
return path.resolve(process.env.CARGO_TARGET_DIR);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const metadataRaw = execSync("cargo metadata --format-version 1 --no-deps", {
|
|
|
|
|
cwd: engineDir,
|
|
|
|
|
stdio: ["ignore", "pipe", "inherit"],
|
|
|
|
|
env: getCargoEnvironment(),
|
|
|
|
|
}).toString();
|
|
|
|
|
const metadata = JSON.parse(metadataRaw);
|
|
|
|
|
if (typeof metadata.target_directory !== "string" || metadata.target_directory.length === 0) {
|
|
|
|
|
throw new Error("cargo metadata did not return a target_directory");
|
|
|
|
|
}
|
|
|
|
|
return path.resolve(metadata.target_directory);
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-15 14:11:45 +02:00
|
|
|
console.log(`Building forge-engine (${profile})...`);
|
2026-03-13 12:21:09 -06:00
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
execSync(`cargo ${cargoArgs.join(" ")}`, {
|
|
|
|
|
cwd: engineDir,
|
|
|
|
|
stdio: "inherit",
|
2026-04-15 18:33:47 +02:00
|
|
|
env: getCargoEnvironment(),
|
2026-03-13 12:21:09 -06:00
|
|
|
});
|
|
|
|
|
} catch {
|
|
|
|
|
process.exit(1);
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-15 18:33:47 +02:00
|
|
|
// Locate the built library using Cargo's actual target directory. Under Nix this
|
|
|
|
|
// is often redirected to a shared cache path rather than native/target.
|
|
|
|
|
const cargoTargetRoot = getCargoTargetDirectory();
|
2026-03-19 17:28:44 -04:00
|
|
|
|
|
|
|
|
const targetDir = path.join(cargoTargetRoot, profile);
|
2026-03-13 12:21:09 -06:00
|
|
|
const platformTag = `${process.platform}-${process.arch}`;
|
|
|
|
|
|
|
|
|
|
const libraryNames = {
|
2026-04-15 14:11:45 +02:00
|
|
|
darwin: "libforge_engine.dylib",
|
|
|
|
|
linux: "libforge_engine.so",
|
|
|
|
|
win32: "forge_engine.dll",
|
2026-03-13 12:21:09 -06:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const libName = libraryNames[process.platform];
|
|
|
|
|
if (!libName) {
|
|
|
|
|
console.error(`Unsupported platform: ${process.platform}`);
|
|
|
|
|
process.exit(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const sourcePath = path.join(targetDir, libName);
|
|
|
|
|
if (!fs.existsSync(sourcePath)) {
|
|
|
|
|
console.error(`Built library not found at: ${sourcePath}`);
|
|
|
|
|
process.exit(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fs.mkdirSync(addonDir, { recursive: true });
|
|
|
|
|
|
|
|
|
|
const destFilename = isDev
|
2026-04-15 14:11:45 +02:00
|
|
|
? "forge_engine.dev.node"
|
|
|
|
|
: `forge_engine.${platformTag}.node`;
|
2026-03-13 12:21:09 -06:00
|
|
|
const destPath = path.join(addonDir, destFilename);
|
|
|
|
|
|
|
|
|
|
fs.copyFileSync(sourcePath, destPath);
|
|
|
|
|
console.log(`Installed: ${destPath}`);
|
2026-04-28 21:10:17 +02:00
|
|
|
|
|
|
|
|
// Also copy into the npm platform package so `require('@singularity-forge/engine-<platform>')`
|
|
|
|
|
// works when the package is symlinked into node_modules during local development.
|
|
|
|
|
if (!isDev) {
|
|
|
|
|
const platformPackageMap = {
|
|
|
|
|
"darwin-arm64": "darwin-arm64",
|
|
|
|
|
"darwin-x64": "darwin-x64",
|
|
|
|
|
"linux-x64": "linux-x64-gnu",
|
|
|
|
|
"linux-arm64": "linux-arm64-gnu",
|
|
|
|
|
"win32-x64": "win32-x64-msvc",
|
|
|
|
|
};
|
|
|
|
|
const npmSuffix = platformPackageMap[platformTag];
|
|
|
|
|
if (npmSuffix) {
|
|
|
|
|
const npmPackageDir = path.join(nativeRoot, "npm", npmSuffix);
|
|
|
|
|
const npmDest = path.join(npmPackageDir, "forge_engine.node");
|
|
|
|
|
if (fs.existsSync(npmPackageDir)) {
|
|
|
|
|
fs.copyFileSync(sourcePath, npmDest);
|
|
|
|
|
console.log(`Installed npm package binary: ${npmDest}`);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-13 12:21:09 -06:00
|
|
|
console.log("Build complete.");
|