build(electron): app now builds and runs on macOS as a standalone app
This commit is contained in:
91
electron/build-backend.js
Normal file
91
electron/build-backend.js
Normal file
@@ -0,0 +1,91 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Build script to ensure PyInstaller backend is built before Electron packaging
|
||||
* This script is called by electron-builder before packaging
|
||||
*/
|
||||
|
||||
const { spawn } = require('child_process');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
|
||||
function runCommand(command, args, cwd) {
|
||||
return new Promise((resolve, reject) => {
|
||||
console.log(`Running: ${command} ${args.join(' ')} in ${cwd}`);
|
||||
const process = spawn(command, args, {
|
||||
cwd,
|
||||
stdio: 'inherit',
|
||||
shell: true
|
||||
});
|
||||
|
||||
process.on('close', (code) => {
|
||||
if (code === 0) {
|
||||
resolve();
|
||||
} else {
|
||||
reject(new Error(`Command failed with exit code ${code}`));
|
||||
}
|
||||
});
|
||||
|
||||
process.on('error', (err) => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function buildBackend() {
|
||||
const projectRoot = path.resolve(__dirname, '..');
|
||||
const buildDir = path.join(projectRoot, 'build');
|
||||
const backendDistPath = path.join(buildDir, 'electron', 'dist', 'fourdst-backend');
|
||||
|
||||
console.log('Building PyInstaller backend...');
|
||||
console.log(`Project root: ${projectRoot}`);
|
||||
console.log(`Build directory: ${buildDir}`);
|
||||
console.log(`Target platform: ${process.platform}`);
|
||||
|
||||
try {
|
||||
// Check if meson build directory exists
|
||||
if (!fs.existsSync(buildDir)) {
|
||||
console.log('Meson build directory not found. Setting up build...');
|
||||
await runCommand('meson', ['setup', 'build', '--buildtype=release', '-Dbuild-py-backend=true'], projectRoot);
|
||||
} else {
|
||||
// Ensure py-backend option is enabled
|
||||
console.log('Reconfiguring meson build with py-backend enabled...');
|
||||
await runCommand('meson', ['configure', 'build', '-Dbuild-py-backend=true'], projectRoot);
|
||||
}
|
||||
|
||||
// Build the backend using meson
|
||||
console.log('Building backend with meson...');
|
||||
await runCommand('meson', ['compile', '-C', 'build'], projectRoot);
|
||||
|
||||
// Verify the backend executable was created
|
||||
const executableName = process.platform === 'win32' ? 'fourdst-backend.exe' : 'fourdst-backend';
|
||||
const backendExecutable = path.join(backendDistPath, executableName);
|
||||
|
||||
if (fs.existsSync(backendExecutable)) {
|
||||
console.log(`✅ Backend executable built successfully: ${backendExecutable}`);
|
||||
|
||||
// Make executable on Unix systems
|
||||
if (process.platform !== 'win32') {
|
||||
const { execSync } = require('child_process');
|
||||
execSync(`chmod +x "${backendExecutable}"`);
|
||||
console.log('✅ Backend executable permissions set');
|
||||
}
|
||||
} else {
|
||||
throw new Error(`Backend executable not found at: ${backendExecutable}`);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Failed to build backend:', error.message);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Run the build if this script is called directly
|
||||
if (require.main === module) {
|
||||
buildBackend().catch(error => {
|
||||
console.error('Build failed:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = { buildBackend };
|
||||
@@ -1,6 +1,7 @@
|
||||
# -*- mode: python ; coding: utf-8 -*-
|
||||
|
||||
import sys
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
# This is a PyInstaller spec file. It is used to bundle the Python backend
|
||||
@@ -14,6 +15,15 @@ project_root = Path(SPECPATH).parent
|
||||
# We need to add the project root to the path so that PyInstaller can find the 'fourdst' module.
|
||||
sys.path.insert(0, str(project_root))
|
||||
|
||||
# Platform-specific configurations
|
||||
platform = sys.platform
|
||||
print(f"Building for platform: {platform}")
|
||||
|
||||
# Determine executable name based on platform
|
||||
exe_name = 'fourdst-backend'
|
||||
if platform == 'win32':
|
||||
exe_name += '.exe'
|
||||
|
||||
# The main script to be bundled.
|
||||
analysis = Analysis(['bridge.py'],
|
||||
pathex=[str(project_root)],
|
||||
@@ -35,7 +45,7 @@ exe = EXE(pyz,
|
||||
analysis.scripts,
|
||||
[],
|
||||
exclude_binaries=True,
|
||||
name='fourdst-backend',
|
||||
name=exe_name,
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
|
||||
@@ -2,8 +2,13 @@ const { app, BrowserWindow, ipcMain, nativeTheme } = require('electron');
|
||||
const path = require('path');
|
||||
|
||||
// Handle creating/removing shortcuts on Windows when installing/uninstalling.
|
||||
if (require('electron-squirrel-startup')) {
|
||||
app.quit();
|
||||
try {
|
||||
if (require('electron-squirrel-startup')) {
|
||||
app.quit();
|
||||
}
|
||||
} catch (error) {
|
||||
// electron-squirrel-startup is not available or not needed on this platform
|
||||
console.log('electron-squirrel-startup not available, continuing...');
|
||||
}
|
||||
|
||||
let mainWindow;
|
||||
|
||||
@@ -6,10 +6,16 @@ const { spawn } = require('child_process');
|
||||
function runPythonCommand(command, kwargs, event) {
|
||||
const buildDir = path.resolve(__dirname, '..', '..', 'build');
|
||||
let backendPath;
|
||||
|
||||
// Determine executable name based on platform
|
||||
const executableName = process.platform === 'win32' ? 'fourdst-backend.exe' : 'fourdst-backend';
|
||||
|
||||
if (app.isPackaged) {
|
||||
backendPath = path.join(process.resourcesPath, 'fourdst-backend');
|
||||
// In packaged app, backend is in resources/backend/ directory
|
||||
backendPath = path.join(process.resourcesPath, 'backend', executableName);
|
||||
} else {
|
||||
backendPath = path.join(buildDir, 'electron', 'dist', 'fourdst-backend', 'fourdst-backend');
|
||||
// In development, use the meson build output
|
||||
backendPath = path.join(buildDir, 'electron', 'dist', 'fourdst-backend', executableName);
|
||||
}
|
||||
|
||||
console.log(`[MAIN_PROCESS] Spawning backend: ${backendPath}`);
|
||||
|
||||
7
electron/package-lock.json
generated
7
electron/package-lock.json
generated
@@ -11,6 +11,7 @@
|
||||
"dependencies": {
|
||||
"@electron/remote": "^2.0.0",
|
||||
"adm-zip": "^0.5.14",
|
||||
"electron-squirrel-startup": "^1.0.1",
|
||||
"fs-extra": "^11.0.0",
|
||||
"js-yaml": "^4.1.0",
|
||||
"plotly.js-dist": "^2.26.0",
|
||||
@@ -19,8 +20,7 @@
|
||||
"devDependencies": {
|
||||
"adm-zip": "^0.5.14",
|
||||
"electron": "^31.0.2",
|
||||
"electron-builder": "^24.0.0",
|
||||
"electron-squirrel-startup": "^1.0.1"
|
||||
"electron-builder": "^24.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@develar/schema-utils": {
|
||||
@@ -1800,7 +1800,6 @@
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/electron-squirrel-startup/-/electron-squirrel-startup-1.0.1.tgz",
|
||||
"integrity": "sha512-sTfFIHGku+7PsHLJ7v0dRcZNkALrV+YEozINTW8X1nM//e5O3L+rfYuvSW00lmGHnYmUjARZulD8F2V8ISI9RA==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"debug": "^2.2.0"
|
||||
@@ -1810,7 +1809,6 @@
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "2.0.0"
|
||||
@@ -1820,7 +1818,6 @@
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/emoji-regex": {
|
||||
|
||||
@@ -6,7 +6,9 @@
|
||||
"scripts": {
|
||||
"start": "electron .",
|
||||
"dev": "electron .",
|
||||
"prebuild": "node build-backend.js",
|
||||
"build": "electron-builder",
|
||||
"prepack": "node build-backend.js",
|
||||
"pack": "electron-builder --dir"
|
||||
},
|
||||
"repository": {
|
||||
@@ -23,8 +25,7 @@
|
||||
"devDependencies": {
|
||||
"electron": "^31.0.2",
|
||||
"adm-zip": "^0.5.14",
|
||||
"electron-builder": "^24.0.0",
|
||||
"electron-squirrel-startup": "^1.0.1"
|
||||
"electron-builder": "^24.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"fs-extra": "^11.0.0",
|
||||
@@ -32,7 +33,8 @@
|
||||
"adm-zip": "^0.5.14",
|
||||
"@electron/remote": "^2.0.0",
|
||||
"python-shell": "^5.0.0",
|
||||
"plotly.js-dist": "^2.26.0"
|
||||
"plotly.js-dist": "^2.26.0",
|
||||
"electron-squirrel-startup": "^1.0.1"
|
||||
},
|
||||
"build": {
|
||||
"appId": "com.fourdst.bundlemanager",
|
||||
@@ -40,6 +42,20 @@
|
||||
"directories": {
|
||||
"output": "dist"
|
||||
},
|
||||
"icon": "toolkitIcon.png",
|
||||
"files": [
|
||||
"**/*",
|
||||
"node_modules/**/*",
|
||||
"!node_modules/electron/**/*",
|
||||
"!node_modules/electron-builder/**/*"
|
||||
],
|
||||
"extraResources": [
|
||||
{
|
||||
"from": "../build/electron/dist/fourdst-backend/",
|
||||
"to": "backend/",
|
||||
"filter": ["**/*"]
|
||||
}
|
||||
],
|
||||
"mac": {
|
||||
"category": "public.app-category.developer-tools",
|
||||
"target": [
|
||||
|
||||
@@ -504,7 +504,7 @@ body.dark-mode .info-button:hover {
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
min-width: 0;
|
||||
padding: 20px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#welcome-screen {
|
||||
|
||||
Reference in New Issue
Block a user