node.js - node - Bunding precompilado binario en aplicación electrónica
npm (4)
Aquí hay otro método, probado con Mac y Windows hasta ahora. Requiere el paquete ''app-root-dir'', no requiere agregar nada manualmente a node_modules dir.
Ponga sus archivos en recursos / $ os /, donde $ os es "mac" , "linux" o "win" . El proceso de compilación copiará los archivos de esos directorios según el sistema operativo de la compilación.
Ponga la opción
extraFiles
en sus configuraciones de construcción de la siguiente manera:
paquete.json
"build": {
"extraFiles": [
{
"from": "resources/${os}",
"to": "Resources/bin",
"filter": ["**/*"]
}
],
- Utilice algo como esto para determinar la plataforma actual.
get-platform.js
import { platform } from ''os'';
export default () => {
switch (platform()) {
case ''aix'':
case ''freebsd'':
case ''linux'':
case ''openbsd'':
case ''android'':
return ''linux'';
case ''darwin'':
case ''sunos'':
return ''mac'';
case ''win32'':
return ''win'';
}
};
- Llame al ejecutable desde su aplicación dependiendo de env y OS. Aquí asumo que las versiones construidas están en modo de producción y versiones de origen en otros modos, pero puede crear su propia lógica de llamada.
import { join as joinPath, dirname } from ''path'';
import { exec } from ''child_process'';
import appRootDir from ''app-root-dir'';
import env from ''./env'';
import getPlatform from ''./get-platform'';
const execPath = (env.name === ''production'') ?
joinPath(dirname(appRootDir.get()), ''bin''):
joinPath(appRootDir.get(), ''resources'', getPlatform());
const cmd = `${joinPath(execPath, ''my-executable'')}`;
exec(cmd, (err, stdout, stderr) => {
// do things
});
Creo que estaba usando electron-builder como base, la generación de archivos env viene con eso. Básicamente es solo un archivo de configuración JSON.
¿Existe una buena solución sobre cómo incluir binarios precompilados de terceros como imagemagick en una aplicación electrónica? hay módulos node.js pero todos son envoltorios o enlaces nativos a las bibliotecas instaladas en todo el sistema. Me pregunto si es posible agrupar binarios precompilados dentro de la distribución.
Encontré una solución para esto, pero no tengo idea de si esto se considera la mejor práctica. No pude encontrar ninguna buena documentación para incluir binarios precompilados de terceros, así que simplemente jugué con ella hasta que finalmente funcionó con mi binario ffmpeg. Esto es lo que hice (comenzando con el inicio rápido del electrón, node.js v6):
Método Mac OS X
Desde el directorio de la aplicación ejecuté los siguientes comandos en Terminal para incluir el binario ffmpeg como un módulo:
mkdir node_modules/ffmpeg
cp /usr/local/bin/ffmpeg node_modules/ffmpeg/
cd node_modules/.bin
ln -s ../ffmpeg/ffmpeg ffmpeg
(Reemplace /usr/local/bin/ffmpeg
con su ruta binaria actual, descárguela desde aquí). Al colocar el enlace, el paquete de electrones incluido para incluir el binario que node_modules/ffmpeg/
en node_modules/ffmpeg/
.
Luego, para obtener la ruta de la aplicación incluida (de modo que pudiera usar una ruta absoluta para mi binario ... las rutas relativas no parecen funcionar a pesar de lo que hice). Instalé el paquete npm app-root-dir ejecutando lo siguiente mando:
npm i -S app-root-dir
Ahora que tenía el directorio de la aplicación raíz, solo agregué la subcarpeta para mi binario y la generé desde allí. Este es el código que puse en renderer.js :.
var appRootDir = require(''app-root-dir'').get();
var ffmpegpath=appRootDir+''/node_modules/ffmpeg/ffmpeg'';
console.log(ffmpegpath);
const
spawn = require( ''child_process'' ).spawn,
ffmpeg = spawn( ffmpegpath, [''-i'',clips_input[0]]); //add whatever switches you need here
ffmpeg.stdout.on( ''data'', data => {
console.log( `stdout: ${data}` );
});
ffmpeg.stderr.on( ''data'', data => {
console.log( `stderr: ${data}` );
});
Método de Windows
- Abra su carpeta base electrónica (electron-quick-start es el nombre predeterminado), luego vaya a la carpeta node_modules. Cree una carpeta allí llamada ffmpeg y copie su binario estático en este directorio. Nota: debe ser la versión estática de tu binario, para ffmpeg tomé la última compilación de Windows here .
Para obtener la ruta de la aplicación incluida (de modo que pudiera usar una ruta absoluta para mi binario ... las rutas relativas no parecen funcionar a pesar de lo que hice). Instalé el paquete npm app-root-dir ejecutando el siguiente comando desde un símbolo del sistema en mi directorio de aplicaciones:
npm i -S app-root-dir
Dentro de su carpeta node_modules, navegue a la subcarpeta .bin. Debe crear un par de archivos de texto aquí para indicar al nodo que incluya el archivo exe binario que acaba de copiar. Use su editor de texto favorito y cree dos archivos, uno llamado
ffmpeg
con el siguiente contenido:#!/bin/sh basedir=$(dirname "$(echo "$0" | sed -e ''s,//,/,g'')") case `uname` in *CYGWIN*) basedir=`cygpath -w "$basedir"`;; esac if [ -x "$basedir/node" ]; then "$basedir/node" "$basedir/../ffmpeg/ffmpeg" "$@" ret=$? else node "$basedir/../ffmpeg/ffmpeg" "$@" ret=$? fi exit $ret
Y el segundo archivo de texto, llamado
ffmpeg.cmd
:@IF EXIST "%~dp0/node.exe" ( "%~dp0/node.exe" "%~dp0/../ffmpeg/ffmpeg" %* ) ELSE ( @SETLOCAL @SET PATHEXT=%PATHEXT:;.JS;=;% node "%~dp0/../ffmpeg/ffmpeg" %* )
A continuación, puede ejecutar ffmpeg en su distribución electrónica de Windows (en renderer.js) de la siguiente manera (también estoy usando el módulo de nodo app-root-dir). Tenga en cuenta las citas agregadas a la ruta binaria, si su aplicación se instala en un directorio con espacios (por ejemplo, C:/Program Files/YourApp
), no funcionará sin estos.
var appRootDir = require(''app-root-dir'').get();
var ffmpegpath = appRootDir + ''//node_modules//ffmpeg//ffmpeg'';
const
spawn = require( ''child_process'' ).spawn;
var ffmpeg = spawn( ''cmd.exe'', [''/c'', ''"''+ffmpegpath+ ''"'', ''-i'', clips_input[0]]); //add whatever switches you need here, test on command line first
ffmpeg.stdout.on( ''data'', data => {
console.log( `stdout: ${data}` );
});
ffmpeg.stderr.on( ''data'', data => {
console.log( `stderr: ${data}` );
});
Las respuestas anteriores me ayudaron a descubrir cómo se hace. Pero hay una manera mucho más eficiente de distribuir archivos binarios.
Tomando señales de la respuesta de tsuriga , aquí está mi código:
Nota: reemplazar o agregar la ruta del sistema operativo en consecuencia.
- Crear un directorio ./resources/mac/bin
- Coloca tus binarios dentro de esta carpeta
- Cree el archivo ./app/binaries.js y pegue el siguiente código:
''use strict''; import path from ''path''; import { remote } from ''electron''; import getPlatform from ''./get-platform''; const IS_PROD = process.env.NODE_ENV === ''production''; const root = process.cwd(); const { isPackaged, getAppPath } = remote.app; const binariesPath = IS_PROD && isPackaged ? path.join(path.dirname(getAppPath()), ''..'', ''./Resources'', ''./bin'') : path.join(root, ''./resources'', getPlatform(), ''./bin''); export const execPath = path.resolve(path.join(binariesPath, ''./exec-file-name''));
- Cree el archivo ./app/get-platform.js y pegue el siguiente código:
''use strict''; import { platform } from ''os''; export default () => { switch (platform()) { case ''aix'': case ''freebsd'': case ''linux'': case ''openbsd'': case ''android'': return ''linux''; case ''darwin'': case ''sunos'': return ''mac''; case ''win32'': return ''win''; } };
- Agregue el siguiente código dentro del archivo ./package.json :
"build": { .... "extraFiles": [ { "from": "resources/mac/bin", "to": "Resources/bin", "filter": [ "**/*" ] } ], .... },
- importar la ruta del archivo binario como:
import { execPath } from ''./binaries''; #your program code: var command = spawn(execPath, arg, {});
¿Por qué esto es mejor?
Las respuestas anteriores requieren un paquete adicional llamado app-root-dir
La respuesta de tsuriga no maneja la compilación (env = producción) o las versiones preenvasadas correctamente. Él / ella solo se ha encargado del desarrollo y las versiones post-empaquetadas.
tl; dr:
¡sí tu puedes! pero requiere que escriba su propio complemento autónomo que no hace ninguna suposición en las bibliotecas del sistema. Además, en algunos casos debes asegurarte de que tu complemento esté compilado para el sistema operativo deseado.
Vamos a romper esta pregunta en varias partes:
- Addons (módulos nativos) :
Los complementos son objetos compartidos dinámicamente vinculados.
En otras palabras, puede escribir su propio complemento sin depender de bibliotecas de todo el sistema (por ejemplo, vinculando estáticamente los módulos necesarios) que contienen todo el código que necesita.
Debe tener en cuenta que dicho enfoque es específico del sistema operativo, lo que significa que debe compilar su complemento para cada sistema operativo que desee admitir. (Dependiendo de que otras bibliotecas pueda usar)
- Módulos nativos para electron :
Electron admite los módulos de Node nativos, pero como Electron está utilizando una versión V8 diferente a la del Nodo oficial, debe especificar manualmente la ubicación de los encabezados de Electron al crear módulos nativos
Esto significa que un módulo nativo que se ha construido contra encabezados de nodo debe reconstruirse para usarse dentro de electron. Usted puede encontrar cómo en documentos electrónicos.
- Paquetes de paquetes con aplicación electrónica :
Supongo que quieres tener tu aplicación como un ejecutable independiente sin requerir que los usuarios instalen electron en sus máquinas. Si es así, puedo sugerir el uso electron-packager .