at master 17 kB view raw
1{ 2 lib, 3 stdenv, 4 removeReferencesTo, 5 pkgsBuildBuild, 6 pkgsBuildHost, 7 pkgsBuildTarget, 8 targetPackages, 9 llvmShared, 10 llvmSharedForBuild, 11 llvmSharedForHost, 12 llvmSharedForTarget, 13 llvmPackages, 14 runCommandLocal, 15 fetchurl, 16 file, 17 python3, 18 cargo, 19 cmake, 20 rustc, 21 rustfmt, 22 pkg-config, 23 openssl, 24 xz, 25 zlib, 26 bintools, 27 which, 28 libffi, 29 withBundledLLVM ? false, 30 enableRustcDev ? true, 31 version, 32 sha256, 33 patches ? [ ], 34 fd, 35 ripgrep, 36 wezterm, 37 firefox, 38 thunderbird, 39 # This only builds std for target and reuses the rustc from build. 40 fastCross, 41 lndir, 42 makeWrapper, 43}: 44 45let 46 inherit (lib) 47 optionals 48 optional 49 optionalString 50 concatStringsSep 51 ; 52 useLLVM = stdenv.targetPlatform.useLLVM or false; 53in 54stdenv.mkDerivation (finalAttrs: { 55 pname = "${targetPackages.stdenv.cc.targetPrefix}rustc"; 56 inherit version; 57 58 src = fetchurl { 59 url = "https://static.rust-lang.org/dist/rustc-${version}-src.tar.gz"; 60 inherit sha256; 61 # See https://nixos.org/manual/nixpkgs/stable/#using-git-bisect-on-the-rust-compiler 62 passthru.isReleaseTarball = true; 63 }; 64 65 hardeningDisable = optionals stdenv.cc.isClang [ 66 # remove once https://github.com/NixOS/nixpkgs/issues/318674 is 67 # addressed properly 68 "zerocallusedregs" 69 ]; 70 71 __darwinAllowLocalNetworking = true; 72 73 # rustc complains about modified source files otherwise 74 dontUpdateAutotoolsGnuConfigScripts = true; 75 76 # Running the default `strip -S` command on Darwin corrupts the 77 # .rlib files in "lib/". 78 # 79 # See https://github.com/NixOS/nixpkgs/pull/34227 80 # 81 # Running `strip -S` when cross compiling can harm the cross rlibs. 82 # See: https://github.com/NixOS/nixpkgs/pull/56540#issuecomment-471624656 83 stripDebugList = [ "bin" ]; 84 85 NIX_LDFLAGS = toString ( 86 # when linking stage1 libstd: cc: undefined reference to `__cxa_begin_catch' 87 # This doesn't apply to cross-building for FreeBSD because the host 88 # uses libstdc++, but the target (used for building std) uses libc++ 89 optional ( 90 stdenv.hostPlatform.isLinux && !withBundledLLVM && !stdenv.targetPlatform.isFreeBSD && !useLLVM 91 ) "--push-state --as-needed -lstdc++ --pop-state" 92 ++ 93 optional 94 (stdenv.hostPlatform.isLinux && !withBundledLLVM && !stdenv.targetPlatform.isFreeBSD && useLLVM) 95 "--push-state --as-needed -L${llvmPackages.libcxx}/lib -lc++ -lc++abi -lLLVM-${lib.versions.major llvmPackages.llvm.version} --pop-state" 96 ++ optional (stdenv.hostPlatform.isDarwin && !withBundledLLVM) "-lc++ -lc++abi" 97 ++ optional stdenv.hostPlatform.isDarwin "-rpath ${llvmSharedForHost.lib}/lib" 98 ); 99 100 # Increase codegen units to introduce parallelism within the compiler. 101 RUSTFLAGS = "-Ccodegen-units=10"; 102 RUSTDOCFLAGS = "-A rustdoc::broken-intra-doc-links"; 103 104 # We need rust to build rust. If we don't provide it, configure will try to download it. 105 # Reference: https://github.com/rust-lang/rust/blob/master/src/bootstrap/configure.py 106 configureFlags = 107 let 108 prefixForStdenv = stdenv: "${stdenv.cc}/bin/${stdenv.cc.targetPrefix}"; 109 ccPrefixForStdenv = 110 stdenv: "${prefixForStdenv stdenv}${if (stdenv.cc.isClang or false) then "clang" else "cc"}"; 111 cxxPrefixForStdenv = 112 stdenv: "${prefixForStdenv stdenv}${if (stdenv.cc.isClang or false) then "clang++" else "c++"}"; 113 setBuild = "--set=target.\"${stdenv.buildPlatform.rust.rustcTargetSpec}\""; 114 setHost = "--set=target.\"${stdenv.hostPlatform.rust.rustcTargetSpec}\""; 115 setTarget = "--set=target.\"${stdenv.targetPlatform.rust.rustcTargetSpec}\""; 116 ccForBuild = ccPrefixForStdenv pkgsBuildBuild.targetPackages.stdenv; 117 cxxForBuild = cxxPrefixForStdenv pkgsBuildBuild.targetPackages.stdenv; 118 ccForHost = ccPrefixForStdenv pkgsBuildHost.targetPackages.stdenv; 119 cxxForHost = cxxPrefixForStdenv pkgsBuildHost.targetPackages.stdenv; 120 ccForTarget = ccPrefixForStdenv pkgsBuildTarget.targetPackages.stdenv; 121 cxxForTarget = cxxPrefixForStdenv pkgsBuildTarget.targetPackages.stdenv; 122 in 123 [ 124 "--sysconfdir=${placeholder "out"}/etc" 125 "--release-channel=stable" 126 "--set=build.rustc=${rustc}/bin/rustc" 127 "--set=build.cargo=${cargo}/bin/cargo" 128 ] 129 ++ lib.optionals (!(finalAttrs.src.passthru.isReleaseTarball or false)) [ 130 # release tarballs vendor the rustfmt source; when 131 # git-bisect'ing from upstream's git repo we must prevent 132 # attempts to download the missing source tarball 133 "--set=build.rustfmt=${rustfmt}/bin/rustfmt" 134 ] 135 ++ [ 136 "--tools=rustc,rustdoc,rust-analyzer-proc-macro-srv" 137 "--enable-rpath" 138 "--enable-vendor" 139 # For Nixpkgs it makes more sense to use stdenv's linker than 140 # letting rustc build its own. 141 "--disable-lld" 142 "--build=${stdenv.buildPlatform.rust.rustcTargetSpec}" 143 "--host=${stdenv.hostPlatform.rust.rustcTargetSpec}" 144 # std is built for all platforms in --target. 145 "--target=${ 146 concatStringsSep "," ( 147 # Other targets that don't need any extra dependencies to build. 148 optionals (!fastCross) [ 149 "wasm32-unknown-unknown" 150 "wasm32v1-none" 151 "bpfel-unknown-none" 152 "bpfeb-unknown-none" 153 ] 154 # (build!=target): When cross-building a compiler we need to add 155 # the build platform as well so rustc can compile build.rs 156 # scripts. 157 ++ optionals (stdenv.buildPlatform != stdenv.targetPlatform && !fastCross) [ 158 stdenv.buildPlatform.rust.rustcTargetSpec 159 ] 160 # (host!=target): When building a cross-targeting compiler we 161 # need to add the host platform as well so rustc can compile 162 # build.rs scripts. 163 ++ optionals (stdenv.hostPlatform != stdenv.targetPlatform && !fastCross) [ 164 stdenv.hostPlatform.rust.rustcTargetSpec 165 ] 166 ++ [ 167 # `make install` only keeps the docs of the last target in the list. 168 # If the `targetPlatform` is not the last entry, we may end up without 169 # `alloc` or `std` docs (if the last target is `no_std`). 170 # More information: https://github.com/rust-lang/rust/issues/140922 171 stdenv.targetPlatform.rust.rustcTargetSpec 172 ] 173 ) 174 }" 175 176 "${setBuild}.cc=${ccForBuild}" 177 "${setHost}.cc=${ccForHost}" 178 "${setTarget}.cc=${ccForTarget}" 179 180 # The Rust compiler build identifies platforms by Rust target 181 # name, and later arguments override previous arguments. This 182 # means that when platforms differ in configuration but overlap 183 # in Rust target name (for instance, `pkgsStatic`), only one 184 # setting will be applied for any given option. 185 # 186 # This is usually mostly harmless, especially as `fastCross` 187 # means that we usually only compile `std` in such cases. 188 # However, the build does still need to link helper tools for the 189 # build platform in that case. This was breaking the Darwin 190 # `pkgsStatic` build, as it was attempting to link build tools 191 # with the target platform’s static linker, and failing to locate 192 # an appropriate static library for `-liconv`. 193 # 194 # Since the static build does not link anything for the target 195 # platform anyway, we put the target linker first so that the 196 # build platform linker overrides it when the Rust target names 197 # overlap, allowing the helper tools to build successfully. 198 # 199 # Note that Rust does not remember these settings in the built 200 # compiler, so this does not compromise any functionality of the 201 # resulting compiler. 202 # 203 # The longer‐term fix would be to get Rust to use a more nuanced 204 # understanding of platforms, such as by explicitly constructing 205 # and passing Rust JSON target definitions that let us 206 # distinguish the platforms in cases like these. That would also 207 # let us supplant various hacks around the wrappers and hooks 208 # that we currently need. 209 "${setTarget}.linker=${ccForTarget}" 210 "${setBuild}.linker=${ccForBuild}" 211 "${setHost}.linker=${ccForHost}" 212 213 "${setBuild}.cxx=${cxxForBuild}" 214 "${setHost}.cxx=${cxxForHost}" 215 "${setTarget}.cxx=${cxxForTarget}" 216 217 "${setBuild}.crt-static=${lib.boolToString stdenv.buildPlatform.isStatic}" 218 "${setHost}.crt-static=${lib.boolToString stdenv.hostPlatform.isStatic}" 219 "${setTarget}.crt-static=${lib.boolToString stdenv.targetPlatform.isStatic}" 220 ] 221 ++ optionals (!withBundledLLVM) [ 222 "--enable-llvm-link-shared" 223 "${setBuild}.llvm-config=${llvmSharedForBuild.dev}/bin/llvm-config" 224 "${setHost}.llvm-config=${llvmSharedForHost.dev}/bin/llvm-config" 225 "${setTarget}.llvm-config=${llvmSharedForTarget.dev}/bin/llvm-config" 226 ] 227 ++ optionals fastCross [ 228 # Since fastCross only builds std, it doesn't make sense (and 229 # doesn't work) to build a linker. 230 "--disable-llvm-bitcode-linker" 231 ] 232 ++ optionals (stdenv.targetPlatform.isLinux && !(stdenv.targetPlatform.useLLVM or false)) [ 233 "--enable-profiler" # build libprofiler_builtins 234 ] 235 ++ optionals stdenv.buildPlatform.isMusl [ 236 "${setBuild}.musl-root=${pkgsBuildBuild.targetPackages.stdenv.cc.libc}" 237 ] 238 ++ optionals stdenv.hostPlatform.isMusl [ 239 "${setHost}.musl-root=${pkgsBuildHost.targetPackages.stdenv.cc.libc}" 240 ] 241 ++ optionals stdenv.targetPlatform.isMusl [ 242 "${setTarget}.musl-root=${pkgsBuildTarget.targetPackages.stdenv.cc.libc}" 243 ] 244 ++ optionals stdenv.targetPlatform.rust.isNoStdTarget [ 245 "--disable-docs" 246 ] 247 ++ optionals (stdenv.hostPlatform.isDarwin && stdenv.hostPlatform.isx86_64) [ 248 # https://github.com/rust-lang/rust/issues/92173 249 "--set rust.jemalloc" 250 ] 251 ++ optionals (useLLVM && !stdenv.targetPlatform.isFreeBSD) [ 252 # https://github.com/NixOS/nixpkgs/issues/311930 253 "--llvm-libunwind=${if withBundledLLVM then "in-tree" else "system"}" 254 "--enable-use-libcxx" 255 ] 256 ++ optionals (!stdenv.hostPlatform.isx86_32) [ 257 # This enables frame pointers for the compiler itself. 258 # 259 # Note that when compiling std, frame pointers (or at least non-leaf 260 # frame pointers, depending on version) are unconditionally enabled and 261 # cannot be overridden, so we do not touch that. (Also note this only 262 # applies to functions that can be immediately compiled when building 263 # std. Generic functions that do codegen when called in user code obey 264 # -Cforce-frame-pointers specified then, if any) 265 "--set rust.frame-pointers" 266 ]; 267 268 # if we already have a rust compiler for build just compile the target std 269 # library and reuse compiler 270 buildPhase = 271 if fastCross then 272 '' 273 runHook preBuild 274 275 mkdir -p build/${stdenv.hostPlatform.rust.rustcTargetSpec}/stage0-{std,rustc}/${stdenv.hostPlatform.rust.rustcTargetSpec}/release/ 276 ln -s ${rustc.unwrapped}/lib/rustlib/${stdenv.hostPlatform.rust.rustcTargetSpec}/libstd-*.so build/${stdenv.hostPlatform.rust.rustcTargetSpec}/stage0-std/${stdenv.hostPlatform.rust.rustcTargetSpec}/release/libstd.so 277 ln -s ${rustc.unwrapped}/lib/rustlib/${stdenv.hostPlatform.rust.rustcTargetSpec}/librustc_driver-*.so build/${stdenv.hostPlatform.rust.rustcTargetSpec}/stage0-rustc/${stdenv.hostPlatform.rust.rustcTargetSpec}/release/librustc.so 278 ln -s ${rustc.unwrapped}/bin/rustc build/${stdenv.hostPlatform.rust.rustcTargetSpec}/stage0-rustc/${stdenv.hostPlatform.rust.rustcTargetSpec}/release/rustc-main 279 touch build/${stdenv.hostPlatform.rust.rustcTargetSpec}/stage0-std/${stdenv.hostPlatform.rust.rustcTargetSpec}/release/.libstd-stamp 280 touch build/${stdenv.hostPlatform.rust.rustcTargetSpec}/stage0-rustc/${stdenv.hostPlatform.rust.rustcTargetSpec}/release/.librustc-stamp 281 python ./x.py --keep-stage=0 --stage=1 build library 282 283 runHook postBuild 284 '' 285 else 286 null; 287 288 installPhase = 289 if fastCross then 290 '' 291 runHook preInstall 292 293 python ./x.py --keep-stage=0 --stage=1 install library/std 294 mkdir -v $doc $man 295 ln -s ${rustc.unwrapped}/{bin,libexec} $out 296 rm -rf -v $out/lib/rustlib/{manifest-rust-std-,}${stdenv.hostPlatform.rust.rustcTargetSpec} 297 ln -s ${rustc.unwrapped}/lib/rustlib/{manifest-rust-std-,}${stdenv.hostPlatform.rust.rustcTargetSpec} $out/lib/rustlib/ 298 ln -s ${rustc.unwrapped}/lib/rustlib/etc $out/lib/rustlib/ 299 echo rust-std-${stdenv.hostPlatform.rust.rustcTargetSpec} >> $out/lib/rustlib/components 300 lndir ${rustc.doc} $doc 301 lndir ${rustc.man} $man 302 303 runHook postInstall 304 '' 305 else 306 null; 307 308 # the rust build system complains that nix alters the checksums 309 dontFixLibtool = true; 310 311 inherit patches; 312 313 postPatch = '' 314 patchShebangs src/etc 315 316 # rust-lld is the name rustup uses for its bundled lld, so that it 317 # doesn't conflict with any system lld. This is not an 318 # appropriate default for Nixpkgs, where there is no rust-lld. 319 substituteInPlace compiler/rustc_target/src/spec/*/*.rs \ 320 --replace-quiet '"rust-lld"' '"lld"' 321 322 ${optionalString (!withBundledLLVM) "rm -rf src/llvm"} 323 324 # Useful debugging parameter 325 # export VERBOSE=1 326 '' 327 + lib.optionalString (stdenv.hostPlatform.isDarwin || stdenv.targetPlatform.isDarwin) '' 328 # Replace hardcoded path to strip with llvm-strip 329 # https://github.com/NixOS/nixpkgs/issues/299606 330 substituteInPlace compiler/rustc_codegen_ssa/src/back/link.rs \ 331 --replace-fail "/usr/bin/strip" "${lib.getExe' llvmShared "llvm-strip"}" 332 '' 333 + lib.optionalString (stdenv.hostPlatform.isDarwin && stdenv.hostPlatform.isx86_64) '' 334 # See https://github.com/jemalloc/jemalloc/issues/1997 335 # Using a value of 48 should work on both emulated and native x86_64-darwin. 336 export JEMALLOC_SYS_WITH_LG_VADDR=48 337 '' 338 + lib.optionalString (!(finalAttrs.src.passthru.isReleaseTarball or false)) '' 339 mkdir .cargo 340 cat > .cargo/config.toml <<\EOF 341 [source.crates-io] 342 replace-with = "vendored-sources" 343 [source.vendored-sources] 344 directory = "vendor" 345 EOF 346 '' 347 + lib.optionalString (stdenv.hostPlatform.isFreeBSD) '' 348 # lzma-sys bundles an old version of xz that doesn't build 349 # on modern FreeBSD, use the system one instead 350 substituteInPlace src/bootstrap/src/core/build_steps/tool.rs \ 351 --replace 'cargo.env("LZMA_API_STATIC", "1");' ' ' 352 ''; 353 354 # rustc unfortunately needs cmake to compile llvm-rt but doesn't 355 # use it for the normal build. This disables cmake in Nix. 356 dontUseCmakeConfigure = true; 357 358 depsBuildBuild = [ 359 pkgsBuildHost.stdenv.cc 360 pkg-config 361 ]; 362 depsBuildTarget = lib.optionals stdenv.targetPlatform.isMinGW [ bintools ]; 363 364 nativeBuildInputs = [ 365 file 366 python3 367 rustc 368 cmake 369 which 370 libffi 371 removeReferencesTo 372 pkg-config 373 xz 374 ] 375 ++ optionals fastCross [ 376 lndir 377 makeWrapper 378 ]; 379 380 buildInputs = [ 381 openssl 382 ] 383 ++ optionals stdenv.hostPlatform.isDarwin [ 384 zlib 385 ] 386 ++ optional (!withBundledLLVM) llvmShared.lib 387 ++ optional (useLLVM && !withBundledLLVM) llvmPackages.libunwind; 388 389 outputs = [ 390 "out" 391 "man" 392 "doc" 393 ]; 394 setOutputFlags = false; 395 396 postInstall = 397 lib.optionalString (enableRustcDev && !fastCross) '' 398 # install rustc-dev components. Necessary to build rls, clippy... 399 python x.py dist rustc-dev 400 tar xf build/dist/rustc-dev*tar.gz 401 cp -r rustc-dev*/rustc-dev*/lib/* $out/lib/ 402 rm $out/lib/rustlib/install.log 403 for m in $out/lib/rustlib/manifest-rust* 404 do 405 sort --output=$m < $m 406 done 407 408 '' 409 + '' 410 # remove references to llvm-config in lib/rustlib/x86_64-unknown-linux-gnu/codegen-backends/librustc_codegen_llvm-llvm.so 411 # and thus a transitive dependency on ncurses 412 find $out/lib -name "*.so" -type f -exec remove-references-to -t ${llvmShared} '{}' '+' 413 414 # remove uninstall script that doesn't really make sense for Nix. 415 rm $out/lib/rustlib/uninstall.sh 416 ''; 417 418 configurePlatforms = [ ]; 419 420 enableParallelBuilding = true; 421 422 setupHooks = ./setup-hook.sh; 423 424 requiredSystemFeatures = [ "big-parallel" ]; 425 426 passthru = { 427 llvm = llvmShared; 428 inherit llvmPackages; 429 inherit (rustc) targetPlatforms targetPlatformsWithHostTools badTargetPlatforms; 430 tests = { 431 inherit fd ripgrep wezterm; 432 } 433 // lib.optionalAttrs stdenv.hostPlatform.isLinux { inherit firefox thunderbird; }; 434 }; 435 436 meta = with lib; { 437 homepage = "https://www.rust-lang.org/"; 438 description = "Safe, concurrent, practical language"; 439 maintainers = with maintainers; [ havvy ]; 440 teams = [ teams.rust ]; 441 license = [ 442 licenses.mit 443 licenses.asl20 444 ]; 445 platforms = rustc.targetPlatformsWithHostTools; 446 # If rustc can't target a platform, we also can't build rustc for 447 # that platform. 448 badPlatforms = rustc.badTargetPlatforms; 449 }; 450})