如何使用 Electron 运行 和打包外部可执行文件?
How to run and pack external executable using Electron?
例如,我有一个已编译的二进制文件 cudaDeviceQuery
,其中 returns 设备列表为 JSON。这是一段代码:
export default function cudaDeviceQuery(): Promise<CollectorResponse> {
const throwError = () => {
throw new Error("Unfortunately your platform isn't yet unsupported");
};
const file = __DARWIN__
? path.join(__dirname, 'darwin', 'cudaDeviceQuery')
: __WIN32__
? path.join(__dirname, 'win', 'cudaDeviceQuery.exe')
: throwError();
const descriptor = spawn(file);
return new Promise((resolve, reject) => {
let outerData = '';
descriptor.stdout.on('data', data => {
outerData += data;
});
descriptor.on('close', () => {
try {
resolve(JSON.parse(outerData));
} catch (e) {
reject(e);
}
});
});
}
但是当我从渲染器进程 __dirname
中使用这个函数时 /
,所以我得到 spawn /darwin/cudaDeviceQuery ENOENT
错误。在开发环境中生成它并将其打包到生产环境中的正确方法是什么?
一个 webpack 配置:
webpack.config.base.js
:
/**
* Base webpack config used across other specific configs
*/
const webpack = require('webpack');
const path = require('path');
const getReplacements = require('./app/app-info').getReplacements;
const { dependencies: externals } = require('./app/renderer/package.json');
module.exports = {
module: {
noParse: [path.join(__dirname, 'node_modules/ws')],
rules: [
{
test: /\.tsx?$/,
use: [
{
loader: 'babel-loader',
},
{
loader: 'ts-loader',
},
],
exclude: /node_modules/,
},
],
},
output: {
path: path.join(__dirname, 'app', 'renderer'),
filename: 'bundle.js',
libraryTarget: 'commonjs2',
},
// https://webpack.github.io/docs/configuration.html#resolve
resolve: {
extensions: ['.js', '.ts', '.tsx', 'json'],
modules: [path.join(__dirname, 'app', 'renderer'), 'node_modules'],
},
plugins: [new webpack.DefinePlugin(getReplacements())],
externals: [...Object.keys(externals || {}), 'ws'],
};
webpack.config.development.js
:
/**
* Build config for development process that uses Hot-Module-Replacement
* https://webpack.github.io/docs/hot-module-replacement-with-webpack.html
*/
const webpack = require('webpack');
const merge = require('webpack-merge');
const baseConfig = require('./webpack.config.base');
const getReplacements = require('./app/app-info').getReplacements;
const port = process.env.PORT || 3000;
module.exports = merge(baseConfig, {
devtool: 'inline-source-map',
entry: [
'react-hot-loader/patch',
`webpack-hot-middleware/client?path=http://localhost:${port}/__webpack_hmr&reload=true`,
'./app/renderer/index',
],
output: {
publicPath: `http://localhost:${port}/dist/`,
},
module: {
rules: [
// Css, SCSS, woff loaders are here
],
},
plugins: [
// https://webpack.github.io/docs/hot-module-replacement-with-webpack.html
new webpack.HotModuleReplacementPlugin(),
new webpack.LoaderOptionsPlugin({
debug: true,
}),
],
// https://github.com/chentsulin/webpack-target-electron-renderer#how-this-module-works
target: 'electron-renderer',
});
webpack.config.electron.js
:
/**
* Build config for electron 'Main Process' file
*/
const webpack = require('webpack');
const merge = require('webpack-merge');
const baseConfig = require('./webpack.config.base');
const getReplacements = require('./app/app-info').getReplacements;
module.exports = merge(baseConfig, {
devtool: 'source-map',
entry: ['./app/main/index.ts'],
// 'main.js' in root
output: {
path: __dirname,
filename: './app/main/main.js',
},
plugins: [
// Add source map support for stack traces in node
// https://github.com/evanw/node-source-map-support
// new webpack.BannerPlugin(
// 'require("source-map-support").install();',
// { raw: true, entryOnly: false }
// ),
],
/**
* Set target to Electron specific node.js env.
* https://github.com/chentsulin/webpack-target-electron-renderer#how-this-module-works
*/
target: 'electron-main',
/**
* Disables webpack processing of __dirname and __filename.
* If you run the bundle in node.js it falls back to these values of node.js.
* https://github.com/webpack/webpack/issues/2010
*/
node: {
__dirname: false,
__filename: false
},
});
如您所见,我正在使用开发服务器进行热模块更换,所以也许这就是原因...我有 server.js
用脚本创建服务器,然后我从主进程使用它.这里是 server.js
:
/**
* Setup and run the development server for Hot-Module-Replacement
* https://webpack.github.io/docs/hot-module-replacement-with-webpack.html
*/
const argv = require('minimist')(process.argv.slice(2));
const { spawn } = require('child_process');
async function createMiddleware(port, configPath) {
const express = require('express');
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');
const webpackHotMiddleware = require('webpack-hot-middleware');
const config = require(configPath);
const app = express();
const compiler = webpack(config);
const PORT = process.env.PORT || port;
const wdm = webpackDevMiddleware(compiler, {
publicPath: config.output.publicPath,
stats: {
colors: true,
},
});
app.use(wdm);
app.use(webpackHotMiddleware(compiler));
const server = app.listen(PORT, serverError => {
if (serverError) {
return console.error(serverError);
}
console.log(`Listening at http://localhost:${PORT}`);
});
process.on('SIGTERM', () => {
console.log('Stopping dev server');
wdm.close();
server.close(() => {
process.exit(0);
});
});
}
createMiddleware(3000, './webpack.config.development'); // A main renderer process
createMiddleware(3010, './webpack.config.server'); // A backend for communicating between renderer and remote server
if (argv['start-hot']) {
spawn('npm', ['run', 'start-hot'], {
shell: true,
env: process.env,
stdio: 'inherit',
})
.on('close', code => process.exit(code))
.on('error', spawnError => console.error(spawnError));
}
换句话说,我需要从电子渲染器进程中调用 cudaDeviceQuery
库。我正在使用 electron-builder
但没关系,我可以切换到另一个构建器。
有两件事。如果您在 Web 应用程序配置中设置 __dirname: true
,您将从上下文目录
中获取文件的 relative
路径
如果您设置 __dirname: false
,那么 __dirname
将具有完整路径。
开发模式
你有两个选择
- 设置
__dirname: true
并与os.cwd()
连接
- 设置
__dirname: false
直接使用__dirname
生产模式
- 设置
__dirname: true
并使用os.cwd()
。
- 设置
__dirname: true
并使用process.resourcePath
我更喜欢 2 作为生产中的首选方法
在package.json
中添加:
"scripts": {
"start": "electron .", "install": "electron-rebuild",
"package-osx": "electron-packager . Node-RED --platform=darwin --arch=x64 -- out=build --overwrite",
"package-mac": "electron-packager . --overwrite --platform=darwin --arch=x64 --prune=true --out=release-builds",
"package-win": "electron-packager . electron-serialport --overwrite --asar=true --platform=win32 --arch=x64 --prune=true --out=release-builds --version-string.CompanyName=CE --version-string.FileDescription=CE --version-string.ProductName=\"CryptoApp\"",
"package-linux": "electron-packager . electron-serialport --overwrite --asar=true --platform=linux --arch=x64 --prune=true --out=release-builds"
},
"dependencies": {
"electron-packager": "^12.1.0",
"electron-prebuilt": "^1.4.13",
}
如果不适用于 windows,请使用以下内容:
"package-win": "electron-packager . electron-serialport --overwrite --asar=true --platform=win32 --arch=ia32 --prune=true --out=release-builds --version-string.CompanyName=CE --version-string.FileDescription=CE --version-string.ProductName=\"CryptoApp\"",
谢谢...
例如,我有一个已编译的二进制文件 cudaDeviceQuery
,其中 returns 设备列表为 JSON。这是一段代码:
export default function cudaDeviceQuery(): Promise<CollectorResponse> {
const throwError = () => {
throw new Error("Unfortunately your platform isn't yet unsupported");
};
const file = __DARWIN__
? path.join(__dirname, 'darwin', 'cudaDeviceQuery')
: __WIN32__
? path.join(__dirname, 'win', 'cudaDeviceQuery.exe')
: throwError();
const descriptor = spawn(file);
return new Promise((resolve, reject) => {
let outerData = '';
descriptor.stdout.on('data', data => {
outerData += data;
});
descriptor.on('close', () => {
try {
resolve(JSON.parse(outerData));
} catch (e) {
reject(e);
}
});
});
}
但是当我从渲染器进程 __dirname
中使用这个函数时 /
,所以我得到 spawn /darwin/cudaDeviceQuery ENOENT
错误。在开发环境中生成它并将其打包到生产环境中的正确方法是什么?
一个 webpack 配置:
webpack.config.base.js
:
/**
* Base webpack config used across other specific configs
*/
const webpack = require('webpack');
const path = require('path');
const getReplacements = require('./app/app-info').getReplacements;
const { dependencies: externals } = require('./app/renderer/package.json');
module.exports = {
module: {
noParse: [path.join(__dirname, 'node_modules/ws')],
rules: [
{
test: /\.tsx?$/,
use: [
{
loader: 'babel-loader',
},
{
loader: 'ts-loader',
},
],
exclude: /node_modules/,
},
],
},
output: {
path: path.join(__dirname, 'app', 'renderer'),
filename: 'bundle.js',
libraryTarget: 'commonjs2',
},
// https://webpack.github.io/docs/configuration.html#resolve
resolve: {
extensions: ['.js', '.ts', '.tsx', 'json'],
modules: [path.join(__dirname, 'app', 'renderer'), 'node_modules'],
},
plugins: [new webpack.DefinePlugin(getReplacements())],
externals: [...Object.keys(externals || {}), 'ws'],
};
webpack.config.development.js
:
/**
* Build config for development process that uses Hot-Module-Replacement
* https://webpack.github.io/docs/hot-module-replacement-with-webpack.html
*/
const webpack = require('webpack');
const merge = require('webpack-merge');
const baseConfig = require('./webpack.config.base');
const getReplacements = require('./app/app-info').getReplacements;
const port = process.env.PORT || 3000;
module.exports = merge(baseConfig, {
devtool: 'inline-source-map',
entry: [
'react-hot-loader/patch',
`webpack-hot-middleware/client?path=http://localhost:${port}/__webpack_hmr&reload=true`,
'./app/renderer/index',
],
output: {
publicPath: `http://localhost:${port}/dist/`,
},
module: {
rules: [
// Css, SCSS, woff loaders are here
],
},
plugins: [
// https://webpack.github.io/docs/hot-module-replacement-with-webpack.html
new webpack.HotModuleReplacementPlugin(),
new webpack.LoaderOptionsPlugin({
debug: true,
}),
],
// https://github.com/chentsulin/webpack-target-electron-renderer#how-this-module-works
target: 'electron-renderer',
});
webpack.config.electron.js
:
/**
* Build config for electron 'Main Process' file
*/
const webpack = require('webpack');
const merge = require('webpack-merge');
const baseConfig = require('./webpack.config.base');
const getReplacements = require('./app/app-info').getReplacements;
module.exports = merge(baseConfig, {
devtool: 'source-map',
entry: ['./app/main/index.ts'],
// 'main.js' in root
output: {
path: __dirname,
filename: './app/main/main.js',
},
plugins: [
// Add source map support for stack traces in node
// https://github.com/evanw/node-source-map-support
// new webpack.BannerPlugin(
// 'require("source-map-support").install();',
// { raw: true, entryOnly: false }
// ),
],
/**
* Set target to Electron specific node.js env.
* https://github.com/chentsulin/webpack-target-electron-renderer#how-this-module-works
*/
target: 'electron-main',
/**
* Disables webpack processing of __dirname and __filename.
* If you run the bundle in node.js it falls back to these values of node.js.
* https://github.com/webpack/webpack/issues/2010
*/
node: {
__dirname: false,
__filename: false
},
});
如您所见,我正在使用开发服务器进行热模块更换,所以也许这就是原因...我有 server.js
用脚本创建服务器,然后我从主进程使用它.这里是 server.js
:
/**
* Setup and run the development server for Hot-Module-Replacement
* https://webpack.github.io/docs/hot-module-replacement-with-webpack.html
*/
const argv = require('minimist')(process.argv.slice(2));
const { spawn } = require('child_process');
async function createMiddleware(port, configPath) {
const express = require('express');
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');
const webpackHotMiddleware = require('webpack-hot-middleware');
const config = require(configPath);
const app = express();
const compiler = webpack(config);
const PORT = process.env.PORT || port;
const wdm = webpackDevMiddleware(compiler, {
publicPath: config.output.publicPath,
stats: {
colors: true,
},
});
app.use(wdm);
app.use(webpackHotMiddleware(compiler));
const server = app.listen(PORT, serverError => {
if (serverError) {
return console.error(serverError);
}
console.log(`Listening at http://localhost:${PORT}`);
});
process.on('SIGTERM', () => {
console.log('Stopping dev server');
wdm.close();
server.close(() => {
process.exit(0);
});
});
}
createMiddleware(3000, './webpack.config.development'); // A main renderer process
createMiddleware(3010, './webpack.config.server'); // A backend for communicating between renderer and remote server
if (argv['start-hot']) {
spawn('npm', ['run', 'start-hot'], {
shell: true,
env: process.env,
stdio: 'inherit',
})
.on('close', code => process.exit(code))
.on('error', spawnError => console.error(spawnError));
}
换句话说,我需要从电子渲染器进程中调用 cudaDeviceQuery
库。我正在使用 electron-builder
但没关系,我可以切换到另一个构建器。
有两件事。如果您在 Web 应用程序配置中设置 __dirname: true
,您将从上下文目录
relative
路径
如果您设置 __dirname: false
,那么 __dirname
将具有完整路径。
开发模式
你有两个选择
- 设置
__dirname: true
并与os.cwd()
连接
- 设置
__dirname: false
直接使用__dirname
生产模式
- 设置
__dirname: true
并使用os.cwd()
。 - 设置
__dirname: true
并使用process.resourcePath
我更喜欢 2 作为生产中的首选方法
在package.json
中添加:
"scripts": {
"start": "electron .", "install": "electron-rebuild",
"package-osx": "electron-packager . Node-RED --platform=darwin --arch=x64 -- out=build --overwrite",
"package-mac": "electron-packager . --overwrite --platform=darwin --arch=x64 --prune=true --out=release-builds",
"package-win": "electron-packager . electron-serialport --overwrite --asar=true --platform=win32 --arch=x64 --prune=true --out=release-builds --version-string.CompanyName=CE --version-string.FileDescription=CE --version-string.ProductName=\"CryptoApp\"",
"package-linux": "electron-packager . electron-serialport --overwrite --asar=true --platform=linux --arch=x64 --prune=true --out=release-builds"
},
"dependencies": {
"electron-packager": "^12.1.0",
"electron-prebuilt": "^1.4.13",
}
如果不适用于 windows,请使用以下内容:
"package-win": "electron-packager . electron-serialport --overwrite --asar=true --platform=win32 --arch=ia32 --prune=true --out=release-builds --version-string.CompanyName=CE --version-string.FileDescription=CE --version-string.ProductName=\"CryptoApp\"",
谢谢...