133 lines
5.3 KiB
JavaScript
133 lines
5.3 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* Copies platform-specific native modules to app/src-tauri/resources/native/
|
|
* so they are bundled alongside service.js in the Tauri app.
|
|
*
|
|
* Run after build-sidecar.mjs, before `tauri build`.
|
|
* Auto-detects platform from TARGET_ARCH env or process.platform + process.arch.
|
|
*/
|
|
|
|
import fs from 'node:fs';
|
|
import path from 'node:path';
|
|
import { execFileSync } from 'node:child_process';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
const root = path.resolve(__dirname, '..');
|
|
const nativeDir = path.join(root, 'app', 'src-tauri', 'resources', 'native');
|
|
|
|
const platform = process.platform;
|
|
const arch = process.env.TARGET_ARCH || process.arch;
|
|
|
|
// macOS "universal" is not a valid staging target: sqlite-vec and onnxruntime
|
|
// ship per-arch binaries (no darwin/universal path exists), so a universal run
|
|
// silently falls through to the x64 variant and mis-stages the arm64 half.
|
|
// Build each arch separately and lipo the app bundle instead.
|
|
if (arch === 'universal') {
|
|
console.error(
|
|
'[bundle-native-deps] FATAL — TARGET_ARCH=universal is not supported.\n'
|
|
+ ' Native modules (sqlite-vec, onnxruntime-node) are per-arch. Build each\n'
|
|
+ ' arch separately: TARGET_ARCH=arm64 (aarch64-apple-darwin) and\n'
|
|
+ ' TARGET_ARCH=x64 (x86_64-apple-darwin) — see release.yml\'s macOS matrix\n'
|
|
+ ' and the app tauri:build:mac:arm64 / :x64 scripts.',
|
|
);
|
|
process.exit(1);
|
|
}
|
|
if (platform === 'darwin' && arch !== 'arm64' && arch !== 'x64') {
|
|
throw new Error(`[bundle-native-deps] FATAL — unsupported macOS target architecture: ${arch}`);
|
|
}
|
|
|
|
console.log(`[bundle-native-deps] Platform: ${platform}-${arch}`);
|
|
|
|
// A previous matrix leg must never satisfy this build with stale native files.
|
|
fs.rmSync(nativeDir, { recursive: true, force: true });
|
|
fs.mkdirSync(nativeDir, { recursive: true });
|
|
fs.mkdirSync(path.join(nativeDir, 'onnxruntime'), { recursive: true });
|
|
fs.writeFileSync(path.join(nativeDir, '.gitkeep'), '');
|
|
fs.writeFileSync(path.join(nativeDir, 'onnxruntime', '.gitkeep'), '');
|
|
|
|
let totalFiles = 0;
|
|
let totalBytes = 0;
|
|
|
|
function assertDarwinArchitecture(srcPath) {
|
|
if (platform !== 'darwin' || !/\.(?:dylib|node)$/i.test(srcPath)) return;
|
|
const expected = arch === 'x64' ? 'x86_64' : 'arm64';
|
|
const architectures = execFileSync('/usr/bin/lipo', ['-archs', srcPath], {
|
|
encoding: 'utf8',
|
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
}).trim().split(/\s+/);
|
|
if (!architectures.includes(expected)) {
|
|
throw new Error(
|
|
`[bundle-native-deps] FATAL — ${path.relative(root, srcPath)} has Mach-O architecture `
|
|
+ `${architectures.join(', ') || 'unknown'}, expected ${expected}`,
|
|
);
|
|
}
|
|
}
|
|
|
|
function copyFile(src, destName) {
|
|
const srcPath = path.join(root, src);
|
|
const destPath = path.join(nativeDir, destName);
|
|
|
|
if (!fs.existsSync(srcPath)) {
|
|
throw new Error(`[bundle-native-deps] FATAL — required native dependency ${src} is missing`);
|
|
}
|
|
|
|
const sourceSize = fs.statSync(srcPath).size;
|
|
if (sourceSize === 0) throw new Error(`[bundle-native-deps] FATAL — required native dependency ${src} is empty`);
|
|
assertDarwinArchitecture(srcPath);
|
|
fs.copyFileSync(srcPath, destPath);
|
|
const size = fs.statSync(destPath).size;
|
|
totalBytes += size;
|
|
totalFiles++;
|
|
console.log(` ${destName} (${(size / 1024 / 1024).toFixed(1)} MB)`);
|
|
return true;
|
|
}
|
|
|
|
function copyDir(srcDir, destSubDir) {
|
|
const srcPath = path.join(root, srcDir);
|
|
if (!fs.existsSync(srcPath)) {
|
|
throw new Error(`[bundle-native-deps] FATAL — required native dependency directory ${srcDir} is missing`);
|
|
}
|
|
|
|
const destPath = path.join(nativeDir, destSubDir);
|
|
fs.mkdirSync(destPath, { recursive: true });
|
|
|
|
let copied = 0;
|
|
for (const file of fs.readdirSync(srcPath)) {
|
|
const fullSrc = path.join(srcPath, file);
|
|
const stat = fs.statSync(fullSrc);
|
|
if (stat.isFile()) {
|
|
if (stat.size === 0) throw new Error(`[bundle-native-deps] FATAL — required native dependency ${fullSrc} is empty`);
|
|
assertDarwinArchitecture(fullSrc);
|
|
const destFile = path.join(destPath, file);
|
|
fs.copyFileSync(fullSrc, destFile);
|
|
totalBytes += stat.size;
|
|
totalFiles++;
|
|
copied++;
|
|
console.log(` ${destSubDir}/${file} (${(stat.size / 1024 / 1024).toFixed(1)} MB)`);
|
|
}
|
|
}
|
|
if (copied === 0) throw new Error(`[bundle-native-deps] FATAL — required native dependency directory ${srcDir} is empty`);
|
|
}
|
|
|
|
// 1. better-sqlite3
|
|
console.log('[bundle-native-deps] better-sqlite3:');
|
|
copyFile('node_modules/better-sqlite3/build/Release/better_sqlite3.node', 'better_sqlite3.node');
|
|
|
|
// 2. sqlite-vec
|
|
console.log('[bundle-native-deps] sqlite-vec:');
|
|
const vecOs = platform === 'win32' ? 'windows' : platform === 'darwin' ? 'darwin' : 'linux';
|
|
const vecExt = platform === 'win32' ? 'dll' : platform === 'darwin' ? 'dylib' : 'so';
|
|
copyFile(
|
|
`node_modules/sqlite-vec-${vecOs}-${arch}/vec0.${vecExt}`,
|
|
`vec0.${vecExt}`,
|
|
);
|
|
|
|
// 3. onnxruntime-node (multiple files — binding + shared libraries)
|
|
console.log('[bundle-native-deps] onnxruntime-node:');
|
|
const ortOs = platform === 'win32' ? 'win32' : platform === 'darwin' ? 'darwin' : 'linux';
|
|
const ortDir = `node_modules/onnxruntime-node/bin/napi-v3/${ortOs}/${arch}`;
|
|
copyDir(ortDir, 'onnxruntime');
|
|
|
|
console.log(`[bundle-native-deps] Copied ${totalFiles} files (${(totalBytes / 1024 / 1024).toFixed(1)} MB total) to resources/native/`);
|