webentwicklung-frage-antwort-db.com.de

bündeln von vorkompilierten Binärdateien in die Elektron-App

Gibt es eine gute Lösung, um vorkompilierte Binaries von Drittanbietern wie Imagemagick in eine Elektronen-App aufzunehmen? Es gibt node.js-Module, aber alle sind Wrapper oder native Bindungen an die systemweit installierten Bibliotheken. Ich frage mich, ob es möglich ist, vorkompilierte Binärdateien in der Distribution zu bündeln.

23
Toby

Ich habe eine Lösung dafür gefunden, aber ich habe keine Ahnung, ob dies als Best Practice gilt. Ich konnte keine gute Dokumentation für das Zusammenstellen von vorkompilierten Binärdateien von Drittanbietern finden, also habe ich einfach damit herumgezappelt, bis es schließlich mit meiner ffmpeg-Binärdatei funktionierte. Folgendes habe ich gemacht (beginnend mit dem Elektronischen Schnellstart, node.js v6):

Mac OS X-Methode

Aus dem App-Verzeichnis habe ich die folgenden Befehle in Terminal ausgeführt, um die ffmpeg-Binärdatei als Modul einzuschließen:

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

(Ersetzen Sie /usr/local/bin/ffmpeg durch Ihren aktuellen binären Pfad, laden Sie ihn hier herunter.) Wenn Sie den Link platzieren, kann der Elektron-Packager die in node_modules/ffmpeg/ gespeicherte Binärdatei enthalten.

Um den gebündelten App-Pfad zu erhalten (damit ich einen absoluten Pfad für meine Binärdatei verwenden konnte ... scheinbar funktionierten relative Pfade nicht, egal was ich tat). Ich installierte das npm-Paket app-root-dir, indem ich Folgendes ausführte Befehl:

npm i -S app-root-dir

Jetzt, da ich das Root-App-Verzeichnis hatte, füge ich einfach den Unterordner für meine Binärdatei hinzu und erzeugte von dort aus. Dies ist der Code, den ich in renderer.js: eingefügt habe.

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

Windows-Methode

  1. Öffnen Sie den Ordner für die Elektronenbasis (der Schnellstart für Elektronen ist der Standardname), und wechseln Sie in den Ordner node_modules. Erstellen Sie dort einen Ordner namens ffmpeg und kopieren Sie Ihre statische Binärdatei in dieses Verzeichnis. Hinweis: Es muss sich um die statische Version Ihrer Binärdatei handeln. Für ffmpeg habe ich das neueste Windows-Build hier - verwendet. 
  2. Um den gebündelten App-Pfad zu erhalten (damit ich einen absoluten Pfad für meine Binärdatei verwenden konnte ... scheinbar funktionierten relative Pfade nicht, egal was ich tat), installierte ich das npm-Paket app-root-dir, indem ich den folgenden Befehl ausführte von einer Eingabeaufforderung in meinem App-Verzeichnis:

     npm i -S app-root-dir
    
  3. Navigieren Sie im Ordner node_modules zum Unterordner .bin. Sie müssen hier ein paar Textdateien erstellen, um Node mit der binären Exe-Datei zu versehen, die Sie gerade kopiert haben. Verwenden Sie Ihren bevorzugten Texteditor, und erstellen Sie zwei Dateien mit dem Namen ffmpeg mit folgendem Inhalt:

    #!/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" "[email protected]"
      ret=$?
    else
      node  "$basedir/../ffmpeg/ffmpeg" "[email protected]"
      ret=$?
    fi
    exit $ret
    

    Und die zweite Textdatei mit dem Namen ffmpeg.cmd:

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

Als Nächstes können Sie ffmpeg in Ihrer Windows-Elektronendistribution (in renderer.js) wie folgt ausführen (ich verwende auch das Knotenmodul app-root-dir). Beachten Sie die Anführungszeichen, die dem binären Pfad hinzugefügt wurden. Wenn Ihre App in einem Verzeichnis mit Leerzeichen (z. B. C:\Program Files\YourApp) installiert ist, funktioniert sie ohne diese nicht.

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

Hier ist eine andere Methode, die bisher mit Mac und Windows getestet wurde. Benötigt das 'app-root-dir'-Paket. Es muss nicht manuell etwas zum node_modules dir hinzugefügt werden.

  1. Stellen Sie Ihre Dateien unter resources/$ os /, wobei $ os entweder "mac", "linux" oder "win" ist. Der Erstellungsprozess kopiert Dateien aus diesen Verzeichnissen gemäß Erstellungsziel-OS.

  2. Fügen Sie die extraFiles-Option wie folgt in Ihre Build-Konfigurationen ein:

package.json

  "build": {
    "extraFiles": [
      {
        "from": "resources/${os}",
        "to": "Resources/bin",
        "filter": ["**/*"]
      }
    ],
  1. Verwenden Sie so etwas, um die aktuelle Plattform zu ermitteln.

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. Rufen Sie die ausführbare Datei in Ihrer App auf, je nach Umgebung und Betriebssystem. Ich gehe davon aus, dass die Build-Versionen im Produktionsmodus und die Quellversionen in anderen Modi sind, aber Sie können Ihre eigene Aufruflogik erstellen.
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
});

Ich glaube, ich habe electron-builder als Basis verwendet, die env-Dateigeneration kommt mit. Im Grunde ist es nur eine JSON-Konfigurationsdatei.

12
tsuriga

tl; dr:

ja, du kannst! Sie müssen jedoch ein eigenes, in sich geschlossenes Addon schreiben, das keine Annahmen zu Systembibliotheken macht. In einigen Fällen müssen Sie außerdem sicherstellen, dass Ihr Addon für das gewünschte Betriebssystem kompiliert wird.


Lassen Sie uns diese Frage in mehreren Teilen brechen:

- Addons (native Module) :

Addons sind dynamisch verknüpfte Shared Objects.

Mit anderen Worten, Sie können einfach Ihr eigenes Addon schreiben, ohne von systemweiten Bibliotheken abhängig zu sein (z. B. durch statisches Verknüpfen erforderlicher Module), die den gesamten erforderlichen Code enthalten.

Sie müssen bedenken, dass ein solcher Ansatz betriebssystemspezifisch ist. Das bedeutet, dass Sie Ihr Addon für jedes Betriebssystem, das Sie unterstützen möchten, kompilieren müssen! (abhängig davon, welche anderen Bibliotheken Sie verwenden können)

- Native Module für Elektron :

Die nativen Node-Module werden von Electron unterstützt. Da Electron jedoch eine andere V8-Version als den offiziellen Node verwendet, müssen Sie beim Erstellen von nativen Modulen manuell die Position der Electron-Header angeben

Dies bedeutet, dass ein natives Modul, das aus Knoten-Headern aufgebaut wurde, neu aufgebaut werden muss, um im Elektron verwendet zu werden. Sie finden wie in Elektronendokumenten.

- Module mit Elektronen-App bündeln :

Ich nehme an, Sie möchten, dass Ihre App eine eigenständige ausführbare Datei ist, ohne dass Benutzer auf ihren Computern Elektron installieren müssen. Wenn ja, kann ich die Verwendung von electron-packager vorschlagen.

6
Yan Foto

Die obigen Antworten haben mir geholfen herauszufinden, wie es gemacht wird. Es gibt jedoch eine sehr effiziente Möglichkeit, binäre Dateien zu verteilen.

Stichworte aus tsurigas Antwort entnehmen , hier ist mein Code:

Hinweis: Ersetzen oder fügen Sie den Pfad OS entsprechend hinzu.

  • Erstellen Sie ein Verzeichnis ./resources/mac/bin
  • Legen Sie Ihre Binärdateien in diesen Ordner 
  • Erstellen Sie die Datei ./app/binaries.js und fügen Sie den folgenden Code ein:
'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'));
  • Erstellen Sie die Datei ./app/get-platform.js und fügen Sie den folgenden Code ein:
'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';
  }
};
  • Fügen Sie den folgenden Code in die Datei ./package.json ein:
"build": {
....

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

....
},
  • binärdateipfad importieren als:
import { execPath } from './binaries';

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

Warum ist das besser?

  • Die obigen Antworten erfordern ein zusätzliches Paket namens app-root-dir

  • tsurigas Antwort behandelt die (env = production) build oder die vorgepackten-Versionen nicht richtig. Er hat sich nur um die Entwicklung und die nachverpackten Versionen gekümmert.

3

folgende Antwort von Ganesh war wirklich eine große Hilfe, in meinem Fall war das, was in binaries.js funktionierte (für einen Mac-Build - nicht für Windows oder Linux getestet):

"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");

Beachten Sie, dass sich my_exec_name im Ordner ./external/bin befand und im App-Paket in ./Resources/bin kopiert wurde. Ich habe das Script get_platforms.js nicht verwendet (in meinem Fall nicht erforderlich). app.getAppPath () erzeugte einen Absturz, als die App gepackt wurde. Hoffe, es kann helfen.

0
fred foc