web-dev-qa-db-fra.com

regroupement de fichiers binaires précompilés dans une application électronique

Existe-t-il une bonne solution pour inclure des fichiers binaires pré-compilés tiers, comme imagemagick, dans une application électronique? il existe des modules node.js mais ils constituent tous des wrappers ou des liaisons natives aux bibliothèques installées à l'échelle du système. Je me demande s'il est possible de regrouper des fichiers binaires précompilés dans la distribution.

23
Toby

J'ai trouvé une solution à cela, mais je ne sais pas si cela est considéré comme une pratique exemplaire. Je ne pouvais trouver aucune bonne documentation pour inclure les fichiers binaires précompilés par une tierce partie, aussi je me suis contenté de la manipuler jusqu'à ce que tout fonctionne avec le fichier binaire ffmpeg. Voici ce que j'ai fait (en commençant par le démarrage rapide de l'électron, node.js v6):

Méthode Mac OS X

Depuis le répertoire de l'application, j'ai exécuté les commandes suivantes dans Terminal pour inclure le fichier binaire ffmpeg en tant que module:

mkdir node_modules/ffmpeg
cp /usr/local/bin/ffmpeg node_modules/ffmpeg/
cd node_modules/.bin
ln -s ../ffmpeg/ffmpeg ffmpeg

(remplacez /usr/local/bin/ffmpeg par votre chemin binaire actuel, téléchargez-le à partir d'ici) Placez le lien qui permettait à emballeur électronique d'inclure le binaire que j'avais enregistré dans node_modules/ffmpeg/.

Ensuite, pour obtenir le chemin d’application fourni (pour pouvoir utiliser un chemin absolu pour mon fichier binaire ... les chemins relatifs ne semblent pas fonctionner, peu importe ce que je faisais), j’ai installé le paquet npm app-root-dir en lançant ce qui suit: commander:

npm i -S app-root-dir

Maintenant que j'ai le répertoire racine de l'application, je viens d'ajouter le sous-dossier de mon binaire et de le générer. C'est le code que j'ai placé dans 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éthode Windows

  1. Ouvrez votre dossier de base electron (electron-quick-start est le nom par défaut), puis accédez au dossier node_modules. Créez un dossier appelé ffmpeg et copiez votre binaire statique dans ce répertoire. Remarque: il doit s'agir de la version statique de votre binaire. Pour ffmpeg, j'ai récupéré la dernière version de Windows ici
  2. Pour obtenir le chemin d'application fourni (afin que je puisse utiliser un chemin absolu pour mon binaire ... les chemins relatifs ne semblent pas fonctionner, peu importe ce que j'ai fait), j'ai installé le paquet npm app-root-dir en exécutant la commande suivante à partir d'une invite de commande dans mon répertoire d'applications:

     npm i -S app-root-dir
    
  3. Dans votre dossier node_modules, accédez au sous-dossier .bin. Vous devez créer ici quelques fichiers texte pour indiquer à noeud d’inclure le fichier exe binaire que vous venez de copier. Utilisez votre éditeur de texte préféré et créez deux fichiers, l'un nommé ffmpeg avec le contenu suivant:

    #!/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
    

    Et le deuxième fichier texte, nommé ffmpeg.cmd:

    @IF EXIST "%~dp0\node.exe" (
     "%~dp0\node.exe"  "%~dp0\..\ffmpeg\ffmpeg" %*
    ) ELSE (
       @SETLOCAL
     @SET PATHEXT=%PATHEXT:;.JS;=;%
     node  "%~dp0\..\ffmpeg\ffmpeg" %*
    )
    

Ensuite, vous pouvez exécuter ffmpeg dans votre distribution électronique Windows (dans renderer.js) comme suit (j'utilise également le module de nœud app-root-dir). Notez les guillemets ajoutés au chemin binaire. Si votre application est installée dans un répertoire avec des espaces (par exemple, C:\Program Files\YourApp), elle ne fonctionnera pas sans ceux-ci.

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}` );
 });
12
UltrasoundJelly

Voici une autre méthode, testée avec Mac et Windows jusqu'à présent. Requiert le package 'app-root-dir', sans rien ajouter manuellement à node_modules dir.

  1. Placez vos fichiers sous resources/$ os /, où $ os est soit "mac", "linux" ou "win". Le processus de construction copie les fichiers de ces répertoires selon le système d’exploitation cible.

  2. Mettez l'option extraFiles dans vos configurations de construction comme suit:

package.json

  "build": {
    "extraFiles": [
      {
        "from": "resources/${os}",
        "to": "Resources/bin",
        "filter": ["**/*"]
      }
    ],
  1. Utilisez quelque chose comme ceci pour déterminer la plate-forme actuelle.

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';
  }
};
  1. Appelez l'exécutable à partir de votre application en fonction de env et du système d'exploitation. Je suppose ici que les versions construites sont en mode production et les versions sources en d’autres modes, mais vous pouvez créer votre propre logique d’appel.
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
});

Je pense que j'utilisais electron-builder comme base, la génération du fichier env vient avec. Fondamentalement, c'est juste un fichier de configuration JSON.

12
tsuriga

tl; dr:

oui, vous pouvez! mais cela nécessite que vous écriviez votre propre addon autonome qui ne fait aucune supposition sur les bibliothèques système. De plus, dans certains cas, vous devez vous assurer que votre addon est compilé pour le système d'exploitation souhaité.


Permet de casser cette question en plusieurs parties:

- Addons (modules natifs):

Les addons sont des objets partagés liés dynamiquement.

En d'autres termes, vous pouvez simplement écrire votre propre addon sans aucune dépendance vis-à-vis des bibliothèques système (par exemple en liant statiquement les modules requis) contenant tout le code dont vous avez besoin.

Vous devez considérer que cette approche est spécifique à un système d'exploitation, ce qui signifie que vous devez compiler votre addon pour chaque système d'exploitation que vous souhaitez prendre en charge! (en fonction des autres bibliothèques que vous pouvez utiliser)

- Modules natifs pour electron:

Les modules de nœud natifs sont pris en charge par Electron, mais comme Electron utilise une version V8 différente de celle du nœud officiel, vous devez spécifier manuellement l'emplacement des en-têtes d'Electron lors de la création de modules natifs.

Cela signifie qu'un module natif qui a été construit avec les en-têtes de nœud doit être reconstruit pour être utilisé dans electron. Vous pouvez trouver comment dans les documents électroniques.

- Bundle de modules avec application électronique:

Je suppose que vous voulez avoir votre application en tant qu'exécutable autonome sans obliger les utilisateurs à installer electron sur leurs machines. Si c'est le cas, je peux suggérer d'utiliser electron-packager .

6
Yan Foto

Les réponses ci-dessus m'ont aidé à comprendre comment cela se fait. Mais il existe un moyen beaucoup plus efficace de distribuer des fichiers binaires.

En prenant exemple sur la réponse de tsuriga , voici mon code:

Remarque: remplacez ou ajoutez le chemin OS en conséquence.

  • Créez un répertoire ./resources/mac/bin _
  • Placez vous binaires dans ce dossier 
  • Créez le fichier ./app/binaries.js et collez le code suivant:
'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'));
  • Créez le fichier ./app/get-platform.js et collez le code suivant:
'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';
  }
};
  • Ajoutez le code suivant dans le fichier ./package.json:
"build": {
....

 "extraFiles": [
      {
        "from": "resources/mac/bin",
        "to": "Resources/bin",
        "filter": [
          "**/*"
        ]
      }
    ],

....
},
  • importer le chemin du fichier binaire en tant que:
import { execPath } from './binaries';

#your program code:
var command = spawn(execPath, arg, {});

Pourquoi c'est mieux?

  • Les réponses ci-dessus nécessitent un package supplémentaire appelé app-root-dir

  • la réponse de tsuriga ne gère pas correctement les versions (env = production) build ou préemballées. Il/elle n'a pris en charge que les versions de développement et de post-emballage.

3
Ganesh Rathinavel

suivre les réponses de Ganesh, ce qui m'a vraiment été d'une grande aide, dans mon cas, ce qui fonctionnait dans binaries.js (pour une version Mac - n'a pas testé Windows ni Linux) était:

"use strict";
import path from "path";
import { app } from "electron";

const IS_PROD = process.env.NODE_ENV === "production";
const root = process.cwd();
const { isPackaged } = app;

const binariesPath =
  IS_PROD && isPackaged
    ? path.join(process.resourcesPath, "./bin")
    : path.join(root, "./external");

export const execPath = path.join(binariesPath, "./my_exec_name");

Considérant que my_exec_name était dans le dossier ./external/bin et copié dans le package d'application dans ./Resources/bin. Je n'ai pas utilisé le script get_platforms.js (inutile dans mon cas). app.getAppPath () était en train de générer un blocage lorsque l'application a été empaquetée . J'espère que cela pourra vous aider.

0
fred foc