Skip to content

Commit b6b198f

Browse files
arvclaude
andcommitted
icu.js: link ICU dynamically on Alpine too (musl static is non-PIC)
Alpine's icu-static (libicuuc.a) is NOT built with -fPIC, so it can't link into the .node shared object — the alpine prebuild failed with the same "recompile with -fPIC" error as glibc. Correcting my earlier assumption that musl static archives are PIC. Static ICU is therefore only viable on macOS (Homebrew). Link ICU dynamically on ALL Linux (glibc + Alpine); the runtime image must provide ICU (`apk add icu-libs` in the zero-cache Alpine image). - icu.js: useStatic = isMac only; drop the Alpine static branch. - build.yml: drop `icu-static` from the alpine prebuild apk installs (icu-dev provides the headers and the .so we now link against). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent c811fbe commit b6b198f

6 files changed

Lines changed: 44 additions & 52 deletions

File tree

.github/workflows/build.yml

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ on:
1414

1515
env:
1616
# See https://github.com/nodejs/release#release-schedule
17-
# Node.js v20 EOL = 2026-04-30. v22 EOL = 2027-04-30. v23 EOL = 2025-06-01. v24 EOL = 2028-04-30. v25 EOL = 2026-06-01.
18-
# Node.js 20-24 can build with GCC 10 (bullseye)
19-
NODE_BUILD_CMD_LEGACY: npx --no-install prebuild -r node -t 20.0.0 -t 22.0.0 -t 23.0.0 -t 24.0.0 --include-regex '_sqlite3'
17+
# Node.js v22 EOL = 2027-04-30. v23 EOL = 2025-06-01. v24 EOL = 2028-04-30. v25 EOL = 2026-06-01. v26 EOL = 2029-04-30.
18+
# Node.js 22-24 can build with GCC 10 (bullseye)
19+
NODE_BUILD_CMD_LEGACY: npx --no-install prebuild -r node -t 22.0.0 -t 23.0.0 -t 24.0.0 --include-regex '_sqlite3'
2020
# Node.js 25+ requires GCC 11+ for <source_location> header (bookworm)
21-
NODE_BUILD_CMD_MODERN: npx --no-install prebuild -r node -t 25.0.0 --include-regex '_sqlite3'
21+
NODE_BUILD_CMD_MODERN: npx --no-install prebuild -r node -t 25.0.0 -t 26.0.0 --include-regex '_sqlite3'
2222

2323
jobs:
2424
test:
@@ -31,11 +31,10 @@ jobs:
3131
- macos-15-intel
3232
- windows-2022
3333
node:
34-
- 20
3534
- 22
36-
- 23
3735
- 24
3836
- 25
37+
- 26
3938
name: Testing Node ${{ matrix.node }} on ${{ matrix.os }}
4039
runs-on: ${{ matrix.os }}
4140
steps:
@@ -137,7 +136,7 @@ jobs:
137136
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
138137
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
139138
with:
140-
node-version: 20
139+
node-version: 22
141140
registry-url: https://registry.npmjs.org
142141
- name: Upgrade npm for OIDC support
143142
run: npm install -g npm@latest
@@ -161,7 +160,7 @@ jobs:
161160
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
162161
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
163162
with:
164-
node-version: 20
163+
node-version: 22
165164
- if: ${{ startsWith(matrix.os, 'windows') }}
166165
run: pip.exe install setuptools
167166
- if: ${{ startsWith(matrix.os, 'macos') }}
@@ -182,7 +181,7 @@ jobs:
182181
contents: write # prebuild -u uploads the .node assets to the GitHub release
183182
name: Prebuild on Linux x64
184183
runs-on: ubuntu-latest
185-
container: node:20-bullseye
184+
container: node:22-bullseye
186185
needs: test
187186
steps:
188187
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
@@ -196,7 +195,7 @@ jobs:
196195
contents: write # prebuild -u uploads the .node assets to the GitHub release
197196
name: Prebuild on Linux x64 (Node 25+)
198197
runs-on: ubuntu-latest
199-
container: node:20-bookworm
198+
container: node:22-bookworm
200199
needs: test
201200
steps:
202201
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
@@ -210,11 +209,11 @@ jobs:
210209
contents: write # prebuild -u uploads the .node assets to the GitHub release
211210
name: Prebuild on alpine
212211
runs-on: ubuntu-latest
213-
container: node:20-alpine
212+
container: node:22-alpine
214213
needs: test
215214
steps:
216215
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
217-
- run: apk add build-base git python3 py3-setuptools libstdc++ readline-dev ncurses-dev icu-dev icu-static --update-cache
216+
- run: apk add build-base git python3 py3-setuptools libstdc++ readline-dev ncurses-dev icu-dev --update-cache
218217
- run: npm install --ignore-scripts
219218
- run: ${{ env.NODE_BUILD_CMD_LEGACY }} -u ${{ secrets.GITHUB_TOKEN }}
220219
- run: ${{ env.NODE_BUILD_CMD_MODERN }} -u ${{ secrets.GITHUB_TOKEN }}
@@ -236,8 +235,8 @@ jobs:
236235
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
237236
- uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3
238237
- run: |
239-
docker run --rm -v $(pwd):/tmp/project --entrypoint /bin/sh --platform linux/${{ matrix.arch }} node:20-alpine -c "\
240-
apk add build-base git python3 py3-setuptools libstdc++ readline-dev ncurses-dev icu-dev icu-static --update-cache && \
238+
docker run --rm -v $(pwd):/tmp/project --entrypoint /bin/sh --platform linux/${{ matrix.arch }} node:22-alpine -c "\
239+
apk add build-base git python3 py3-setuptools libstdc++ readline-dev ncurses-dev icu-dev --update-cache && \
241240
cd /tmp/project && \
242241
npm install --ignore-scripts && \
243242
${{ env.NODE_BUILD_CMD_LEGACY }} -u ${{ secrets.GITHUB_TOKEN }} && \
@@ -260,7 +259,7 @@ jobs:
260259
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
261260
- uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3
262261
- run: |
263-
docker run --rm -v $(pwd):/tmp/project --entrypoint /bin/sh --platform linux/${{ matrix.arch }} node:20-bullseye -c "\
262+
docker run --rm -v $(pwd):/tmp/project --entrypoint /bin/sh --platform linux/${{ matrix.arch }} node:22-bullseye -c "\
264263
apt-get update && apt-get install -y libicu-dev && \
265264
cd /tmp/project && \
266265
npm install --ignore-scripts && \
@@ -283,7 +282,7 @@ jobs:
283282
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
284283
- uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3
285284
- run: |
286-
docker run --rm -v $(pwd):/tmp/project --entrypoint /bin/sh --platform linux/${{ matrix.arch }} node:20-bookworm -c "\
285+
docker run --rm -v $(pwd):/tmp/project --entrypoint /bin/sh --platform linux/${{ matrix.arch }} node:22-bookworm -c "\
287286
apt-get update && apt-get install -y libicu-dev && \
288287
cd /tmp/project && \
289288
npm install --ignore-scripts && \

.github/workflows/bump-version.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ jobs:
2828
fetch-depth: 0
2929
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
3030
with:
31-
node-version: 20
31+
node-version: 22
3232
- name: Configure user
3333
run: |
3434
git config --local user.name "${{ github.actor }}"

.github/workflows/update-sqlite.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ jobs:
2424
fetch-depth: 0
2525
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
2626
with:
27-
node-version: 20
27+
node-version: 22
2828
- name: Create new update branch
2929
run: git checkout -b sqlite-update-${{ env.ENV_VERSION }}
3030
- name: Update download script

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@ So far the changes are:
44

55
* Build the [bedrock](https://sqlite.org/src/timeline?r=bedrock) branch of SQLite to enable [`begin concurrent`](https://www.sqlite.org/src/doc/begin-concurrent/doc/begin_concurrent.md).
66
* Create a shell too, so we can debug db files created
7-
* Supports both Node.js (20.x, 22.x, 23.x, 24.x) and Bun (>=1.1.0) runtimes
7+
* Supports both Node.js (22.x, 23.x, 24.x, 25.x, 26.x) and Bun (>=1.1.0) runtimes
88

99
Other changes will be likely be made over time.

deps/icu.js

Lines changed: 25 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -8,24 +8,23 @@
88
// auto-registers Unicode-aware LIKE/upper()/lower()/REGEXP on every
99
// connection. That code calls into ICU.
1010
//
11-
// We prefer STATIC linking so the prebuilt .node binaries stay self-contained:
12-
// zero-cache ships them via prebuild-install onto runtime images (e.g. Alpine)
13-
// that do not have ICU installed, and a dynamic NEEDED libicu*.so.<ver> would
14-
// fail to load there. Static linking is only possible where the ICU archives
15-
// are -fPIC, which holds on macOS (Homebrew) and Alpine (musl). Debian/Ubuntu
16-
// (glibc) ship non-PIC static archives, so there we link ICU dynamically
17-
// against the system .so (the consumer must have ICU installed at runtime).
18-
// See `useStatic` below.
11+
// We STATIC-link ICU where we can, so the prebuilt .node stays self-contained.
12+
// That is only possible where the ICU static archives are position-independent
13+
// (-fPIC) and can be linked into a shared object (the .node) — which in practice
14+
// is ONLY macOS (Homebrew). Both Linux flavors ship NON-PIC static archives:
15+
// Debian/Ubuntu (glibc) AND Alpine (musl) both fail with "recompile with -fPIC".
16+
// So on every Linux we link ICU dynamically against the system .so, and the
17+
// consumer must have ICU installed at runtime (e.g. `apk add icu-libs` in the
18+
// zero-cache Alpine image). See `useStatic` below.
1919
//
2020
// Usage:
2121
// node icu.js include -> the ICU include directory (for #include <unicode/...>)
2222
// node icu.js libs -> newline-separated linker inputs (static archive
2323
// paths or -L/-l flags), then the C++ runtime /
2424
// system libraries ICU depends on.
2525
//
26-
// Discovery order: pkg-config (Linux/Alpine) -> Homebrew icu4c (macOS) ->
27-
// common system locations. Set ICU_ROOT to override (expects ICU_ROOT/lib and
28-
// ICU_ROOT/include).
26+
// Discovery order: pkg-config (Linux) -> Homebrew icu4c (macOS) -> common system
27+
// locations. Set ICU_ROOT to override (expects ICU_ROOT/lib and ICU_ROOT/include).
2928
//
3029
// ICU is not enabled on Windows (see deps/sqlite3.gyp), so this script only
3130
// ever runs on macOS and Linux.
@@ -36,20 +35,15 @@ const fs = require('fs');
3635
const path = require('path');
3736

3837
const isMac = process.platform === 'darwin';
39-
const isLinux = process.platform === 'linux';
40-
const isAlpine = isLinux && fs.existsSync('/etc/alpine-release');
41-
42-
// We static-link ICU only where the static archives are position-independent
43-
// (-fPIC) and can therefore be linked into a shared object (the .node):
44-
// * macOS — Homebrew's icu4c archives are PIC.
45-
// * Alpine — musl builds everything PIC, so icu-static is PIC.
46-
// Debian/Ubuntu (glibc) ship NON-PIC static archives (libicu*.a), which fail to
47-
// link into a shared object ("recompile with -fPIC"), so on glibc Linux we link
48-
// ICU dynamically against the distro .so instead — those consumers must have
49-
// ICU installed at runtime. ICU_ALLOW_DYNAMIC=1 forces dynamic everywhere as a
50-
// local-dev escape hatch.
51-
const useStatic =
52-
(isMac || isAlpine) && process.env.ICU_ALLOW_DYNAMIC !== '1';
38+
39+
// Static-link ICU only on macOS, the one platform whose static archives are
40+
// -fPIC and so can be linked into the .node shared object. Both Debian/Ubuntu
41+
// (glibc) and Alpine (musl) ship NON-PIC static archives (libicu*.a) that fail
42+
// with "relocation ... can not be used when making a shared object; recompile
43+
// with -fPIC", so on all Linux we link ICU dynamically against the system .so
44+
// and the consumer must have ICU at runtime. ICU_ALLOW_DYNAMIC=1 forces dynamic
45+
// even on macOS, as a local-dev escape hatch.
46+
const useStatic = isMac && process.env.ICU_ALLOW_DYNAMIC !== '1';
5347

5448
function run(cmd) {
5549
try {
@@ -125,15 +119,14 @@ function staticLibInputs(loc) {
125119
if (full && fs.existsSync(full)) {
126120
return full;
127121
}
128-
// On the static platforms (macOS, Alpine) a missing archive is fatal: we
129-
// must not silently produce a dynamically-linked binary, since zero-cache
130-
// ships these prebuilds onto images (e.g. Alpine) that have no ICU.
122+
// macOS is the only static platform; a missing archive is fatal here (we
123+
// must not silently fall back to a dynamic build that would change the
124+
// shipped binary's behavior).
131125
fail(
132126
`static ICU archive ${name}.a not found in ${loc.libDir || '(unknown library dir)'}.\n` +
133-
` This platform links ICU statically to stay self-contained, so the build is aborting\n` +
134-
` rather than linking ICU dynamically. Install the static ICU libraries (icu-dev +\n` +
135-
` icu-static on Alpine, icu4c via Homebrew on macOS), or set ICU_ALLOW_DYNAMIC=1 to\n` +
136-
` allow a dynamic fallback for local development.`,
127+
` macOS links ICU statically to stay self-contained, so the build is aborting rather\n` +
128+
` than linking ICU dynamically. Install icu4c via Homebrew (brew install icu4c), or\n` +
129+
` set ICU_ALLOW_DYNAMIC=1 to allow a dynamic fallback for local development.`,
137130
);
138131
return null; // unreachable; fail() exits
139132
});

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
"shell.js"
2121
],
2222
"engines": {
23-
"node": "20.x || 22.x || 23.x || 24.x || 25.x",
23+
"node": "22.x || 23.x || 24.x || 25.x || 26.x",
2424
"bun": ">=1.1.0"
2525
},
2626
"types": "lib/index.d.ts",

0 commit comments

Comments
 (0)