|
730 | 730 | dest='shared_zstd_libpath', |
731 | 731 | help='a directory to search for the shared zstd DLL') |
732 | 732 |
|
| 733 | +shared_optgroup.add_argument('--shared-v8', |
| 734 | + action='store_true', |
| 735 | + dest='shared_v8', |
| 736 | + default=None, |
| 737 | + help='link to a shared v8 DLL instead of static linking') |
| 738 | + |
| 739 | +shared_optgroup.add_argument('--shared-v8-includes', |
| 740 | + action='store', |
| 741 | + dest='shared_v8_includes', |
| 742 | + help='directory containing v8 header files') |
| 743 | + |
| 744 | +shared_optgroup.add_argument('--shared-v8-libname', |
| 745 | + action='store', |
| 746 | + dest='shared_v8_libname', |
| 747 | + default='v8,v8_libplatform', |
| 748 | + help='alternative lib name to link to [default: %(default)s]') |
| 749 | + |
| 750 | +shared_optgroup.add_argument('--shared-v8-libpath', |
| 751 | + action='store', |
| 752 | + dest='shared_v8_libpath', |
| 753 | + help='a directory to search for the shared v8 DLL') |
| 754 | + |
733 | 755 | parser.add_argument_group(shared_optgroup) |
734 | 756 |
|
735 | 757 | for builtin in shareable_builtins: |
|
1087 | 1109 | action='store_true', |
1088 | 1110 | dest='without_bundled_v8', |
1089 | 1111 | default=False, |
1090 | | - help='do not use V8 includes from the bundled deps folder. ' + |
1091 | | - '(This mode is not officially supported for regular applications)') |
| 1112 | + help='DEPRECATED: Use --shared-v8 instead.') |
1092 | 1113 |
|
1093 | 1114 | parser.add_argument('--verbose', |
1094 | 1115 | action='store_true', |
@@ -2015,6 +2036,110 @@ def configure_library(lib, output, pkgname=None): |
2015 | 2036 | output['libraries'] += pkg_libs.split() |
2016 | 2037 |
|
2017 | 2038 |
|
| 2039 | +def read_v8_version_from_header(header_path): |
| 2040 | + """Read V8 version components from v8-version.h.""" |
| 2041 | + version = {} |
| 2042 | + with open(header_path, 'r') as f: |
| 2043 | + for line in f: |
| 2044 | + for component in ('V8_MAJOR_VERSION', 'V8_MINOR_VERSION', |
| 2045 | + 'V8_BUILD_NUMBER', 'V8_PATCH_LEVEL'): |
| 2046 | + if '#define ' + component in line: |
| 2047 | + version[component] = int(line.strip().split()[-1]) |
| 2048 | + return version |
| 2049 | + |
| 2050 | +def parse_promise_field_count(header_path): |
| 2051 | + """Read V8_PROMISE_INTERNAL_FIELD_COUNT from v8-promise.h.""" |
| 2052 | + with open(header_path, 'r') as f: |
| 2053 | + for line in f: |
| 2054 | + if '#define V8_PROMISE_INTERNAL_FIELD_COUNT' in line: |
| 2055 | + # Line format: #define V8_PROMISE_INTERNAL_FIELD_COUNT <n> |
| 2056 | + return int(line.strip().split()[-1]) |
| 2057 | + return 0 # V8 default if not defined |
| 2058 | + |
| 2059 | +def has_define_in_header(header_path, define_name): |
| 2060 | + """Check if a header file contains a #define for the given name.""" |
| 2061 | + with open(header_path, 'r') as f: |
| 2062 | + for line in f: |
| 2063 | + if f'#define {define_name}' in line: |
| 2064 | + return True |
| 2065 | + return False |
| 2066 | + |
| 2067 | +def find_shared_v8_includes(): |
| 2068 | + """Resolve shared V8 include path from --shared-v8-includes or pkg-config.""" |
| 2069 | + if options.shared_v8_includes: |
| 2070 | + return options.shared_v8_includes |
| 2071 | + (_, pkg_cflags, _, _) = pkg_config('v8') |
| 2072 | + if pkg_cflags: |
| 2073 | + for flag in pkg_cflags.split(): |
| 2074 | + if flag.startswith('-I'): |
| 2075 | + return flag[2:] |
| 2076 | + return None |
| 2077 | + |
| 2078 | +def validate_shared_v8(shared_includes): |
| 2079 | + """Validate that the shared V8 meets Node.js build configuration requirements. |
| 2080 | + Errors are fatal. Configure will not proceed with an incompatible V8.""" |
| 2081 | + |
| 2082 | + # --- Version check (hard error on major.minor mismatch) --- |
| 2083 | + bundled_header = os.path.join('deps', 'v8', 'include', 'v8-version.h') |
| 2084 | + shared_header = os.path.join(shared_includes, 'v8-version.h') |
| 2085 | + if not os.path.exists(shared_header): |
| 2086 | + alt = os.path.join(shared_includes, 'v8', 'v8-version.h') |
| 2087 | + if os.path.exists(alt): |
| 2088 | + shared_header = alt |
| 2089 | + else: |
| 2090 | + error('Could not find v8-version.h in shared V8 includes at ' |
| 2091 | + f'{shared_includes}. Cannot validate V8 compatibility. ' |
| 2092 | + 'Use --shared-v8-includes to specify the correct path.') |
| 2093 | + |
| 2094 | + bundled = read_v8_version_from_header(bundled_header) |
| 2095 | + shared = read_v8_version_from_header(shared_header) |
| 2096 | + b_ver = f"{bundled['V8_MAJOR_VERSION']}.{bundled['V8_MINOR_VERSION']}.{bundled['V8_BUILD_NUMBER']}" |
| 2097 | + s_ver = f"{shared['V8_MAJOR_VERSION']}.{shared['V8_MINOR_VERSION']}.{shared['V8_BUILD_NUMBER']}" |
| 2098 | + |
| 2099 | + if bundled['V8_MAJOR_VERSION'] != shared['V8_MAJOR_VERSION'] or \ |
| 2100 | + bundled['V8_MINOR_VERSION'] != shared['V8_MINOR_VERSION']: |
| 2101 | + error(f'Shared V8 version ({s_ver}) does not match required ' |
| 2102 | + f'({b_ver}). Major and minor version must match.') |
| 2103 | + |
| 2104 | + if bundled['V8_BUILD_NUMBER'] != shared['V8_BUILD_NUMBER']: |
| 2105 | + warn(f'Shared V8 build number ({s_ver}) differs from bundled ({b_ver}). ' |
| 2106 | + f'Build may succeed but runtime behavior may differ.') |
| 2107 | + |
| 2108 | + # --- Promise internal field count (warning, not error; fallback exists) --- |
| 2109 | + promise_header = os.path.join(shared_includes, 'v8-promise.h') |
| 2110 | + if os.path.exists(promise_header): |
| 2111 | + field_count = parse_promise_field_count(promise_header) |
| 2112 | + if field_count < 1: |
| 2113 | + warn(f'Shared V8 has V8_PROMISE_INTERNAL_FIELD_COUNT={field_count}. ' |
| 2114 | + f'async_hooks will use slower symbol-property fallback. ' |
| 2115 | + f'For best performance, rebuild V8 with ' |
| 2116 | + f'v8_promise_internal_field_count=1.') |
| 2117 | + |
| 2118 | + # --- Pointer compression: auto-detect from shared V8, set Node.js to match --- |
| 2119 | + v8config = os.path.join(shared_includes, 'v8config.h') |
| 2120 | + if os.path.exists(v8config): |
| 2121 | + shared_has_pc = has_define_in_header(v8config, 'V8_COMPRESS_POINTERS') |
| 2122 | + if shared_has_pc != bool(options.enable_pointer_compression): |
| 2123 | + # Auto-match instead of erroring. Node.js adapts to the shared V8 |
| 2124 | + options.enable_pointer_compression = shared_has_pc |
| 2125 | + warn(f'Auto-{"enabling" if shared_has_pc else "disabling"} pointer ' |
| 2126 | + f'compression to match shared V8.') |
| 2127 | + |
| 2128 | + shared_has_sandbox = has_define_in_header(v8config, 'V8_ENABLE_SANDBOX') |
| 2129 | + if shared_has_sandbox: |
| 2130 | + error('Shared V8 was built with V8_ENABLE_SANDBOX. Node.js does not ' |
| 2131 | + 'support the V8 sandbox (backing store pointers are in C++ ' |
| 2132 | + 'memory, not sandbox memory). Rebuild V8 with: ' |
| 2133 | + 'v8_enable_sandbox=false') |
| 2134 | + |
| 2135 | + # --- Extensible RO snapshot: must be disabled for snapshot compatibility --- |
| 2136 | + shared_has_ext_ro = has_define_in_header(v8config, 'V8_ENABLE_EXTENSIBLE_RO_SNAPSHOT') |
| 2137 | + if shared_has_ext_ro: |
| 2138 | + error('Shared V8 was built with V8_ENABLE_EXTENSIBLE_RO_SNAPSHOT. ' |
| 2139 | + 'Node.js requires this to be disabled for snapshot compatibility. ' |
| 2140 | + 'Rebuild V8 with: v8_enable_extensible_ro_snapshot=false') |
| 2141 | + |
| 2142 | + |
2018 | 2143 | def configure_v8(o, configs): |
2019 | 2144 | set_configuration_variable(configs, 'v8_enable_v8_checks', release=0, debug=1) |
2020 | 2145 |
|
@@ -2064,16 +2189,40 @@ def configure_v8(o, configs): |
2064 | 2189 | o['variables']['node_enable_v8windbg'] = b(options.enable_v8windbg) |
2065 | 2190 | if options.enable_d8: |
2066 | 2191 | o['variables']['test_isolation_mode'] = 'noop' # Needed by d8.gyp. |
| 2192 | + |
2067 | 2193 | if options.without_bundled_v8: |
| 2194 | + if not options.shared_v8: |
| 2195 | + warn('--without-bundled-v8 is deprecated. Use --shared-v8 instead.') |
| 2196 | + options.shared_v8 = True |
| 2197 | + |
| 2198 | + if options.shared_v8: |
| 2199 | + o['variables']['node_use_bundled_v8'] = b(False) |
| 2200 | + |
2068 | 2201 | if options.enable_d8: |
2069 | | - raise Exception('--enable-d8 is incompatible with --without-bundled-v8.') |
| 2202 | + error('--enable-d8 is incompatible with --shared-v8') |
2070 | 2203 | if options.enable_v8windbg: |
2071 | | - raise Exception('--enable-v8windbg is incompatible with --without-bundled-v8.') |
2072 | | - (pkg_libs, pkg_cflags, pkg_libpath, _) = pkg_config("v8") |
2073 | | - if pkg_libs and pkg_libpath: |
2074 | | - output['libraries'] += [pkg_libpath] + pkg_libs.split() |
2075 | | - if pkg_cflags: |
2076 | | - output['include_dirs'] += [flag for flag in [flag.strip() for flag in pkg_cflags.split('-I')] if flag] |
| 2204 | + error('--enable-v8windbg is incompatible with --shared-v8') |
| 2205 | + |
| 2206 | + # Standard configure_library call - handles pkg-config, includes, |
| 2207 | + # libpath, libname exactly like every other shared dependency. |
| 2208 | + configure_library('v8', o) |
| 2209 | + |
| 2210 | + # Ensure the build can find the shared V8 at mksnapshot execution time |
| 2211 | + if options.shared_v8_libpath: |
| 2212 | + o['variables']['node_shared_v8_libpath'] = options.shared_v8_libpath |
| 2213 | + |
| 2214 | + # validate shared v8 meets Node.js requirements (hard errors on failure) |
| 2215 | + shared_includes = find_shared_v8_includes() |
| 2216 | + if shared_includes: |
| 2217 | + validate_shared_v8(shared_includes) |
| 2218 | + else: |
| 2219 | + warn('Could not determine shared v8 include path. ' |
| 2220 | + 'Skipping build configuration validation. ' |
| 2221 | + 'use --shared-v8-includes to enable validation.') |
| 2222 | + |
| 2223 | + else: |
| 2224 | + o['variables']['node_use_bundled_v8'] = b(True) |
| 2225 | + |
2077 | 2226 | if options.static_zoslib_gyp: |
2078 | 2227 | o['variables']['static_zoslib_gyp'] = options.static_zoslib_gyp |
2079 | 2228 | if flavor != 'linux' and options.v8_enable_hugepage: |
|
0 commit comments