mirror of
https://github.com/oxalica/rust-overlay.git
synced 2025-10-06 00:02:40 +02:00
*: move internal files into lib
subdir
They are not part of our public interfaces and must not be imported alone.
This commit is contained in:
11
lib/cargo-miri-wrapper.sh
Executable file
11
lib/cargo-miri-wrapper.sh
Executable file
@@ -0,0 +1,11 @@
|
||||
#!@bash@ -e
|
||||
src_dir="@out@/lib/rustlib/src/rust/library"
|
||||
if [[ ! -v XARGO_RUST_SRC ]]; then
|
||||
if [[ ! -d "$src_dir" ]]; then
|
||||
echo '`rust-src` is required by miri but not installed.' >&2
|
||||
echo 'Please either install component `rust-src` or set `XARGO_RUST_SRC`.' >&2
|
||||
exit 1
|
||||
fi
|
||||
export XARGO_RUST_SRC="$src_dir"
|
||||
fi
|
||||
exec -a "$0" "@cargo_miri@" "$@"
|
125
lib/manifests.nix
Normal file
125
lib/manifests.nix
Normal file
@@ -0,0 +1,125 @@
|
||||
final: prev:
|
||||
let
|
||||
inherit (builtins) match isString toString;
|
||||
|
||||
inherit (final.lib)
|
||||
attrNames concatMap elemAt filter hasAttr mapAttrs mapAttrs' removeSuffix;
|
||||
|
||||
targets = import ../manifests/targets.nix // { _ = "*"; };
|
||||
renamesList = import ../manifests/renames.nix;
|
||||
profilesList = import ../manifests/profiles.nix;
|
||||
|
||||
inherit (final.rust-bin) distRoot;
|
||||
|
||||
# Extensions for mixed `rust` pkg.
|
||||
components = [
|
||||
"rustc"
|
||||
"rust-std"
|
||||
"cargo"
|
||||
];
|
||||
singleTargetExtensions = [
|
||||
"clippy-preview"
|
||||
"miri-preview"
|
||||
"rls-preview"
|
||||
"rust-analyzer-preview"
|
||||
"rustfmt-preview"
|
||||
"llvm-tools-preview"
|
||||
"rust-analysis"
|
||||
];
|
||||
multiTargetExtensions = [
|
||||
"rust-std"
|
||||
"rustc-dev"
|
||||
"rustc-docs"
|
||||
"rust-src" # This has only one special target `*`
|
||||
];
|
||||
rustPkgExtra = pkgs: target: let
|
||||
singleTargetTups = map
|
||||
(pkg: { inherit pkg target; })
|
||||
(filter (p: hasAttr p pkgs && hasAttr target pkgs.${p}.target) singleTargetExtensions);
|
||||
multiTargetTups = concatMap
|
||||
(pkg: map (target: { inherit pkg target; }) (attrNames pkgs.${pkg}.target))
|
||||
(filter (p: hasAttr p pkgs) multiTargetExtensions);
|
||||
in {
|
||||
components = map (pkg: { inherit pkg target; }) components;
|
||||
extensions = singleTargetTups ++ multiTargetTups;
|
||||
};
|
||||
|
||||
# Uncompress the compressed manifest to the original one
|
||||
# (not complete but has enough information to make up the toolchain).
|
||||
uncompressManifest = channel: version: {
|
||||
v, # Rustc version
|
||||
d, # Date
|
||||
r, # Renames index
|
||||
p ? null, # Profiles index
|
||||
...
|
||||
}@manifest: rec {
|
||||
|
||||
# Version used for derivation.
|
||||
version = if match ".*(nightly|beta).*" v != null
|
||||
then "${v}-${d}" # 1.51.0-nightly-2021-01-01, 1.52.0-beta.2-2021-03-27
|
||||
else v; # 1.51.0
|
||||
|
||||
date = d;
|
||||
renames = mapAttrs (from: to: { inherit to; }) (elemAt renamesList r);
|
||||
|
||||
pkg =
|
||||
mapAttrs (pkgName: { u ? null /* Version appears in URL */, ... }@hashes: {
|
||||
# We use rustc version for all components to reduce manifest size.
|
||||
# This version is just used for component derivation name.
|
||||
version = "${v} (000000000 ${d})"; # "<version> (<commit-hash> yyyy-mm-dd)"
|
||||
target = let
|
||||
results = mapAttrs' (targetIdx: hash: let
|
||||
target = targets.${targetIdx};
|
||||
pkgNameStripped = removeSuffix "-preview" pkgName;
|
||||
targetTail = if targetIdx == "_" then "" else "-" + target;
|
||||
urlVersion =
|
||||
if u != null then u # Use specified version for URL if exists.
|
||||
else if channel == "stable" then v # For stable channel, default to be rustc version.
|
||||
else channel; # Otherwise, for beta/nightly channel, default to be "beta"/"nightly".
|
||||
in {
|
||||
name = target;
|
||||
value =
|
||||
# Normally, hash is just the hash.
|
||||
if isString hash then
|
||||
{
|
||||
xz_url = "${distRoot}/${date}/${pkgNameStripped}-${urlVersion}${targetTail}.tar.xz";
|
||||
xz_hash = hash;
|
||||
} // (if pkgName == "rust" then rustPkgExtra pkg target else {})
|
||||
# But hash can be an integer to forward to another URL.
|
||||
# This occurs in aarch64-apple-darwin rust-docs on 2022-02-02.
|
||||
else
|
||||
results.${targets."_${toString hash}"};
|
||||
}) (removeAttrs hashes ["u"]);
|
||||
in
|
||||
results;
|
||||
}) (removeAttrs manifest ["v" "d" "r" "p"]);
|
||||
|
||||
profiles = if p == null
|
||||
then {}
|
||||
else elemAt profilesList p;
|
||||
|
||||
targetComponentsList = [
|
||||
"rust-std"
|
||||
"rustc-dev"
|
||||
"rustc-docs"
|
||||
];
|
||||
};
|
||||
|
||||
uncompressManifestSet = channel: set: let
|
||||
ret = mapAttrs (uncompressManifest channel) (removeAttrs set ["latest"]);
|
||||
in ret // { latest = ret.${set.latest}; };
|
||||
|
||||
in {
|
||||
rust-bin = (prev.rust-bin or {}) // {
|
||||
# The dist url for fetching.
|
||||
# Override it if you want to use a mirror server.
|
||||
distRoot = "https://static.rust-lang.org/dist";
|
||||
|
||||
# For internal usage.
|
||||
manifests = {
|
||||
stable = uncompressManifestSet "stable" (import ../manifests/stable);
|
||||
beta = uncompressManifestSet "beta" (import ../manifests/beta);
|
||||
nightly = uncompressManifestSet "nightly" (import ../manifests/nightly);
|
||||
};
|
||||
};
|
||||
}
|
94
lib/mk-aggregated.nix
Normal file
94
lib/mk-aggregated.nix
Normal file
@@ -0,0 +1,94 @@
|
||||
{ lib, stdenv, symlinkJoin, pkgsTargetTarget, bash, curl }:
|
||||
{ pname, version, date, selectedComponents, availableComponents ? selectedComponents }:
|
||||
let
|
||||
inherit (lib) optional;
|
||||
inherit (stdenv) targetPlatform;
|
||||
in
|
||||
symlinkJoin {
|
||||
name = pname + "-" + version;
|
||||
inherit pname version;
|
||||
|
||||
paths = selectedComponents;
|
||||
|
||||
passthru = { inherit availableComponents; };
|
||||
|
||||
# Ourselves have offset -1. In order to make these offset -1 dependencies of downstream derivation,
|
||||
# they are offset 0 propagated.
|
||||
|
||||
# CC for build script linking.
|
||||
# Workaround: should be `pkgsHostHost.cc` but `stdenv`'s cc itself have -1 offset.
|
||||
depsHostHostPropagated = [ stdenv.cc ];
|
||||
|
||||
# CC for crate linking.
|
||||
# Workaround: should be `pkgsHostTarget.cc` but `stdenv`'s cc itself have -1 offset.
|
||||
# N.B. WASM targets don't need our CC.
|
||||
propagatedBuildInputs =
|
||||
optional (!targetPlatform.isWasm) pkgsTargetTarget.stdenv.cc;
|
||||
|
||||
# Link dependency for target, required by darwin std.
|
||||
depsTargetTargetPropagated =
|
||||
optional (targetPlatform.isDarwin) pkgsTargetTarget.libiconv;
|
||||
|
||||
# If rustc or rustdoc is in the derivation, we need to copy their
|
||||
# executable into the final derivation. This is required
|
||||
# for making them find the correct SYSROOT.
|
||||
postBuild = ''
|
||||
for file in $out/bin/{rustc,rustdoc,miri,cargo-miri}; do
|
||||
if [ -e $file ]; then
|
||||
cp --remove-destination "$(realpath -e $file)" $file
|
||||
fi
|
||||
done
|
||||
''
|
||||
# Workaround: https://github.com/rust-lang/rust/pull/103660
|
||||
# FIXME: This duplicates the space usage since `librustc_driver` is huge.
|
||||
+ lib.optionalString (date == null || date >= "2022-11-01") ''
|
||||
for file in $out/bin/{rustc,rustdoc,miri,cargo-miri,cargo-clippy,clippy-driver}; do
|
||||
if [ -e $file ]; then
|
||||
[[ $file != */*clippy* ]] || cp --remove-destination "$(realpath -e $file)" $file
|
||||
chmod +w $file
|
||||
${lib.optionalString stdenv.isLinux ''
|
||||
patchelf --set-rpath $out/lib "$file" || true
|
||||
''}
|
||||
${lib.optionalString stdenv.isDarwin ''
|
||||
install_name_tool -add_rpath $out/lib "$file" || true
|
||||
''}
|
||||
fi
|
||||
done
|
||||
${lib.optionalString stdenv.isDarwin ''
|
||||
cargo="$out/bin/cargo"
|
||||
if [ -e "$cargo" ]; then
|
||||
cp --remove-destination "$(realpath -e $cargo)" "$cargo"
|
||||
chmod +w "$cargo"
|
||||
install_name_tool -change "/usr/lib/libcurl.4.dylib" "${curl.out}/lib/libcurl.4.dylib" "$cargo"
|
||||
fi
|
||||
''}
|
||||
shopt nullglob
|
||||
for file in $out/lib/librustc_driver*; do
|
||||
cp --remove-destination "$(realpath -e $file)" $file
|
||||
done
|
||||
|
||||
# installed with rustc-dev
|
||||
for file in $out/lib/rustlib/*/lib/librustc_driver*; do
|
||||
cp --remove-destination "$(realpath -e $file)" $file
|
||||
done
|
||||
''
|
||||
+ ''
|
||||
if [ -e $out/bin/cargo-miri ]; then
|
||||
mv $out/bin/{cargo-miri,.cargo-miri-wrapped}
|
||||
cp -f ${./cargo-miri-wrapper.sh} $out/bin/cargo-miri
|
||||
chmod +w $out/bin/cargo-miri
|
||||
substituteInPlace $out/bin/cargo-miri \
|
||||
--replace "@bash@" "${bash}/bin/bash" \
|
||||
--replace "@cargo_miri@" "$out/bin/.cargo-miri-wrapped" \
|
||||
--replace "@out@" "$out"
|
||||
fi
|
||||
|
||||
# symlinkJoin doesn't automatically handle it. Thus do it manually.
|
||||
mkdir -p $out/nix-support
|
||||
echo "$depsHostHostPropagated " >$out/nix-support/propagated-host-host-deps
|
||||
[[ -z "$propagatedBuildInputs" ]] || echo "$propagatedBuildInputs " >$out/nix-support/propagated-build-inputs
|
||||
[[ -z "$depsTargetTargetPropagated" ]] || echo "$depsTargetTargetPropagated " >$out/nix-support/propagated-target-target-deps
|
||||
'';
|
||||
|
||||
meta.platforms = lib.platforms.all;
|
||||
}
|
157
lib/mk-component-set.nix
Normal file
157
lib/mk-component-set.nix
Normal file
@@ -0,0 +1,157 @@
|
||||
# Define component derivations and special treatments.
|
||||
{ lib, stdenv, stdenvNoCC, gnutar, autoPatchelfHook, bintools, zlib, gccForLibs
|
||||
# The path to nixpkgs root.
|
||||
, path
|
||||
, toRustTarget, removeNulls
|
||||
}:
|
||||
# Release version of the whole set.
|
||||
{ version
|
||||
# The host platform of this set.
|
||||
, platform
|
||||
# Set of pname -> src
|
||||
, srcs
|
||||
# { clippy.to = "clippy-preview"; }
|
||||
, renames
|
||||
}:
|
||||
let
|
||||
inherit (lib) elem mapAttrs optional optionalString;
|
||||
inherit (stdenv) hostPlatform;
|
||||
|
||||
mkComponent = pname: src: let
|
||||
# These components link to `librustc_driver*.so` or `libLLVM*.so`.
|
||||
linksToRustc = elem pname [
|
||||
"clippy-preview"
|
||||
"miri-preview"
|
||||
"rls-preview"
|
||||
"rust-analyzer-preview"
|
||||
"rustc-codegen-cranelift-preview"
|
||||
"rustc-dev"
|
||||
"rustfmt-preview"
|
||||
];
|
||||
in
|
||||
stdenvNoCC.mkDerivation rec {
|
||||
inherit pname version src;
|
||||
name = "${pname}-${version}-${platform}";
|
||||
|
||||
passthru.platform = platform;
|
||||
|
||||
# No point copying src to a build server, then copying back the
|
||||
# entire unpacked contents after just a little twiddling.
|
||||
preferLocalBuild = true;
|
||||
|
||||
nativeBuildInputs = [ gnutar ] ++
|
||||
# Darwin doesn't use ELF, and they usually just work due to relative RPATH.
|
||||
optional (!dontFixup && !hostPlatform.isDarwin) autoPatchelfHook ++
|
||||
# For `install_name_tool`.
|
||||
optional (hostPlatform.isDarwin && linksToRustc) bintools;
|
||||
|
||||
buildInputs =
|
||||
optional (elem pname [ "rustc" "cargo" "llvm-tools-preview" "rust" ]) zlib ++
|
||||
optional linksToRustc self.rustc;
|
||||
|
||||
# Most of binaries links to `libgcc.so` on Linux, which lives in `gccForLibs.libgcc`
|
||||
# since https://github.com/NixOS/nixpkgs/pull/209870
|
||||
# See https://github.com/oxalica/rust-overlay/issues/121
|
||||
#
|
||||
# Nightly `rustc` since 2022-02-17 links to `libstdc++.so.6` on Linux,
|
||||
# which lives in `gccForLibs.lib`.
|
||||
# https://github.com/oxalica/rust-overlay/issues/73
|
||||
# See https://github.com/oxalica/rust-overlay/issues/73
|
||||
#
|
||||
# FIXME: `libstdc++.so` is not necessary now. Figure out the time point
|
||||
# of it so we can use `gccForLibs.libgcc` instead.
|
||||
#
|
||||
# N.B. `gcc` is a compiler which is sensitive to `targetPlatform`.
|
||||
# We use `depsHostHost` instead of `buildInputs` to force it ignore the target,
|
||||
# since binaries produced by `rustc` don't actually relies on this gccForLibs.
|
||||
depsHostHost =
|
||||
optional (!dontFixup && !hostPlatform.isDarwin) gccForLibs.lib;
|
||||
|
||||
dontConfigure = true;
|
||||
dontBuild = true;
|
||||
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
installerVersion=$(< ./rust-installer-version)
|
||||
if [[ "$installerVersion" != 3 ]]; then
|
||||
echo "Unknown installer version: $installerVersion"
|
||||
fi
|
||||
mkdir -p "$out"
|
||||
while read -r comp; do
|
||||
echo "Installing component $comp"
|
||||
# We don't want to parse the file and invoking cp in bash due to slow forking.
|
||||
cut -d: -f2 <"$comp/manifest.in" | tar -cf - -C "$comp" --files-from - | tar -xC "$out"
|
||||
done <./components
|
||||
runHook postInstall
|
||||
'';
|
||||
|
||||
postInstall = ''
|
||||
# Function moves well-known files from etc/
|
||||
handleEtc() {
|
||||
if [[ -d "$1" ]]; then
|
||||
mkdir -p "$(dirname "$2")"
|
||||
mv -T "$1" "$2"
|
||||
fi
|
||||
}
|
||||
if [[ -e "$out/etc" ]]; then
|
||||
handleEtc "$out/etc/bash_completion.d" "$out/share/bash-completion/completions"
|
||||
rmdir $out/etc || { echo "Installer tries to install to /etc: $(ls $out/etc)"; exit 1; }
|
||||
fi
|
||||
'';
|
||||
|
||||
# Only contain tons of html files. Don't waste time scanning files.
|
||||
dontFixup = elem pname [ "rust-docs" "rustc-docs" ];
|
||||
|
||||
# Darwin binaries usually just work... except for these linking to rustc from another drv.
|
||||
postFixup = optionalString (hostPlatform.isDarwin && linksToRustc) ''
|
||||
for f in $out/bin/*; do
|
||||
install_name_tool -add_rpath "${self.rustc}/lib" "$f" || true
|
||||
done
|
||||
''
|
||||
# Wrap the shipped `rust-lld` (lld), which is used by default on some targets.
|
||||
# Unfortunately there is no hook to conveniently wrap CC tools inside
|
||||
# derivation and `wrapBintools` is designed for wrapping a standalone
|
||||
# bintools derivation. We hereby copy minimal of their implementation.
|
||||
# The `wrap()` is from:
|
||||
# https://github.com/NixOS/nixpkgs/blob/bfb7a882678e518398ce9a31a881538679f6f092/pkgs/build-support/bintools-wrapper/default.nix#L178
|
||||
+ optionalString (pname == "rustc") ''
|
||||
wrap() {
|
||||
local dst="$1"
|
||||
local wrapper="$2"
|
||||
export prog="$3"
|
||||
export use_response_file_by_default=0
|
||||
substituteAll "$wrapper" "$dst"
|
||||
chmod +x "$dst"
|
||||
}
|
||||
|
||||
dsts=( "$out"/lib/rustlib/*/bin/gcc-ld/ld.lld )
|
||||
if [[ ''${#dsts} -ne 0 ]]; then
|
||||
mkdir -p $out/nix-support
|
||||
substituteAll ${path + "/pkgs/build-support/wrapper-common/utils.bash"} $out/nix-support/utils.bash
|
||||
substituteAll ${path + "/pkgs/build-support/bintools-wrapper/add-flags.sh"} $out/nix-support/add-flags.sh
|
||||
substituteAll ${path + "/pkgs/build-support/bintools-wrapper/add-hardening.sh"} $out/nix-support/add-hardening.sh
|
||||
|
||||
for dst in "''${dsts[@]}"; do
|
||||
# The ld.lld is path/name sensitive because itself is a wrapper. Keep its original name.
|
||||
unwrapped="$(dirname "$dst")-unwrapped/ld.lld"
|
||||
mkdir -p "$(dirname "$unwrapped")"
|
||||
mv "$dst" "$unwrapped"
|
||||
wrap "$dst" ${path + "/pkgs/build-support/bintools-wrapper/ld-wrapper.sh"} "$unwrapped"
|
||||
done
|
||||
fi
|
||||
'';
|
||||
|
||||
env = lib.optionalAttrs (pname == "rustc") {
|
||||
inherit (stdenv.cc.bintools) expandResponseParams shell suffixSalt wrapperName coreutils_bin;
|
||||
hardening_unsupported_flags = "";
|
||||
};
|
||||
|
||||
dontStrip = true;
|
||||
};
|
||||
|
||||
self = mapAttrs mkComponent srcs;
|
||||
|
||||
in
|
||||
removeNulls (
|
||||
self //
|
||||
mapAttrs (alias: { to }: self.${to} or null) renames)
|
475
lib/rust-bin.nix
Normal file
475
lib/rust-bin.nix
Normal file
@@ -0,0 +1,475 @@
|
||||
# Define component resolution and utility functions.
|
||||
self: super:
|
||||
|
||||
let
|
||||
inherit (builtins) compareVersions fromTOML match readFile tryEval;
|
||||
|
||||
inherit (self.lib)
|
||||
any attrNames attrValues concatStringsSep elem elemAt filter flatten foldl'
|
||||
hasPrefix head isString length listToAttrs makeOverridable mapAttrs
|
||||
mapAttrsToList optional optionalAttrs replaceStrings substring trace unique;
|
||||
|
||||
# Remove keys from attrsets whose value is null.
|
||||
removeNulls = set:
|
||||
removeAttrs set
|
||||
(filter (name: set.${name} == null)
|
||||
(attrNames set));
|
||||
|
||||
# FIXME: https://github.com/NixOS/nixpkgs/pull/146274
|
||||
toRustTarget = platform:
|
||||
if platform.isWasi then
|
||||
"${platform.parsed.cpu.name}-wasi"
|
||||
else
|
||||
platform.rust.rustcTarget or (super.rust.toRustTarget platform);
|
||||
|
||||
# The platform where `rustc` is running.
|
||||
rustHostPlatform = toRustTarget self.stdenv.hostPlatform;
|
||||
# The platform of binary which `rustc` produces.
|
||||
rustTargetPlatform = toRustTarget self.stdenv.targetPlatform;
|
||||
|
||||
mkComponentSet = self.callPackage ./mk-component-set.nix {
|
||||
inherit toRustTarget removeNulls;
|
||||
};
|
||||
|
||||
mkAggregated = self.callPackage ./mk-aggregated.nix {};
|
||||
|
||||
# Manifest selector.
|
||||
selectManifest = { channel, date ? null }: let
|
||||
inherit (self.rust-bin) manifests;
|
||||
|
||||
assertWith = cond: msg: body: if cond then body else throw msg;
|
||||
|
||||
# https://rust-lang.github.io/rustup/concepts/toolchains.html#toolchain-specification
|
||||
# <channel> = stable|beta|nightly|<major.minor>|<major.minor.patch>
|
||||
|
||||
asVersion = match "[0-9]+\\.[0-9]+(\\.[0-9]+)?" channel;
|
||||
asNightlyDate = let m = match "nightly-([0-9]+-[0-9]+-[0-9]+)" channel; in
|
||||
if m == null then null else elemAt m 0;
|
||||
asBetaDate = let m = match "beta-([0-9]+-[0-9]+-[0-9]+)" channel; in
|
||||
if m == null then null else elemAt m 0;
|
||||
|
||||
maxWith = zero: f: foldl' (lhs: rhs: if lhs == zero || f lhs rhs < 0 then rhs else lhs) zero;
|
||||
|
||||
latestStableWithMajorMinor =
|
||||
maxWith "" compareVersions
|
||||
(filter (hasPrefix (channel + "."))
|
||||
(attrNames manifests.stable));
|
||||
|
||||
in
|
||||
# "stable"
|
||||
if channel == "stable" then
|
||||
assertWith (date == null) "Stable version with specific date is not supported"
|
||||
manifests.stable.latest
|
||||
# "nightly"
|
||||
else if channel == "nightly" then
|
||||
manifests.nightly.${if date != null then date else "latest"} or (throw "Nightly ${date} is not available")
|
||||
# "beta"
|
||||
else if channel == "beta" then
|
||||
manifests.beta.${if date != null then date else "latest"} or (throw "Beta ${date} is not available")
|
||||
# "1.49.0" or "1.49"
|
||||
else if asVersion != null then
|
||||
assertWith (date == null) "Stable version with specific date is not supported" (
|
||||
# "1.49"
|
||||
if asVersion == [ null ] then
|
||||
manifests.stable.${latestStableWithMajorMinor} or (throw "No stable ${channel}.* is available")
|
||||
# "1.49.0"
|
||||
else
|
||||
manifests.stable.${channel} or (throw "Stable ${channel} is not available"))
|
||||
# "beta-2021-01-01"
|
||||
else if asBetaDate != null then
|
||||
assertWith (date == null) "Cannot specify date in both `channel` and `date`"
|
||||
manifests.beta.${asBetaDate} or (throw "Beta ${asBetaDate} is not available")
|
||||
# "nightly-2021-01-01"
|
||||
else if asNightlyDate != null then
|
||||
assertWith (date == null) "Cannot specify date in both `channel` and `date`"
|
||||
manifests.nightly.${asNightlyDate} or (throw "Nightly ${asNightlyDate} is not available")
|
||||
# Otherwise
|
||||
else throw "Unknown channel: ${channel}";
|
||||
|
||||
# Select a toolchain and aggregate components by rustup's `rust-toolchain` file format.
|
||||
# See: https://rust-lang.github.io/rustup/concepts/profiles.html
|
||||
# Or see source: https://github.com/rust-lang/rustup/blob/84974df1387812269c7b29fa5f3bb1c6480a6500/doc/src/overrides.md#the-toolchain-file
|
||||
fromRustupToolchain = { path ? null, channel ? null, profile ? null, components ? [], targets ? [] }:
|
||||
if path != null then throw "`path` is not supported, please directly add it to your PATH instead"
|
||||
else if channel == null then throw "`channel` is required"
|
||||
else
|
||||
let
|
||||
toolchain = toolchainFromManifest (selectManifest { inherit channel; });
|
||||
profile' = if profile == null then "default" else profile;
|
||||
pkg =
|
||||
if toolchain._profiles != {} then
|
||||
toolchain._profiles.${profile'} or (throw ''
|
||||
Rust ${toolchain._version} doesn't have profile `${profile'}`.
|
||||
Available profiles are: ${concatStringsSep ", " (attrNames toolchain._profiles)}
|
||||
'')
|
||||
# Fallback to package `rust` when profiles are not supported and not specified.
|
||||
else if profile == null then
|
||||
toolchain.rust
|
||||
else
|
||||
throw "Cannot select profile `${profile'}` since rust ${toolchain._version} is too early to support profiles";
|
||||
in pkg.override {
|
||||
extensions = components;
|
||||
inherit targets;
|
||||
};
|
||||
|
||||
# Same as `fromRustupToolchain` but read from a `rust-toolchain` file (legacy one-line string or in TOML).
|
||||
fromRustupToolchainFile = path: let
|
||||
content = readFile path;
|
||||
legacy = match "([^\r\n]+)\r?\n?" content;
|
||||
in if legacy != null
|
||||
then fromRustupToolchain { channel = head legacy; }
|
||||
else fromRustupToolchain (fromTOML content).toolchain;
|
||||
|
||||
mkComponentSrc = { url, sha256 }:
|
||||
let
|
||||
url' = replaceStrings [" "] ["%20"] url; # This is required or download will fail.
|
||||
# Filter names like `llvm-tools-1.34.2 (6c2484dc3 2019-05-13)-aarch64-unknown-linux-gnu.tar.xz`
|
||||
matchParenPart = match ".*/([^ /]*) [(][^)]*[)](.*)" url;
|
||||
name = if matchParenPart == null then "" else (elemAt matchParenPart 0) + (elemAt matchParenPart 1);
|
||||
in
|
||||
self.fetchurl { inherit name sha256; url = url'; };
|
||||
|
||||
# Resolve final components to install from mozilla-overlay style `extensions`, `targets` and `targetExtensions`.
|
||||
#
|
||||
# `componentSet` has a layout of `componentSet.<name>.<rust-target> : Derivation`.
|
||||
# `targetComponentsList` is a list of all component names for target platforms.
|
||||
# `name` is only used for error message.
|
||||
#
|
||||
# Returns a list of component derivations, or throw if failed.
|
||||
resolveComponents =
|
||||
{ name
|
||||
, componentSet
|
||||
, allComponentSet
|
||||
, allPlatformSet
|
||||
, targetComponentsList
|
||||
, profileComponents
|
||||
, extensions
|
||||
, targets
|
||||
, targetExtensions
|
||||
}:
|
||||
let
|
||||
# Components for target platform like `rust-std`.
|
||||
collectTargetComponents = allowMissing: name:
|
||||
let
|
||||
targetSelected = flatten (map (tgt: componentSet.${tgt}.${name} or []) targets);
|
||||
in if !allowMissing -> targetSelected != [] then
|
||||
targetSelected
|
||||
else
|
||||
"Component `${name}` doesn't support any of targets: ${concatStringsSep ", " targets}";
|
||||
|
||||
collectComponents = allowMissing: name:
|
||||
if elem name targetComponentsList then
|
||||
collectTargetComponents allowMissing name
|
||||
else
|
||||
# Components for host platform like `rustc`.
|
||||
componentSet.${rustHostPlatform}.${name} or (
|
||||
if allowMissing then []
|
||||
else "Host component `${name}` doesn't support `${rustHostPlatform}`");
|
||||
|
||||
# Profile components can be skipped silently when missing.
|
||||
# Eg. `rust-mingw` on non-Windows platforms, or `rust-docs` on non-tier1 platforms.
|
||||
result =
|
||||
flatten (map (collectComponents true) profileComponents) ++
|
||||
flatten (map (collectComponents false) extensions) ++
|
||||
flatten (map (collectTargetComponents false) targetExtensions);
|
||||
|
||||
isTargetUnused = target:
|
||||
!any (name: componentSet ? ${target}.${name})
|
||||
# FIXME: Get rid of the legacy component `rust`.
|
||||
(filter (name: name == "rust" || elem name targetComponentsList)
|
||||
(profileComponents ++ extensions)
|
||||
++ targetExtensions);
|
||||
|
||||
# Fail-fast for typo in `targets`, `extensions`, `targetExtensions`.
|
||||
fastErrors =
|
||||
flatten (
|
||||
map (tgt: optional (!(allPlatformSet ? ${tgt}))
|
||||
"Unknown target `${tgt}`, typo or not supported by this version?")
|
||||
targets ++
|
||||
map (name: optional (!(allComponentSet ? ${name}))
|
||||
"Unknown component `${name}`, typo or not support by this version?")
|
||||
(profileComponents ++ extensions ++ targetExtensions));
|
||||
|
||||
errors =
|
||||
if fastErrors != [] then
|
||||
fastErrors
|
||||
else
|
||||
filter isString result ++
|
||||
map (tgt: "Target `${tgt}` is not supported by any components or extensions")
|
||||
(filter isTargetUnused targets);
|
||||
|
||||
notes = [
|
||||
"note: profile components: ${toString profileComponents}"
|
||||
] ++ optional (targets != []) "note: selected targets: ${toString targets}"
|
||||
++ optional (extensions != []) "note: selected extensions: ${toString extensions}"
|
||||
++ optional (targetExtensions != []) "note: selected targetExtensions: ${toString targetExtensions}"
|
||||
++ flatten (map (platform:
|
||||
optional (componentSet ? ${platform})
|
||||
"note: components available for ${platform}: ${toString (attrNames componentSet.${platform})}"
|
||||
) (unique ([ rustHostPlatform ] ++ targets)))
|
||||
++ [
|
||||
''
|
||||
note: check here to see all targets and which components are available on each targets:
|
||||
https://rust-lang.github.io/rustup-components-history
|
||||
''
|
||||
];
|
||||
|
||||
in
|
||||
if errors == [] then result
|
||||
else throw ''
|
||||
Component resolution failed for ${name}
|
||||
${concatStringsSep "\n" (errors ++ notes)}
|
||||
'';
|
||||
|
||||
# Genereate the toolchain set from a parsed manifest.
|
||||
#
|
||||
# Manifest files are organized as follow:
|
||||
# { date = "2017-03-03";
|
||||
# pkg.cargo.version= "0.18.0-nightly (5db6d64 2017-03-03)";
|
||||
# pkg.cargo.target.x86_64-unknown-linux-gnu = {
|
||||
# available = true;
|
||||
# hash = "abce..."; # sha256
|
||||
# url = "https://static.rust-lang.org/dist/....tar.gz";
|
||||
# xz_hash = "abce..."; # sha256
|
||||
# xz_url = "https://static.rust-lang.org/dist/....tar.xz";
|
||||
# };
|
||||
# }
|
||||
#
|
||||
# The packages available usually are:
|
||||
# cargo, rust-analysis, rust-docs, rust-src, rust-std, rustc, and
|
||||
# rust, which aggregates them in one package.
|
||||
#
|
||||
# For each package the following options are available:
|
||||
# extensions - The extensions that should be installed for the package.
|
||||
# For example, install the package rust and add the extension rust-src.
|
||||
# targets - The package will always be installed for the host system, but with this option
|
||||
# extra targets can be specified, e.g. "mips-unknown-linux-musl". The target
|
||||
# will only apply to components of the package that support being installed for
|
||||
# a different architecture. For example, the rust package will install rust-std
|
||||
# for the host system and the targets.
|
||||
# targetExtensions - If you want to force extensions to be installed for the given targets, this is your option.
|
||||
# All extensions in this list will be installed for the target architectures.
|
||||
# *Attention* If you want to install an extension like rust-src, that has no fixed architecture (arch *),
|
||||
# you will need to specify this extension in the extensions options or it will not be installed!
|
||||
toolchainFromManifest = manifest: let
|
||||
maybeRename = name: manifest.renames.${name}.to or name;
|
||||
|
||||
# platform -> true
|
||||
# For fail-fast test.
|
||||
allPlatformSet =
|
||||
listToAttrs (
|
||||
flatten (
|
||||
mapAttrsToList (compName: { target, ... }:
|
||||
map (platform: { name = platform; value = true; })
|
||||
(attrNames target)
|
||||
) manifest.pkg));
|
||||
|
||||
# componentName -> true
|
||||
# May also contains unavailable components. Just for fail-fast test.
|
||||
allComponentSet =
|
||||
mapAttrs (compName: _: true)
|
||||
(manifest.pkg // manifest.renames);
|
||||
|
||||
# componentSet.x86_64-unknown-linux-gnu.cargo = <derivation>;
|
||||
componentSet =
|
||||
mapAttrs (platform: _:
|
||||
mkComponentSet {
|
||||
inherit (manifest) version renames;
|
||||
inherit platform;
|
||||
srcs = removeNulls
|
||||
(mapAttrs (compName: { target, ... }:
|
||||
let content = target.${platform} or target."*" or null; in
|
||||
if content == null then
|
||||
null
|
||||
else
|
||||
mkComponentSrc {
|
||||
url = content.xz_url;
|
||||
sha256 = content.xz_hash;
|
||||
}
|
||||
) manifest.pkg);
|
||||
}
|
||||
) allPlatformSet;
|
||||
|
||||
mkProfile = name: profileComponents:
|
||||
makeOverridable ({ extensions, targets, targetExtensions }:
|
||||
mkAggregated {
|
||||
pname = "rust-${name}";
|
||||
inherit (manifest) version date;
|
||||
availableComponents = componentSet.${rustHostPlatform};
|
||||
selectedComponents = resolveComponents {
|
||||
name = "rust-${name}-${manifest.version}";
|
||||
inherit allPlatformSet allComponentSet componentSet profileComponents targetExtensions;
|
||||
inherit (manifest) targetComponentsList;
|
||||
extensions = extensions;
|
||||
targets = unique ([
|
||||
rustHostPlatform # Build script requires host std.
|
||||
rustTargetPlatform
|
||||
] ++ targets);
|
||||
};
|
||||
}
|
||||
) {
|
||||
extensions = [];
|
||||
targets = [];
|
||||
targetExtensions = [];
|
||||
};
|
||||
|
||||
profiles = mapAttrs mkProfile manifest.profiles;
|
||||
|
||||
result =
|
||||
# Individual components.
|
||||
componentSet.${rustHostPlatform} //
|
||||
# Profiles.
|
||||
profiles // {
|
||||
# Legacy support for special pre-aggregated package.
|
||||
# It has more components than `default` profile but less than `complete` profile.
|
||||
rust =
|
||||
let
|
||||
pkg = mkProfile "legacy" [ "rust" ];
|
||||
in if profiles != {} then
|
||||
trace ''
|
||||
Rust ${manifest.version}:
|
||||
Pre-aggregated package `rust` is not encouraged for stable channel since it contains almost all and uncertain components.
|
||||
Consider use `default` profile like `rust-bin.stable.latest.default` and override it with extensions you need.
|
||||
See README for more information.
|
||||
'' pkg
|
||||
else
|
||||
pkg;
|
||||
};
|
||||
|
||||
in
|
||||
# If the platform is not supported for the current version, return nothing here,
|
||||
# so others can easily check it by `toolchain ? default`.
|
||||
optionalAttrs (componentSet ? ${rustHostPlatform}) result //
|
||||
{
|
||||
# Internal use.
|
||||
_components = componentSet;
|
||||
_profiles = profiles;
|
||||
_version = manifest.version;
|
||||
_manifest = manifest;
|
||||
};
|
||||
|
||||
# Same as `toolchainFromManifest` but read from a manifest file.
|
||||
toolchainFromManifestFile = path: toolchainFromManifest (fromTOML (readFile path));
|
||||
|
||||
# Override all pkgs of a toolchain set.
|
||||
overrideToolchain = attrs: mapAttrs (name: pkg: pkg.override attrs);
|
||||
|
||||
# From a git revision of rustc.
|
||||
# This does the same thing as crate `rustup-toolchain-install-master`.
|
||||
# But you need to manually provide component hashes.
|
||||
fromRustcRev = {
|
||||
# Package name of the derivation.
|
||||
pname ? "rust-custom",
|
||||
# Git revision of rustc.
|
||||
rev,
|
||||
# Version of the built package.
|
||||
version ? substring 0 7 rev,
|
||||
# Attrset with component name as key and its SRI hash as value.
|
||||
components,
|
||||
# Rust target to download.
|
||||
target ? rustTargetPlatform
|
||||
}: let
|
||||
hashToSrc = compName: hash:
|
||||
self.fetchurl {
|
||||
url = if compName == "rust-src"
|
||||
then "https://ci-artifacts.rust-lang.org/rustc-builds/${rev}/${compName}-nightly.tar.xz"
|
||||
else "https://ci-artifacts.rust-lang.org/rustc-builds/${rev}/${compName}-nightly-${target}.tar.xz";
|
||||
inherit hash;
|
||||
};
|
||||
components' = mkComponentSet {
|
||||
inherit version;
|
||||
platform = target;
|
||||
srcs = mapAttrs hashToSrc components;
|
||||
};
|
||||
in
|
||||
mkAggregated {
|
||||
inherit pname version;
|
||||
date = null;
|
||||
selectedComponents = attrValues components';
|
||||
};
|
||||
|
||||
# Select latest nightly toolchain which makes selected profile builds.
|
||||
# Some components are missing in some nightly releases.
|
||||
# Usage:
|
||||
# `selectLatestNightlyWith (toolchain: toolchain.default.override { extensions = ["llvm-tools-preview"]; })`
|
||||
selectLatestNightlyWith = selector:
|
||||
let
|
||||
nightlyDates = attrNames (removeAttrs self.rust-bin.nightly [ "latest" ]);
|
||||
dateLength = length nightlyDates;
|
||||
go = idx:
|
||||
let ret = selector (self.rust-bin.nightly.${elemAt nightlyDates idx}); in
|
||||
if idx == 0 then
|
||||
ret
|
||||
else if dateLength - idx >= 256 then
|
||||
trace "Failed to select nightly version after 100 tries" ret
|
||||
else if ret != null && (tryEval ret.drvPath).success then
|
||||
ret
|
||||
else
|
||||
go (idx - 1);
|
||||
in
|
||||
go (length nightlyDates - 1);
|
||||
|
||||
in {
|
||||
# For each channel:
|
||||
# rust-bin.stable.latest.{minimal,default,complete} # Profiles.
|
||||
# rust-bin.stable.latest.rust # Pre-aggregate from upstream.
|
||||
# rust-bin.stable.latest.cargo # Components...
|
||||
# rust-bin.stable.latest.rustc
|
||||
# rust-bin.stable.latest.rust-docs
|
||||
# ...
|
||||
#
|
||||
# For a specific version of stable:
|
||||
# rust-bin.stable."1.47.0".default
|
||||
#
|
||||
# For a specific date of beta:
|
||||
# rust-bin.beta."2021-01-01".default
|
||||
#
|
||||
# For a specific date of nightly:
|
||||
# rust-bin.nightly."2020-01-01".default
|
||||
rust-bin =
|
||||
(super.rust-bin or {}) //
|
||||
mapAttrs (channel: mapAttrs (version: toolchainFromManifest)) super.rust-bin.manifests //
|
||||
{
|
||||
inherit fromRustupToolchain fromRustupToolchainFile;
|
||||
inherit selectLatestNightlyWith;
|
||||
inherit fromRustcRev;
|
||||
};
|
||||
|
||||
# All attributes below are for compatiblity with mozilla overlay.
|
||||
|
||||
lib = (super.lib or {}) // {
|
||||
rustLib = (super.lib.rustLib or {}) // {
|
||||
manifest_v2_url = throw ''
|
||||
`manifest_v2_url` is not supported.
|
||||
Select a toolchain from `rust-bin` or using `rustChannelOf` instead.
|
||||
See also README at https://github.com/oxalica/rust-overlay
|
||||
'';
|
||||
fromManifest = throw ''
|
||||
`fromManifest` is not supported due to network access during evaluation.
|
||||
Select a toolchain from `rust-bin` or using `rustChannelOf` instead.
|
||||
See also README at https://github.com/oxalica/rust-overlay
|
||||
'';
|
||||
fromManifestFile = manifestFilePath: { stdenv, fetchurl, patchelf }@deps: trace ''
|
||||
`fromManifestFile` is deprecated.
|
||||
Select a toolchain from `rust-bin` or using `rustChannelOf` instead.
|
||||
See also README at https://github.com/oxalica/rust-overlay
|
||||
'' (overrideToolchain deps (toolchainFromManifestFile manifestFilePath));
|
||||
};
|
||||
};
|
||||
|
||||
rustChannelOf = manifestArgs: toolchainFromManifest (selectManifest manifestArgs);
|
||||
|
||||
latest = (super.latest or {}) // {
|
||||
rustChannels = {
|
||||
stable = self.rust-bin.stable.latest;
|
||||
beta = self.rust-bin.beta.latest;
|
||||
nightly = self.rust-bin.nightly.latest;
|
||||
};
|
||||
};
|
||||
|
||||
rustChannelOfTargets = channel: date: targets:
|
||||
(self.rustChannelOf { inherit channel date; })
|
||||
.rust.override { inherit targets; };
|
||||
|
||||
rustChannels = self.latest.rustChannels;
|
||||
}
|
Reference in New Issue
Block a user