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