Skip to content

Commit 06c86b0

Browse files
committed
feat: gnu support
1 parent a56ffc2 commit 06c86b0

4 files changed

Lines changed: 187 additions & 23 deletions

File tree

.github/workflows/build.yml

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
name: Build Addons
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
pull_request:
8+
9+
jobs:
10+
linux-gnu:
11+
runs-on: ubuntu-latest
12+
steps:
13+
- name: Checkout
14+
uses: actions/checkout@v4
15+
16+
- name: Setup Node.js
17+
uses: actions/setup-node@v4
18+
with:
19+
node-version: 20
20+
21+
- name: Install system dependencies
22+
run: |
23+
sudo apt-get update
24+
sudo apt-get install -y build-essential cmake ninja-build python3 unzip curl
25+
26+
- name: Install npm dependencies
27+
run: npm ci
28+
29+
- name: Build RocketMQ native deps
30+
run: ./deps/rocketmq/build.sh
31+
32+
- name: Build addon (glibc)
33+
run: npx cmake-js compile --CDCMAKE_BUILD_TYPE=Release
34+
35+
- name: Package artifact
36+
run: |
37+
mkdir -p Release
38+
cp build/rocketmq.node Release/linux-x86_64-gnu-rocketmq.node
39+
40+
- name: Upload artifact
41+
uses: actions/upload-artifact@v4
42+
with:
43+
name: linux-x86_64-gnu
44+
path: Release/linux-x86_64-gnu-rocketmq.node
45+
46+
linux-musl:
47+
runs-on: ubuntu-latest
48+
container:
49+
image: node:20-alpine
50+
steps:
51+
- name: Checkout
52+
uses: actions/checkout@v4
53+
54+
- name: Install system dependencies
55+
run: |
56+
apk add --no-cache bash build-base cmake ninja python3 curl unzip git
57+
58+
- name: Install npm dependencies
59+
run: npm ci
60+
61+
- name: Build RocketMQ native deps
62+
run: ./deps/rocketmq/build.sh
63+
64+
- name: Build addon (musl)
65+
run: npx cmake-js compile --CDCMAKE_BUILD_TYPE=Release
66+
67+
- name: Package artifact
68+
run: |
69+
mkdir -p Release
70+
cp build/rocketmq.node Release/linux-x86_64-musl-rocketmq.node
71+
72+
- name: Upload artifact
73+
uses: actions/upload-artifact@v4
74+
with:
75+
name: linux-x86_64-musl
76+
path: Release/linux-x86_64-musl-rocketmq.node
77+
78+
macos-universal:
79+
runs-on: macos-latest
80+
steps:
81+
- name: Checkout
82+
uses: actions/checkout@v4
83+
84+
- name: Setup Node.js
85+
uses: actions/setup-node@v4
86+
with:
87+
node-version: 20
88+
89+
- name: Install build tools
90+
run: brew install cmake ninja || true
91+
92+
- name: Install npm dependencies
93+
run: npm ci
94+
95+
- name: Build universal addon
96+
run: ./build.sh build Darwin
97+
98+
- name: Upload artifact
99+
uses: actions/upload-artifact@v4
100+
with:
101+
name: darwin-universal
102+
path: Release/darwin-universal-rocketmq.node

CMakeLists.txt

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ add_definitions(-DNAPI_VERSION=9)
66
add_definitions(-DNODE_RUNTIME=node)
77
add_definitions(-DBUILDING_NODE_EXTENSION)
88

9+
option(ROCKETMQ_FORCE_STATIC_MUSL "Link against a provided static musl libc archive" OFF)
10+
911
set(NPX_CMD npx)
1012

1113
execute_process(
@@ -27,24 +29,29 @@ set_target_properties(${PROJECT_NAME}
2729
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/build"
2830
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/build"
2931
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/build")
30-
target_link_libraries(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/deps/rocketmq/bin/librocketmq.a)
32+
find_package(Threads REQUIRED)
33+
target_link_libraries(${PROJECT_NAME}
34+
${CMAKE_CURRENT_SOURCE_DIR}/deps/rocketmq/bin/librocketmq.a
35+
Threads::Threads)
3136

3237
set(CMAKE_DEPENDS_USE_COMPILER FALSE)
3338
set(CMAKE_SKIP_DEPENDENCY_TRACKING TRUE)
3439

3540
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
36-
set(MUSL_LIBC_PATH $ENV{MUSL_LIBC_PATH})
37-
if(NOT MUSL_LIBC_PATH)
38-
message(FATAL_ERROR "MUSL_LIBC_PATH environment variable is not set")
39-
endif()
40-
if(NOT EXISTS "${MUSL_LIBC_PATH}")
41-
message(FATAL_ERROR "musl libc archive not found at ${MUSL_LIBC_PATH}")
42-
endif()
43-
target_link_libraries(${PROJECT_NAME} ${MUSL_LIBC_PATH})
4441
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -std=c++11")
45-
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libgcc -static-libstdc++ -static")
46-
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fPIC")
47-
set_target_properties(${PROJECT_NAME} PROPERTIES LINK_DEPENDS_NO_SHARED TRUE)
42+
if(ROCKETMQ_FORCE_STATIC_MUSL)
43+
set(MUSL_LIBC_PATH $ENV{MUSL_LIBC_PATH})
44+
if(NOT MUSL_LIBC_PATH)
45+
message(FATAL_ERROR "ROCKETMQ_FORCE_STATIC_MUSL=ON but MUSL_LIBC_PATH is not set")
46+
endif()
47+
if(NOT EXISTS "${MUSL_LIBC_PATH}")
48+
message(FATAL_ERROR "musl libc archive not found at ${MUSL_LIBC_PATH}")
49+
endif()
50+
target_link_libraries(${PROJECT_NAME} ${MUSL_LIBC_PATH})
51+
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libgcc -static-libstdc++ -static")
52+
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fPIC")
53+
set_target_properties(${PROJECT_NAME} PROPERTIES LINK_DEPENDS_NO_SHARED TRUE)
54+
endif()
4855
else()
4956
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -std=c++11 -stdlib=libc++")
5057
endif()

build.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,7 @@ build_for_linux() {
286286
--CDCMAKE_SYSTEM_NAME="${CMAKE_SYSTEM_NAME}" \
287287
--CDCMAKE_SYSTEM_PROCESSOR="${CMAKE_SYSTEM_PROCESSOR}" \
288288
--CDCMAKE_SHARED_LINKER_FLAGS="-fPIC" \
289+
--CDROCKETMQ_FORCE_STATIC_MUSL=ON \
289290
--CDCMAKE_SKIP_DEPENDENCY_TRACKING=ON \
290291
--verbose
291292
}

lib/binding.js

Lines changed: 65 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,61 @@
11
"use strict";
22

3+
const fs = require('fs');
34
const os = require('os');
5+
const childProcess = require('child_process');
6+
7+
function normalizeArch(arch) {
8+
if (arch === 'x64') return 'x86_64';
9+
if (arch === 'arm64') return 'aarch64';
10+
return arch;
11+
}
12+
13+
function detectLibc() {
14+
if (process.report && typeof process.report.getReport === 'function') {
15+
try {
16+
const report = process.report.getReport();
17+
if (report?.header?.glibcVersionRuntime) {
18+
return 'gnu';
19+
}
20+
} catch (_) {
21+
// ignore and continue with fallbacks
22+
}
23+
}
24+
25+
try {
26+
const output = childProcess.execSync('ldd --version', { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] });
27+
if (/musl/i.test(output)) {
28+
return 'musl';
29+
}
30+
if (/glibc|gnu libc/i.test(output)) {
31+
return 'gnu';
32+
}
33+
} catch (_) {
34+
// ignore failures, fall back to fs detection
35+
}
36+
37+
if (
38+
fs.existsSync('/lib/ld-musl-x86_64.so.1') ||
39+
fs.existsSync('/lib/ld-musl-aarch64.so.1') ||
40+
fs.existsSync('/lib/ld-musl.so.1')
41+
) {
42+
return 'musl';
43+
}
44+
45+
return 'gnu';
46+
}
447

548
function getPlatform() {
649
const platform = os.platform();
750
const arch = os.arch();
851

952
if (platform === 'linux') {
10-
if (arch === 'arm64') {
11-
return 'linux-aarch64';
12-
}
13-
if (arch === 'x64') {
14-
return 'linux-x86_64';
53+
const normalizedArch = normalizeArch(arch);
54+
if (!normalizedArch) {
55+
throw new Error(`Unsupported architecture for Linux: ${arch}`);
1556
}
57+
const libc = detectLibc();
58+
return `linux-${normalizedArch}-${libc}`;
1659
}
1760

1861
if (platform === 'darwin') {
@@ -22,14 +65,25 @@ function getPlatform() {
2265
throw new Error(`Unsupported platform: ${platform}, architecture: ${arch}`);
2366
}
2467

25-
function binding() {
68+
function loadBinding() {
2669
const platform = getPlatform();
70+
const candidates = new Set([`${platform}-rocketmq.node`]);
2771

28-
try {
29-
return require('bindings')(`${platform}-rocketmq.node`)
30-
} catch (err) {
31-
throw new Error(`Failed to load RocketMQ addon: ${err.message}`);
72+
if (platform.startsWith('linux-')) {
73+
const legacy = platform.replace(/-(gnu|musl)$/, '');
74+
candidates.add(`${legacy}-rocketmq.node`);
3275
}
76+
77+
let lastError;
78+
for (const candidate of candidates) {
79+
try {
80+
return require('bindings')(candidate);
81+
} catch (err) {
82+
lastError = err;
83+
}
84+
}
85+
86+
throw new Error(`Failed to load RocketMQ addon (${platform}): ${lastError?.message || 'unknown error'}`);
3387
}
3488

35-
module.exports = binding();
89+
module.exports = loadBinding();

0 commit comments

Comments
 (0)