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