at 25.11-pre 20 kB view raw
1{ 2 lib, 3 stdenv, 4 fetchurl, 5 openssl, 6 python, 7 zlib, 8 libuv, 9 sqlite, 10 http-parser, 11 icu, 12 bash, 13 ninja, 14 pkgconf, 15 unixtools, 16 runCommand, 17 buildPackages, 18 testers, 19 # for `.pkgs` attribute 20 callPackage, 21 # Updater dependencies 22 writeScript, 23 coreutils, 24 gnugrep, 25 jq, 26 curl, 27 common-updater-scripts, 28 runtimeShell, 29 gnupg, 30 installShellFiles, 31}: 32 33{ 34 enableNpm ? true, 35 version, 36 sha256, 37 patches ? [ ], 38}@args: 39 40let 41 42 majorVersion = lib.versions.major version; 43 minorVersion = lib.versions.minor version; 44 45 pname = if enableNpm then "nodejs" else "nodejs-slim"; 46 47 canExecute = stdenv.buildPlatform.canExecute stdenv.hostPlatform; 48 emulator = stdenv.hostPlatform.emulator buildPackages; 49 canEmulate = stdenv.hostPlatform.emulatorAvailable buildPackages; 50 buildNode = buildPackages."${pname}_${majorVersion}"; 51 52 # See valid_os and valid_arch in configure.py. 53 destOS = 54 let 55 platform = stdenv.hostPlatform; 56 in 57 if platform.isiOS then 58 "ios" 59 else if platform.isAndroid then 60 "android" 61 else if platform.isWindows then 62 "win" 63 else if platform.isDarwin then 64 "mac" 65 else if platform.isLinux then 66 "linux" 67 else if platform.isOpenBSD then 68 "openbsd" 69 else if platform.isFreeBSD then 70 "freebsd" 71 else 72 throw "unsupported os ${platform.uname.system}"; 73 destCPU = 74 let 75 platform = stdenv.hostPlatform; 76 in 77 if platform.isAarch then 78 "arm" + lib.optionalString platform.is64bit "64" 79 else if platform.isMips32 then 80 "mips" + lib.optionalString platform.isLittleEndian "le" 81 else if platform.isMips64 && platform.isLittleEndian then 82 "mips64el" 83 else if platform.isPower then 84 "ppc" + lib.optionalString platform.is64bit "64" 85 else if platform.isx86_64 then 86 "x64" 87 else if platform.isx86_32 then 88 "ia32" 89 else if platform.isS390x then 90 "s390x" 91 else if platform.isRiscV64 then 92 "riscv64" 93 else if platform.isLoongArch64 then 94 "loong64" 95 else 96 throw "unsupported cpu ${platform.uname.processor}"; 97 destARMFPU = 98 let 99 platform = stdenv.hostPlatform; 100 in 101 if platform.isAarch32 && platform ? gcc.fpu then 102 lib.throwIfNot (builtins.elem platform.gcc.fpu [ 103 "vfp" 104 "vfpv2" 105 "vfpv3" 106 "vfpv3-d16" 107 "neon" 108 ]) "unsupported ARM FPU ${platform.gcc.fpu}" platform.gcc.fpu 109 else 110 null; 111 destARMFloatABI = 112 let 113 platform = stdenv.hostPlatform; 114 in 115 if platform.isAarch32 && platform ? gcc.float-abi then 116 lib.throwIfNot (builtins.elem platform.gcc.float-abi [ 117 "soft" 118 "softfp" 119 "hard" 120 ]) "unsupported ARM float ABI ${platform.gcc.float-abi}" platform.gcc.float-abi 121 else 122 null; 123 # TODO: also handle MIPS flags (mips_arch, mips_fpu, mips_float_abi). 124 125 useSharedHttpParser = 126 !stdenv.hostPlatform.isDarwin && lib.versionOlder "${majorVersion}.${minorVersion}" "11.4"; 127 useSharedSQLite = lib.versionAtLeast version "22.5"; 128 129 sharedLibDeps = 130 { 131 inherit openssl zlib libuv; 132 } 133 // (lib.optionalAttrs useSharedHttpParser { 134 inherit http-parser; 135 }) 136 // (lib.optionalAttrs useSharedSQLite { 137 inherit sqlite; 138 }); 139 140 copyLibHeaders = map (name: "${lib.getDev sharedLibDeps.${name}}/include/*") ( 141 builtins.attrNames sharedLibDeps 142 ); 143 144 # Currently stdenv sets CC/LD/AR/etc environment variables to program names 145 # instead of absolute paths. If we add cctools to nativeBuildInputs, that 146 # would shadow stdenv’s bintools and potentially break other parts of the 147 # build. The correct behavior is to use absolute paths, and there is a PR for 148 # that, see https://github.com/NixOS/nixpkgs/pull/314920. As a temporary 149 # workaround, we use only a single program we need (and that is not part of 150 # the stdenv). 151 darwin-cctools-only-libtool = 152 # Would be nice to have onlyExe builder similar to onlyBin… 153 runCommand "darwin-cctools-only-libtool" { cctools = lib.getBin buildPackages.cctools; } '' 154 mkdir -p "$out/bin" 155 ln -s "$cctools/bin/libtool" "$out/bin/libtool" 156 ''; 157 158 # a script which claims to be a different script but switches to simply touching its output 159 # when an environment variable is set. See CC_host, --cross-compiling, and postConfigure. 160 touchScript = 161 real: 162 writeScript "touch-script" '' 163 #!${stdenv.shell} 164 if [ -z "$FAKE_TOUCH" ]; then 165 exec "${real}" "$@" 166 fi 167 while [ "$#" != "0" ]; do 168 if [ "$1" == "-o" ]; then 169 shift 170 touch "$1" 171 fi 172 shift 173 done 174 ''; 175 176 downloadDir = if lib.strings.hasInfix "-rc." version then "download/rc" else "dist"; 177 178 package = stdenv.mkDerivation ( 179 finalAttrs: 180 let 181 /** 182 the final package fixed point, after potential overrides 183 */ 184 self = finalAttrs.finalPackage; 185 in 186 { 187 inherit pname version; 188 189 src = fetchurl { 190 url = "https://nodejs.org/${downloadDir}/v${version}/node-v${version}.tar.xz"; 191 inherit sha256; 192 }; 193 194 strictDeps = true; 195 196 env = 197 { 198 # Tell ninja to avoid ANSI sequences, otherwise we don’t see build 199 # progress in Nix logs. 200 # 201 # Note: do not set TERM=dumb environment variable globally, it is used in 202 # test-ci-js test suite to skip tests that otherwise run fine. 203 NINJA = "TERM=dumb ninja"; 204 } 205 // lib.optionalAttrs (!canExecute && !canEmulate) { 206 # these are used in the --cross-compiling case. see comment at postConfigure. 207 CC_host = touchScript "${buildPackages.stdenv.cc}/bin/cc"; 208 CXX_host = touchScript "${buildPackages.stdenv.cc}/bin/c++"; 209 AR_host = touchScript "${buildPackages.stdenv.cc}/bin/ar"; 210 }; 211 212 # NB: technically, we do not need bash in build inputs since all scripts are 213 # wrappers over the corresponding JS scripts. There are some packages though 214 # that use bash wrappers, e.g. polaris-web. 215 buildInputs = [ 216 zlib 217 libuv 218 openssl 219 http-parser 220 icu 221 bash 222 ] ++ lib.optionals useSharedSQLite [ sqlite ]; 223 224 nativeBuildInputs = 225 [ 226 installShellFiles 227 ninja 228 pkgconf 229 python 230 ] 231 ++ lib.optionals stdenv.buildPlatform.isDarwin [ 232 # gyp checks `sysctl -n hw.memsize` if `sys.platform == "darwin"`. 233 unixtools.sysctl 234 ] 235 ++ lib.optionals stdenv.hostPlatform.isDarwin [ 236 # For gyp-mac-tool if `flavor == "mac"`. 237 darwin-cctools-only-libtool 238 ]; 239 240 # We currently rely on Makefile and stdenv for build phases, so do not let 241 # ninja’s setup hook to override default stdenv phases. 242 dontUseNinjaBuild = true; 243 dontUseNinjaCheck = true; 244 dontUseNinjaInstall = true; 245 246 outputs = [ 247 "out" 248 "libv8" 249 ] ++ lib.optionals (stdenv.hostPlatform == stdenv.buildPlatform) [ "dev" ]; 250 setOutputFlags = false; 251 moveToDev = false; 252 253 configureFlags = 254 [ 255 "--ninja" 256 "--with-intl=system-icu" 257 "--openssl-use-def-ca-store" 258 # --cross-compiling flag enables use of CC_host et. al 259 (if canExecute || canEmulate then "--no-cross-compiling" else "--cross-compiling") 260 "--dest-os=${destOS}" 261 "--dest-cpu=${destCPU}" 262 ] 263 ++ lib.optionals (destARMFPU != null) [ "--with-arm-fpu=${destARMFPU}" ] 264 ++ lib.optionals (destARMFloatABI != null) [ "--with-arm-float-abi=${destARMFloatABI}" ] 265 ++ lib.optionals (!canExecute && canEmulate) [ 266 # Node.js requires matching bitness between build and host platforms, e.g. 267 # for V8 startup snapshot builder (see tools/snapshot) and some other 268 # tools. We apply a patch that runs these tools using a host platform 269 # emulator and avoid cross-compiling altogether (from the build system’s 270 # perspective). 271 "--emulator=${emulator}" 272 ] 273 ++ lib.optionals (lib.versionOlder version "19") [ "--without-dtrace" ] 274 ++ lib.optionals (!enableNpm) [ "--without-npm" ] 275 ++ lib.concatMap (name: [ 276 "--shared-${name}" 277 "--shared-${name}-libpath=${lib.getLib sharedLibDeps.${name}}/lib" 278 /** 279 Closure notes: we explicitly avoid specifying --shared-*-includes, 280 as that would put the paths into bin/nodejs. 281 Including pkg-config in build inputs would also have the same effect! 282 283 FIXME: the statement above is outdated, we have to include pkg-config 284 in build inputs for system-icu. 285 */ 286 ]) (builtins.attrNames sharedLibDeps); 287 288 configurePlatforms = [ ]; 289 290 dontDisableStatic = true; 291 292 configureScript = writeScript "nodejs-configure" '' 293 exec ${python.executable} configure.py "$@" 294 ''; 295 296 # In order to support unsupported cross configurations, we copy some intermediate executables 297 # from a native build and replace all the build-system tools with a script which simply touches 298 # its outfile. We have to indiana-jones-swap the build-system-targeted tools since they are 299 # tested for efficacy at configure time. 300 postConfigure = lib.optionalString (!canEmulate && !canExecute) '' 301 cp ${buildNode.dev}/bin/* out/Release 302 export FAKE_TOUCH=1 303 ''; 304 305 enableParallelBuilding = true; 306 307 # Don't allow enabling content addressed conversion as `nodejs` 308 # checksums it's image before conversion happens and image loading 309 # breaks: 310 # $ nix build -f. nodejs --arg config '{ contentAddressedByDefault = true; }' 311 # $ ./result/bin/node 312 # Check failed: VerifyChecksum(blob). 313 __contentAddressed = false; 314 315 passthru.interpreterName = "nodejs"; 316 317 passthru.pkgs = callPackage ../../node-packages/default.nix { 318 nodejs = self; 319 }; 320 321 setupHook = ./setup-hook.sh; 322 323 pos = builtins.unsafeGetAttrPos "version" args; 324 325 inherit patches; 326 327 __darwinAllowLocalNetworking = true; # for tests 328 329 doCheck = canExecute; 330 331 # See https://github.com/nodejs/node/issues/22006 332 enableParallelChecking = false; 333 334 # Some dependencies required for tools/doc/node_modules (and therefore 335 # test-addons, jstest and others) target are not included in the tarball. 336 # Run test targets that do not require network access. 337 checkTarget = lib.concatStringsSep " " ( 338 [ 339 "build-js-native-api-tests" 340 "build-node-api-tests" 341 "tooltest" 342 "cctest" 343 ] 344 ++ lib.optionals (!stdenv.buildPlatform.isDarwin || lib.versionAtLeast version "20") [ 345 # There are some test failures on macOS before v20 that are not worth the 346 # time to debug for a version that would be eventually removed in less 347 # than a year (Node.js 18 will be EOL at 2025-04-30). Note that these 348 # failures are specific to Nix sandbox on macOS and should not affect 349 # actual functionality. 350 "test-ci-js" 351 ] 352 ); 353 354 checkFlags = 355 [ 356 # Do not create __pycache__ when running tests. 357 "PYTHONDONTWRITEBYTECODE=1" 358 ] 359 ++ lib.optionals (stdenv.buildPlatform.isDarwin && stdenv.buildPlatform.isx86_64) [ 360 # Python 3.12 introduced a warning for calling `os.fork()` in a 361 # multi‐threaded program. For some reason, the Node.js 362 # `tools/pseudo-tty.py` program used for PTY‐related tests 363 # triggers this warning on Hydra, on `x86_64-darwin` only, 364 # despite not creating any threads itself. This causes the 365 # Node.js test runner to misinterpret the warnings as part of the 366 # test output and fail. It does not reproduce reliably off Hydra 367 # on Intel Macs, or occur on the `aarch64-darwin` builds. 368 # 369 # This seems likely to be related to Rosetta 2, but it could also 370 # be some strange x86‐64‐only threading behaviour of the Darwin 371 # system libraries, or a bug in CPython, or something else 372 # haunted about the Nixpkgs/Hydra build environment. We silence 373 # the warnings in the hope that closing our eyes will make the 374 # ghosts go away. 375 "PYTHONWARNINGS=ignore::DeprecationWarning" 376 ] 377 ++ lib.optionals (!stdenv.buildPlatform.isDarwin || lib.versionAtLeast version "20") [ 378 "FLAKY_TESTS=skip" 379 # Skip some tests that are not passing in this context 380 "CI_SKIP_TESTS=${ 381 lib.concatStringsSep "," ( 382 [ 383 # Tests don't work in sandbox. 384 "test-child-process-exec-env" 385 "test-child-process-uid-gid" 386 "test-fs-write-stream-eagain" 387 "test-process-euid-egid" 388 "test-process-initgroups" 389 "test-process-setgroups" 390 "test-process-uid-gid" 391 "test-setproctitle" 392 # This is a bit weird, but for some reason fs watch tests fail with 393 # sandbox. 394 "test-fs-promises-watch" 395 "test-fs-watch" 396 "test-fs-watch-encoding" 397 "test-fs-watch-non-recursive" 398 "test-fs-watch-recursive-add-file" 399 "test-fs-watch-recursive-add-file-to-existing-subfolder" 400 "test-fs-watch-recursive-add-file-to-new-folder" 401 "test-fs-watch-recursive-add-file-with-url" 402 "test-fs-watch-recursive-add-folder" 403 "test-fs-watch-recursive-assert-leaks" 404 "test-fs-watch-recursive-promise" 405 "test-fs-watch-recursive-symlink" 406 "test-fs-watch-recursive-sync-write" 407 "test-fs-watch-recursive-update-file" 408 "test-fs-watchfile" 409 "test-runner-run" 410 "test-runner-watch-mode" 411 "test-watch-mode-files_watcher" 412 ] 413 ++ lib.optionals (!lib.versionAtLeast version "22") [ 414 "test-tls-multi-key" 415 ] 416 ++ lib.optionals stdenv.hostPlatform.is32bit [ 417 # utime (actually utimensat) fails with EINVAL on 2038 timestamp 418 "test-fs-utimes-y2K38" 419 ] 420 ++ lib.optionals stdenv.buildPlatform.isDarwin [ 421 # Disable tests that don’t work under macOS sandbox. 422 "test-macos-app-sandbox" 423 "test-os" 424 "test-os-process-priority" 425 426 # Debugger tests failing on macOS 15.4 427 "test-debugger-extract-function-name" 428 "test-debugger-random-port-with-inspect-port" 429 "test-debugger-launch" 430 "test-debugger-pid" 431 ] 432 ++ lib.optionals (stdenv.buildPlatform.isDarwin && stdenv.buildPlatform.isx86_64) [ 433 # These tests fail on x86_64-darwin (even without sandbox). 434 # TODO: revisit at a later date. 435 "test-fs-readv" 436 "test-fs-readv-sync" 437 "test-vm-memleak" 438 ] 439 ++ lib.optional ( 440 stdenv.buildPlatform.isDarwin && stdenv.buildPlatform.isx86_64 && majorVersion == "20" 441 ) "test-tick-processor-arguments" # flaky 442 ) 443 }" 444 ]; 445 446 postInstall = 447 let 448 # nodejs_18 does not have node_js2c, and we don't want to rebuild the other ones 449 # FIXME: fix this cleanly in staging 450 tools = 451 if majorVersion == "18" then 452 "{bytecode_builtins_list_generator,mksnapshot,torque,gen-regexp-special-case}" 453 else 454 "{bytecode_builtins_list_generator,mksnapshot,torque,node_js2c,gen-regexp-special-case}"; 455 in 456 lib.optionalString (stdenv.hostPlatform == stdenv.buildPlatform) '' 457 mkdir -p $dev/bin 458 cp out/Release/${tools} $dev/bin 459 '' 460 + '' 461 462 HOST_PATH=$out/bin patchShebangs --host $out 463 464 ${lib.optionalString canExecute '' 465 $out/bin/node --completion-bash > node.bash 466 installShellCompletion node.bash 467 ''} 468 469 ${lib.optionalString enableNpm '' 470 mkdir -p $out/share/bash-completion/completions 471 ln -s $out/lib/node_modules/npm/lib/utils/completion.sh \ 472 $out/share/bash-completion/completions/npm 473 for dir in "$out/lib/node_modules/npm/man/"*; do 474 mkdir -p $out/share/man/$(basename "$dir") 475 for page in "$dir"/*; do 476 ln -rs $page $out/share/man/$(basename "$dir") 477 done 478 done 479 ''} 480 481 # install the missing headers for node-gyp 482 # TODO: add dev output and use propagatedBuildInputs instead of copying headers. 483 cp -r ${lib.concatStringsSep " " copyLibHeaders} $out/include/node 484 485 # assemble a static v8 library and put it in the 'libv8' output 486 mkdir -p $libv8/lib 487 pushd out/Release/obj 488 find . -path "**/torque_*/**/*.o" -or -path "**/v8*/**/*.o" \ 489 -and -not -name "torque.*" \ 490 -and -not -name "mksnapshot.*" \ 491 -and -not -name "gen-regexp-special-case.*" \ 492 -and -not -name "bytecode_builtins_list_generator.*" \ 493 | sort -u >files 494 test -s files # ensure that the list is not empty 495 $AR -cqs $libv8/lib/libv8.a @files 496 popd 497 498 # copy v8 headers 499 cp -r deps/v8/include $libv8/ 500 501 # create a pkgconfig file for v8 502 major=$(grep V8_MAJOR_VERSION deps/v8/include/v8-version.h | cut -d ' ' -f 3) 503 minor=$(grep V8_MINOR_VERSION deps/v8/include/v8-version.h | cut -d ' ' -f 3) 504 patch=$(grep V8_PATCH_LEVEL deps/v8/include/v8-version.h | cut -d ' ' -f 3) 505 mkdir -p $libv8/lib/pkgconfig 506 cat > $libv8/lib/pkgconfig/v8.pc << EOF 507 Name: v8 508 Description: V8 JavaScript Engine 509 Version: $major.$minor.$patch 510 Libs: -L$libv8/lib -lv8 -pthread -licui18n -licuuc 511 Cflags: -I$libv8/include 512 EOF 513 ''; 514 515 passthru.tests = { 516 version = testers.testVersion { 517 package = self; 518 version = "v${lib.head (lib.strings.splitString "-rc." version)}"; 519 }; 520 }; 521 522 passthru.updateScript = import ./update.nix { 523 inherit 524 writeScript 525 common-updater-scripts 526 coreutils 527 curl 528 fetchurl 529 gnugrep 530 gnupg 531 jq 532 runtimeShell 533 ; 534 inherit lib; 535 inherit majorVersion; 536 }; 537 538 meta = with lib; { 539 description = "Event-driven I/O framework for the V8 JavaScript engine"; 540 homepage = "https://nodejs.org"; 541 changelog = "https://github.com/nodejs/node/releases/tag/v${version}"; 542 license = licenses.mit; 543 maintainers = with maintainers; [ aduh95 ]; 544 platforms = platforms.linux ++ platforms.darwin ++ platforms.freebsd; 545 # This broken condition is likely too conservative. Feel free to loosen it if it works. 546 broken = 547 !canExecute && !canEmulate && (stdenv.buildPlatform.parsed.cpu != stdenv.hostPlatform.parsed.cpu); 548 mainProgram = "node"; 549 knownVulnerabilities = optional (versionOlder version "18") "This NodeJS release has reached its end of life. See https://nodejs.org/en/about/releases/."; 550 }; 551 552 passthru.python = python; # to ensure nodeEnv uses the same version 553 } 554 ); 555in 556package