mirror of
https://github.com/webadderallorg/Recordly.git
synced 2026-09-26 15:55:35 +00:00
Prebundle mac native helpers and avoid runtime Swift compile in packaged app
This commit is contained in:
@@ -44,6 +44,9 @@ jobs:
|
||||
- name: Install app dependencies
|
||||
run: npx electron-builder install-app-deps
|
||||
|
||||
- name: Build native macOS helpers
|
||||
run: npm run build:native-helpers
|
||||
|
||||
- name: Build macOS x64
|
||||
run: |
|
||||
npx tsc
|
||||
@@ -77,6 +80,9 @@ jobs:
|
||||
- name: Install app dependencies
|
||||
run: npx electron-builder install-app-deps
|
||||
|
||||
- name: Build native macOS helpers
|
||||
run: npm run build:native-helpers
|
||||
|
||||
- name: Build macOS arm64
|
||||
run: |
|
||||
npx tsc
|
||||
|
||||
@@ -3,6 +3,7 @@ import { execFile, spawn, spawnSync } from 'node:child_process'
|
||||
import type { ChildProcessWithoutNullStreams } from 'node:child_process'
|
||||
|
||||
import fs from 'node:fs/promises'
|
||||
import { constants as fsConstants } from 'node:fs'
|
||||
import { createRequire } from 'node:module'
|
||||
import path from 'node:path'
|
||||
import { promisify } from 'node:util'
|
||||
@@ -202,6 +203,14 @@ function getNativeCaptureHelperSourcePath() {
|
||||
return resolveUnpackedAppPath('electron', 'native', 'ScreenCaptureKitRecorder.swift')
|
||||
}
|
||||
|
||||
function getNativeArchTag() {
|
||||
return process.arch === 'arm64' ? 'darwin-arm64' : 'darwin-x64'
|
||||
}
|
||||
|
||||
function getPrebundledNativeHelperPath(binaryName: string) {
|
||||
return resolveUnpackedAppPath('electron', 'native', 'bin', getNativeArchTag(), binaryName)
|
||||
}
|
||||
|
||||
function getNativeCaptureHelperBinaryPath() {
|
||||
return path.join(app.getPath('userData'), 'native-tools', 'openscreen-screencapturekit-helper')
|
||||
}
|
||||
@@ -247,7 +256,26 @@ type NativeMacWindowSource = {
|
||||
let cachedNativeMacWindowSources: NativeMacWindowSource[] | null = null
|
||||
let cachedNativeMacWindowSourcesAtMs = 0
|
||||
|
||||
async function ensureSwiftHelperBinary(sourcePath: string, binaryPath: string, label: string) {
|
||||
async function ensureSwiftHelperBinary(
|
||||
sourcePath: string,
|
||||
binaryPath: string,
|
||||
label: string,
|
||||
prebundledBinaryName?: string,
|
||||
) {
|
||||
if (prebundledBinaryName) {
|
||||
const prebundledPath = getPrebundledNativeHelperPath(prebundledBinaryName)
|
||||
try {
|
||||
await fs.access(prebundledPath, fsConstants.X_OK)
|
||||
return prebundledPath
|
||||
} catch {
|
||||
if (app.isPackaged) {
|
||||
throw new Error(
|
||||
`${label} is missing from this app build (${prebundledPath}). Reinstall or update the app.`
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const helperDir = path.dirname(binaryPath)
|
||||
|
||||
await fs.mkdir(helperDir, { recursive: true })
|
||||
@@ -284,7 +312,8 @@ async function ensureNativeCaptureHelperBinary() {
|
||||
return ensureSwiftHelperBinary(
|
||||
getNativeCaptureHelperSourcePath(),
|
||||
getNativeCaptureHelperBinaryPath(),
|
||||
'native ScreenCaptureKit helper'
|
||||
'native ScreenCaptureKit helper',
|
||||
'openscreen-screencapturekit-helper'
|
||||
)
|
||||
}
|
||||
|
||||
@@ -292,7 +321,8 @@ async function ensureNativeWindowListBinary() {
|
||||
return ensureSwiftHelperBinary(
|
||||
getNativeWindowListSourcePath(),
|
||||
getNativeWindowListBinaryPath(),
|
||||
'native ScreenCaptureKit window list helper'
|
||||
'native ScreenCaptureKit window list helper',
|
||||
'openscreen-window-list'
|
||||
)
|
||||
}
|
||||
|
||||
@@ -348,7 +378,8 @@ async function getSystemCursorAssets() {
|
||||
const binaryPath = await ensureSwiftHelperBinary(
|
||||
sourcePath,
|
||||
getSystemCursorHelperBinaryPath(),
|
||||
'system cursor helper'
|
||||
'system cursor helper',
|
||||
'openscreen-system-cursors'
|
||||
)
|
||||
|
||||
const { stdout } = await execFileAsync(binaryPath, [], { timeout: 15000, maxBuffer: 20 * 1024 * 1024 })
|
||||
@@ -748,7 +779,8 @@ async function ensureNativeCursorMonitorBinary() {
|
||||
return ensureSwiftHelperBinary(
|
||||
getNativeCursorMonitorSourcePath(),
|
||||
getNativeCursorMonitorBinaryPath(),
|
||||
'native cursor monitor helper'
|
||||
'native cursor monitor helper',
|
||||
'openscreen-native-cursor-monitor'
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
+3
-2
@@ -17,13 +17,14 @@
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"postinstall": "npm run rebuild:native",
|
||||
"build": "tsc && vite build && electron-builder",
|
||||
"build": "npm run build:native-helpers && tsc && vite build && electron-builder",
|
||||
"lint": "biome check .",
|
||||
"lint:fix": "biome check --write .",
|
||||
"format": "biome format --write .",
|
||||
"preview": "vite preview",
|
||||
"rebuild:native": "electron-rebuild --force --only uiohook-napi",
|
||||
"build:mac": "tsc && vite build && electron-builder --mac",
|
||||
"build:native-helpers": "node scripts/build-native-helpers.mjs",
|
||||
"build:mac": "npm run build:native-helpers && tsc && vite build && electron-builder --mac",
|
||||
"build:win": "tsc && vite build && electron-builder --win",
|
||||
"build:linux": "tsc && vite build && electron-builder --linux",
|
||||
"test": "vitest --run",
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import { chmod, mkdir } from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
|
||||
const projectRoot = process.cwd();
|
||||
const nativeRoot = path.join(projectRoot, 'electron', 'native');
|
||||
|
||||
if (process.platform !== 'darwin') {
|
||||
console.log('[build-native-helpers] Skipping: host platform is not macOS.');
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const archTag = process.arch === 'arm64' ? 'darwin-arm64' : 'darwin-x64';
|
||||
const outputDir = path.join(nativeRoot, 'bin', archTag);
|
||||
|
||||
const helpers = [
|
||||
{
|
||||
source: 'ScreenCaptureKitRecorder.swift',
|
||||
output: 'openscreen-screencapturekit-helper',
|
||||
},
|
||||
{
|
||||
source: 'ScreenCaptureKitWindowList.swift',
|
||||
output: 'openscreen-window-list',
|
||||
},
|
||||
{
|
||||
source: 'SystemCursorAssets.swift',
|
||||
output: 'openscreen-system-cursors',
|
||||
},
|
||||
{
|
||||
source: 'NativeCursorMonitor.swift',
|
||||
output: 'openscreen-native-cursor-monitor',
|
||||
},
|
||||
];
|
||||
|
||||
const swiftcCheck = spawnSync('swiftc', ['--version'], { encoding: 'utf8' });
|
||||
if (swiftcCheck.status !== 0) {
|
||||
const details = [swiftcCheck.stderr, swiftcCheck.stdout].filter(Boolean).join('\n').trim();
|
||||
throw new Error(details || 'swiftc is unavailable; install Xcode Command Line Tools.');
|
||||
}
|
||||
|
||||
await mkdir(outputDir, { recursive: true });
|
||||
|
||||
for (const helper of helpers) {
|
||||
const sourcePath = path.join(nativeRoot, helper.source);
|
||||
const outputPath = path.join(outputDir, helper.output);
|
||||
|
||||
const result = spawnSync('swiftc', ['-O', sourcePath, '-o', outputPath], {
|
||||
encoding: 'utf8',
|
||||
timeout: 120000,
|
||||
});
|
||||
|
||||
if (result.status !== 0) {
|
||||
const details = [result.stderr, result.stdout].filter(Boolean).join('\n').trim();
|
||||
throw new Error(details || `Failed to compile ${helper.source}`);
|
||||
}
|
||||
|
||||
await chmod(outputPath, 0o755);
|
||||
console.log(`[build-native-helpers] Built ${helper.output} -> ${outputPath}`);
|
||||
}
|
||||
Reference in New Issue
Block a user