nodejs: fix cross compilation for non-emulatable platforms (#384413)

authored by Audrey Dutcher and committed by GitHub 6a8334df 157cb194

+88 -9
+44 -5
pkgs/development/web/nodejs/nodejs.nix
··· 22 22 23 23 canExecute = stdenv.buildPlatform.canExecute stdenv.hostPlatform; 24 24 emulator = stdenv.hostPlatform.emulator buildPackages; 25 + canEmulate = stdenv.hostPlatform.emulatorAvailable buildPackages; 26 + buildNode = buildPackages."${pname}_${majorVersion}"; 25 27 26 28 # See valid_os and valid_arch in configure.py. 27 29 destOS = ··· 126 128 ln -s "$cctools/bin/libtool" "$out/bin/libtool" 127 129 ''; 128 130 131 + # a script which claims to be a different script but switches to simply touching its output 132 + # when an environment variable is set. See CC_host, --cross-compiling, and postConfigure. 133 + touchScript = real: writeScript "touch-script" '' 134 + #!${stdenv.shell} 135 + if [ -z "$FAKE_TOUCH" ]; then 136 + exec "${real}" "$@" 137 + fi 138 + while [ "$#" != "0" ]; do 139 + if [ "$1" == "-o" ]; then 140 + shift 141 + touch "$1" 142 + fi 143 + shift 144 + done 145 + ''; 146 + 129 147 package = stdenv.mkDerivation (finalAttrs: 130 148 let 131 149 /** the final package fixed point, after potential overrides */ ··· 148 166 # Note: do not set TERM=dumb environment variable globally, it is used in 149 167 # test-ci-js test suite to skip tests that otherwise run fine. 150 168 NINJA = "TERM=dumb ninja"; 169 + } // lib.optionalAttrs (!canExecute && !canEmulate) { 170 + # these are used in the --cross-compiling case. see comment at postConfigure. 171 + CC_host = touchScript "${buildPackages.stdenv.cc}/bin/cc"; 172 + CXX_host = touchScript "${buildPackages.stdenv.cc}/bin/c++"; 173 + AR_host = touchScript "${buildPackages.stdenv.cc}/bin/ar"; 151 174 }; 152 175 153 176 # NB: technically, we do not need bash in build inputs since all scripts are ··· 179 202 dontUseNinjaCheck = true; 180 203 dontUseNinjaInstall = true; 181 204 182 - outputs = [ "out" "libv8" ]; 205 + outputs = [ "out" "libv8" ] ++ lib.optionals (stdenv.hostPlatform == stdenv.buildPlatform) [ "dev" ]; 183 206 setOutputFlags = false; 184 207 moveToDev = false; 185 208 ··· 188 211 "--ninja" 189 212 "--with-intl=system-icu" 190 213 "--openssl-use-def-ca-store" 191 - "--no-cross-compiling" 214 + # --cross-compiling flag enables use of CC_host et. al 215 + (if canExecute || canEmulate then "--no-cross-compiling" else "--cross-compiling") 192 216 "--dest-os=${destOS}" 193 217 "--dest-cpu=${destCPU}" 194 218 ] 195 219 ++ lib.optionals (destARMFPU != null) [ "--with-arm-fpu=${destARMFPU}" ] 196 220 ++ lib.optionals (destARMFloatABI != null) [ "--with-arm-float-abi=${destARMFloatABI}" ] 197 - ++ lib.optionals (!canExecute) [ 221 + ++ lib.optionals (!canExecute && canEmulate) [ 198 222 # Node.js requires matching bitness between build and host platforms, e.g. 199 223 # for V8 startup snapshot builder (see tools/snapshot) and some other 200 224 # tools. We apply a patch that runs these tools using a host platform ··· 225 249 exec ${python.executable} configure.py "$@" 226 250 ''; 227 251 252 + # In order to support unsupported cross configurations, we copy some intermediate executables 253 + # from a native build and replace all the build-system tools with a script which simply touches 254 + # its outfile. We have to indiana-jones-swap the build-system-targeted tools since they are 255 + # tested for efficacy at configure time. 256 + postConfigure = lib.optionalString (!canEmulate && !canExecute) '' 257 + cp ${buildNode.dev}/bin/* out/Release 258 + export FAKE_TOUCH=1 259 + ''; 260 + 228 261 enableParallelBuilding = true; 229 262 230 263 # Don't allow enabling content addressed conversion as `nodejs` ··· 343 376 ])}" 344 377 ]; 345 378 346 - postInstall = '' 379 + postInstall = lib.optionalString (stdenv.hostPlatform == stdenv.buildPlatform) '' 380 + mkdir -p $dev/bin 381 + cp out/Release/{bytecode_builtins_list_generator,mksnapshot,torque,node_js2c,gen-regexp-special-case} $dev/bin 382 + '' + '' 383 + 347 384 HOST_PATH=$out/bin patchShebangs --host $out 348 385 349 386 ${lib.optionalString canExecute '' ··· 416 453 changelog = "https://github.com/nodejs/node/releases/tag/v${version}"; 417 454 license = licenses.mit; 418 455 maintainers = with maintainers; [ aduh95 ]; 419 - platforms = platforms.linux ++ platforms.darwin; 456 + platforms = platforms.linux ++ platforms.darwin ++ platforms.freebsd; 457 + # This broken condition is likely too conservative. Feel free to loosen it if it works. 458 + broken = !canExecute && !canEmulate && (stdenv.buildPlatform.parsed.cpu != stdenv.hostPlatform.parsed.cpu); 420 459 mainProgram = "node"; 421 460 knownVulnerabilities = optional (versionOlder version "18") "This NodeJS release has reached its end of life. See https://nodejs.org/en/about/releases/."; 422 461 };
+18 -2
pkgs/development/web/nodejs/v22.nix
··· 1 - { callPackage, openssl, python3, enableNpm ? true }: 1 + { lib, stdenv, buildPackages, callPackage, fetchpatch2, openssl, python3, enableNpm ? true }: 2 2 3 3 let 4 4 buildNodejs = callPackage ./nodejs.nix { ··· 10 10 inherit enableNpm; 11 11 version = "22.14.0"; 12 12 sha256 = "c609946bf793b55c7954c26582760808d54c16185d79cb2fb88065e52de21914"; 13 - patches = [ 13 + patches = (if (stdenv.hostPlatform.emulatorAvailable buildPackages) then [ 14 14 ./configure-emulator.patch 15 + ] else [ 16 + (fetchpatch2 { 17 + url = "https://raw.githubusercontent.com/buildroot/buildroot/2f0c31bffdb59fb224387e35134a6d5e09a81d57/package/nodejs/nodejs-src/0003-include-obj-name-in-shared-intermediate.patch"; 18 + hash = "sha256-3g4aS+NmmUYNOYRNc6UMJKYoaTlpP5Knt9UHegx+o0Y="; 19 + }) 20 + ]) ++ lib.optionals (stdenv.hostPlatform != stdenv.buildPlatform && stdenv.hostPlatform.isFreeBSD) [ 21 + # This patch is concerning. 22 + # https://github.com/nodejs/node/issues/54576 23 + # It is only supposed to affect clang >= 17, but I'm seeing it on clang 19. 24 + # I'm keeping the predicate for this patch pretty strict out of caution, 25 + # so if you see the error it's supposed to prevent, feel free to loosen it. 26 + (fetchpatch2 { 27 + url = "https://raw.githubusercontent.com/rubyjs/libv8-node/62476a398d4c9c1a670240a3b070d69544be3761/patch/v8-no-assert-trivially-copyable.patch"; 28 + hash = "sha256-hSTLljmVzYmc3WAVeRq9EPYluXGXFeWVXkykufGQPVw="; 29 + }) 30 + ] ++ [ 15 31 ./configure-armv6-vfpv2.patch 16 32 ./disable-darwin-v8-system-instrumentation-node19.patch 17 33 ./bypass-darwin-xcrun-node16.patch
+26 -2
pkgs/development/web/nodejs/v23.nix
··· 1 1 { 2 2 lib, 3 3 stdenv, 4 + buildPackages, 4 5 callPackage, 5 6 fetchpatch2, 6 7 openssl, ··· 19 20 version = "23.10.0"; 20 21 sha256 = "b39e5fbd3debb8318ddea6af3e89b07dafb891421fb7ca99fbe19c99adabe5fd"; 21 22 patches = 22 - [ 23 - ./configure-emulator.patch 23 + ( 24 + if (stdenv.hostPlatform.emulatorAvailable buildPackages) then 25 + [ 26 + ./configure-emulator.patch 27 + ] 28 + else 29 + [ 30 + (fetchpatch2 { 31 + url = "https://raw.githubusercontent.com/buildroot/buildroot/2f0c31bffdb59fb224387e35134a6d5e09a81d57/package/nodejs/nodejs-src/0003-include-obj-name-in-shared-intermediate.patch"; 32 + hash = "sha256-3g4aS+NmmUYNOYRNc6UMJKYoaTlpP5Knt9UHegx+o0Y="; 33 + }) 34 + ] 35 + ) 36 + ++ lib.optionals (stdenv.hostPlatform != stdenv.buildPlatform && stdenv.hostPlatform.isFreeBSD) [ 37 + # This patch is concerning. 38 + # https://github.com/nodejs/node/issues/54576 39 + # It is only supposed to affect clang >= 17, but I'm seeing it on clang 19. 40 + # I'm keeping the predicate for this patch pretty strict out of caution, 41 + # so if you see the error it's supposed to prevent, feel free to loosen it. 42 + (fetchpatch2 { 43 + url = "https://raw.githubusercontent.com/rubyjs/libv8-node/62476a398d4c9c1a670240a3b070d69544be3761/patch/v8-no-assert-trivially-copyable.patch"; 44 + hash = "sha256-hSTLljmVzYmc3WAVeRq9EPYluXGXFeWVXkykufGQPVw="; 45 + }) 46 + ] 47 + ++ [ 24 48 ./configure-armv6-vfpv2.patch 25 49 ./disable-darwin-v8-system-instrumentation-node19.patch 26 50 ./bypass-darwin-xcrun-node16.patch