本文完整阅读约需 45 分钟,如时间较长请考虑收藏后慢慢阅读~

Seafile作为一款开源的文件同步套件,为各种主流平台提供了预编译的客户端,但由于近年其对开源热情的逐渐消退(参见我的这篇文章),Seafile-GUI在Ubuntu官方仓库的最新版本只支持到7.x,而官网提供支持最新版本8.x的仓库地址却只有AMD64的预编译版本,不巧笔者需要在ARM64平台(NanoPi NEO3)上使用Seafile-GUI,安装官方仓库里的预编译版本却无法正常使用,于是只能自己动手,尝试编译安装。本文将以Ubuntu 18.04 LTS为例,为读者介绍在ARM64架构下编译Seafile及其依赖的过程。

0x01 前言

首先介绍本文涉及的实验环境:

  • 机器:NanoPi NEO3(来自友善之臂,很不错的国产品牌,体积小巧)
  • CPU:RK3328 四核A53架构
  • 内存:2GB
  • 发行版&内核版本:Ubuntu Linux 18.04 LTS / Linux kernel 5.4
  • 桌面环境:LXQt
  • Seafile服务器版本:8.0.7

需要注意的是,尽管本文的案例基于ARM64平台,但遵循同样思路,该方法依旧可以在x86等其他架构下进行Seafile-GUI的编译安装。

如引言所示,笔者最初抱着『又不是不能用』的心态,尝试安装了发行版官方仓库&Ubuntu Launchpad中的Seafile(均为7.0.10版本),却都在配置服务器地址和账号密码后闪退,报错 Seafile意外退出 或者 segmentation fault ,如下图所示:

Seafile官网的Seafile只支持AMD64,而唯一支持ARM64的官方仓库Seafile又无法正常使用,无奈之下,笔者只能尝试自行编译。

0x02 准备

1. 了解Linux发行版的概念

在开始准备编译之前,笔者首先想介绍一下Linux中发行版的概念。

通常来说,发行版在发布时会选择一些软件的最新或最稳定版本,保证大/中版本号不变(通常是形如a.b.c这样语义化版本号中的b,对于部分刷版本号的软件则是a),其中包括内核版本,也就是说除非有安全漏洞,否则官方仓库通常不会经常对软件进行更新,所谓的发行版支持周期(如Ubuntu LTS的五年周期)也由此得来,并不代表支持周期内更新功能,只保证安全更新。比如我们通常会说CentOS和Debian软件比较老,而Arch软件比较新(因为不存在发行版本的概念,滚动更新,有最新版本都可以安装),就是因为现在广泛使用的CentOS和Debian版本已经是若干年前的版本,其发行时选择的软件版本已经过时。

知道发行版的概念有什么用呢?这是因为我们在发行版中使用包管理器安装一个软件时,不仅要安装这个软件,还要安装其依赖(除非使用静态编译如Golang),如果软件的依赖不被发行版支持,我们就需要再手动安装依赖,再安装依赖的依赖…对于一些依赖比较复杂的软件,甚至安装到最后连内核都要换掉,但如果其他软件的依赖和当前想安装/编译的软件依赖不同,这就又要花很多时间来管理同一个库的不同版本…这样折腾的后果就是:安装一个软件,搞坏整个环境;或是安装一个软件,顺带升级所有软件,颇有为了一碟醋买两斤螃蟹,还包了盘饺子的感觉。

当然如果使用chroot或Docker等容器技术可以实现环境的隔离,但这样使用起来也不是很顺手,尤其是本文所需要安装的GUI软件,还要处理X11转发以及X11版本的兼容性问题…正是因为大家都不想处理这一团乱麻,所以才出现了绑定软件、依赖、内核版本的发行版概念,我们没有必要在编译软件这种偶然情况下开倒车。

2. 选择合适的版本

笔者这台NanoPi NEO3安装的Linux发行版是Ubuntu 18.04 LTS,于是我们在Ubuntu的软件包官网中搜索Seafile:

可以看到,Ubuntu 18.04所支持Seafile的最新版本居然只有6.1.5,并不能很好满足我们的需求。可以看到,搜索结果中同样展示Ubuntu 20.04 LTS(focal)支持Seafile 7.0.6,而当我们对比Ubuntu 18.04和Ubuntu 20.04时,可以发现这两个版本的Seafile-GUI对底层库(如libc、libsqlite等)要求基本一致,而Seafile7.x所依赖的其他高级库(如libqt5gui5>=5.8.0)在Ubuntu 18.04中同样满足:

由于Seafile-GUI涉及的依赖较多,不便于一一枚举检查,但既然大差不差,笔者就以Ubuntu 20.04支持的Seafile-GUI 7.0.8为例(毕竟越新越好,如果读者阅读到这里时发现届时版本号更高的Seafile-GUI对依赖库的需求类似,同样可以采用高版本尝试,思路类似),对其进行编译安装。

3. 获取对应的源码和依赖

既然我们选定了将默认支持Ubuntu 20.04的Seafile 7.0.6编译到Ubuntu 18.04上来,那么首先要做的就是获取对应的源码。阅读seafile-client仓库的README,我们得知,其主要的依赖有以下四个:

  • Qt5及其相关的子包
  • cmake: 用于工程化构建Makefile的工具
  • libsearpc: Seafile团队开发的C语言RPC框架
  • seafile: 包含seafile-daemon和seafile-cli以及libseafile等相关依赖

其中cmake和Qt5是Ubuntu 18.04支持的,libsearpc则因为发行版自带库版本(3.0.8)低于所需版本(>=3.2.0),需要我们手动编译安装,而Seafile由于要求和Seafile-GUI版本一致,我们也需要进行编译安装。

接下来,笔者按照Ubuntu仓库中的依赖列表安装了对应的依赖及其头文件(大部分是lib开头的包名结尾加上-dev),这里由于每个人的Ubuntu中预安装过的依赖版本不同,不再赘述,但编译过程中缺少的所有包都可以在Ubuntu 18.04的官方仓库中找到)。

至于源码文件,笔者从Ubuntu仓库中同样下载了对应版本的源码tarball包:

这里笔者将其下载到/share/homes/lurenjiasworld/build目录中,并逐一解压,最终得到如下三个目录:

0x03 试错

在介绍正式的编译过程之前,请允许笔者插入一节用于记录笔者在编译Seafile-GUI过程中犯下的错误,愿读者引以为戒:

1. 直接使用make && make install安装

诚然,大部分的在线教程甚至官方文档都会鼓励使用这种方式,笔者也基于习惯在编译Seafile时使用这种方式进行安装,但它存在如下致命缺陷:

  1. 无法管理版本,如果以后需要更新版本或想知道当前安装依赖库的版本,只能抓瞎;
  2. 在遗失源码包(或源码Makefile不提供卸载脚本)情况下,无法彻底卸载(谁知道它把文件放哪去了呢);
  3. 如果以后安装别的软件,这些软件依赖同样的包但不同版本,包管理器根本不会意识到!最终你辛辛苦苦编译安装的软件很有可能会因为被覆盖安装了不兼容版本的依赖库导致无法正常启动,而排除这一错误可能需要很长时间。

包管理器的一大特点就是支持版本管理,避免tarball编译安装无法控制版本的痛点,而当笔者意识到这一点时,已经是在使用make编译安装的最后阶段,前面两个小时的工作只能作废。

2. 使用apt-get source编译安装

上文笔者刚接受完make install的教训,决定痛定思痛开始使用包管理器,接下来就被apt打了个措手不及。

笔者的“错误”思路如下:

  1. 既然需要使用包管理器安装软件,那么在Ubuntu下,就应该使用apt?于是笔者通过其文档发现了apt source

听起来是不是很棒?和apt install类似的用法,支持管理源码包的依赖,并按照依赖顺序进行编译安装。

但问题在于,正是因为它和apt install类似,导致它实际上是一个食之无味的『鸡肋』:它只能从官方仓库拉取源码包。但我们之所以需要手动编译,往往就是因为官方仓库的软件版本、支持架构或编译参数无法满足我们的需求,在这种情况下,使用apt source几乎没有任何价值。

无奈笔者天生愚笨,在阅读了半小时的文档,尝试了apt source各种参数后,才意识到方向错了,于是立刻返辙,转而开始认真思考:既然apt的底层是基于dpkg(如同yum/dnf的底层是基于rpm),而dpkg是不限制版本甚至可以无视依赖的(只要是平台兼容的deb包都可以安装),那么dpkg是否有手动编译tarball的选项呢?

笔者本来只是凭直觉瞎猜了下,没想到在翻阅dpkg的文档后,发现其真的支持从源码编译:[第 6 章 构建软件包] https://www.debian.org/doc/manuals/maint-guide/build.zh-cn.html 。按照文档中的指引,我们需要在源码目录中(需要有debian目录,以及里面正确的构建脚本,如果是从官方仓库下载的源码包,一定是有这个目录的)执行如下命令:

dpkg-buildpackage -us -uc

dpkg会为我们完成所有的工作,并将二进制包放在上层目录中供我们进行安装或签名发布。在这里我们不需要签名发布,因此只需要使用这个命令安装即可。后文将会介绍具体的安装流程,而这里只是为了用笔者的实际教训来警示读者,避免在源码编译安装软件的过程中走弯路。

3. 将源码库放在MicroSD卡中安装

笔者想要分享的第三个血泪教训就是:永远不要在MicroSD卡上执行任何涉及IO的工作(把它当做储存数据的仓库吧,不要幻想技术已经发达到指甲盖大小的MicroSD卡性能和2.5寸企业级SSD持平)。

MicroSD卡有多烂,无需笔者多言,如果无法感同身受的读者可以去看看喷神James的吐槽:You Know What’s BS!? Micro SD Cards – YouTube

最初笔者以为Seafile项目不大,就没有插入移动硬盘,直接将其下载到了内存卡上进行编译安装,结果安装其依赖(主要是Qt相关)花了20分钟,三个总计2M左右的tarball居然也花了10分钟时间解压(只有不到1000个文件),而编译时,花在IO上的时间则高达惊人的80%(是的,笔者也是在编译软件时第一次看到80%的iowait和60+的负载),刚编译完libsearpc这一个包,整个系统就已经卡顿到无法移动窗口。无奈之下,笔者只好找来一块移动硬盘,插入NanoPi,重新下载源码包,解压编译。

但如果非要在如树莓派、NanoPi等只支持MicroSD卡的设备上进行此类工作(或是将其作为主要的工作设备使用)该怎么办呢?如果你的设备确定支持eMMC,建议使用eMMC转MicroSD卡的模块,如下图所示(仅供建议,不为图中品牌背书):

为什么eMMC会和MicroSD(TF)卡兼容?这是因为实际上SD和eMMC的协议是基本一致的,感兴趣的读者可以了解eMMC中『MMC』和『SD』的渊源:多媒体存储卡 – 维基百科,自由的百科全书,此处不再赘述。

需要注意的是,编译过程必须在支持软链接的文件系统上进行,如exFAT、cifs等文件系统都是不支持软链接的,推荐使用ext4(笔者最后的解决方案是iSCSI Initiator挂载NAS上的LUN,然后将其格式化为ext4)。

0x04 编译

经过上文不断试错的过程,我们终于可以开始正式的编译。

此时我们的目录结构如下所示:

$ ll
total 0
drwxr-xr-x 2 pi pi 0 10月 24  2019 libsearpc-3.2.0
drwxr-xr-x 2 pi pi 0 2月  13  2020 seafile-7.0.6
drwxr-xr-x 2 pi pi 0 2月  13  2020 seafile-client-7.0.6

1. 编译安装libsearpc

目录结构:

$ cd libsearpc-3.2.0
$ ll
total 40
-rwxr-xr-x 1 pi pi   94 9月  21 14:14 AUTHORS
-rwxr-xr-x 1 pi pi 2184 9月  21 14:14 autogen.sh
-rwxr-xr-x 1 pi pi    0 10月 24  2019 ChangeLog
-rwxr-xr-x 1 pi pi 2812 9月  21 14:14 configure.ac
drwxr-xr-x 2 pi pi    0 10月 24  2019 debian
drwxr-xr-x 2 pi pi    0 10月 24  2019 demo
-rwxr-xr-x 1 pi pi 1608 9月  21 14:14 INSTALL
drwxr-xr-x 2 pi pi    0 10月 24  2019 lib
-rwxr-xr-x 1 pi pi  273 9月  21 14:14 libsearpc.pc.in
-rwxr-xr-x 1 pi pi  572 9月  21 14:14 LICENSE.txt
drwxr-xr-x 2 pi pi    0 10月 24  2019 m4
-rwxr-xr-x 1 pi pi  635 9月  21 14:14 Makefile.am
-rwxr-xr-x 1 pi pi    0 10月 24  2019 NEWS
drwxr-xr-x 2 pi pi    0 10月 24  2019 pysearpc
-rwxr-xr-x 1 pi pi 9848 9月  21 14:14 README.markdown
drwxr-xr-x 2 pi pi    0 10月 24  2019 tests

开始编译:

过了两分钟左右,编译成功:

此时我们可以在上层目录中找到其编译出的.deb包,直接使用dpkg -i进行安装即可:

此时libsearpc安装完成。

2. 编译安装seafile

目录结构:

$ cd seafile-7.0.6
$ ll
total 1492
-rw-rw-r--  1 pi pi  63217 10月  7 05:27 aclocal.m4
drwxr-xr-x  2 pi pi   4096 10月  7 05:27 app
-rwxr-xr-x  1 pi pi   3412 9月  21 14:54 autogen.sh
drwxr-xr-x  2 pi pi   4096 10月  7 05:27 autom4te.cache
drwxr-xr-x  4 pi pi   4096 10月  7 05:27 common
lrwxrwxrwx  1 pi pi     32 10月  7 05:27 compile -> /usr/share/automake-1.15/compile
lrwxrwxrwx  1 pi pi     37 10月  7 05:27 config.guess -> /usr/share/automake-1.15/config.guess
-rw-rw-r--  1 pi pi   2334 10月  7 05:27 config.h
-rw-rw-r--  1 pi pi   2110 10月  7 05:27 config.h.in
-rw-rw-r--  1 pi pi  32835 10月  7 05:27 config.log
-rwxrwxr-x  1 pi pi  61199 10月  7 05:27 config.status
lrwxrwxrwx  1 pi pi     35 10月  7 05:27 config.sub -> /usr/share/automake-1.15/config.sub
-rwxrwxr-x  1 pi pi 505718 10月  7 05:27 configure
-rwxr-xr-x  1 pi pi   6688 9月  21 14:54 configure.ac
drwxr-xr-x  3 pi pi   4096 10月  7 05:29 daemon
drwxr-xr-x 12 pi pi   4096 10月  7 05:30 debian
lrwxrwxrwx  1 pi pi     32 10月  7 05:27 depcomp -> /usr/share/automake-1.15/depcomp
drwxr-xr-x  3 pi pi   4096 2月  13  2020 dmg
drwxr-xr-x  2 pi pi   4096 10月  7 05:27 doc
drwxr-xr-x  2 pi pi   4096 10月  7 05:27 include
lrwxrwxrwx  1 pi pi     35 10月  7 05:27 install-sh -> /usr/share/automake-1.15/install-sh
drwxr-xr-x  2 pi pi   4096 2月  13  2020 integration-tests
drwxr-xr-x  3 pi pi   4096 10月  7 05:29 lib
-rwxrwxr-x  1 pi pi 339448 10月  7 05:27 libtool
-rwxr-xr-x  1 pi pi  18673 9月  21 14:54 LICENSE.txt
-rw-r--r--  1 pi pi 324412 10月  7 05:26 ltmain.sh
drwxr-xr-x  2 pi pi   4096 10月  7 05:26 m4
-rw-rw-r--  1 pi pi  27251 10月  7 05:27 Makefile
-rwxr-xr-x  1 pi pi    286 9月  21 14:54 Makefile.am
-rw-rw-r--  1 pi pi  27126 10月  7 05:27 Makefile.in
lrwxrwxrwx  1 pi pi     32 10月  7 05:27 missing -> /usr/share/automake-1.15/missing
drwxr-xr-x  3 pi pi   4096 2月  13  2020 msi
drwxrwxr-x  2 pi pi   4096 10月  7 05:26 po
lrwxrwxrwx  1 pi pi     35 10月  7 05:27 py-compile -> /usr/share/automake-1.15/py-compile
drwxr-xr-x  3 pi pi   4096 10月  7 05:27 python
-rwxr-xr-x  1 pi pi   4004 9月  21 14:54 README.markdown
drwxr-xr-x  3 pi pi   4096 2月  13  2020 scripts
-rwxr-xr-x  1 pi pi   6992 9月  21 14:54 setupwin.py
-rw-rw-r--  1 pi pi     23 10月  7 05:27 stamp-h1
drwxr-xr-x  3 pi pi   4096 2月  13  2020 tests
-rwxr-xr-x  1 pi pi    604 9月  21 14:54 updateversion.sh

开始编译:

等待五分钟左右,编译成功:

同样回到上层目录,使用dpkg -i进行安装:

3. 编译安装seafile-client

目录结构:

$ cd seafile-client-7.0.6
$ ll
total 524
-rwxr-xr-x  1 pi pi    623 9月  21 14:54 Application.manifest
-rwxr-xr-x  1 pi pi  25832 9月  21 14:54 CMakeLists.txt
-rwxr-xr-x  1 pi pi   1046 9月  21 14:54 coding-style.md
drwxr-xr-x  3 pi pi   4096 2月  13  2020 data
drwxr-xr-x  3 pi pi   4096 2月  13  2020 debian
-rwxr-xr-x  1 pi pi    570 9月  21 14:54 dev-guide.md
-rwxr-xr-x  1 pi pi 102768 9月  21 14:54 Doxyfile.in
drwxr-xr-x  2 pi pi   4096 2月  13  2020 extensions
drwxr-xr-x 34 pi pi   4096 2月  13  2020 fsplugin
drwxr-xr-x  2 pi pi   4096 2月  13  2020 i18n
drwxr-xr-x 12 pi pi   4096 2月  13  2020 images
-rwxr-xr-x  1 pi pi   2860 9月  21 14:54 Info.plist
-rwxr-xr-x  1 pi pi  11608 9月  21 14:54 LICENSE
-rwxr-xr-x  1 pi pi  12910 9月  21 14:54 qt.css
-rwxr-xr-x  1 pi pi     52 9月  21 14:54 qt-linux.css
-rwxr-xr-x  1 pi pi    644 9月  21 14:54 qt-mac.css
-rwxr-xr-x  1 pi pi    269 9月  21 14:54 qt-win.css
-rwxr-xr-x  1 pi pi   1082 9月  21 14:54 README.md
drwxr-xr-x  2 pi pi   4096 2月  13  2020 scripts
-rwxr-xr-x  1 pi pi   1188 9月  21 14:54 seafile-applet.rc.in
-rwxr-xr-x  1 pi pi  11852 9月  21 14:54 seafile-client.qrc
-rwxr-xr-x  1 pi pi 233297 9月  21 14:54 seafile.icns
-rwxr-xr-x  1 pi pi  40063 9月  21 14:54 seafile.ico
-rwxr-xr-x  1 pi pi   1308 9月  21 14:54 sparkle-readme.md
drwxr-xr-x  9 pi pi   4096 2月  13  2020 src
drwxr-xr-x  2 pi pi   4096 2月  13  2020 tests
drwxr-xr-x  3 pi pi   4096 2月  13  2020 third_party
drwxr-xr-x  2 pi pi   4096 2月  13  2020 ui

开始编译:

结果编译失败,提示找不到pthread_create函数:

该项目使用cmake构建,如果要了解其构建过程太过麻烦,且因为seafile-client不会成为其他软件的依赖,不需要管理版本,因此我们更换思路,采用手动编译安装。按照README.md里的说明进行编译:

发现其提示缺少quazip的对应文件,查看发行版仓库源码发现的确没有对应文件,但GitHub仓库里的源码有,于是我们重新从GitHub拉取仓库,并checkout到对应版本:

然后我们继续按照README.md的说明编译。

先运行cmake

再运行make -j 2(避免NanoPi过热):

等待大约半小时后,编译成功:

接下来我们运行sudo make install安装:

此时可以在桌面环境的互联网分类中找到Seafile,或在终端中执行seafile-applet运行Seafile:

查看版本:

编译安装成功!