From 4a42b861ac26ce5284cdd21bd1c6ddea3ec8fb3c Mon Sep 17 00:00:00 2001 From: Stefan Budeanu Date: Tue, 19 Jul 2016 21:03:33 -0400 Subject: [PATCH 1/4] build: cherry pick V8 change for windows DLL support PR-URL: https://github.com/nodejs/node/pull/8084 Reviewed-By: Ben Noordhuis Reviewed-By: James M Snell --- deps/v8/build/toolchain.gypi | 7 ++++--- deps/v8/include/v8-version.h | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/deps/v8/build/toolchain.gypi b/deps/v8/build/toolchain.gypi index 4dbf42bfe3795c..d484ac9e118ab7 100644 --- a/deps/v8/build/toolchain.gypi +++ b/deps/v8/build/toolchain.gypi @@ -39,6 +39,7 @@ 'ubsan_vptr%': 0, 'v8_target_arch%': '<(target_arch)', 'v8_host_byteorder%': ' Date: Mon, 4 Jul 2016 12:03:14 +0100 Subject: [PATCH 2/4] 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 Ref: https://github.com/nodejs/node/pull/9385 Reviewed-By: Ben Noordhuis Reviewed-By: Fedor Indutny Reviewed-By: James M Snell Reviewed-By: Michael Dawson Reference: nodejs/node#7487 --- common.gypi | 9 +++++++ configure | 35 +++++++++++++++++++++++--- node.gyp | 56 +++++++++++++++++++++++++++++++++++------ src/node.cc | 37 ++++++++++++++++++++++----- src/node.h | 14 ++++++++--- tools/getnodeversion.py | 14 +++++++---- tools/install.py | 17 +++++++++++-- 7 files changed, 154 insertions(+), 28 deletions(-) diff --git a/common.gypi b/common.gypi index 258cd1603ef54d..f26b66e6483c58 100644 --- a/common.gypi +++ b/common.gypi @@ -11,6 +11,12 @@ 'msvs_multi_core_compile': '0', # we do enable multicore compiles, but not using the V8 way 'python%': 'python', + 'node_shared%': 'false', + 'force_dynamic_crt%': 0, + 'node_use_v8_platform%': 'true', + 'node_use_bundled_v8%': 'true', + 'node_module_version%': '', + 'node_tag%': '', 'uv_library%': 'static_library', @@ -291,6 +297,9 @@ ], 'ldflags!': [ '-rdynamic' ], }], + [ 'node_shared=="true"', { + 'cflags': [ '-fPIC' ], + }] ], }], ['OS=="android"', { diff --git a/configure b/configure index 27ab9a54a99981..2b9df01a243558 100755 --- a/configure +++ b/configure @@ -24,6 +24,9 @@ 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')) + # parse our options parser = optparse.OptionParser() @@ -385,6 +388,26 @@ parser.add_option('--enable-static', dest='enable_static', help='build as static library') +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. @@ -774,7 +797,14 @@ def configure_node(o): if options.enable_static: o['variables']['node_target_type'] = 'static_library' - o['variables']['node_module_version'] = 46 + 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) + node_module_version = getmoduleversion.get_version() + shlib_suffix = '%s.dylib' if sys.platform == 'darwin' else 'so.%s' + shlib_suffix %= node_module_version + o['variables']['node_module_version'] = int(node_module_version) + o['variables']['shlib_suffix'] = shlib_suffix if options.linked_module: o['variables']['library_files'] = options.linked_module @@ -820,8 +850,7 @@ def configure_v8(o): o['variables']['v8_random_seed'] = 0 # Use a random seed for hash tables. o['variables']['v8_use_snapshot'] = 'false' if options.without_snapshot else 'true' o['variables']['node_enable_d8'] = b(options.enable_d8) - - + o['variables']['force_dynamic_crt'] = 1 if options.shared else 0 def configure_openssl(o): o['variables']['node_use_openssl'] = b(not options.without_ssl) o['variables']['node_shared_openssl'] = b(options.shared_openssl) diff --git a/node.gyp b/node.gyp index caddcfac020c7b..7f2afb5f25f072 100644 --- a/node.gyp +++ b/node.gyp @@ -5,6 +5,11 @@ 'node_use_lttng%': 'false', 'node_use_etw%': 'false', 'node_use_perfctr%': 'false', + 'node_use_v8_platform%': 'true', + 'node_use_bundled_v8%': 'true', + 'node_shared%': 'false', + 'force_dynamic_crt%': 0, + 'node_module_version%': 'true', 'node_has_winsdk%': 'false', 'node_shared_zlib%': 'false', 'node_shared_http_parser%': 'false', @@ -101,6 +106,11 @@ }, { 'use_openssl_def': 0, }], + [ 'node_shared=="true"', { + 'node_target_type%': 'shared_library', + }, { + 'node_target_type%': 'executable', + }], ], }, @@ -220,6 +230,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_enable_d8=="true"', { 'dependencies': [ 'deps/v8/src/d8.gyp:d8' ], }], @@ -292,7 +338,7 @@ ], }, 'conditions': [ - ['OS in "linux freebsd"', { + ['OS in "linux freebsd" and node_shared=="false"', { 'ldflags': [ '-Wl,--whole-archive,' '<(PRODUCT_DIR)/obj.target/deps/openssl/' @@ -460,7 +506,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' ] @@ -469,12 +515,6 @@ 'ldflags': [ '-Wl,-M,/usr/lib/ld/map.noexstk' ], }], ], - 'msvs_settings': { - 'VCManifestTool': { - 'EmbedManifest': 'true', - 'AdditionalManifestFiles': 'src/res/node.exe.extra.manifest' - } - }, }, { 'target_name': 'mkssldef', diff --git a/src/node.cc b/src/node.cc index 155d1cdcd2005e..085c3d6a074377 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" @@ -167,6 +169,30 @@ static v8::Platform* default_platform; static uv_sem_t debug_semaphore; #endif +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; + } + + v8::Platform* platform_; +#else // !NODE_USE_V8_PLATFORM + void Initialize(int thread_pool_size) {} + void PumpMessageLoop(Isolate* isolate) {} + void Dispose() {} +#endif // !NODE_USE_V8_PLATFORM +} v8_platform; + static void PrintErrorString(const char* format, ...) { va_list ap; va_start(ap, format); @@ -4226,11 +4252,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 @@ -4291,8 +4317,8 @@ int Start(int argc, char** argv) { #endif const int thread_pool_size = 4; - default_platform = v8::platform::CreateDefaultPlatform(thread_pool_size); - V8::InitializePlatform(default_platform); + + v8_platform.Initialize(thread_pool_size); V8::Initialize(); int exit_code = 1; @@ -4309,8 +4335,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 f70b5f8e784382..4d3293eab6b3c3 100644 --- a/src/node.h +++ b/src/node.h @@ -396,17 +396,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/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 b1997d48525209..64441bacd10182 100755 --- a/tools/install.py +++ b/tools/install.py @@ -123,9 +123,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 _InstallablaeTargetInstallPath function. + output_prefix += 'lib.target/' + output_file = 'lib' + output_file + '.so' + + action([output_prefix + output_file], 'bin/' + output_file) if 'true' == variables.get('node_use_dtrace'): action(['out/Release/node.d'], 'lib/dtrace/node.d') From d0078bcca45d4239c513c0fd59d752fdf5e79b0a Mon Sep 17 00:00:00 2001 From: Stewart Addison Date: Wed, 16 Nov 2016 14:34:51 +0000 Subject: [PATCH 3/4] build: windows sharedlib support --- common.gypi | 29 ++++++++++++++++++++++++++++- node.gyp | 2 +- vcbuild.bat | 3 +++ 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/common.gypi b/common.gypi index f26b66e6483c58..55440bc8fde917 100644 --- a/common.gypi +++ b/common.gypi @@ -79,6 +79,20 @@ ['OS == "android"', { 'cflags': [ '-fPIE' ], 'ldflags': [ '-fPIE', '-pie' ] + }], + ['node_shared=="true"', { + 'msvs_settings': { + 'VCCLCompilerTool': { + 'RuntimeLibrary': 3 # MultiThreadedDebugDLL (/MDd) + } + } + }], + ['node_shared=="false"', { + 'msvs_settings': { + 'VCCLCompilerTool': { + 'RuntimeLibrary': 1 # MultiThreadedDebug (/MTd) + } + } }] ], 'msvs_settings': { @@ -116,11 +130,24 @@ ['OS == "android"', { 'cflags': [ '-fPIE' ], 'ldflags': [ '-fPIE', '-pie' ] + }], + ['node_shared=="true"', { + 'msvs_settings': { + 'VCCLCompilerTool': { + 'RuntimeLibrary': 2 # MultiThreadedDLL (/MD) + } + } + }], + ['node_shared=="false"', { + 'msvs_settings': { + 'VCCLCompilerTool': { + 'RuntimeLibrary': 0 # MultiThreaded (/MT) + } + } }] ], 'msvs_settings': { 'VCCLCompilerTool': { - 'RuntimeLibrary': 0, # static release 'Optimization': 3, # /Ox, full optimization 'FavorSizeOrSpeed': 1, # /Ot, favour speed over size 'InlineFunctionExpansion': 2, # /Ob2, inline anything eligible diff --git a/node.gyp b/node.gyp index 7f2afb5f25f072..ea7b17135192f3 100644 --- a/node.gyp +++ b/node.gyp @@ -242,7 +242,7 @@ 'NODE_SHARED_MODE', ], 'conditions': [ - [ 'node_module_version!=""', { + [ 'node_module_version!="" and OS!="win"', { 'product_extension': 'so.<(node_module_version)', }] ], diff --git a/vcbuild.bat b/vcbuild.bat index 77c0d01a14f486..a052504b61b639 100644 --- a/vcbuild.bat +++ b/vcbuild.bat @@ -37,6 +37,7 @@ set build_release= set configure_flags= set build_addons= set enable_vtune_profiling= +set dll= :next-arg if "%1"=="" goto args-done @@ -76,6 +77,7 @@ if /i "%1"=="intl-none" set i18n_arg=%1&goto arg-ok if /i "%1"=="download-all" set download_arg="--download=all"&goto arg-ok if /i "%1"=="ignore-flaky" set test_args=%test_args% --flaky-tests=dontcare&goto arg-ok if /i "%1"=="enable-vtune" set enable_vtune_profiling="--enable-vtune-profiling"&goto arg-ok +if /i "%1"=="dll" set dll=1&goto arg-ok echo Error: invalid command line option `%1`. exit /b 1 @@ -105,6 +107,7 @@ if defined noetw set configure_flags=%configure_flags% --without-etw& set noetw_ if defined noperfctr set configure_flags=%configure_flags% --without-perfctr& set noperfctr_msi_arg=/p:NoPerfCtr=1 if defined release_urlbase set configure_flags=%configure_flags% --release-urlbase=%release_urlbase% if defined download_arg set configure_flags=%configure_flags% %download_arg% +if defined dll set configure_flags=%configure_flags% --shared if "%i18n_arg%"=="full-icu" set configure_flags=%configure_flags% --with-intl=full-icu if "%i18n_arg%"=="small-icu" set configure_flags=%configure_flags% --with-intl=small-icu From 28dc7116afc7aa9d25491f9ea7193ee1ff45fc89 Mon Sep 17 00:00:00 2001 From: Stewart Addison Date: Tue, 12 Jul 2016 19:04:29 +0100 Subject: [PATCH 4/4] build: abstract out shared library suffix WIP: Add soname & fix make install PR-URL: https://github.com/nodejs/node/pull/6994 Ref: https://github.com/nodejs/node/pull/9385 Reviewed-By: James M Snell Reviewed-By: Michael Dawson Reviewed-By: Ben Noordhuis Reviewed-By: Fedor Indutny The build system currently creates a shared library on OS X with the same name as on Linux i.e. libnode.so.48. This is inconsistent with the conventions on OS X which uses libnode.48.dylib This commit changes the build process and install.py (used by make binary) to build with the correct name on OS X when the --shared configure parameter is used. PR-URL: https://github.com/nodejs/node/pull/7687 Reviewed-By: James M Snell Reviewed-By: Michael Dawson --- configure | 1 + node.gyp | 2 +- tools/getmoduleversion.py | 24 ++++++++++++++++++++++++ tools/install.py | 9 +++++---- 4 files changed, 31 insertions(+), 5 deletions(-) create mode 100644 tools/getmoduleversion.py diff --git a/configure b/configure index 2b9df01a243558..02e926d4b3cf29 100755 --- a/configure +++ b/configure @@ -26,6 +26,7 @@ import nodedownload # imports in tools/ sys.path.insert(0, os.path.join(root_dir, 'tools')) +import getmoduleversion # parse our options parser = optparse.OptionParser() diff --git a/node.gyp b/node.gyp index ea7b17135192f3..26a9f615d028aa 100644 --- a/node.gyp +++ b/node.gyp @@ -243,7 +243,7 @@ ], 'conditions': [ [ 'node_module_version!="" and OS!="win"', { - 'product_extension': 'so.<(node_module_version)', + 'product_extension': '<(shlib_suffix)', }] ], }], 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/install.py b/tools/install.py index 64441bacd10182..daf2d292c51322 100755 --- a/tools/install.py +++ b/tools/install.py @@ -133,10 +133,11 @@ def files(action): if is_windows: output_file += '.dll' else: - # GYP will output to lib.target, this is hardcoded in its source, - # see the _InstallablaeTargetInstallPath function. - output_prefix += 'lib.target/' - output_file = 'lib' + output_file + '.so' + output_file = 'lib' + output_file + '.' + variables.get('shlib_suffix') + # GYP will output to lib.target except on OS X, this is hardcoded + # in its source - see the _InstallableTargetInstallPath function. + if sys.platform != 'darwin': + output_prefix += 'lib.target/' action([output_prefix + output_file], 'bin/' + output_file)