feat: Add script to build Python sidecar executable
This commit is contained in:
110
scripts/build-python-sidecar.mjs
Normal file
110
scripts/build-python-sidecar.mjs
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
import { existsSync, mkdirSync, statSync } from "node:fs";
|
||||||
|
import { dirname, join, resolve } from "node:path";
|
||||||
|
import { execFileSync, spawnSync } from "node:child_process";
|
||||||
|
import { fileURLToPath } from "node:url";
|
||||||
|
|
||||||
|
const root = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
||||||
|
const python = existsSync(join(root, ".venv", "bin", "python"))
|
||||||
|
? join(root, ".venv", "bin", "python")
|
||||||
|
: "python3";
|
||||||
|
|
||||||
|
function run(command, args, options = {}) {
|
||||||
|
const result = spawnSync(command, args, {
|
||||||
|
cwd: root,
|
||||||
|
stdio: "inherit",
|
||||||
|
env: process.env,
|
||||||
|
...options,
|
||||||
|
});
|
||||||
|
if (result.status !== 0) {
|
||||||
|
process.exit(result.status ?? 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function read(command, args) {
|
||||||
|
return execFileSync(command, args, { cwd: root, encoding: "utf8" }).trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
function targetTriple() {
|
||||||
|
const explicit =
|
||||||
|
process.env.TAURI_ENV_TARGET_TRIPLE ||
|
||||||
|
process.env.CARGO_BUILD_TARGET ||
|
||||||
|
process.env.TARGET;
|
||||||
|
if (explicit) return explicit;
|
||||||
|
|
||||||
|
const rustc = read("rustc", ["-vV"]);
|
||||||
|
const host = rustc
|
||||||
|
.split(/\r?\n/)
|
||||||
|
.find((line) => line.startsWith("host:"))
|
||||||
|
?.replace("host:", "")
|
||||||
|
.trim();
|
||||||
|
if (!host) {
|
||||||
|
throw new Error("Could not determine Rust target triple.");
|
||||||
|
}
|
||||||
|
return host;
|
||||||
|
}
|
||||||
|
|
||||||
|
function newestMtime(paths) {
|
||||||
|
return Math.max(...paths.map((path) => statSync(path).mtimeMs));
|
||||||
|
}
|
||||||
|
|
||||||
|
const target = targetTriple();
|
||||||
|
const binariesDir = join(root, "src-tauri", "binaries");
|
||||||
|
const sidecar = join(binariesDir, `concept-api-${target}${target.includes("windows") ? ".exe" : ""}`);
|
||||||
|
const sources = [
|
||||||
|
join(root, "concept_api.py"),
|
||||||
|
join(root, "websearch.py"),
|
||||||
|
];
|
||||||
|
|
||||||
|
mkdirSync(binariesDir, { recursive: true });
|
||||||
|
|
||||||
|
if (
|
||||||
|
existsSync(sidecar) &&
|
||||||
|
statSync(sidecar).mtimeMs >= newestMtime(sources) &&
|
||||||
|
process.argv.includes("--force") === false
|
||||||
|
) {
|
||||||
|
console.log(`Python sidecar is up to date: ${sidecar}`);
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const pyinstallerCheck = spawnSync(python, ["-m", "PyInstaller", "--version"], {
|
||||||
|
cwd: root,
|
||||||
|
stdio: "ignore",
|
||||||
|
});
|
||||||
|
if (pyinstallerCheck.status !== 0) {
|
||||||
|
run(python, ["-m", "pip", "install", "pyinstaller"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
run(python, [
|
||||||
|
"-m",
|
||||||
|
"PyInstaller",
|
||||||
|
"--noconfirm",
|
||||||
|
"--clean",
|
||||||
|
"--onefile",
|
||||||
|
"--name",
|
||||||
|
`concept-api-${target}`,
|
||||||
|
"--distpath",
|
||||||
|
binariesDir,
|
||||||
|
"--workpath",
|
||||||
|
join(root, ".sidecar-build", "work"),
|
||||||
|
"--specpath",
|
||||||
|
join(root, ".sidecar-build"),
|
||||||
|
"--paths",
|
||||||
|
root,
|
||||||
|
"--hidden-import",
|
||||||
|
"fitz",
|
||||||
|
"--hidden-import",
|
||||||
|
"bs4",
|
||||||
|
"--hidden-import",
|
||||||
|
"PIL",
|
||||||
|
"--hidden-import",
|
||||||
|
"PIL.Image",
|
||||||
|
"--exclude-module",
|
||||||
|
"torch",
|
||||||
|
"--exclude-module",
|
||||||
|
"transformers",
|
||||||
|
"--exclude-module",
|
||||||
|
"whisper",
|
||||||
|
"--exclude-module",
|
||||||
|
"cv2",
|
||||||
|
join(root, "concept_api.py"),
|
||||||
|
]);
|
||||||
Reference in New Issue
Block a user