最近想在手机的 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:
pkg install -y nodejs-lts python make clang pkg-config libvips我这台设备上还少了点东西:编译时报了 xproto.pc 找不到、X11 protocol headers 相关的错。如果你也撞上,把 xorgproto 补上:
pkg install -y xorgproto装完我顺手确认了一下 libvips 能不能被 pkg-config 认出来:
pkg-config --modversion vips-cpp只要能打印出版本号,比如:
8.18.2就说明 Termux 这边的 libvips 已经就位了。
2. 为什么我没直接 npm install sharp
我一开始当然是图省事,直接来了一发:
npm install sharp在普通 Linux 上这通常能直接拉到预编译包,但在 Termux 里,因为平台不被官方支持,它会退回去走源码编译,然后就甩给我这么个错:
sharp: Attempting to build from source via node-gypsharp: Please add node-addon-api to your dependencies我照着提示往下走,它又接着要:
sharp: Please add node-gyp to your dependencies到这我就明白了:node-addon-api 和 node-gyp 是从源码编译 sharp 必须的两个包。但它们只在编译时用得到,我不想让它们污染 package.json,所以后面我是临时把它们装进来的。
3. 在项目里装 sharp
先进到项目目录,我的是:
cd ~/project/blog我的项目里其实早就写了 sharp 依赖,只是之前一直装不上。所以我先用 --ignore-scripts 把其余依赖装好,免得 sharp 的安装脚本在这一步又把整个安装拖垮:
npm install --ignore-scripts然后再临时把编译要用的两个包塞进来:
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:
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 路径:
pkg-config --cflags vips-cpp如果你这时候还同时看到:
Package 'xproto', required by 'xrender', not found那就是缺了 X11 的 protocol headers——跟第 1 节那个坑是一回事。把 xorgproto 装上再重建就好:
pkg install -y xorgprotoSHARP_FORCE_GLOBAL_LIBVIPS=1 npm rebuild sharp5. 验证它是真的能用
编译过了不代表就能跑,我还是验一下。先看版本:
node -e "const sharp=require('sharp'); console.log('sharp', sharp.versions.sharp); console.log('vips', sharp.versions.vips)"我这边打出来是:
sharp 0.34.5vips 8.18.2光有版本号还不够,我又让它真的生成一张图试试:
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,索性整站构建跑一遍:
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-api和node-gyp临时补上源码编译的依赖
就这么三步,sharp 在我手机的 Termux 里稳定跑起来了。如果你也卡在同样的地方,希望这篇能让你少折腾一点。