chrisa/node-dtrace-provider#96 Improving compilation failure behaviour

Reviewed by: Julien Gilli <[email protected]>
This commit is contained in:
Cody Peter Mello
2018-01-11 23:54:45 +00:00
parent a00c95b879
commit 1474252dcc
18 changed files with 240 additions and 236 deletions
+2
View File
@@ -5,3 +5,5 @@
*#
build
*.log
test
TODO.md
+3
View File
@@ -3,6 +3,9 @@ dtrace-provider - Changes
## HISTORY
* 0.8.6:
Improved compilation failure behaviour (#96)
* 0.8.5:
Reverted "Install fails on Debian due to differently named node binary" for
now
+29 -4
View File
@@ -174,6 +174,31 @@ npm modules to embed probes and include a dependency on this module.
GNU Make is required to build libusdt; the build scripts will look for
gmake in `PATH` first, and then for make.
### TROUBLESHOOTING BUILD ISSUES
If compilation fails during installation on platforms with DTrace, then
the library will fall back to the stub implementation that does nothing.
To force an installation failure when compiling fails, set the environment
variable `NODE_DTRACE_PROVIDER_REQUIRE` to `hard`:
```shell
$ NODE_DTRACE_PROVIDER_REQUIRE=hard npm install
```
This will then show you the output of the build process so you can see at
which point it's having an issue. Common issues are:
- Missing a C/C++ compiler toolchain for your platform.
- `python` is Python 3 instead of Python 2; run `npm config set python python2.7`
(or similar) to set the Python binary npm uses.
- On OS X you may need to agree to the XCode license if that's the compiler
toolchain you're using. This will usually manifest with an error like
`Agreeing to the Xcode/iOS license requires admin privileges, please re-run as root via sudo.`
To accept the license, you can run `sudo xcodebuild -license`.
Once you've found and fixed the issue, you can run `npm rebuild` to rerun
the lifecycle scripts.
## CAVEATS
There is some overhead to probes, even when disabled. Probes are
@@ -184,12 +209,12 @@ unless probes are placed in particularly hot code paths.
## CONTRIBUTING
The source is available at:
To clone the project's source code:
https://github.com/chrisa/node-dtrace-provider
$ git clone --recursive https://github.com/chrisa/node-dtrace-provider.git
For issues, please use the GitHub issue tracker linked to the
repository. GitHub pull requests are very welcome.
For issues, please use the [GitHub issue tracker](https://github.com/chrisa/node-dtrace-provider/issues)
linked to the repository. GitHub pull requests are very welcome.
## RUNNING THE TESTS
+35
View File
@@ -0,0 +1,35 @@
{
'conditions': [
# If we are on Mac OS X, FreeBSD, or a Solarish system, attempt
# to build the DTrace provider extension.
['OS=="mac" or OS=="solaris" or OS=="freebsd"', {
'targets': [
{
'target_name': 'ndtp',
'type': 'none',
'actions': [{
'inputs': [''],
'outputs': [''],
'action_name': 'build_ndtp',
'action': [
'bash', 'build.sh'
]
}]
}
]
},
# If we are on another system (like Windows or Linux), then DTrace is
# unavailable. This target is necessary because GYP requires at least
# one target to exist. We end up building nothing, and fall back to the
# stub implementation when the package is loaded.
{
'targets': [
{
'target_name': 'DTraceProviderStub',
'type': 'none'
}
]
}]
]
}
+86
View File
@@ -0,0 +1,86 @@
#!/usr/bin/env bash
set -o pipefail
if [[ -n "$V" ]]; then
set -o xtrace
fi
fail() {
if [[ -z "$NODE_DTRACE_PROVIDER_REQUIRE" && -z "$V" ]]; then
echo "-----------------------------------------------------------------"
echo "Building dtrace-provider failed with exit code $1, falling back"
echo "on stub implementation."
echo ""
echo "Re-run install with V set in your environment to see the build"
echo "output, or NODE_DTRACE_PROVIDER_REQUIRE=hard to force an"
echo "installation failure."
echo "-----------------------------------------------------------------"
fi
if [[ "$NODE_DTRACE_PROVIDER_REQUIRE" == "hard" ]]; then
exit 1
else
exit 0
fi
}
buildUSDT() {
if [[ -z "$NODE_DTRACE_PROVIDER_REQUIRE" && -z "$V" ]]; then
exec 1> /dev/null
exec 2> /dev/null
fi
# GYP's MAKEFLAGS confuses libusdt's Makefile
unset MAKEFLAGS
# Ask node what architecture it's been built for ("target_arch" in
# config.gypi), and build libusdt to match.
#
# We use node from the path; npm will have adjusted PATH for us if
# necessary, otherwise we assume the user did so when building by
# hand.
#
# (This will need to change at the point that GYP is able to build
# node extensions universal on the Mac - for now we'll go with x86_64
# on a 64 bit Mac, because that's the default architecture in that
# situation.)
export ARCH=`node -e "console.log(process.arch === 'x64' ? 'x86_64' : 'i386')"`
echo "Building libusdt for ${ARCH}"
# Respect a MAKE variable if set
if [[ -z $MAKE ]]; then
# Default to `gmake` first if available, because we require GNU make
# and `make` isn't GNU make on some plats.
MAKE=`which gmake`
if [[ -z $MAKE ]]; then
MAKE=make
fi
fi
# Build libusdt.
$MAKE -C libusdt clean all
}
buildNDTP() {
if [[ -z "$NODE_DTRACE_PROVIDER_REQUIRE" && -z "$V" ]]; then
exec 1> /dev/null
exec 2> /dev/null
fi
node-gyp rebuild -C src
}
(buildUSDT)
LIBUSDT_STATUS=$?
if [[ "$LIBUSDT_STATUS" -ne 0 ]]; then
fail $LIBUSDT_STATUS
fi
(buildNDTP)
NODE_GYP_STATUS=$?
if [[ "$NODE_GYP_STATUS" -ne 0 ]]; then
fail $NODE_GYP_STATUS
fi
exit 0
-82
View File
@@ -1,82 +0,0 @@
{
'conditions': [
['OS=="mac" or OS=="solaris" or OS=="freebsd"', {
'conditions' : [
['OS=="mac" or OS=="solaris"', {
'variables': {
'escaped_root': '<!(printf %q "<(module_root_dir)")',
}
}],
['OS=="freebsd"', {
'variables' : {
'escaped_root': '<!(printf %s "<(module_root_dir)")'
}
}]
],
# If we are on Mac OS X, FreeBSD, or a Solarish system, attempt
# to build the DTrace provider extension.
'targets': [
{
'target_name': 'DTraceProviderBindings',
'sources': [
'dtrace_provider.cc',
'dtrace_probe.cc',
'dtrace_argument.cc'
],
'conditions': [
['OS=="mac" or OS=="solaris"',
{ 'include_dirs': [
'libusdt',
'<!(node -e "require(\'nan\')")',
]
}
],
['OS=="freebsd"',
{ 'include_dirs': [
'/usr/src/cddl/compat/opensolaris/',
'/usr/src/sys/cddl/compat/opensolaris',
'/usr/src/sys/cddl/contrib/opensolaris/uts/common/',
'libusdt',
'<!(node -e "require(\'nan\')")'
]
}
]
],
'dependencies': [
'libusdt'
],
'libraries': [
'-L<(escaped_root)/libusdt -l usdt'
]
},
{
'target_name': 'libusdt',
'type': 'none',
'actions': [{
'inputs': [''],
'outputs': [''],
'action_name': 'build_libusdt',
'action': [
'sh', 'libusdt-build.sh'
]
}]
}
]
},
# If we are not on Mac OS X, FreeBSD or a Solarish system, DTrace is
# unavailable. This target is necessary because GYP requires at least
# one target to exist.
{
'targets': [
{
'target_name': 'DTraceProviderStub',
'type': 'none'
}
]
}],
]
}
+6 -12
View File
@@ -13,9 +13,9 @@ DTraceProviderStub.prototype.disable = function() {};
var builds = ['Release', 'default', 'Debug'];
var err = null;
for (var i in builds) {
for (var i = 0; i < builds.length; i++) {
try {
var binding = require('./build/' + builds[i] + '/DTraceProviderBindings');
var binding = require('./src/build/' + builds[i] + '/DTraceProviderBindings');
DTraceProvider = binding.DTraceProvider;
break;
} catch (e) {
@@ -26,17 +26,11 @@ for (var i in builds) {
}
if (!DTraceProvider) {
/*
* If the platform looks like it _should_ have DTrace
* available, log a failure to load the bindings.
*/
if (process.platform === 'darwin' ||
process.platform === 'sunos' ||
process.platform === 'freebsd') {
console.error(err);
if (process.env.NODE_DTRACE_PROVIDER_REQUIRE === 'hard') {
throw err;
} else {
DTraceProvider = DTraceProviderStub;
}
DTraceProvider = DTraceProviderStub;
}
exports.DTraceProvider = DTraceProvider;
-13
View File
@@ -1,13 +0,0 @@
// Output the architecture of this node build, either 'i386' or
// 'x86_64'
//
// This copes with both node 0.6.x and 0.8.x.
try {
// node 0.8
console.log(process.config.variables.target_arch == 'x64' ? 'x86_64' : 'i386')
}
catch (TypeError) {
// node 0.6
console.log(process.arch == 'x64' ? 'x86_64' : 'i386')
};
-34
View File
@@ -1,34 +0,0 @@
#!/bin/sh
# GYP's MAKEFLAGS confuses libusdt's Makefile
#
unset MAKEFLAGS
# Ask node what arch it's been built for, and build libusdt to match.
#
# We use node from the path; npm will have adjusted PATH for us if
# necessary, otherwise we assume the user did so when building by
# hand.
#
# (this will need to change at the point that GYP is able to build
# node extensions universal on the Mac - for now we'll go with x86_64
# on a 64 bit Mac, because that's the default architecture in that
# situation).
#
ARCH=`node libusdt-arch.js`
echo "Building libusdt for ${ARCH}"
export ARCH
# Respect a MAKE variable if set
if [ -z $MAKE ]; then
# Default to `gmake` first if available, because we require GNU make
# and `make` isn't GNU make on some plats.
MAKE=`which gmake`
if [ -z $MAKE ]; then
MAKE=make
fi
fi
# Build.
#
$MAKE -C libusdt clean all
+4 -4
View File
@@ -1,6 +1,6 @@
{
"name": "dtrace-provider",
"version": "0.8.5",
"version": "0.8.6",
"description": "Native DTrace providers for node.js applications",
"keywords": [
"dtrace",
@@ -23,11 +23,11 @@
"nan": "^2.3.3"
},
"devDependencies": {
"tap": "^0.2.0"
"tap": "0.7.1"
},
"scripts": {
"install": "node scripts/install.js",
"test": "tap test/*test.js"
"install": "node-gyp rebuild || node suppress-error.js",
"test": "tap test/*.test.js"
},
"main": "./dtrace-provider.js"
}
-57
View File
@@ -1,57 +0,0 @@
#!/usr/bin/env node
var movedFile = false;
if (["linux", "win32"].indexOf(process.platform) !== -1)
return;
var fs = require('fs');
var path = require('path');
var src = path.join(__dirname, '..', 'compile.py');
var dst = path.join(__dirname, '..', 'binding.gyp');
fs.renameSync(src, dst);
movedFile = true;
var spawn = require('child_process').spawn;
var stdio = 'ignore';
if (process.env.V) {
stdio = 'inherit';
}
var options = {
cwd: path.join(__dirname, '..'),
stdio: stdio
};
var child = spawn('node-gyp', [ 'rebuild' ], options);
child.on('error', function (err) {
if (err.code === 'ENOENT') {
console.error('cannot locate node-gyp');
} else {
console.error(err);
}
process.exit(0);
});
child.on('close', function(code, signal) {
if ((code || signal) && process.env.V === undefined) {
console.error('---------------');
console.error('Building dtrace-provider failed with exit code %d and signal %d',
code, signal);
console.error('re-run install with environment variable V set to see the build output');
console.error('---------------');
}
process.exit(0);
});
process.on('exit', function() {
if (movedFile)
fs.renameSync(dst, src);
});
+47
View File
@@ -0,0 +1,47 @@
{
'conditions' : [
['OS=="mac" or OS=="solaris"', {
'variables': {
'escaped_root': '<!(printf %q "<(module_root_dir)")',
}
}],
['OS=="freebsd"', {
'variables' : {
'escaped_root': '<!(printf %s "<(module_root_dir)")'
}
}]
],
'targets': [
{
'target_name': 'DTraceProviderBindings',
'sources': [
'dtrace_provider.cc',
'dtrace_probe.cc',
'dtrace_argument.cc'
],
'conditions': [
['OS=="mac" or OS=="solaris"',
{ 'include_dirs': [
'../libusdt',
'<!(node -e "require(\'nan\')")',
]
}
],
['OS=="freebsd"',
{ 'include_dirs': [
'/usr/src/cddl/compat/opensolaris/',
'/usr/src/sys/cddl/compat/opensolaris',
'/usr/src/sys/cddl/contrib/opensolaris/uts/common/',
'../libusdt',
'<!(node -e "require(\'nan\')")'
]
}
]
],
'libraries': [
'-L<(escaped_root)/../libusdt -l usdt'
]
}
]
}
+28
View File
@@ -0,0 +1,28 @@
/**
* During `npm install`, we run:
*
* node-gyp rebuild || node suppress-error.js
*
* (Note that this expression is written to work on *nix and Windows.)
*
* When we run node-gyp, we detect whether or not we're on a supported platform,
* and either compile the node addon or exit having done nothing. It's possible
* for the platform to lack the requirements to run node-gyp though (such as
* Python 2, make, Visual C++ Build Tools, and so on), in which case we fall
* back onto this script.
*
* If NODE_DTRACE_PROVIDER_REQUIRE is set to "hard", then we want to propagate
* the failure and stop the install. Otherwise, we want to suppress it and
* allow the program to fall back onto the stub code.
*
* There is one case where we might stop an install and not want to: on Debian
* and Ubuntu, where the binary is named "nodejs" instead of "node", the
* fallback will fail to run. There doesn't really seem to be a great way to
* handle this scenario, so users on those systems will need to either install
* node-gyp's requirements or set up a "node" symbolic link.
*/
if (process.env.NODE_DTRACE_PROVIDER_REQUIRE === 'hard') {
process.exit(1);
} else {
process.exit(0);
}
-30
View File
@@ -1,30 +0,0 @@
import Options, Utils, sys
from os import unlink, symlink, popen
from os.path import exists, islink
srcdir = '.'
blddir = 'build'
VERSION = '0.2.8'
libusdtdir = 'libusdt'
def set_options(ctx):
ctx.tool_options('compiler_cxx')
def configure(ctx):
ctx.check_tool('compiler_cxx')
ctx.check_tool('node_addon')
def build(ctx):
if sys.platform.startswith("sunos") or sys.platform.startswith("darwin") or sys.platform.startswith("freebsd"):
ctx.new_task_gen(
rule = "cd ../" + libusdtdir + " && ARCH=i386 make clean all && cd -",
shell = True
)
t = ctx.new_task_gen('cxx', 'shlib', 'node_addon')
t.target = 'DTraceProviderBindings'
t.source = ['dtrace_provider.cc', 'dtrace_probe.cc', 'dtrace_argument.cc']
t.includes = [libusdtdir]
t.staticlib = 'usdt'
t.libpath = "../" + libusdtdir