Подтвердить что ты не робот

Объединение предварительно скомпилированного бинарного в электронное приложение

Есть ли хорошее решение о том, как включить сторонние предварительно скомпилированные двоичные файлы, такие как imagemagick в электронное приложение? существуют модули node.js, но все они являются оболочками или привязкой к установленным библиотекам. Интересно, возможно ли связать предварительно скомпилированные двоичные файлы внутри дистрибутива.

4b9b3361

Ответ 1

Я нашел решение для этого, но я понятия не имею, считается ли это наилучшей практикой. Я не смог найти хорошую документацию для включения сторонних скомпилированных двоичных файлов, поэтому я просто возился с этим, пока он, наконец, не работал с моим двоичным файлом ffmpeg. Вот что я сделал (начиная с быстрого старта электронов, node.js v6):

Метод Mac OS X

Из каталога приложения я запустил следующие команды в терминале, чтобы включить двоичный файл ffmpeg в качестве модуля:

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

(замените /usr/local/bin/ffmpeg на ваш текущий двоичный путь, загрузите его отсюда). Размещение ссылки позволило электронному упаковщику включить двоичный файл, который я сохранил в node_modules/ffmpeg/.

Затем, чтобы получить путь к приложенному приложению (чтобы я мог использовать абсолютный путь для своего двоичного файла... относительные пути не работали, независимо от того, что я делал), я установил пакет npm app-root-dir, выполнив следующую команду команда:

npm i -S app-root-dir

Теперь, когда у меня есть корневой каталог приложений, я просто добавляю подпапку для своего двоичного файла и порождаю его оттуда. Это код, который я поместил в 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}' );
    });

Метод Windows

  1. Откройте папку с электронными базами (электронное быстрое начало - это имя по умолчанию), затем перейдите в папку node_modules. Создайте там папку с именем ffmpeg и скопируйте статический двоичный файл в этот каталог. Примечание: это должна быть статическая версия вашего бинарного файла, для ffmpeg я взял последнюю версию Windows здесь.
  2. Чтобы получить путь к приложенному приложению (чтобы я мог использовать абсолютный путь для своего двоичного файла... относительные пути не работали, независимо от того, что я делал), я установил пакет npm app-root-dir, выполнив следующую команду из командной строки в каталоге моего приложения:

     npm i -S app-root-dir
    
  3. В папке node_modules перейдите в подпапку .bin. Вам нужно создать пару текстовых файлов здесь, чтобы указать узлу включить двоичный исполняемый файл, который вы только что скопировали. Используйте ваш любимый текстовый редактор и создайте два файла, один с именем ffmpeg со следующим содержимым:

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

    И второй текстовый файл с именем ffmpeg.cmd:

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

Затем вы можете запустить ffmpeg в вашем электронном распределении Windows (в renderer.js) следующим образом (я также использую модуль узла app-root-dir). Обратите внимание на кавычки, добавленные в двоичный путь, если ваше приложение установлено в каталог с пробелами (например, C:\Program Files\YourApp), оно не будет работать без них.

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

Ответ 2

Вот еще один метод, протестированный до сих пор с Mac и Windows. Требуется пакет 'app-root-dir', не требующий добавления чего-либо вручную в каталог node_modules.

  1. Поместите свои файлы в ресурсы /$ os/, где $ os - это либо "mac", "linux", либо "win". Процесс сборки будет копировать файлы из этих каталогов в соответствии с целевой сборкой ОС.

  2. Поместите параметр extraFiles в свои extraFiles сборки следующим образом:

package.json

  "build": {
    "extraFiles": [
      {
        "from": "resources/${os}",
        "to": "Resources/bin",
        "filter": ["**/*"]
      }
    ],
  1. Используйте что-то вроде этого, чтобы определить текущую платформу.

получить-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. Вызовите исполняемый файл из вашего приложения в зависимости от env и ОС. Здесь я предполагаю, что встроенные версии находятся в рабочем режиме, а исходные версии - в других режимах, но вы можете создать свою собственную логику вызова.
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
});

Я думаю, что я использовал строитель электронов в качестве основы, генерация файла env идет с этим. В основном это просто файл конфигурации JSON.

Ответ 3

TL;DR:

да, вы можете! но для этого требуется написать собственный автономный аддон, который не делает никаких допущений в системных библиотеках. Более того, в некоторых случаях вы должны убедиться, что ваш аддон скомпилирован для желаемой ОС.


Позволяет разбить этот вопрос в нескольких частях:

- Addons (родные модули):

Добавления - это динамически связанные общие объекты.

Другими словами, вы можете просто написать свой собственный аддон без зависимости от системных библиотек (например, путем статической привязки необходимых модулей), содержащих весь необходимый код.

Вы должны учитывать, что такой подход является специфичным для ОС, а это значит, что вам нужно скомпилировать свой аддон для каждой ОС, которую вы хотите поддержать! (в зависимости от того, какие другие библиотеки вы можете использовать)

- Родные модули для электрона:

Нативные Node модули поддерживаются Electron, но поскольку Electron использует другую версию V8 от официального Node, вам нужно вручную указать расположение заголовков Electron при создании собственных модулей

Это означает, что встроенный модуль, который был создан против заголовков Node, должен быть перестроен для использования внутри электрона. Вы можете найти, как в электронных документах.

- Модули пакетов с электронным приложением:

Я предполагаю, что вы хотите, чтобы ваше приложение было автономным исполняемым файлом, не требуя от пользователей установки электрона на своих машинах. Если это так, я могу предложить использовать electron-packager.

Ответ 4

Приведенные выше ответы помогли мне понять, как это делается. Но есть очень эффективный способ распространения двоичных файлов.

Принимая подсказки от ответа Цурига, вот мой код:

Примечание: соответственно замените или добавьте путь к ОС.

  • Создайте каталог. /resources/mac/bin
  • Поместите ваши двоичные файлы в эту папку
  • Создайте файл. /app/binaries.js и вставьте следующий код:
'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'));
  • Создайте файл. /app/get-platform.js и вставьте следующий код:
'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';
  }
};
  • Добавьте следующий код в файл. /package.json:
"build": {
....

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

....
},
  • импортировать путь двоичного файла как:
import { execPath } from './binaries';

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

Почему это лучше?

  • Приведенные выше ответы требуют дополнительного пакета под названием app-root-dir

  • Ответ tsuriga неправильно обрабатывает сборку (env = production) или предварительно упакованные версии. Он/она позаботился только о разработке и пост-упакованных версиях.

Ответ 5

следующий ответ Ганеша, который был действительно большим подспорьем, в моем случае то, что работало в binaries.js (для сборки mac - не тестировал для windows или linux):

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

Учитывая, что my_exec_name было в папке ./external/bin и скопировано в пакет приложения в ./Resources/bin. Я не использовал скрипт get_platforms.js (не требуется в моем случае). app.getAppPath() генерировал сбой, когда приложение было упаковано. Надеюсь, это поможет.