Skip to content

Commit 1153b46

Browse files
committed
perf(detect-libc): faster musl check by looking for ldd file
1 parent 43e5a43 commit 1153b46

File tree

5 files changed

+534
-15
lines changed

5 files changed

+534
-15
lines changed

lib/cache.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/**
2+
* @type {Record<string, string | undefined | null>}
3+
*/
4+
const cacheObject = {};
5+
6+
module.exports = {
7+
cacheFn: (key, fn, async = false) => {
8+
return () => {
9+
const cachedValue = cacheObject[key];
10+
11+
if (cachedValue !== undefined) {
12+
if (async) {
13+
return Promise.resolve(cachedValue);
14+
}
15+
16+
return cachedValue;
17+
}
18+
19+
const result = fn();
20+
21+
if (async) {
22+
return result.then(value => {
23+
cacheObject[key] = value;
24+
25+
return value;
26+
});
27+
}
28+
29+
cacheObject[key] = result;
30+
31+
return result;
32+
};
33+
}
34+
};

lib/detect-libc.js

Lines changed: 91 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
55

66
const childProcess = require('child_process');
77
const { isLinux, getReport } = require('./process');
8+
const { LDD_PATH, readFile, readFileSync } = require('./filesystem');
9+
10+
let cachedFamilyFilesystem;
11+
let cachedVersionFilesystem;
812

913
const command = 'getconf GNU_LIBC_VERSION 2>&1 || true; ldd --version 2>&1 || true';
1014
let commandOut = '';
@@ -39,6 +43,13 @@ const safeCommandSync = () => {
3943
*/
4044
const GLIBC = 'glibc';
4145

46+
/**
47+
* A Regexp constant to get the GLIBC Version.
48+
* @type {string}
49+
* @public
50+
*/
51+
const RE_GLIBC_VERSION = /GLIBC\s(\d+\.\d+)/;
52+
4253
/**
4354
* A String constant containing the value `musl`.
4455
* @type {string}
@@ -72,14 +83,49 @@ const familyFromCommand = (out) => {
7283
return null;
7384
};
7485

86+
const familyFromFilesystem = async () => {
87+
if (cachedFamilyFilesystem !== undefined) {
88+
return cachedFamilyFilesystem;
89+
}
90+
cachedFamilyFilesystem = null;
91+
try {
92+
const lddContent = await readFile(LDD_PATH);
93+
if (lddContent.includes('musl')) {
94+
cachedFamilyFilesystem = MUSL;
95+
} else if (lddContent.includes('GLIBC')) {
96+
cachedFamilyFilesystem = GLIBC;
97+
}
98+
} catch (e) {}
99+
return cachedFamilyFilesystem;
100+
};
101+
102+
const familyFromFilesystemSync = () => {
103+
if (cachedFamilyFilesystem !== undefined) {
104+
return cachedFamilyFilesystem;
105+
}
106+
cachedFamilyFilesystem = null;
107+
try {
108+
const lddContent = readFileSync(LDD_PATH);
109+
if (lddContent.includes('musl')) {
110+
cachedFamilyFilesystem = MUSL;
111+
} else if (lddContent.includes('GLIBC')) {
112+
cachedFamilyFilesystem = GLIBC;
113+
}
114+
} catch (e) {}
115+
return cachedFamilyFilesystem;
116+
};
117+
75118
/**
76119
* Resolves with the libc family when it can be determined, `null` otherwise.
77120
* @returns {Promise<?string>}
78121
*/
79122
const family = async () => {
80123
let family = null;
81124
if (isLinux()) {
82-
family = familyFromReport();
125+
family = await familyFromFilesystem();
126+
if (!family) {
127+
family = familyFromReport();
128+
}
83129
if (!family) {
84130
const out = await safeCommand();
85131
family = familyFromCommand(out);
@@ -95,7 +141,10 @@ const family = async () => {
95141
const familySync = () => {
96142
let family = null;
97143
if (isLinux()) {
98-
family = familyFromReport();
144+
family = familyFromFilesystemSync();
145+
if (!family) {
146+
family = familyFromReport();
147+
}
99148
if (!family) {
100149
const out = safeCommandSync();
101150
family = familyFromCommand(out);
@@ -116,6 +165,38 @@ const isNonGlibcLinux = async () => isLinux() && await family() !== GLIBC;
116165
*/
117166
const isNonGlibcLinuxSync = () => isLinux() && familySync() !== GLIBC;
118167

168+
const versionFromFilesystem = async () => {
169+
if (cachedVersionFilesystem !== undefined) {
170+
return cachedVersionFilesystem;
171+
}
172+
cachedVersionFilesystem = null;
173+
try {
174+
const lddContent = await readFile(LDD_PATH);
175+
const versionMatch = lddContent.match(RE_GLIBC_VERSION);
176+
177+
if (versionMatch) {
178+
cachedVersionFilesystem = versionMatch[1];
179+
}
180+
} catch (e) {}
181+
return cachedVersionFilesystem;
182+
};
183+
184+
const versionFromFilesystemSync = () => {
185+
if (cachedVersionFilesystem !== undefined) {
186+
return cachedVersionFilesystem;
187+
}
188+
cachedVersionFilesystem = null;
189+
try {
190+
const lddContent = readFileSync(LDD_PATH);
191+
const versionMatch = lddContent.match(RE_GLIBC_VERSION);
192+
193+
if (versionMatch) {
194+
cachedVersionFilesystem = versionMatch[1];
195+
}
196+
} catch (e) {}
197+
return cachedVersionFilesystem;
198+
};
199+
119200
const versionFromReport = () => {
120201
const report = getReport();
121202
if (report.header && report.header.glibcVersionRuntime) {
@@ -144,7 +225,10 @@ const versionFromCommand = (out) => {
144225
const version = async () => {
145226
let version = null;
146227
if (isLinux()) {
147-
version = versionFromReport();
228+
version = await versionFromFilesystem();
229+
if (!version) {
230+
version = versionFromReport();
231+
}
148232
if (!version) {
149233
const out = await safeCommand();
150234
version = versionFromCommand(out);
@@ -160,7 +244,10 @@ const version = async () => {
160244
const versionSync = () => {
161245
let version = null;
162246
if (isLinux()) {
163-
version = versionFromReport();
247+
version = versionFromFilesystemSync();
248+
if (!version) {
249+
version = versionFromReport();
250+
}
164251
if (!version) {
165252
const out = safeCommandSync();
166253
version = versionFromCommand(out);

lib/filesystem.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
const fs = require('fs');
2+
3+
/**
4+
* The path where we can find the ldd
5+
*/
6+
const LDD_PATH = '/usr/bin/ldd';
7+
8+
/**
9+
* Read the content of a file synchronous
10+
*
11+
* @param {string} path
12+
* @returns {string}
13+
*/
14+
const readFileSync = path => fs.readFileSync(path, 'utf-8');
15+
16+
/**
17+
* Read the content of a file
18+
*
19+
* @param {string} path
20+
* @returns {Promise<string>}
21+
*/
22+
const readFile = path => new Promise((resolve, reject) => {
23+
fs.readFile(path, 'utf-8', (err, data) => {
24+
if (err) {
25+
reject(err);
26+
} else {
27+
resolve(data);
28+
}
29+
});
30+
});
31+
32+
module.exports = {
33+
LDD_PATH,
34+
readFileSync,
35+
readFile
36+
};

test/fixtexture-file.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
1

0 commit comments

Comments
 (0)