Skip to content

Commit b76dc33

Browse files
committed
feat: use build manifest to load compatible binaries
This adds a `manifest.json` for the built binaries under built that allows the package to load the correct addon path during runtime. This PR also adds the libc/abi as information in the key, so it will avoid the conflicts between Musl/Glib, and loading will be faster since the compatible binaries are found deterministically.
1 parent b8058fd commit b76dc33

File tree

6 files changed

+119
-26
lines changed

6 files changed

+119
-26
lines changed

.github/workflows/CI.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,8 +147,7 @@ jobs:
147147
- name: Build Native Windows 32
148148
if: ${{ matrix.os == 'windows-2019' && matrix.node_arch == 'ia32' }}
149149
run:
150-
node ./node_modules/cmake-ts/build/main.js named-configs
151-
windows-x86
150+
node ./node_modules/cmake-ts/build/main.js named-configs windows-x86
152151

153152
- name: Use Node 20
154153
if: ${{ matrix.native }}

lib/load-addon.js

Lines changed: 40 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/load-addon.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
},
2020
"homepage": "http://zeromq.github.io/zeromq.js/",
2121
"dependencies": {
22-
"cmake-ts": "^0.5.3",
22+
"cmake-ts": "^0.6.0",
2323
"node-addon-api": "^8.3.0"
2424
},
2525
"devDependencies": {

pnpm-lock.yaml

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/load-addon.ts

Lines changed: 71 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,43 @@ function devWarn(message: string) {
1616
function findAddon(): any | undefined {
1717
let addon: undefined | any = undefined
1818
try {
19-
const addonParentDir = path.resolve(
20-
path.join(
21-
__dirname,
22-
"..",
23-
"build",
24-
process.platform,
25-
process.arch,
26-
"node",
27-
),
28-
)
29-
const addOnAbiDirs = fs.readdirSync(addonParentDir).sort((a, b) => {
19+
const buildDir = path.resolve(__dirname, "..", "build")
20+
21+
const manifest = JSON.parse(
22+
fs.readFileSync(path.resolve(buildDir, "manifest.json"), "utf-8"),
23+
) as Record<string, string>
24+
25+
// compatible addons (abi -> addon path)
26+
const compatibleAddons: Record<string, string> = {}
27+
28+
const configs = Object.keys(manifest)
29+
for (const configStr of configs) {
30+
const config = JSON.parse(configStr) as BuildConfiguration
31+
32+
// check if the config is compatible with the current runtime
33+
if (config.os !== process.platform || config.arch !== process.arch) {
34+
continue
35+
}
36+
const libc = detectLibc()
37+
if (config.libc !== libc) {
38+
continue
39+
}
40+
41+
const addonRelativePath = manifest[configStr]
42+
compatibleAddons[config.abi ?? 0] = path.resolve(
43+
buildDir,
44+
addonRelativePath,
45+
)
46+
}
47+
48+
// sort the compatible abis in descending order
49+
const compatibleAbis = Object.keys(compatibleAddons).sort((a, b) => {
3050
return Number.parseInt(b, 10) - Number.parseInt(a, 10)
3151
})
3252

3353
// try each available addon ABI
34-
for (const addOnAbiDir of addOnAbiDirs) {
35-
const addonPath = path.join(addonParentDir, addOnAbiDir, "addon.node")
54+
for (const abi of compatibleAbis) {
55+
const addonPath = compatibleAddons[abi]
3656
try {
3757
addon = require(addonPath)
3858
break
@@ -57,5 +77,43 @@ function findAddon(): any | undefined {
5777
return addon
5878
}
5979

80+
/**
81+
* Build configuration (from cmake-ts)
82+
*/
83+
type BuildConfiguration = {
84+
name: string
85+
dev: boolean
86+
os: typeof process.platform
87+
arch: typeof process.arch
88+
runtime: string
89+
runtimeVersion: string
90+
toolchainFile: string | null
91+
CMakeOptions?: {name: string; value: string}[]
92+
addonSubdirectory: string
93+
// list of additional definitions to fixup node quirks for some specific versions
94+
additionalDefines: string[]
95+
/** The ABI number that is used by the runtime. */
96+
abi?: number
97+
/** The libc that is used by the runtime. */
98+
libc?: string
99+
}
100+
101+
/**
102+
* Detect the libc used by the runtime (from cmake-ts)
103+
*/
104+
function detectLibc() {
105+
if (process.platform === "linux") {
106+
if (fs.existsSync("/etc/alpine-release")) {
107+
return "musl"
108+
}
109+
return "glibc"
110+
} else if (process.platform === "darwin") {
111+
return "libc"
112+
} else if (process.platform === "win32") {
113+
return "msvc"
114+
}
115+
return "unknown"
116+
}
117+
60118
const addon = findAddon()
61119
export default addon

0 commit comments

Comments
 (0)