如何将基于 Electron 的应用程序移植到 FreeBSD

Electron 是一款流行的框架,可用于使用 HTML、CSS 和 JavaScript 等 Web 技术开发桌面应用程序。基于 Electron 构建的应用程序有很多(无论开源还是闭源),可以在以下网站找到一些例子:

Wanted Ports 列表中待了一段时间后,Electron 于 2019 年 5 月正式进入 FreeBSD 的 Ports,目前支持两个主要版本(4 和 6)。自那以后,两款基于 Electron 的应用程序——Visual Studio CodeAtom(恰好都是代码编辑器)——已进入 Ports。

如果你查看这些 ports,会发现它们包含冗长的 Makefile 和大量的补丁文件。因此,你可能会认为移植基于 Electron 的应用程序是件繁琐的工作。然而,事实并非如此。这两个编辑器属于特例,移植基于 Electron 的应用程序通常比你想象的要简单。

借助一些 辅助 Makefile(仍在开发中),你可以通过编写不到 100 行的 Makefile 和一些配套文件来创建一个 port。

在这篇文章中,我将介绍如何使用这些 .mk 文件来移植基于 Electron 的应用程序。我选择了一个简单但仍然实用的应用程序 YouTube Music Desktop 作为示例。

准备工作

在开始移植之前,我们需要进行一些准备工作。首先,我们需要获取关于该应用程序的信息,例如:

  • 该应用程序所依赖的 Electron 版本,

  • 构建该应用程序所需的 Node 版本,

  • 构建该应用程序所需的 Node 包管理器。

应用程序的源代码归档文件中的 README 文档/ package.json 文件通常会提供这些信息。让我们下载该归档文件并查看内容。

fetch https://github.com/ytmdesktop/ytmdesktop/archive/v1.8.2.tar.gz
tar -xzf v1.8.2.tar.gz
less ytmdesktop-1.8.2/package.json
  • Electron 版本 —— Electron 通常被指定为应用程序项目的开发依赖项。在 package.json 文件的 devDependencies 部分,我们找到如下行:"electron": "^7.1.11",,因此所需的 Electron 版本为 7

  • Node 版本 —— 该归档文件中没有找到关于 Node 版本的具体描述。因此,我们假设可以使用所有受支持的 Node 版本。在本例中,我们选择 12 版本。(注意:这只是因为我个人喜欢使用最新的 LTS(长期支持)版本,其他受支持的版本应该也可以正常运行。)

  • 包管理器 —— 应用程序项目通常使用一个文件来锁定所有依赖项(Node 模块)的版本,以确保项目的可复现性。如果存在 package-lock.json 文件,则表示使用的是“NPM”包管理器;而 yarn.lock 文件表示使用的是“Yarn”。在本例中,该归档文件包含了这两个文件,这可能意味着可以使用任意一款包管理器。这里我们选择 NPM 作为包管理器(纯粹是个人决定 :-)。

总结

我们将使用以下版本:

  • Electron: 7

  • Node: 12

  • 包管理器:NPM

还需要进行一步准备工作。如上所述,port 将使用那些仍在开发中的 .mk 文件提供的功能。因此,我们需要获取这些文件并将它们复制到 ports 目录中。

git clone https://github.com/tagattie/FreeBSD-Electron.git
cp FreeBSD-Electron/Mk/Uses/*.mk ${PORTSDIR}/Mk/Uses

现在,我们已准备好开始创建 port。

移植

注意

可在 我复刻版 Ports 仓库 中找到完整的 port。如果你想快速查看它的样子,可以前往该链接。

初始化 port

首先,初始化 port,例如执行以下命令。(注意:这里使用了 ports-mgmt/porttools。)

cd ${PORTSDIR}
port create multimedia/ytmdesktop

放置 package.json 和锁定文件

将应用程序源代码归档文件中的 package.jsonpackage-lock.json 复制到 port 的 files/packagejsons 目录中。

cd multimedia/ytmdesktop
mkdir -p files/packagejsons
cp /path/to/archive/ytmdesktop-1.8.2/package*.json files/packagejsons

为什么需要这样做?

我认为移植基于 Electron 的应用程序最麻烦的部分是准备分发文件。官方 port 需要能够使用 Poudriere 进行构建,这意味着必须预先下载所有分发文件(包括应用程序依赖的所有 Node 模块),并使用校验和文件 (distinfo) 验证其完整性。

使用 NPM 的项目会在 package.jsonpackage-lock.json 中指定所有模块依赖项。辅助 .mk 文件利用这些文件来“预获取”应用程序所需的所有 Node 模块,并将它们打包成一个归档文件,以便进行校验和验证。

因此,你无需手动处理 Node 模块的分发文件。Ports 基础设施会在执行 make makesum 时自动处理这些 Node 模块的命名、获取和校验。

编写 Makefile

现在,让我们来编写 Makefile。我只会介绍与 Electron 相关的部分。完整的 Makefile 请参考 我的复刻版仓库

我们需要指定三个重要的变量:USESUSE_NODEUSE_ELECTRON。首先来看 USESUSE_NODE。通过定义这两个变量,指定的 Electron 版本、Node 版本以及包管理器会被自动添加到必要的依赖项中。

USES=           electron:7 node:12,build
USE_NODE=       npm

接下来的 USE_ELECTRON 变量涉及提供的核心功能,因此我会详细解释这些特性。

USE_ELECTRON=   prefetch extract prebuild build:builder
PREFETCH_TIMESTAMP=     1582793516
  • prefetch —— 如果在 ${DISTDIR} 目录中找不到分发文件,则在 fetch 阶段会使用预存的 package.jsonpackage-lock.json 下载应用程序所依赖的所有 Node 模块。下载的 Node 模块会被打包成一个自动命名的 tar 文件,作为 DISTFILES 之一。

    • PREFETCH_TIMESTAMP —— 如果使用 prefetch 功能,则必须定义此变量。该变量是时间戳,会赋值给 tar 归档中的所有目录、文件和链接,以确保归档文件的可复现性。你可以使用命令 date '+%s' 获取一个合适的值。

  • extract —— 在 extract 阶段,将预获取的 Node 模块安装到 port 的工作源码目录中。

  • prebuild —— 在 build 阶段,会针对指定版本的 Node 重新编译本机 Node 模块,使其能够被 Node 执行,以便构建应用程序。此外,该功能还会在应用程序打包之前,针对指定版本的 Electron 重新编译本机 Node 模块。

简单来说,这三个特性将 npm install 过程拆分为三个阶段,使其适用于 port 构建流程。

最后一个特性与应用程序的打包有关,它会生成可分发的应用程序形式。根据 官方文档,常见的打包工具包括:

当前,该特性支持 electron-builderelectron-packager(分别对应 build:builderbuild:packager)。

打包工具通常被指定为开发依赖项。查看 package.json 文件的 devDependencies 部分,我们可以找到如下行:

"electron-builder": "^21.2.0"

这表明该应用程序依赖于 electron-builder

我们已经快要完成了,但还有最后一件事要做。安装阶段的准备工作是最繁琐的部分。你需要从零开始编写安装目标,主要涉及以下几个步骤:

  • 为应用程序创建一个包装脚本;

  • 为应用程序创建 .desktop 入口文件;

  • 生成应用程序图标(除非源代码归档中已经包含了图标);

  • 在 Makefile 中编写 do-install 目标。

包装脚本的模板可能如下所示:

#! /bin/sh

export NODE_ENV=production
export ELECTRON_IS_DEV=0

electron%%ELECTRON_VER_MAJOR%% %%DATADIR%%/resources/app.asar $@

do-install 目标中,手动安装创建的包装脚本、.desktop 文件和图标。此外,不要忘记将打包工具生成的应用程序资源目录复制到 ${DATADIR}

do-install:
        # 将包装脚本、.desktop 入口文件和图标安装到适当位置
        # (此处省略部分代码)
        # 安装应用程序数据目录到 ${DATADIR}
        ${MKDIR} ${STAGEDIR}${DATADIR}
        cd ${WRKSRC}/dist/linux-unpacked && \
                ${COPYTREE_SHARE} resources ${STAGEDIR}${DATADIR}

构建

最后,我们已经准备好构建这个 port 了。

make makesum # 生成 distinfo
make build

此外,我们仍然需要 pkg-descrpkg-plist 文件来打包该应用程序。不过,由于这些工作与 Electron 无关,这部分就留给你自行完成了。

最后更新于