-
-
Notifications
You must be signed in to change notification settings - Fork 101
Expand file tree
/
Copy pathlldb.js
More file actions
241 lines (220 loc) · 7.59 KB
/
lldb.js
File metadata and controls
241 lines (220 loc) · 7.59 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
'use strict';
const child_process = require('child_process');
const path = require('path');
const os = require('os');
const fs = require('fs');
/**
* @param {string} version lldb version, either in the form '3.9' or '39'
* @returns {string} Branch of the corresponding lldb release
*/
function versionToBranch(version) {
// `llvm-project` v1-3 branches are in the form `release/major.minor.x`:
// release/1.0.x ... release/3.9.x
// `llvm-project` v4-latest branches are in the form `release/major.x`:
// release/4.x ... release/12.x
// split into array of semver components
var chars = (version.indexOf('.') === -1
? version.split('') // split `39` into ['3', '9']
: version.split('.').filter(x => x !== '.') // split `3.9` into ['3', '9']
);
// if version < 4, keep `major.minor` components
// if version >= 4, only keep `major` component
chars = chars.slice(0, (+chars[0] >= 4) ? 1 : 2);
// join components into the form `release/3.9.x`
const releaseBranch = 'release/' + chars.concat('x').join('.');
// Use the release branch if it exists in the remote
if (0 === child_process.spawnSync('git', [
'ls-remote',
'--exit-code',
'--heads',
'https://github.com/llvm/llvm-project.git',
'refs/heads/' + releaseBranch
]).status) {
return releaseBranch;
}
const devTag = 'llvmorg-' + chars.join('.') + '-init';
// Use the main branch if the LLVM version is the current "dev" release
if (0 === child_process.spawnSync('git', [
'ls-remote',
'--exit-code',
'--tags',
'https://github.com/llvm/llvm-project.git',
'refs/tags/' + devTag
]).status) {
return 'main';
}
throw new Error('Unknown LLVM version: ' + version);
}
/**
* @param {string} includeDir Path to the equivalent of llvm-config --includedir
* @returns {string} Path to the lldb API headers
*/
function getApiHeadersPath(includeDir) {
return path.join(includeDir, 'lldb', 'API');
}
/**
* @param {string} includeDir Path to the equivalent of llvm-config --libddir
* @returns {string} Path to the lldb shared library
*/
function getLibPath(libDir) {
const lib = os.type() === 'Darwin' ? 'liblldb.dylib' : 'liblldb.so';
return path.join(libDir, lib);
}
/**
* Check if the installed version of git supports sparse checkout. Notably,
* Ubuntu 18.04 (which is not EOL at time of writing) has git 2.17, which does
* not support sparse-checkout.
*/
function doesGitSupportSparseCheckout() {
const returnCode = child_process.spawnSync('git', ['sparse-checkout', '--help']).status;
return returnCode === 0;
}
/**
* Check out source code of the lldb for headers
* @param {string} lldbVersion Version of lldb, either like 3.9 or 39
* @param {string} buildDir Path to the llnode module directory
* @returns {string} The include directory in the downloaded lldb source code
*/
function cloneHeaders(lldbVersion, buildDir) {
const lldbHeadersBranch = versionToBranch(lldbVersion);
const lldbInstallDir = path.resolve(buildDir, `lldb-${lldbVersion}`);
if (!fs.existsSync(lldbInstallDir)) {
console.log(`\nCloning lldb ${lldbHeadersBranch} into ${lldbInstallDir}`);
if (doesGitSupportSparseCheckout()) {
// use `git clone --filter` in git v2.19 to only download `lldb` dir of `llvm-project` monorepo
// see: https://stackoverflow.com/a/52269934/3117331
// see: https://github.blog/2020-01-17-bring-your-monorepo-down-to-size-with-sparse-checkout/
child_process.execFileSync(
'git', ['clone',
'--depth', '1',
'--filter=blob:none',
'--sparse',
'--branch', lldbHeadersBranch,
'https://github.com/llvm/llvm-project.git',
lldbInstallDir
],
{ stdio: 'inherit' }); // show progress
child_process.execFileSync(
'git', [
'-C', lldbInstallDir,
'sparse-checkout',
'set', 'lldb'
],
{ stdio: 'inherit' }); // show progress
} else {
// There's no sparse checkout support, so we need to clone the entire
// repo.
child_process.execFileSync(
'git', ['clone',
'--depth', '1',
'--branch', lldbHeadersBranch,
'https://github.com/llvm/llvm-project.git',
lldbInstallDir
],
{ stdio: 'inherit' }); // show progress
}
} else {
console.log(`\nSkip cloning lldb headers because ${lldbInstallDir} exists`);
}
return path.join(lldbInstallDir, 'lldb', 'include');
}
/**
* Try to find the first valid executable out of an array executable names.
* Returns undefined if none of the provided executables is valid, otherwise
* returns the path to the first found valid executable.
* @param {string[]} exeNames
* @returns {string|undefined}
*/
function tryExecutables(exeNames) {
for (let name of exeNames) {
let exePath;
try {
exePath = child_process.execFileSync(
'which', [name], { stdio: 'pipe' } // to suppress stderr
).toString().trim();
} catch (err) {
// Do nothing - we expect not to find some of these.
}
// If the result starts with '/' `which` found a path.
if (exePath && exePath.startsWith('/')) {
return exePath;
}
}
}
/**
* Find a directory containing a LLVM executable in Windows.
* The search happens in the following order:
* - the directory specified by the user using npm --lldb_exe=...
* - using 'where' to find the executable in the PATH
* - the default LLVM location in Program Files
* Returns undefined if the executable was not found.
* @param {string} exeName
* @returns {string|undefined}
*/
function findWindowsExeDir(exeName) {
// Look for exeName at the location of lldb_exe
if (process.env.npm_config_lldb_exe !== undefined) {
const exeDir = path.dirname(process.env.npm_config_lldb_exe);
if (fs.existsSync(path.join(exeDir, exeName))) {
return exeDir;
}
console.log(`Could not find ${exeName} in the directory of lldb_exe`);
}
// Look for exeName in the PATH
let exePath;
try {
exePath = child_process.execSync(
`where ${exeName}`,
{ stdio: 'pipe' } // to suppress stderr
).toString().trim().split(/[\r\n]+/g)[0].trim();
} catch (err) { /* Do nothing. */ }
// Ensure the string returned by 'where' is not an error
if (exePath && fs.existsSync(exePath)) {
return path.dirname(exePath);
}
console.log(`Could not find ${exeName} in the PATH`);
// Look for exeName in Program Files
if (process.env['ProgramFiles']) {
const exeDir = path.join(process.env['ProgramFiles'], 'LLVM', 'bin');
if (fs.existsSync(path.join(exeDir, exeName))) {
return exeDir;
}
}
if (process.env['ProgramFiles(x86)']) {
const exeDir = path.join(process.env['ProgramFiles(x86)'], 'LLVM', 'bin');
if (fs.existsSync(path.join(exeDir, exeName))) {
return exeDir;
}
}
}
/**
* Get the lldb version from the lldb executable, exit the process with 1
* if failed.
* @param {string} lldbExe
* @returns {string} Version of the executable in the form like '3.9'
*/
function getLldbVersion(lldbExe) {
let lldbStr;
try {
lldbStr = child_process.execFileSync(lldbExe, ['-v']).toString();
} catch (err) {
console.log(err);
return undefined;
}
// Ignore minor revisions like 3.8.1
const versionMatch = lldbStr.match(/version (\d+.\d+)/);
if (versionMatch) {
return versionMatch[1];
}
console.log(`Output from \`${lldbExe} -v\` was ${lldbStr}`);
return undefined;
}
module.exports = {
versionToBranch,
getApiHeadersPath,
getLibPath,
cloneHeaders,
tryExecutables,
findWindowsExeDir,
getLldbVersion
};