Blog

编译 go-sqlite3

2026-01-28

#Go
#cgo
#水

编译 mattn/go-sqlite3 久病成医,把踩过的坑都写一轮,先从 Linux 说起

Linux

glibc

我自己常用的大多数 Linux 发行版都是自带 glibc 的(Debian/Ubuntu...),默认使用动态链接,这玩意不论是官方还是民间讨论都不建议做静态编译,即使是动态编译,也是清一色的建议用古董版本的 Linux 发行版来干

要找合适的版本可以参考 pypa/manylinux,这仓库列出了用于 python 库构建的版本的建议,鉴于 python 有一堆 C/C++ 的库,拿来给 cgo 参考也是足够的

当然啦,硬要上静态编译也是可以的

SH
GOOS=linux GOARCH=arm64 CGO_LDFLAGS="-static" CGO_ENABLED=1 \
go build -ldflags "-linkmode external -extldflags -static" -tags "netgo sqlite_omit_load_extension osusergo"

Linux 编译带上 osusergo 会导致在 termux 运行跳 Bad System Call,应该不会有人无聊到编译一个 cli 程序到 Android 设备上用 termux 跑的……对吧?

musl

给嵌入式设备用的,还有那个经典的 docker 镜像底包 Alpine Linux 用的也是 musl,最大的优势就是静态编译友好,一处编译,随处可用,不用头疼找旧系统来编译

虽然有人会说要踩 DNS 的坑,但跟我 netgo 有什么关系呢~

SH
# apt install -y musl-dev
CC=musl-gcc CGO_ENABLED=1 go build -ldflags "-linkmode external -extldflags -static" -tags netgo

属于是 go-sqlite3 给部分系统推荐的 CC

MacOS

由于在非 Mac 平台编译需要用到自己准备的 SDK(包括后文的 zig),这玩意只能自己在 MacOS 提取或者在网上找别人分享出来的,凑齐了以后就可以自行编译了,不过我纠结了半天最后决定用 Actions 的 MacOS runner 来解决

尽管官网还没更新,从 go1.25.0 开始最低只能运行在 MacOS 12 Monterey,直接编译打包会导致 minos 被设置成 12,不过手动设置 -mmacosx-version-min=11.0 就可以改回去了

SH
# arm
CGO_CFLAGS="-mmacosx-version-min=11.0 -arch arm64" CGO_LDFLAGS="-mmacosx-version-min=11.0 -arch arm64" GOOS=darwin GOARCH=arm64 go build -tags netgo
# x86_64
CGO_CFLAGS="-mmacosx-version-min=11.0 -arch x86_64" CGO_LDFLAGS="-mmacosx-version-min=11.0 -arch x86_64" GOOS=darwin GOARCH=amd64 go build -tags netgo

如果不用 cgo 怎么办,那不靠奇技淫巧就只能到 12 了

Windows

Windows 的兼容性不用担心,最近十年来也就只有 10 和 11 两代(以及基于它们的的各种分支版本),直接编译就好了

选择 MinGW-w64 和 MSVC 最后运行起来都差不多

POWERSHELL
# dumpbin /DEPENDENTS filename.exe | findstr ".dll"

# MinGW-w64
KERNEL32.dll
msvcrt.dll

# MSVC
KERNEL32.dll
api-ms-win-crt-environment-l1-1-0.dll
api-ms-win-crt-heap-l1-1-0.dll
api-ms-win-crt-locale-l1-1-0.dll
api-ms-win-crt-math-l1-1-0.dll
api-ms-win-crt-private-l1-1-0.dll
api-ms-win-crt-runtime-l1-1-0.dll
api-ms-win-crt-stdio-l1-1-0.dll
api-ms-win-crt-string-l1-1-0.dll
api-ms-win-crt-time-l1-1-0.dll
api-ms-win-crt-utility-l1-1-0.dll

跨平台

zig

zig 拿来直接做跨平台编译(不含 MacOS)大概是足够的,可以在 zig targets 找到合适的 CC,选完再编译
SH
# https://ziglang.org/download/
CC="zig cc -target x86_64-linux-musl" GOOS=linux GOARCH=amd64 CGO_ENABLED=1 go build -ldflags "-linkmode external -extldflags -static" -tags netgo
CC="zig cc -target x86_64-windows-gnu -O2" GOOS=windows GOARCH=amd64 CGO_ENABLED=1 go build -tags netgo -ldflags "-linkmode external"

xgo

xgo 很好用,gitea 也在用,多人用的好处就是不用太担心停更,有人用不了的时候自然会去更新,实际上也坑过我一回,但已经忘记原因了,最后只记得必须锁定版本

坏处就是依赖 docker,镜像超级大,调用 Github Actions 编译时 workflow 大量时间都花在下载,而且体积过大还导致 Cache 并不能保存,每次启动都要 pull 这个巨大的镜像

SH
# go install src.techknowlogick.com/xgo@v1.8.1-0.20250401170454-4b368d8a5afa
# docker pull ghcr.io/techknowlogick/xgo:go-1.25.6
CGO_ENABLED=1 $HOME/go/bin/xgo -go go-1.25.6 -tags netgo --targets=windows/amd64,darwin/amd64,darwin/arm64 ./

cgo-free

太麻烦了,有没有不用 cgo 的办法,有的还真有,比如 gitlab:cznic/sqlite

由于是用纯 Go 来翻译 C 库,所以支持的系统范围就有限了

参考


评论区