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