在 Android Termux 中安装 sharp

最近想在手机的 Termux 里跑我的 Astro 博客,结果第一步就卡住了——卡在 sharp 上。

sharp 是很多 Node.js 项目都会用到的图片处理库,负责压缩、格式转换、生成 WebP/AVIF 这些活儿,Astro、Next.js 做图片优化基本都靠它。我本以为 npm install 一下就完事,结果直接报错。

原因其实不难猜:Termux 并不是普通的 Linux 发行版,它跑在 Android 上,Node.js 报告的平台是 android arm64。而 sharp 的官方预编译包主要照顾 Linux、macOS、Windows,根本没给这个平台准备现成的二进制。

折腾了一圈之后,我找到了一条更靠谱的路:不去跟官方预编译包较劲,而是直接用 Termux 自带的 libvips,把 sharp 从源码编译出来。下面就是我跑通的完整过程。

1. 先把系统依赖装齐

sharp 之前,我先把编译要用到的东西都装上——Node.js、编译工具链,还有最关键的 libvips

Terminal window
pkg install -y nodejs-lts python make clang pkg-config libvips

我这台设备上还少了点东西:编译时报了 xproto.pc 找不到、X11 protocol headers 相关的错。如果你也撞上,把 xorgproto 补上:

Terminal window
pkg install -y xorgproto

装完我顺手确认了一下 libvips 能不能被 pkg-config 认出来:

Terminal window
pkg-config --modversion vips-cpp

只要能打印出版本号,比如:

8.18.2

就说明 Termux 这边的 libvips 已经就位了。

2. 为什么我没直接 npm install sharp

我一开始当然是图省事,直接来了一发:

Terminal window
npm install sharp

在普通 Linux 上这通常能直接拉到预编译包,但在 Termux 里,因为平台不被官方支持,它会退回去走源码编译,然后就甩给我这么个错:

sharp: Attempting to build from source via node-gyp
sharp: Please add node-addon-api to your dependencies

我照着提示往下走,它又接着要:

sharp: Please add node-gyp to your dependencies

到这我就明白了:node-addon-apinode-gyp 是从源码编译 sharp 必须的两个包。但它们只在编译时用得到,我不想让它们污染 package.json,所以后面我是临时把它们装进来的。

3. 在项目里装 sharp

先进到项目目录,我的是:

Terminal window
cd ~/project/blog

我的项目里其实早就写了 sharp 依赖,只是之前一直装不上。所以我先用 --ignore-scripts 把其余依赖装好,免得 sharp 的安装脚本在这一步又把整个安装拖垮:

Terminal window
npm install --ignore-scripts

然后再临时把编译要用的两个包塞进来:

Terminal window
npm install --no-save --package-lock=false --ignore-scripts node-addon-api node-gyp

这几个参数我是特意选的:--no-save 不往 package.json 里写,--package-lock=false 不动 lock 文件,--ignore-scripts 跳过这俩包自己的安装脚本——我只要它们的文件,不要它们顺便干别的。

4. 让 sharp 用上 Termux 的 libvips

依赖凑齐,接下来才是重头戏,重建 sharp

Terminal window
SHARP_FORCE_GLOBAL_LIBVIPS=1 npm rebuild sharp

前面这个环境变量是关键:它会让 sharp 去链接系统里现成的 libvips,而不是傻乎乎地下载、或者用它自己捆绑的那份 bundled 版本。

顺利的话,会看到类似这样的输出:

rebuilt dependencies successfully

我第一次跑的时候没这么顺,半路杀出来一个:

fatal error: 'glib-object.h' file not found

我先回头确认 pkg-config 能不能吐出完整的 include 路径:

Terminal window
pkg-config --cflags vips-cpp

如果你这时候还同时看到:

Package 'xproto', required by 'xrender', not found

那就是缺了 X11 的 protocol headers——跟第 1 节那个坑是一回事。把 xorgproto 装上再重建就好:

Terminal window
pkg install -y xorgproto
SHARP_FORCE_GLOBAL_LIBVIPS=1 npm rebuild sharp

5. 验证它是真的能用

编译过了不代表就能跑,我还是验一下。先看版本:

Terminal window
node -e "const sharp=require('sharp'); console.log('sharp', sharp.versions.sharp); console.log('vips', sharp.versions.vips)"

我这边打出来是:

sharp 0.34.5
vips 8.18.2

光有版本号还不够,我又让它真的生成一张图试试:

Terminal window
node -e "const sharp=require('sharp'); sharp({create:{width:2,height:2,channels:4,background:'#336699ff'}}).webp().toBuffer().then(buf=>console.log('webp bytes', buf.length)).catch(err=>{console.error(err); process.exit(1)})"

能打印出字节数,就说明底层的 native binding 真的活了:

webp bytes 64

最后,既然我本来就是为了跑 Astro,索性整站构建跑一遍:

Terminal window
npm run build:site

当 Astro 打出 generating optimized images、WebP 图片也顺利生成时,我才算确认:sharp 这回是真的被调用起来了。

写在最后

回头看,在 Android Termux 上装 sharp,我踩的那些弯路其实都源于一个执念:总想把环境伪装成普通 Linux。真正走通之后才明白,顺着 Termux 自己的环境来反而最省事:

  • pkg install libvips 把底层图像处理库交给 Termux 准备
  • SHARP_FORCE_GLOBAL_LIBVIPS=1 告诉 sharp 别下载、就用系统这份
  • node-addon-apinode-gyp 临时补上源码编译的依赖

就这么三步,sharp 在我手机的 Termux 里稳定跑起来了。如果你也卡在同样的地方,希望这篇能让你少折腾一点。