Skip to content

Commit cd6d64f

Browse files
committed
build: add Windows support
Ref: #17 PR-URL: #203
1 parent 8fbc64d commit cd6d64f

File tree

11 files changed

+218
-5
lines changed

11 files changed

+218
-5
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
*.dylib
22
*.so
33
*.dSYM
4+
*.dll
45
tools/
56
out/
67
lldb/

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,17 @@ version is LLDB 3.9 and above.
8181
ln -s /usr/local/bin/lldb39 /usr/bin/lldb
8282
```
8383

84+
- Windows
85+
- You can install a [binary distribution of LLVM](http://releases.llvm.org/download.html)
86+
directly or using [Chocolatey](https://chocolatey.org/install#installing-chocolatey):
87+
88+
```bat
89+
cinst -y visualstudio2017buildtools visualstudio2017-workload-vctools llvm git
90+
```
91+
92+
Visual Studio is required for MSBuild and headers when building llnode. Git
93+
is required to download the lldb headers.
94+
8495
### Install the Plugin
8596

8697
#### Install llnode globally via npm

binding.gyp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,19 @@
5050
},
5151
}],
5252
],
53-
}]
53+
}],
54+
[ "OS == 'win'", {
55+
"sources": [
56+
"<(lldb_include_dir)/../source/Host/common/GetOptInc.cpp",
57+
"windows/llnode.def",
58+
],
59+
"include_dirs": [
60+
"windows/include",
61+
],
62+
"libraries": [
63+
"<(lldb_lib_dir)/<(lldb_lib)",
64+
],
65+
}],
5466
]
5567
},
5668

scripts/cleanup.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,16 @@
22

33
const fs = require('fs');
44
const cwd = process.cwd();
5+
const osName = require('os').type();
56

6-
const libExt = require('os').type() === 'Darwin' ? 'dylib' : 'so';
7+
let libExt;
8+
if (osName === 'Darwin') {
9+
libExt = 'dylib';
10+
} else if (osName === 'Windows_NT') {
11+
libExt = 'dll';
12+
} else {
13+
libExt = 'so';
14+
}
715
const llnodeLib = `plugin.${libExt}`;
816
const destLib = `llnode.${libExt}`;
917

scripts/configure.js

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ function configureInstallation(osName, buildDir) {
4949
} else if (osName === 'FreeBSD') {
5050
installation = require('./freebsd').getLldbInstallation();
5151
config.variables['lldb_lib_dir%'] = installation.libDir;
52+
} else if (osName === 'Windows_NT') {
53+
installation = require('./windows').getLldbInstallation();
54+
config.variables['lldb_lib_dir%'] = installation.libDir;
55+
config.variables['lldb_lib%'] = installation.libName;
5256
} else {
5357
console.log(`Unsupported OS: ${osName}`);
5458
process.exit(1);
@@ -92,8 +96,15 @@ function writeConfig(config) {
9296
}
9397

9498
function scriptText(osName, lldbExe) {
95-
let lib = 'llnode.so';
96-
if (osName === 'Darwin') { lib = 'llnode.dylib'; }
99+
let lib;
100+
if (osName === 'Darwin') {
101+
lib = 'llnode.dylib';
102+
} else if (osName === 'Windows_NT') {
103+
lib = 'llnode.dll';
104+
lldbExe = lldbExe.replace(/\\/g, '\\\\');
105+
} else {
106+
lib = 'llnode.so';
107+
}
97108

98109
return `#!/usr/bin/env node
99110

scripts/install.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,38 @@
11
'use strict';
22

33
const child_process = require('child_process');
4+
const os = require('os');
5+
const fs = require('fs');
6+
const path = require('path');
7+
8+
const lldb = require('./lldb');
49

510
main();
611

712
function main() {
13+
const osName = os.type();
14+
if (osName === 'Windows_NT') {
15+
return mainWindows();
16+
}
17+
818
runFile('node-gyp', ['rebuild']);
919
}
1020

21+
function mainWindows() {
22+
const clangExe = 'clang-cl.exe';
23+
const clangDir = lldb.findWindowsExeDir(clangExe);
24+
25+
if (!clangDir) {
26+
console.log(`Could not find ${clangExe}`);
27+
process.exit(1);
28+
}
29+
console.log(`Using ${clangExe} found at ${clangDir}`);
30+
31+
runShell('node-gyp clean');
32+
runShell('node-gyp configure');
33+
runShell(`node-gyp build /p:CLToolExe="${clangExe}" /p:CLToolPath="${clangDir}"`);
34+
}
35+
1136
/**
1237
* execFileSync wrapper, exits on error.
1338
* @param {string} file Executable to run

scripts/lldb.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,55 @@ function tryExecutables(exeNames) {
8282
}
8383
}
8484

85+
/**
86+
* Find a directory containing a LLVM executable in Windows.
87+
* The search happens in the following order:
88+
* - the directory specified by the user using npm --lldb_exe=...
89+
* - using 'where' to find the executable in the PATH
90+
* - the default LLVM location in Program Files
91+
* Returns undefined if the executable was not found.
92+
* @param {string} exeName
93+
* @returns {string|undefined}
94+
*/
95+
function findWindowsExeDir(exeName) {
96+
// Look for exeName at the location of lldb_exe
97+
if (process.env.npm_config_lldb_exe !== undefined) {
98+
const exeDir = path.dirname(process.env.npm_config_lldb_exe);
99+
if (fs.existsSync(path.join(exeDir, exeName))) {
100+
return exeDir;
101+
}
102+
console.log(`Could not find ${exeName} in the directory of lldb_exe`);
103+
}
104+
105+
// Look for exeName in the PATH
106+
let exePath;
107+
try {
108+
exePath = child_process.execSync(
109+
`where ${exeName}`,
110+
{ stdio: 'pipe' } // to suppress stderr
111+
).toString().trim().split(/[\r\n]+/g)[0].trim();
112+
} catch (err) { /* Do nothing. */ }
113+
// Ensure the string returned by 'where' is not an error
114+
if (exePath && fs.existsSync(exePath)) {
115+
return path.dirname(exePath);
116+
}
117+
console.log(`Could not find ${exeName} in the PATH`);
118+
119+
// Look for exeName in Program Files
120+
if (process.env['ProgramFiles']) {
121+
const exeDir = path.join(process.env['ProgramFiles'], 'LLVM', 'bin');
122+
if (fs.existsSync(path.join(exeDir, exeName))) {
123+
return exeDir;
124+
}
125+
}
126+
if (process.env['ProgramFiles(x86)']) {
127+
const exeDir = path.join(process.env['ProgramFiles(x86)'], 'LLVM', 'bin');
128+
if (fs.existsSync(path.join(exeDir, exeName))) {
129+
return exeDir;
130+
}
131+
}
132+
}
133+
85134
/**
86135
* Get the lldb version from the lldb executable, exit the process with 1
87136
* if failed.
@@ -112,5 +161,6 @@ module.exports = {
112161
getLibPath,
113162
cloneHeaders,
114163
tryExecutables,
164+
findWindowsExeDir,
115165
getLldbVersion
116166
};

scripts/windows.js

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
'use strict';
2+
3+
const child_process = require('child_process');
4+
const fs = require('fs');
5+
const path = require('path');
6+
const lldb = require('./lldb');
7+
8+
/**
9+
* Find the lldb to use in the installation of LLVM.
10+
* @returns {string} Path to the lldb executable or undefined
11+
*/
12+
function getLldbExecutable() {
13+
if (process.env.npm_config_lldb_exe !== undefined) {
14+
return process.env.npm_config_lldb_exe;
15+
}
16+
17+
const lldbDir = lldb.findWindowsExeDir('lldb.exe');
18+
if (lldbDir) {
19+
return path.join(lldbDir, 'lldb.exe');
20+
}
21+
}
22+
23+
/**
24+
* Get the directory containing the lldb library.
25+
* @param {string} lldbExe Path to the corresponding lldb executable
26+
* @returns {{dir:string, name:string}}
27+
*/
28+
function getLib(lldbExe) {
29+
const libName = 'liblldb.lib';
30+
const libDir = path.resolve(path.dirname(lldbExe), '..', 'lib');
31+
32+
if (!fs.existsSync(path.join(libDir, libName))) {
33+
return {
34+
dir: undefined,
35+
name: libName
36+
};
37+
}
38+
39+
return {
40+
dir: libDir,
41+
name: libName
42+
};
43+
}
44+
45+
/**
46+
* Get the lldb installation. If prefix is undefined, the headers need to
47+
* be downloaded.
48+
* The version string will be in the form like '3.9'
49+
*/
50+
function getLldbInstallation() {
51+
console.log('Looking for lldb executable...');
52+
const lldbExe = getLldbExecutable();
53+
if (!lldbExe) {
54+
console.log('Could not find any usable lldb executable');
55+
console.log('Please see the README.md on how to install lldb');
56+
process.exit(1);
57+
}
58+
console.log(`Found lldb executable ${lldbExe}`);
59+
60+
console.log('\nReading lldb version...');
61+
const lldbVersion = lldb.getLldbVersion(lldbExe);
62+
if (!lldbVersion) {
63+
console.log(`Unable to get the version from the lldb binary ${lldbExe}`);
64+
process.exit(1);
65+
}
66+
console.log(`Installing llnode for ${lldbExe}, lldb version ${lldbVersion}`);
67+
68+
console.log(`\nLooking for shared libraries for lldb ${lldbVersion}...`);
69+
const lib = getLib(lldbExe);
70+
if (!lib.dir) {
71+
console.log(`Could not find ${lib.name} in the same path as the lldb executable`);
72+
process.exit(1);
73+
} else {
74+
console.log(`Found ${lib.name} in ${lib.dir}`);
75+
}
76+
77+
return {
78+
executable: lldbExe,
79+
version: lldbVersion,
80+
includeDir: undefined, // Windows binary distribution does not contain headers
81+
libDir: lib.dir,
82+
libName: lib.name
83+
};
84+
}
85+
86+
module.exports = {
87+
getLldbInstallation
88+
};

test/common.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ const debug = exports.debug =
2525
let pluginName;
2626
if (process.platform === 'darwin')
2727
pluginName = 'llnode.dylib';
28-
else if (process.platform === 'windows')
28+
else if (process.platform === 'win32')
2929
pluginName = 'llnode.dll';
3030
else
3131
pluginName = 'llnode.so';

windows/include/getopt.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
#include <lldb/Host/common/GetOptInc.h>

0 commit comments

Comments
 (0)