From 410296c37acb2ad5d939bd4633b2c3fbe3782084 Mon Sep 17 00:00:00 2001 From: Stefan Budeanu Date: Sat, 26 Mar 2016 20:17:55 -0400 Subject: [PATCH] build: configure --shared Add configure flag for building a shared library that can be embedded in other applications (like Electron). Add flags --without-bundled-v8 and --without-v8-platform to control V8 dependencies used. PR-URL: https://github.com/nodejs/node/pull/6994 Reviewed-By: Ben Noordhuis Reviewed-By: Fedor Indutny Reviewed-By: James M Snell Reviewed-By: Michael Dawson --- common.gypi | 8 +++ configure | 28 ++++++++++ node.gyp | 81 ++++++++++++++++++++++------ src/node.cc | 51 +++++++++++++++--- src/node.h | 14 +++-- test/parallel/test-module-version.js | 10 ++++ tools/getmoduleversion.py | 24 +++++++++ tools/getnodeversion.py | 14 +++-- tools/install.py | 18 ++++++- 9 files changed, 212 insertions(+), 36 deletions(-) create mode 100644 test/parallel/test-module-version.js create mode 100644 tools/getmoduleversion.py diff --git a/common.gypi b/common.gypi index 8da603d00f618e..a892c2d6e38f5b 100644 --- a/common.gypi +++ b/common.gypi @@ -11,6 +11,11 @@ 'msvs_multi_core_compile': '0', # we do enable multicore compiles, but not using the V8 way 'python%': 'python', + 'node_shared%': 'false', + 'node_use_v8_platform%': 'true', + 'node_use_bundled_v8%': 'true', + 'node_module_version%': '', + 'node_tag%': '', 'uv_library%': 'static_library', @@ -290,6 +295,9 @@ ], 'ldflags!': [ '-rdynamic' ], }], + [ 'node_shared=="true"', { + 'cflags': [ '-fPIC' ], + }], ], }], [ 'OS=="android"', { diff --git a/configure b/configure index d622a6f0625a18..879c12b496017f 100755 --- a/configure +++ b/configure @@ -24,6 +24,10 @@ from gyp.common import GetFlavor sys.path.insert(0, os.path.join(root_dir, 'tools', 'configure.d')) import nodedownload +# imports in tools/ +sys.path.insert(0, os.path.join(root_dir, 'tools')) +import getmoduleversion + # parse our options parser = optparse.OptionParser() @@ -420,6 +424,26 @@ parser.add_option('--without-inspector', dest='without_inspector', help='disable experimental V8 inspector support') +parser.add_option('--shared', + action='store_true', + dest='shared', + help='compile shared library for embedding node in another project. ' + + '(This mode is not officially supported for regular applications)') + +parser.add_option('--without-v8-platform', + action='store_true', + dest='without_v8_platform', + default=False, + help='do not initialize v8 platform during node.js startup. ' + + '(This mode is not officially supported for regular applications)') + +parser.add_option('--without-bundled-v8', + action='store_true', + dest='without_bundled_v8', + default=False, + help='do not use V8 includes from the bundled deps folder. ' + + '(This mode is not officially supported for regular applications)') + (options, args) = parser.parse_args() # Expand ~ in the install prefix now, it gets written to multiple files. @@ -810,6 +834,10 @@ def configure_node(o): o['variables']['node_target_type'] = 'static_library' o['variables']['node_no_browser_globals'] = b(options.no_browser_globals) + o['variables']['node_shared'] = b(options.shared) + o['variables']['node_use_v8_platform'] = b(not options.without_v8_platform) + o['variables']['node_use_bundled_v8'] = b(not options.without_bundled_v8) + o['variables']['node_module_version'] = int(getmoduleversion.get_version()) if options.linked_module: o['variables']['library_files'] = options.linked_module diff --git a/node.gyp b/node.gyp index c3f591351d52ce..c14f57e94a02f3 100644 --- a/node.gyp +++ b/node.gyp @@ -6,6 +6,10 @@ 'node_use_etw%': 'false', 'node_use_perfctr%': 'false', 'node_no_browser_globals%': 'false', + 'node_use_v8_platform%': 'true', + 'node_use_bundled_v8%': 'true', + 'node_shared%': 'false', + 'node_module_version%': '', 'node_shared_zlib%': 'false', 'node_shared_http_parser%': 'false', 'node_shared_cares%': 'false', @@ -14,7 +18,6 @@ 'node_shared_openssl%': 'false', 'node_v8_options%': '', 'node_enable_v8_vtunejit%': 'false', - 'node_target_type%': 'executable', 'node_core_target_name%': 'node', 'library_files': [ 'lib/internal/bootstrap_node.js', @@ -100,6 +103,13 @@ 'deps/v8/tools/SourceMap.js', 'deps/v8/tools/tickprocessor-driver.js', ], + 'conditions': [ + [ 'node_shared=="true"', { + 'node_target_type%': 'shared_library', + }, { + 'node_target_type%': 'executable', + }], + ], }, 'targets': [ @@ -109,8 +119,6 @@ 'dependencies': [ 'node_js2c#host', - 'deps/v8/tools/gyp/v8.gyp:v8', - 'deps/v8/tools/gyp/v8.gyp:v8_libplatform' ], 'include_dirs': [ @@ -118,7 +126,6 @@ 'tools/msvs/genfiles', 'deps/uv/src/ares', '<(SHARED_INTERMEDIATE_DIR)', # for node_natives.h - 'deps/v8' # include/v8_platform.h ], 'sources': [ @@ -217,6 +224,42 @@ 'conditions': [ + [ 'node_shared=="false"', { + 'msvs_settings': { + 'VCManifestTool': { + 'EmbedManifest': 'true', + 'AdditionalManifestFiles': 'src/res/node.exe.extra.manifest' + } + }, + }, { + 'defines': [ + 'NODE_SHARED_MODE', + ], + 'conditions': [ + [ 'node_module_version!=""', { + 'product_extension': 'so.<(node_module_version)', + }] + ], + }], + [ 'node_use_bundled_v8=="true"', { + 'include_dirs': [ + 'deps/v8', # include/v8_platform.h + ], + + 'dependencies': [ + 'deps/v8/tools/gyp/v8.gyp:v8', + 'deps/v8/tools/gyp/v8.gyp:v8_libplatform' + ], + }], + [ 'node_use_v8_platform=="true"', { + 'defines': [ + 'NODE_USE_V8_PLATFORM=1', + ], + }, { + 'defines': [ + 'NODE_USE_V8_PLATFORM=0', + ], + }], [ 'node_tag!=""', { 'defines': [ 'NODE_TAG="<(node_tag)"' ], }], @@ -245,7 +288,8 @@ 'defines': [ 'NODE_HAVE_SMALL_ICU=1' ], }]], }], - [ 'node_enable_v8_vtunejit=="true" and (target_arch=="x64" or \ + [ 'node_use_bundled_v8=="true" and \ + node_enable_v8_vtunejit=="true" and (target_arch=="x64" or \ target_arch=="ia32" or target_arch=="x32")', { 'defines': [ 'NODE_ENABLE_VTUNE_PROFILING' ], 'dependencies': [ @@ -308,7 +352,7 @@ ], }, 'conditions': [ - ['OS in "linux freebsd"', { + ['OS in "linux freebsd" and node_shared=="false"', { 'ldflags': [ '-Wl,--whole-archive <(PRODUCT_DIR)/<(OPENSSL_PRODUCT)', '-Wl,--no-whole-archive', @@ -395,7 +439,7 @@ [ 'node_no_browser_globals=="true"', { 'defines': [ 'NODE_NO_BROWSER_GLOBALS' ], } ], - [ 'v8_postmortem_support=="true"', { + [ 'node_use_bundled_v8=="true" and v8_postmortem_support=="true"', { 'dependencies': [ 'deps/v8/tools/gyp/v8.gyp:postmortem-metadata' ], 'conditions': [ # -force_load is not applicable for the static library @@ -478,7 +522,7 @@ 'NODE_PLATFORM="sunos"', ], }], - [ 'OS=="freebsd" or OS=="linux"', { + [ '(OS=="freebsd" or OS=="linux") and node_shared=="false"', { 'ldflags': [ '-Wl,-z,noexecstack', '-Wl,--whole-archive <(V8_BASE)', '-Wl,--no-whole-archive' ] @@ -487,12 +531,6 @@ 'ldflags': [ '-Wl,-M,/usr/lib/ld/map.noexstk' ], }], ], - 'msvs_settings': { - 'VCManifestTool': { - 'EmbedManifest': 'true', - 'AdditionalManifestFiles': 'src/res/node.exe.extra.manifest' - } - }, }, # generate ETW header and resource files { @@ -718,8 +756,6 @@ 'deps/http_parser/http_parser.gyp:http_parser', 'deps/gtest/gtest.gyp:gtest', 'deps/uv/uv.gyp:libuv', - 'deps/v8/tools/gyp/v8.gyp:v8', - 'deps/v8/tools/gyp/v8.gyp:v8_libplatform' ], 'include_dirs': [ 'src', @@ -750,7 +786,18 @@ 'src/inspector_socket.cc', 'test/cctest/test_inspector_socket.cc' ] - }] + }], + [ 'node_use_v8_platform=="true"', { + 'dependencies': [ + 'deps/v8/tools/gyp/v8.gyp:v8_libplatform', + ], + }], + [ 'node_use_bundled_v8=="true"', { + 'dependencies': [ + 'deps/v8/tools/gyp/v8.gyp:v8', + 'deps/v8/tools/gyp/v8.gyp:v8_libplatform' + ], + }], ] } ], # end targets diff --git a/src/node.cc b/src/node.cc index b6fa4606f5fae0..cc15ac272aaa00 100644 --- a/src/node.cc +++ b/src/node.cc @@ -39,7 +39,9 @@ #include "string_bytes.h" #include "util.h" #include "uv.h" +#if NODE_USE_V8_PLATFORM #include "libplatform/libplatform.h" +#endif // NODE_USE_V8_PLATFORM #include "v8-debug.h" #include "v8-profiler.h" #include "zlib.h" @@ -183,7 +185,42 @@ static uv_async_t dispatch_debug_messages_async; static Mutex node_isolate_mutex; static v8::Isolate* node_isolate; -static v8::Platform* default_platform; + +static struct { +#if NODE_USE_V8_PLATFORM + void Initialize(int thread_pool_size) { + platform_ = v8::platform::CreateDefaultPlatform(thread_pool_size); + V8::InitializePlatform(platform_); + } + + void PumpMessageLoop(Isolate* isolate) { + v8::platform::PumpMessageLoop(platform_, isolate); + } + + void Dispose() { + delete platform_; + platform_ = nullptr; + } + +#if HAVE_INSPECTOR + void StartInspector(Environment *env, int port, bool wait) { + env->inspector_agent()->Start(platform_, port, wait); + } +#endif // HAVE_INSPECTOR + + v8::Platform* platform_; +#else // !NODE_USE_V8_PLATFORM + void Initialize(int thread_pool_size) {} + void PumpMessageLoop(Isolate* isolate) {} + void Dispose() {} +#if HAVE_INSPECTOR + void StartInspector(Environment *env, int port, bool wait) { + env->ThrowError("Node compiled with NODE_USE_V8_PLATFORM=0"); + } +#endif // HAVE_INSPECTOR + +#endif // !NODE_USE_V8_PLATFORM +} v8_platform; #ifdef __POSIX__ static uv_sem_t debug_semaphore; @@ -3652,7 +3689,7 @@ static void StartDebug(Environment* env, bool wait) { CHECK(!debugger_running); #if HAVE_INSPECTOR if (use_inspector) { - env->inspector_agent()->Start(default_platform, inspector_port, wait); + v8_platform.StartInspector(env, inspector_port, wait); debugger_running = true; } else { #endif @@ -4299,11 +4336,11 @@ static void StartNodeInstance(void* arg) { SealHandleScope seal(isolate); bool more; do { - v8::platform::PumpMessageLoop(default_platform, isolate); + v8_platform.PumpMessageLoop(isolate); more = uv_run(env.event_loop(), UV_RUN_ONCE); if (more == false) { - v8::platform::PumpMessageLoop(default_platform, isolate); + v8_platform.PumpMessageLoop(isolate); EmitBeforeExit(&env); // Emit `beforeExit` if the loop became alive either after emitting @@ -4364,8 +4401,7 @@ int Start(int argc, char** argv) { V8::SetEntropySource(crypto::EntropySource); #endif - default_platform = v8::platform::CreateDefaultPlatform(v8_thread_pool_size); - V8::InitializePlatform(default_platform); + v8_platform.Initialize(v8_thread_pool_size); V8::Initialize(); int exit_code = 1; @@ -4382,8 +4418,7 @@ int Start(int argc, char** argv) { } V8::Dispose(); - delete default_platform; - default_platform = nullptr; + v8_platform.Dispose(); delete[] exec_argv; exec_argv = nullptr; diff --git a/src/node.h b/src/node.h index 0347dc10cbcdbb..873551fa338a97 100644 --- a/src/node.h +++ b/src/node.h @@ -411,17 +411,23 @@ extern "C" NODE_EXTERN void node_module_register(void* mod); # define NODE_MODULE_EXPORT __attribute__((visibility("default"))) #endif +#ifdef NODE_SHARED_MODE +# define NODE_CTOR_PREFIX +#else +# define NODE_CTOR_PREFIX static +#endif + #if defined(_MSC_VER) #pragma section(".CRT$XCU", read) #define NODE_C_CTOR(fn) \ - static void __cdecl fn(void); \ + NODE_CTOR_PREFIX void __cdecl fn(void); \ __declspec(dllexport, allocate(".CRT$XCU")) \ void (__cdecl*fn ## _)(void) = fn; \ - static void __cdecl fn(void) + NODE_CTOR_PREFIX void __cdecl fn(void) #else #define NODE_C_CTOR(fn) \ - static void fn(void) __attribute__((constructor)); \ - static void fn(void) + NODE_CTOR_PREFIX void fn(void) __attribute__((constructor)); \ + NODE_CTOR_PREFIX void fn(void) #endif #define NODE_MODULE_X(modname, regfunc, priv, flags) \ diff --git a/test/parallel/test-module-version.js b/test/parallel/test-module-version.js new file mode 100644 index 00000000000000..7f96e8bb60f0ac --- /dev/null +++ b/test/parallel/test-module-version.js @@ -0,0 +1,10 @@ +'use strict'; +require('../common'); +var assert = require('assert'); + +// check for existence +assert(process.config.variables.hasOwnProperty('node_module_version')); + +// ensure that `node_module_version` is an Integer > 0 +assert(Number.isInteger(process.config.variables.node_module_version)); +assert(process.config.variables.node_module_version > 0); diff --git a/tools/getmoduleversion.py b/tools/getmoduleversion.py new file mode 100644 index 00000000000000..fb86ba1fa923dd --- /dev/null +++ b/tools/getmoduleversion.py @@ -0,0 +1,24 @@ +from __future__ import print_function +import os +import re + +def get_version(): + node_version_h = os.path.join( + os.path.dirname(__file__), + '..', + 'src', + 'node_version.h') + + f = open(node_version_h) + + regex = '^#define NODE_MODULE_VERSION [0-9]+' + + for line in f: + if re.match(regex, line): + major = line.split()[2] + return major + + raise Exception('Could not find pattern matching %s' % regex) + +if __name__ == '__main__': + print(get_version()) diff --git a/tools/getnodeversion.py b/tools/getnodeversion.py index 766e4f60dc07ad..f2032cccefe936 100644 --- a/tools/getnodeversion.py +++ b/tools/getnodeversion.py @@ -1,16 +1,20 @@ -import os,re +import os +import re -node_version_h = os.path.join(os.path.dirname(__file__), '..', 'src', +node_version_h = os.path.join( + os.path.dirname(__file__), + '..', + 'src', 'node_version.h') f = open(node_version_h) for line in f: - if re.match('#define NODE_MAJOR_VERSION', line): + if re.match('^#define NODE_MAJOR_VERSION', line): major = line.split()[2] - if re.match('#define NODE_MINOR_VERSION', line): + if re.match('^#define NODE_MINOR_VERSION', line): minor = line.split()[2] - if re.match('#define NODE_PATCH_VERSION', line): + if re.match('^#define NODE_PATCH_VERSION', line): patch = line.split()[2] print '%(major)s.%(minor)s.%(patch)s'% locals() diff --git a/tools/install.py b/tools/install.py index f3fa4fe898157b..a3986c5033904d 100755 --- a/tools/install.py +++ b/tools/install.py @@ -6,6 +6,7 @@ import re import shutil import sys +from getmoduleversion import get_version # set at init time node_prefix = '/usr/local' # PREFIX variable from Makefile @@ -107,9 +108,22 @@ def subdir_files(path, dest, action): def files(action): is_windows = sys.platform == 'win32' + output_file = 'node' + output_prefix = 'out/Release/' - exeext = '.exe' if is_windows else '' - action(['out/Release/node' + exeext], 'bin/node' + exeext) + if 'false' == variables.get('node_shared'): + if is_windows: + output_file += '.exe' + else: + if is_windows: + output_file += '.dll' + else: + # GYP will output to lib.target, this is hardcoded in its source, + # see the _InstallableTargetInstallPath function. + output_prefix += 'lib.target/' + output_file = 'lib' + output_file + '.so.' + get_version() + + action([output_prefix + output_file], 'bin/' + output_file) if 'true' == variables.get('node_use_dtrace'): action(['out/Release/node.d'], 'lib/dtrace/node.d')