commit
815fa068bc
@ -0,0 +1,3 @@ |
|||||||
|
result |
||||||
|
result-* |
||||||
|
*.tmp |
@ -0,0 +1,8 @@ |
|||||||
|
{ |
||||||
|
"[nix]": { |
||||||
|
"editor.tabSize": 2, |
||||||
|
"files.trimFinalNewlines": true, |
||||||
|
"files.insertFinalNewline": true, |
||||||
|
"files.trimTrailingWhitespace": true |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,72 @@ |
|||||||
|
final: prev: |
||||||
|
let |
||||||
|
inherit (prev.lib) filter hasAttr attrNames mapAttrs concatMap mapAttrs' replaceStrings; |
||||||
|
|
||||||
|
targets = import ./manifests/targets.nix // { _ = "*"; }; |
||||||
|
|
||||||
|
distServer = "https://static.rust-lang.org"; |
||||||
|
|
||||||
|
# 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; |
||||||
|
}; |
||||||
|
|
||||||
|
# version -> { pkgName = { _1 = "..."; } } -> { pkgName = { x86_64-unknown-linux-gnu = fetchurl { .. }; } } |
||||||
|
uncompressManifest = version: { date, ... }@manifest: rec { |
||||||
|
inherit date; |
||||||
|
pkg = |
||||||
|
mapAttrs (pkgName: { v, ... }@hashes: { |
||||||
|
version = v; |
||||||
|
target = |
||||||
|
mapAttrs' (targetIdx: hash: let |
||||||
|
target = targets.${targetIdx}; |
||||||
|
pkgNameStripped = replaceStrings ["-preview"] [""] pkgName; |
||||||
|
targetTail = if targetIdx == "_" then "" else "-" + target; |
||||||
|
in { |
||||||
|
name = target; |
||||||
|
value = { |
||||||
|
xz_url = "${distServer}/dist/${date}/${pkgNameStripped}-${version}${targetTail}.tar.xz"; |
||||||
|
xz_hash = hash; |
||||||
|
} // (if pkgName == "rust" then rustPkgExtra pkg target else {}); |
||||||
|
}) (removeAttrs hashes ["v"]); |
||||||
|
}) (removeAttrs manifest ["date"]); |
||||||
|
}; |
||||||
|
|
||||||
|
uncompressManifestSet = set: let |
||||||
|
ret = mapAttrs uncompressManifest (removeAttrs set ["latest"]); |
||||||
|
in ret // { latest = ret.${set.latest}; }; |
||||||
|
|
||||||
|
manifests = { |
||||||
|
stable = uncompressManifestSet (import ./manifests/stable); |
||||||
|
}; |
||||||
|
|
||||||
|
# in { inherit manifests; } |
||||||
|
in import ./rust-overlay.nix final prev manifests |
@ -0,0 +1,197 @@ |
|||||||
|
#!/usr/bin/env nix-shell |
||||||
|
#!nix-shell -i python3 -p "python3.withPackages (ps: with ps; [ toml requests ])" |
||||||
|
import base64 |
||||||
|
import json |
||||||
|
import re |
||||||
|
import string |
||||||
|
import sys |
||||||
|
import time |
||||||
|
from pathlib import Path |
||||||
|
|
||||||
|
import toml |
||||||
|
import requests |
||||||
|
|
||||||
|
MAX_TRIES = 3 |
||||||
|
RETRY_DELAY = 3.0 |
||||||
|
SYNC_MAX_FETCH = 5 |
||||||
|
|
||||||
|
STABLE_VERSION_FILTER = lambda v: parse_version(v) >= (1, 29, 0) |
||||||
|
|
||||||
|
DIST_SERVER = 'https://static.rust-lang.org' |
||||||
|
NIX_KEYWORDS = {'', 'if', 'then', 'else', 'assert', 'with', 'let', 'in', 'rec', 'inherit', 'or'} |
||||||
|
MANIFEST_TMP_PATH = Path('manifest.tmp') |
||||||
|
TARGETS_PATH = Path('manifests/targets.nix') |
||||||
|
|
||||||
|
RE_STABLE_VERSION = re.compile(r'^\d+\.\d+\.\d+$') |
||||||
|
|
||||||
|
def to_base64(hash: str) -> str: |
||||||
|
assert len(hash) == 64 |
||||||
|
return base64.b64encode(bytes.fromhex(hash)).decode() |
||||||
|
|
||||||
|
def is_valid_nix_ident(name: str) -> bool: |
||||||
|
return name not in NIX_KEYWORDS and \ |
||||||
|
(name[0] == '_' or name[0].isalpha()) and \ |
||||||
|
all(c in "_-'" or c.isalnum() for c in name) |
||||||
|
|
||||||
|
def escape_nix_string(s: str) -> str: |
||||||
|
return '"' + s.replace('\\', '\\\\').replace('"', '\\"') + '"' |
||||||
|
|
||||||
|
def escape_nix_key(name: str) -> str: |
||||||
|
if is_valid_nix_ident(name): |
||||||
|
return name |
||||||
|
return escape_nix_string(name) |
||||||
|
|
||||||
|
def parse_version(ver: str) -> tuple: |
||||||
|
return tuple(map(int, ver.split('.'))) |
||||||
|
|
||||||
|
def version_less(a: str, b: str): |
||||||
|
return parse_version(a) < parse_version(b) |
||||||
|
|
||||||
|
target_map = dict((line.split('"')[1], i) for i, line in enumerate(TARGETS_PATH.read_text().strip().split('\n')[1:-1])) |
||||||
|
def compress_target(target: str) -> str: |
||||||
|
assert '"' not in target |
||||||
|
if target == '*': |
||||||
|
return '_' |
||||||
|
if target in target_map: |
||||||
|
return f'_{target_map[target]}' |
||||||
|
idx = len(target_map) |
||||||
|
target_map[target] = idx |
||||||
|
|
||||||
|
with open(str(TARGETS_PATH), 'w') as f: |
||||||
|
f.write('{\n') |
||||||
|
for i, target in sorted((v, k) for k, v in target_map.items()): |
||||||
|
f.write(f' _{i} = "{target}";\n') |
||||||
|
f.write('}\n') |
||||||
|
return f'_{idx}' |
||||||
|
|
||||||
|
def retry_with(f): |
||||||
|
i = 0 |
||||||
|
while True: |
||||||
|
try: |
||||||
|
return f() |
||||||
|
except requests.exceptions.RequestException as e: |
||||||
|
i += 1 |
||||||
|
if i >= MAX_TRIES: |
||||||
|
raise |
||||||
|
print(e) |
||||||
|
time.sleep(RETRY_DELAY) |
||||||
|
|
||||||
|
def translate_dump_manifest(manifest: str, f): |
||||||
|
manifest = toml.loads(manifest) |
||||||
|
date = manifest['date'] |
||||||
|
version = manifest['pkg']['rustc']['version'].split()[0] |
||||||
|
strip_tail = '-preview' |
||||||
|
|
||||||
|
f.write('{') |
||||||
|
f.write(f'date={escape_nix_string(date)};') |
||||||
|
for pkg_name in sorted(manifest['pkg'].keys()): |
||||||
|
pkg = manifest['pkg'][pkg_name] |
||||||
|
pkg_name_stripped = pkg_name[:-len(strip_tail)] if pkg_name.endswith(strip_tail) else pkg_name |
||||||
|
pkg_targets = sorted(pkg['target'].keys()) |
||||||
|
|
||||||
|
pkg_version = version |
||||||
|
for target_name in pkg_targets: |
||||||
|
target = pkg['target'][target_name] |
||||||
|
if not target['available']: |
||||||
|
continue |
||||||
|
url = target['xz_url'] |
||||||
|
target_tail = '' if target_name == '*' else '-' + target_name |
||||||
|
start = f'https://static.rust-lang.org/dist/{date}/{pkg_name_stripped}-' |
||||||
|
end = f'{target_tail}.tar.xz' |
||||||
|
assert url.startswith(start) and url.endswith(end), f'Unexpected url: {url}' |
||||||
|
pkg_version = url[len(start):-len(end)] |
||||||
|
|
||||||
|
f.write(f'{pkg_name}={{') |
||||||
|
f.write(f'v={escape_nix_string(pkg["version"])};') |
||||||
|
for target_name in pkg_targets: |
||||||
|
target = pkg['target'][target_name] |
||||||
|
if not target['available']: |
||||||
|
continue |
||||||
|
url = target['xz_url'] |
||||||
|
hash = to_base64(target['xz_hash']) # Hash must not contains quotes. |
||||||
|
target_tail = '' if target_name == '*' else '-' + target_name |
||||||
|
expect_url = f'https://static.rust-lang.org/dist/{date}/{pkg_name_stripped}-{pkg_version}{target_tail}.tar.xz' |
||||||
|
assert url == expect_url, f'Unexpected url: {url}, expecting: {expect_url}' |
||||||
|
f.write(f'{compress_target(target_name)}="{hash}";') |
||||||
|
f.write('};') |
||||||
|
f.write('}\n') |
||||||
|
|
||||||
|
def fetch_stable_manifest(version: str, out_path: Path): |
||||||
|
tmp_path = out_path.with_suffix('.tmp') |
||||||
|
print(f'Fetching {version}') |
||||||
|
manifest = retry_with(lambda: requests.get(f'{DIST_SERVER}/dist/channel-rust-{version}.toml')) |
||||||
|
manifest.raise_for_status() |
||||||
|
manifest = manifest.text |
||||||
|
MANIFEST_TMP_PATH.write_text(manifest) |
||||||
|
with open(tmp_path, 'w') as fout: |
||||||
|
translate_dump_manifest(manifest, fout) |
||||||
|
tmp_path.rename(out_path) |
||||||
|
|
||||||
|
def update_stable_index(): |
||||||
|
dir = Path('manifests/stable') |
||||||
|
versions = sorted( |
||||||
|
(file.stem for file in dir.iterdir() if file.stem != 'default' and file.suffix == '.nix'), |
||||||
|
key=parse_version, |
||||||
|
) |
||||||
|
with open(str(dir / 'default.nix'), 'w') as f: |
||||||
|
f.write('{\n') |
||||||
|
for v in versions: |
||||||
|
f.write(f' {escape_nix_key(v)} = import ./{v}.nix;\n') |
||||||
|
f.write(f' latest = {escape_nix_string(versions[-1])};\n') |
||||||
|
f.write('}\n') |
||||||
|
|
||||||
|
def sync_stable_channel(*, stop_if_exists, max_fetch=None): |
||||||
|
GITHUB_RELEASES_URL = 'https://api.github.com/repos/rust-lang/rust/releases' |
||||||
|
|
||||||
|
page = 0 |
||||||
|
count = 0 |
||||||
|
try: |
||||||
|
while True: |
||||||
|
page += 1 |
||||||
|
print(f'Fetching release page {page}') |
||||||
|
release_page = retry_with(lambda: requests.get(GITHUB_RELEASES_URL, params={'page': page})) |
||||||
|
release_page.raise_for_status() |
||||||
|
release_page = release_page.json() |
||||||
|
if not release_page: |
||||||
|
return |
||||||
|
for release in release_page: |
||||||
|
version = release['tag_name'] |
||||||
|
if not RE_STABLE_VERSION.match(version) or not STABLE_VERSION_FILTER(version): |
||||||
|
continue |
||||||
|
out_path = Path(f'manifests/stable/{version}.nix') |
||||||
|
if out_path.exists(): |
||||||
|
if stop_if_exists: |
||||||
|
print('Stopped on existing version') |
||||||
|
return |
||||||
|
continue |
||||||
|
fetch_stable_manifest(version, out_path) |
||||||
|
count += 1 |
||||||
|
if max_fetch is not None and count >= max_fetch: |
||||||
|
print('Reached max fetch count') |
||||||
|
exit(1) |
||||||
|
finally: |
||||||
|
update_stable_index() |
||||||
|
|
||||||
|
def init_sync_all(): |
||||||
|
sync_stable_channel(stop_if_exists=False) |
||||||
|
|
||||||
|
def main(): |
||||||
|
args = sys.argv[1:] |
||||||
|
if len(args) == 0: |
||||||
|
print('Synchronizing channels') |
||||||
|
sync_stable_channel(stop_if_exists=True, max_fetch=SYNC_MAX_FETCH) |
||||||
|
elif len(args) == 1: |
||||||
|
version = args[0] |
||||||
|
assert RE_STABLE_VERSION.match(version), 'Invalid version' |
||||||
|
fetch_stable_manifest(version, Path(f'manifests/stable/{version}.nix')) |
||||||
|
update_stable_index() |
||||||
|
else: |
||||||
|
print(f''' |
||||||
|
Usage: {sys.argv[0]} [version] |
||||||
|
Run without arguments to auto-sync new versions from channels. |
||||||
|
Run with version to fetch a specific version. |
||||||
|
''') |
||||||
|
exit(1) |
||||||
|
|
||||||
|
if __name__ == '__main__': |
||||||
|
main() |
@ -0,0 +1,40 @@ |
|||||||
|
{ |
||||||
|
"nodes": { |
||||||
|
"flake-utils": { |
||||||
|
"locked": { |
||||||
|
"lastModified": 1609246779, |
||||||
|
"narHash": "sha256-eq6ZXE/VWo3EMC65jmIT6H/rrUc9UWOWVujkzav025k=", |
||||||
|
"owner": "numtide", |
||||||
|
"repo": "flake-utils", |
||||||
|
"rev": "08c7ad4a0844adc4a7f9f5bb3beae482e789afa4", |
||||||
|
"type": "github" |
||||||
|
}, |
||||||
|
"original": { |
||||||
|
"owner": "numtide", |
||||||
|
"repo": "flake-utils", |
||||||
|
"type": "github" |
||||||
|
} |
||||||
|
}, |
||||||
|
"nixpkgs": { |
||||||
|
"locked": { |
||||||
|
"lastModified": 1609079092, |
||||||
|
"narHash": "sha256-KhyKfxBHtZlAgudpZ0EJVzeuqROjKfVOwj8j0cuhU50=", |
||||||
|
"path": "/nix/store/5pmay8rdvgw5ml1xi6189xhdbygc850k-source", |
||||||
|
"rev": "2f47650c2f28d87f86ab807b8a339c684d91ec56", |
||||||
|
"type": "path" |
||||||
|
}, |
||||||
|
"original": { |
||||||
|
"id": "nixpkgs", |
||||||
|
"type": "indirect" |
||||||
|
} |
||||||
|
}, |
||||||
|
"root": { |
||||||
|
"inputs": { |
||||||
|
"flake-utils": "flake-utils", |
||||||
|
"nixpkgs": "nixpkgs" |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
"root": "root", |
||||||
|
"version": 7 |
||||||
|
} |
@ -0,0 +1,40 @@ |
|||||||
|
{ |
||||||
|
description = '' |
||||||
|
Pure and reproducible overlay for binary distributed rust toolchains. |
||||||
|
A better replacement for github:mozilla/nixpkgs-mozilla |
||||||
|
''; |
||||||
|
|
||||||
|
inputs = { |
||||||
|
flake-utils.url = "github:numtide/flake-utils"; |
||||||
|
}; |
||||||
|
|
||||||
|
outputs = { self, nixpkgs, flake-utils, ... }@inputs: let |
||||||
|
inherit (nixpkgs) lib; |
||||||
|
overlay = import ./default.nix; |
||||||
|
|
||||||
|
allSystems = [ |
||||||
|
"aarch64-linux" |
||||||
|
"armv6l-linux" |
||||||
|
"armv7a-linux" |
||||||
|
"armv7l-linux" |
||||||
|
"x86_64-linux" |
||||||
|
"x86_64-darwin" |
||||||
|
# "aarch64-darwin" |
||||||
|
]; |
||||||
|
|
||||||
|
in { |
||||||
|
overlay = final: prev: overlay final prev; |
||||||
|
|
||||||
|
} // flake-utils.lib.eachSystem allSystems (system: let |
||||||
|
pkgs = import nixpkgs { inherit system; overlays = [ overlay ]; }; |
||||||
|
in rec { |
||||||
|
defaultApp = { |
||||||
|
type = "app"; |
||||||
|
program = "${defaultPackage}/bin/rustc"; |
||||||
|
}; |
||||||
|
defaultPackage = packages.rust-stable; |
||||||
|
packages = { |
||||||
|
rust-stable = pkgs.latest.rustChannels.stable.rust; |
||||||
|
}; |
||||||
|
}); |
||||||
|
} |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,5 @@ |
|||||||
|
{ |
||||||
|
"1.48.0" = import ./1.48.0.nix; |
||||||
|
"1.49.0" = import ./1.49.0.nix; |
||||||
|
latest = "1.49.0"; |
||||||
|
} |
@ -0,0 +1,93 @@ |
|||||||
|
{ |
||||||
|
_0 = "aarch64-apple-darwin"; |
||||||
|
_1 = "aarch64-pc-windows-msvc"; |
||||||
|
_2 = "aarch64-unknown-linux-gnu"; |
||||||
|
_3 = "aarch64-unknown-linux-musl"; |
||||||
|
_4 = "arm-unknown-linux-gnueabi"; |
||||||
|
_5 = "arm-unknown-linux-gnueabihf"; |
||||||
|
_6 = "armv7-unknown-linux-gnueabihf"; |
||||||
|
_7 = "i686-pc-windows-gnu"; |
||||||
|
_8 = "i686-pc-windows-msvc"; |
||||||
|
_9 = "i686-unknown-linux-gnu"; |
||||||
|
_10 = "mips-unknown-linux-gnu"; |
||||||
|
_11 = "mips64-unknown-linux-gnuabi64"; |
||||||
|
_12 = "mips64el-unknown-linux-gnuabi64"; |
||||||
|
_13 = "mipsel-unknown-linux-gnu"; |
||||||
|
_14 = "powerpc-unknown-linux-gnu"; |
||||||
|
_15 = "powerpc64-unknown-linux-gnu"; |
||||||
|
_16 = "powerpc64le-unknown-linux-gnu"; |
||||||
|
_17 = "riscv64gc-unknown-linux-gnu"; |
||||||
|
_18 = "s390x-unknown-linux-gnu"; |
||||||
|
_19 = "x86_64-apple-darwin"; |
||||||
|
_20 = "x86_64-pc-windows-gnu"; |
||||||
|
_21 = "x86_64-pc-windows-msvc"; |
||||||
|
_22 = "x86_64-unknown-freebsd"; |
||||||
|
_23 = "x86_64-unknown-illumos"; |
||||||
|
_24 = "x86_64-unknown-linux-gnu"; |
||||||
|
_25 = "x86_64-unknown-linux-musl"; |
||||||
|
_26 = "x86_64-unknown-netbsd"; |
||||||
|
_27 = "aarch64-apple-ios"; |
||||||
|
_28 = "aarch64-fuchsia"; |
||||||
|
_29 = "aarch64-linux-android"; |
||||||
|
_30 = "arm-linux-androideabi"; |
||||||
|
_31 = "armv7-linux-androideabi"; |
||||||
|
_32 = "armv7-unknown-linux-gnueabi"; |
||||||
|
_33 = "armv7-unknown-linux-musleabi"; |
||||||
|
_34 = "i586-pc-windows-msvc"; |
||||||
|
_35 = "i686-linux-android"; |
||||||
|
_36 = "i686-unknown-freebsd"; |
||||||
|
_37 = "nvptx64-nvidia-cuda"; |
||||||
|
_38 = "sparcv9-sun-solaris"; |
||||||
|
_39 = "thumbv7neon-linux-androideabi"; |
||||||
|
_40 = "wasm32-unknown-unknown"; |
||||||
|
_41 = "wasm32-wasi"; |
||||||
|
_42 = "x86_64-apple-ios"; |
||||||
|
_43 = "x86_64-fortanix-unknown-sgx"; |
||||||
|
_44 = "x86_64-fuchsia"; |
||||||
|
_45 = "x86_64-linux-android"; |
||||||
|
_46 = "x86_64-sun-solaris"; |
||||||
|
_47 = "x86_64-unknown-linux-gnux32"; |
||||||
|
_48 = "aarch64-unknown-none"; |
||||||
|
_49 = "aarch64-unknown-none-softfloat"; |
||||||
|
_50 = "arm-unknown-linux-musleabi"; |
||||||
|
_51 = "arm-unknown-linux-musleabihf"; |
||||||
|
_52 = "armebv7r-none-eabi"; |
||||||
|
_53 = "armebv7r-none-eabihf"; |
||||||
|
_54 = "armv5te-unknown-linux-gnueabi"; |
||||||
|
_55 = "armv5te-unknown-linux-musleabi"; |
||||||
|
_56 = "armv7-unknown-linux-musleabihf"; |
||||||
|
_57 = "armv7a-none-eabi"; |
||||||
|
_58 = "armv7r-none-eabi"; |
||||||
|
_59 = "armv7r-none-eabihf"; |
||||||
|
_60 = "asmjs-unknown-emscripten"; |
||||||
|
_61 = "i586-unknown-linux-gnu"; |
||||||
|
_62 = "i586-unknown-linux-musl"; |
||||||
|
_63 = "i686-unknown-linux-musl"; |
||||||
|
_64 = "mips-unknown-linux-musl"; |
||||||
|
_65 = "mips64-unknown-linux-muslabi64"; |
||||||
|
_66 = "mips64el-unknown-linux-muslabi64"; |
||||||
|
_67 = "mipsel-unknown-linux-musl"; |
||||||
|
_68 = "riscv32i-unknown-none-elf"; |
||||||
|
_69 = "riscv32imac-unknown-none-elf"; |
||||||
|
_70 = "riscv32imc-unknown-none-elf"; |
||||||
|
_71 = "riscv64gc-unknown-none-elf"; |
||||||
|
_72 = "riscv64imac-unknown-none-elf"; |
||||||
|
_73 = "sparc64-unknown-linux-gnu"; |
||||||
|
_74 = "thumbv6m-none-eabi"; |
||||||
|
_75 = "thumbv7em-none-eabi"; |
||||||
|
_76 = "thumbv7em-none-eabihf"; |
||||||
|
_77 = "thumbv7m-none-eabi"; |
||||||
|
_78 = "thumbv7neon-unknown-linux-gnueabihf"; |
||||||
|
_79 = "thumbv8m.base-none-eabi"; |
||||||
|
_80 = "thumbv8m.main-none-eabi"; |
||||||
|
_81 = "thumbv8m.main-none-eabihf"; |
||||||
|
_82 = "wasm32-unknown-emscripten"; |
||||||
|
_83 = "x86_64-rumprun-netbsd"; |
||||||
|
_84 = "x86_64-unknown-redox"; |
||||||
|
_85 = "i686-apple-darwin"; |
||||||
|
_86 = "armv7-apple-ios"; |
||||||
|
_87 = "armv7s-apple-ios"; |
||||||
|
_88 = "i386-apple-ios"; |
||||||
|
_89 = "x86_64-unknown-cloudabi"; |
||||||
|
_90 = "wasm32-unknown-wasi"; |
||||||
|
} |
@ -0,0 +1,301 @@ |
|||||||
|
# Modified from: https://github.com/mozilla/nixpkgs-mozilla/blob/8c007b60731c07dd7a052cce508de3bb1ae849b4/rust-overlay.nix |
||||||
|
|
||||||
|
# This file provide a Rust overlay, which provides pre-packaged bleeding edge versions of rustc |
||||||
|
# and cargo. |
||||||
|
self: super: |
||||||
|
manifests: |
||||||
|
|
||||||
|
let |
||||||
|
|
||||||
|
# Manifest selector. |
||||||
|
fromManifest = { channel }: { stdenv, fetchurl, patchelf }: let |
||||||
|
inherit (builtins) match; |
||||||
|
byVersion = match "([0-9]+\\.[0-9]+\\.[0-9]+)" channel != null; |
||||||
|
|
||||||
|
manifest = |
||||||
|
if channel == "stable" then manifests.stable.latest |
||||||
|
else if byVersion then manifests.stable.${channel} or (throw "Version ${channel} is not available") |
||||||
|
else throw "Unknown channel: ${channel}"; |
||||||
|
|
||||||
|
in fromManifestFile manifest { inherit stdenv fetchurl patchelf; }; |
||||||
|
|
||||||
|
getComponentsWithFixedPlatform = pkgs: pkgname: stdenv: |
||||||
|
let |
||||||
|
pkg = pkgs.${pkgname}; |
||||||
|
srcInfo = pkg.target.${super.rust.toRustTarget stdenv.targetPlatform} or pkg.target."*"; |
||||||
|
components = srcInfo.components or []; |
||||||
|
componentNamesList = |
||||||
|
builtins.map (pkg: pkg.pkg) (builtins.filter (pkg: (pkg.target != "*")) components); |
||||||
|
in |
||||||
|
componentNamesList; |
||||||
|
|
||||||
|
getExtensions = pkgs: pkgname: stdenv: |
||||||
|
let |
||||||
|
inherit (super.lib) unique; |
||||||
|
pkg = pkgs.${pkgname}; |
||||||
|
rustTarget = super.rust.toRustTarget stdenv.targetPlatform; |
||||||
|
srcInfo = pkg.target.${rustTarget} or pkg.target."*" or (throw "${pkgname} is no available"); |
||||||
|
extensions = srcInfo.extensions or []; |
||||||
|
extensionNamesList = unique (builtins.map (pkg: pkg.pkg) extensions); |
||||||
|
in |
||||||
|
extensionNamesList; |
||||||
|
|
||||||
|
hasTarget = pkgs: pkgname: target: |
||||||
|
pkgs ? ${pkgname}.target.${target}; |
||||||
|
|
||||||
|
getTuples = pkgs: name: targets: |
||||||
|
builtins.map (target: { inherit name target; }) (builtins.filter (target: hasTarget pkgs name target) targets); |
||||||
|
|
||||||
|
# In the manifest, a package might have different components which are bundled with it, as opposed as the extensions which can be added. |
||||||
|
# By default, a package will include the components for the same architecture, and offers them as extensions for other architectures. |
||||||
|
# |
||||||
|
# This functions returns a list of { name, target } attribute sets, which includes the current system package, and all its components for the selected targets. |
||||||
|
# The list contains the package for the pkgTargets as well as the packages for components for all compTargets |
||||||
|
getTargetPkgTuples = pkgs: pkgname: pkgTargets: compTargets: stdenv: |
||||||
|
let |
||||||
|
inherit (builtins) elem; |
||||||
|
inherit (super.lib) intersectLists; |
||||||
|
components = getComponentsWithFixedPlatform pkgs pkgname stdenv; |
||||||
|
extensions = getExtensions pkgs pkgname stdenv; |
||||||
|
compExtIntersect = intersectLists components extensions; |
||||||
|
tuples = (getTuples pkgs pkgname pkgTargets) ++ (builtins.map (name: getTuples pkgs name compTargets) compExtIntersect); |
||||||
|
in |
||||||
|
tuples; |
||||||
|
|
||||||
|
getFetchUrl = pkgs: pkgname: target: stdenv: fetchurl: |
||||||
|
let |
||||||
|
pkg = pkgs.${pkgname}; |
||||||
|
srcInfo = pkg.target.${target}; |
||||||
|
in |
||||||
|
(super.fetchurl { url = srcInfo.xz_url; sha256 = srcInfo.xz_hash; }); |
||||||
|
|
||||||
|
checkMissingExtensions = pkgs: pkgname: stdenv: extensions: |
||||||
|
let |
||||||
|
inherit (builtins) head; |
||||||
|
inherit (super.lib) concatStringsSep subtractLists; |
||||||
|
availableExtensions = getExtensions pkgs pkgname stdenv; |
||||||
|
missingExtensions = subtractLists availableExtensions extensions; |
||||||
|
extensionsToInstall = |
||||||
|
if missingExtensions == [] then extensions else throw '' |
||||||
|
While compiling ${pkgname}: the extension ${head missingExtensions} is not available. |
||||||
|
Select extensions from the following list: |
||||||
|
${concatStringsSep "\n" availableExtensions}''; |
||||||
|
in |
||||||
|
extensionsToInstall; |
||||||
|
|
||||||
|
getComponents = pkgs: pkgname: targets: extensions: targetExtensions: stdenv: fetchurl: |
||||||
|
let |
||||||
|
inherit (builtins) head map; |
||||||
|
inherit (super.lib) flatten remove subtractLists unique; |
||||||
|
targetExtensionsToInstall = checkMissingExtensions pkgs pkgname stdenv targetExtensions; |
||||||
|
extensionsToInstall = checkMissingExtensions pkgs pkgname stdenv extensions; |
||||||
|
hostTargets = [ "*" (super.rust.toRustTarget stdenv.hostPlatform) (super.rust.toRustTarget stdenv.targetPlatform) ]; |
||||||
|
pkgTuples = flatten (getTargetPkgTuples pkgs pkgname hostTargets targets stdenv); |
||||||
|
extensionTuples = flatten (map (name: getTargetPkgTuples pkgs name hostTargets targets stdenv) extensionsToInstall); |
||||||
|
targetExtensionTuples = flatten (map (name: getTargetPkgTuples pkgs name targets targets stdenv) targetExtensionsToInstall); |
||||||
|
pkgsTuples = pkgTuples ++ extensionTuples ++ targetExtensionTuples; |
||||||
|
missingTargets = subtractLists (map (tuple: tuple.target) pkgsTuples) (remove "*" targets); |
||||||
|
pkgsTuplesToInstall = |
||||||
|
if missingTargets == [] then pkgsTuples else throw '' |
||||||
|
While compiling ${pkgname}: the target ${head missingTargets} is not available for any package.''; |
||||||
|
in |
||||||
|
map (tuple: { name = tuple.name; src = (getFetchUrl pkgs tuple.name tuple.target stdenv fetchurl); }) pkgsTuplesToInstall; |
||||||
|
|
||||||
|
installComponents = stdenv: namesAndSrcs: |
||||||
|
let |
||||||
|
inherit (builtins) map; |
||||||
|
installComponent = name: src: |
||||||
|
stdenv.mkDerivation { |
||||||
|
inherit name; |
||||||
|
inherit src; |
||||||
|
|
||||||
|
# No point copying src to a build server, then copying back the |
||||||
|
# entire unpacked contents after just a little twiddling. |
||||||
|
preferLocalBuild = true; |
||||||
|
|
||||||
|
# (@nbp) TODO: Check on Windows and Mac. |
||||||
|
# This code is inspired by patchelf/setup-hook.sh to iterate over all binaries. |
||||||
|
installPhase = '' |
||||||
|
patchShebangs install.sh |
||||||
|
CFG_DISABLE_LDCONFIG=1 ./install.sh --prefix=$out --verbose |
||||||
|
setInterpreter() { |
||||||
|
local dir="$1" |
||||||
|
[ -e "$dir" ] || return 0 |
||||||
|
header "Patching interpreter of ELF executables and libraries in $dir" |
||||||
|
local i |
||||||
|
while IFS= read -r -d ''$'\0' i; do |
||||||
|
if [[ "$i" =~ .build-id ]]; then continue; fi |
||||||
|
if ! isELF "$i"; then continue; fi |
||||||
|
echo "setting interpreter of $i" |
||||||
|
|
||||||
|
if [[ -x "$i" ]]; then |
||||||
|
# Handle executables |
||||||
|
patchelf \ |
||||||
|
--set-interpreter "$(cat $NIX_CC/nix-support/dynamic-linker)" \ |
||||||
|
--set-rpath "${super.lib.makeLibraryPath [ self.zlib ]}:$out/lib" \ |
||||||
|
"$i" || true |
||||||
|
else |
||||||
|
# Handle libraries |
||||||
|
patchelf \ |
||||||
|
--set-rpath "${super.lib.makeLibraryPath [ self.zlib ]}:$out/lib" \ |
||||||
|
"$i" || true |
||||||
|
fi |
||||||
|
done < <(find "$dir" -type f -print0) |
||||||
|
} |
||||||
|
setInterpreter $out |
||||||
|
''; |
||||||
|
|
||||||
|
postFixup = '' |
||||||
|
# Function moves well-known files from etc/ |
||||||
|
handleEtc() { |
||||||
|
local oldIFS="$IFS" |
||||||
|
# Directories we are aware of, given as substitution lists |
||||||
|
for paths in \ |
||||||
|
"etc/bash_completion.d","share/bash_completion/completions","etc/bash_completions.d","share/bash_completions/completions"; |
||||||
|
do |
||||||
|
# Some directoties may be missing in some versions. If so we just skip them. |
||||||
|
# See https://github.com/mozilla/nixpkgs-mozilla/issues/48 for more infomation. |
||||||
|
if [ ! -e $paths ]; then continue; fi |
||||||
|
IFS="," |
||||||
|
set -- $paths |
||||||
|
IFS="$oldIFS" |
||||||
|
local orig_path="$1" |
||||||
|
local wanted_path="$2" |
||||||
|
# Rename the files |
||||||
|
if [ -d ./"$orig_path" ]; then |
||||||
|
mkdir -p "$(dirname ./"$wanted_path")" |
||||||
|
fi |
||||||
|
mv -v ./"$orig_path" ./"$wanted_path" |
||||||
|
# Fail explicitly if etc is not empty so we can add it to the list and/or report it upstream |
||||||
|
rmdir ./etc || { |
||||||
|
echo Installer tries to install to /etc: |
||||||
|
find ./etc |
||||||
|
exit 1 |
||||||
|
} |
||||||
|
done |
||||||
|
} |
||||||
|
if [ -d "$out"/etc ]; then |
||||||
|
pushd "$out" |
||||||
|
handleEtc |
||||||
|
popd |
||||||
|
fi |
||||||
|
''; |
||||||
|
|
||||||
|
dontStrip = true; |
||||||
|
}; |
||||||
|
in |
||||||
|
map (nameAndSrc: (installComponent nameAndSrc.name nameAndSrc.src)) namesAndSrcs; |
||||||
|
|
||||||
|
# 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! |
||||||
|
fromManifestFile = pkgs: { stdenv, fetchurl, patchelf }: |
||||||
|
let |
||||||
|
inherit (builtins) elemAt; |
||||||
|
inherit (super) makeOverridable; |
||||||
|
inherit (super.lib) flip mapAttrs; |
||||||
|
in |
||||||
|
flip mapAttrs pkgs.pkg (name: pkg: |
||||||
|
makeOverridable ({extensions, targets, targetExtensions}: |
||||||
|
let |
||||||
|
version' = builtins.match "([^ ]*) [(]([^ ]*) ([^ ]*)[)]" pkg.version; |
||||||
|
version = if version' == null then pkg.version else "${elemAt version' 0}-${elemAt version' 2}-${elemAt version' 1}"; |
||||||
|
namesAndSrcs = getComponents pkgs.pkg name targets extensions targetExtensions stdenv fetchurl; |
||||||
|
components = installComponents stdenv namesAndSrcs; |
||||||
|
componentsOuts = builtins.map (comp: (super.lib.strings.escapeNixString (super.lib.getOutput "out" comp))) components; |
||||||
|
in |
||||||
|
super.pkgs.symlinkJoin { |
||||||
|
name = name + "-" + version; |
||||||
|
paths = components; |
||||||
|
postBuild = '' |
||||||
|
# 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. |
||||||
|
for target in $out/bin/{rustc,rustdoc}; do |
||||||
|
if [ -e $target ]; then |
||||||
|
cp --remove-destination "$(realpath -e $target)" $target |
||||||
|
fi |
||||||
|
done |
||||||
|
''; |
||||||
|
|
||||||
|
# Add the compiler as part of the propagated build inputs in order |
||||||
|
# to run: |
||||||
|
# |
||||||
|
# $ nix-shell -p rustChannels.stable.rust |
||||||
|
# |
||||||
|
# And get a fully working Rust compiler, with the stdenv linker. |
||||||
|
propagatedBuildInputs = [ stdenv.cc ]; |
||||||
|
|
||||||
|
meta.platforms = stdenv.lib.platforms.all; |
||||||
|
} |
||||||
|
) { extensions = []; targets = []; targetExtensions = []; } |
||||||
|
); |
||||||
|
|
||||||
|
in |
||||||
|
|
||||||
|
rec { |
||||||
|
lib = super.lib // { |
||||||
|
rustLib = { |
||||||
|
inherit fromManifest fromManifestFile; |
||||||
|
}; |
||||||
|
}; |
||||||
|
|
||||||
|
rustChannelOf = manifest_args: fromManifest |
||||||
|
manifest_args |
||||||
|
{ inherit (self) stdenv fetchurl patchelf; }; |
||||||
|
|
||||||
|
# Set of packages which are automagically updated. Do not rely on these for |
||||||
|
# reproducible builds. |
||||||
|
latest = (super.latest or {}) // { |
||||||
|
rustChannels = { |
||||||
|
nightly = rustChannelOf { channel = "nightly"; }; |
||||||
|
beta = rustChannelOf { channel = "beta"; }; |
||||||
|
stable = rustChannelOf { channel = "stable"; }; |
||||||
|
}; |
||||||
|
}; |
||||||
|
|
||||||
|
# Helper builder |
||||||
|
rustChannelOfTargets = channel: date: targets: |
||||||
|
(rustChannelOf { inherit channel date; }) |
||||||
|
.rust.override { inherit targets; }; |
||||||
|
|
||||||
|
# For backward compatibility |
||||||
|
rustChannels = latest.rustChannels; |
||||||
|
|
||||||
|
# For each channel: |
||||||
|
# latest.rustChannels.nightly.cargo |
||||||
|
# latest.rustChannels.nightly.rust # Aggregate all others. (recommended) |
||||||
|
# latest.rustChannels.nightly.rustc |
||||||
|
# latest.rustChannels.nightly.rust-analysis |
||||||
|
# latest.rustChannels.nightly.rust-docs |
||||||
|
# latest.rustChannels.nightly.rust-src |
||||||
|
# latest.rustChannels.nightly.rust-std |
||||||
|
|
||||||
|
# For a specific date: |
||||||
|
# (rustChannelOf { date = "2017-06-06"; channel = "beta"; }).rust |
||||||
|
} |
Loading…
Reference in new issue