Skip to content

Commit

Permalink
build: add Windows support
Browse files Browse the repository at this point in the history
Ref: nodejs#17
PR-URL: nodejs#203
  • Loading branch information
joaocgreis authored and joyeecheung committed Jun 25, 2018
1 parent 5e61383 commit 8045d77
Show file tree
Hide file tree
Showing 11 changed files with 218 additions and 5 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
*.dylib
*.so
*.dSYM
*.dll
tools/
out/
lldb/
Expand Down
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,17 @@ version is LLDB 3.9 and above.
ln -s /usr/local/bin/lldb39 /usr/bin/lldb
```

- Windows
- You can install a [binary distribution of LLVM](http://releases.llvm.org/download.html)
directly or using [Chocolatey](https://chocolatey.org/install#installing-chocolatey):

```bat
cinst -y visualstudio2017buildtools visualstudio2017-workload-vctools llvm git
```

Visual Studio is required for MSBuild and headers when building llnode. Git
is required to download the lldb headers.

### Install the Plugin

#### Install llnode globally via npm
Expand Down
14 changes: 13 additions & 1 deletion binding.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,19 @@
},
}],
],
}]
}],
[ "OS == 'win'", {
"sources": [
"<(lldb_include_dir)/../source/Host/common/GetOptInc.cpp",
"windows/llnode.def",
],
"include_dirs": [
"windows/include",
],
"libraries": [
"<(lldb_lib_dir)/<(lldb_lib)",
],
}],
]
},

Expand Down
10 changes: 9 additions & 1 deletion scripts/cleanup.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,16 @@

const fs = require('fs');
const cwd = process.cwd();
const osName = require('os').type();

const libExt = require('os').type() === 'Darwin' ? 'dylib' : 'so';
let libExt;
if (osName === 'Darwin') {
libExt = 'dylib';
} else if (osName === 'Windows_NT') {
libExt = 'dll';
} else {
libExt = 'so';
}
const llnodeLib = `plugin.${libExt}`;
const destLib = `llnode.${libExt}`;

Expand Down
15 changes: 13 additions & 2 deletions scripts/configure.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ function configureInstallation(osName, buildDir) {
} else if (osName === 'FreeBSD') {
installation = require('./freebsd').getLldbInstallation();
config.variables['lldb_lib_dir%'] = installation.libDir;
} else if (osName === 'Windows_NT') {
installation = require('./windows').getLldbInstallation();
config.variables['lldb_lib_dir%'] = installation.libDir;
config.variables['lldb_lib%'] = installation.libName;
} else {
console.log(`Unsupported OS: ${osName}`);
process.exit(1);
Expand Down Expand Up @@ -92,8 +96,15 @@ function writeConfig(config) {
}

function scriptText(osName, lldbExe) {
let lib = 'llnode.so';
if (osName === 'Darwin') { lib = 'llnode.dylib'; }
let lib;
if (osName === 'Darwin') {
lib = 'llnode.dylib';
} else if (osName === 'Windows_NT') {
lib = 'llnode.dll';
lldbExe = lldbExe.replace(/\\/g, '\\\\');
} else {
lib = 'llnode.so';
}

return `#!/usr/bin/env node
Expand Down
25 changes: 25 additions & 0 deletions scripts/install.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,38 @@
'use strict';

const child_process = require('child_process');
const os = require('os');
const fs = require('fs');
const path = require('path');

const lldb = require('./lldb');

main();

function main() {
const osName = os.type();
if (osName === 'Windows_NT') {
return mainWindows();
}

runFile('node-gyp', ['rebuild']);
}

function mainWindows() {
const clangExe = 'clang-cl.exe';
const clangDir = lldb.findWindowsExeDir(clangExe);

if (!clangDir) {
console.log(`Could not find ${clangExe}`);
process.exit(1);
}
console.log(`Using ${clangExe} found at ${clangDir}`);

runShell('node-gyp clean');
runShell('node-gyp configure');
runShell(`node-gyp build /p:CLToolExe="${clangExe}" /p:CLToolPath="${clangDir}"`);
}

/**
* execFileSync wrapper, exits on error.
* @param {string} file Executable to run
Expand Down
50 changes: 50 additions & 0 deletions scripts/lldb.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,55 @@ function tryExecutables(exeNames) {
}
}

/**
* 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.
Expand Down Expand Up @@ -112,5 +161,6 @@ module.exports = {
getLibPath,
cloneHeaders,
tryExecutables,
findWindowsExeDir,
getLldbVersion
};
88 changes: 88 additions & 0 deletions scripts/windows.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
'use strict';

const child_process = require('child_process');
const fs = require('fs');
const path = require('path');
const lldb = require('./lldb');

/**
* Find the lldb to use in the installation of LLVM.
* @returns {string} Path to the lldb executable or undefined
*/
function getLldbExecutable() {
if (process.env.npm_config_lldb_exe !== undefined) {
return process.env.npm_config_lldb_exe;
}

const lldbDir = lldb.findWindowsExeDir('lldb.exe');
if (lldbDir) {
return path.join(lldbDir, 'lldb.exe');
}
}

/**
* Get the directory containing the lldb library.
* @param {string} lldbExe Path to the corresponding lldb executable
* @returns {{dir:string, name:string}}
*/
function getLib(lldbExe) {
const libName = 'liblldb.lib';
const libDir = path.resolve(path.dirname(lldbExe), '..', 'lib');

if (!fs.existsSync(path.join(libDir, libName))) {
return {
dir: undefined,
name: libName
};
}

return {
dir: libDir,
name: libName
};
}

/**
* Get the lldb installation. If prefix is undefined, the headers need to
* be downloaded.
* The version string will be in the form like '3.9'
*/
function getLldbInstallation() {
console.log('Looking for lldb executable...');
const lldbExe = getLldbExecutable();
if (!lldbExe) {
console.log('Could not find any usable lldb executable');
console.log('Please see the README.md on how to install lldb');
process.exit(1);
}
console.log(`Found lldb executable ${lldbExe}`);

console.log('\nReading lldb version...');
const lldbVersion = lldb.getLldbVersion(lldbExe);
if (!lldbVersion) {
console.log(`Unable to get the version from the lldb binary ${lldbExe}`);
process.exit(1);
}
console.log(`Installing llnode for ${lldbExe}, lldb version ${lldbVersion}`);

console.log(`\nLooking for shared libraries for lldb ${lldbVersion}...`);
const lib = getLib(lldbExe);
if (!lib.dir) {
console.log(`Could not find ${lib.name} in the same path as the lldb executable`);
process.exit(1);
} else {
console.log(`Found ${lib.name} in ${lib.dir}`);
}

return {
executable: lldbExe,
version: lldbVersion,
includeDir: undefined, // Windows binary distribution does not contain headers
libDir: lib.dir,
libName: lib.name
};
}

module.exports = {
getLldbInstallation
};
2 changes: 1 addition & 1 deletion test/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const debug = exports.debug =
let pluginName;
if (process.platform === 'darwin')
pluginName = 'llnode.dylib';
else if (process.platform === 'windows')
else if (process.platform === 'win32')
pluginName = 'llnode.dll';
else
pluginName = 'llnode.so';
Expand Down
1 change: 1 addition & 0 deletions windows/include/getopt.h
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#include <lldb/Host/common/GetOptInc.h>
6 changes: 6 additions & 0 deletions windows/llnode.def
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
LIBRARY plugin.dll
EXPORTS
;
; llnode exports
;
_ZN4lldb16PluginInitializeENS_10SBDebuggerE=PluginInitialize

0 comments on commit 8045d77

Please sign in to comment.