Clone of https://github.com/NixOS/nixpkgs.git (to stress-test knotserver)

mkBinaryCache: support different compression methods: xz (default), zstd, none

thomasjm 00a218ab 2f5bd177

+82 -24
+8
doc/build-helpers/images/binarycache.section.md
··· 11 `rootPaths` must be a list of derivations. 12 The transitive closure of these derivations' outputs will be copied into the cache. 13 14 ::: {.note} 15 This function is meant for advanced use cases. 16 The more idiomatic way to work with flat-file binary caches is via the [nix-copy-closure](https://nixos.org/manual/nix/stable/command-ref/nix-copy-closure.html) command.
··· 11 `rootPaths` must be a list of derivations. 12 The transitive closure of these derivations' outputs will be copied into the cache. 13 14 + ## Optional arguments {#sec-pkgs-binary-cache-arguments} 15 + 16 + `compression` (`"none"` or `"xz"` or `"zstd"`; _optional_) 17 + 18 + : The compression algorithm to use. 19 + 20 + _Default value:_ `zstd`. 21 + 22 ::: {.note} 23 This function is meant for advanced use cases. 24 The more idiomatic way to work with flat-file binary caches is via the [nix-copy-closure](https://nixos.org/manual/nix/stable/command-ref/nix-copy-closure.html) command.
+3
doc/redirects.json
··· 1959 "sec-pkgs-binary-cache": [ 1960 "index.html#sec-pkgs-binary-cache" 1961 ], 1962 "sec-pkgs-binary-cache-example": [ 1963 "index.html#sec-pkgs-binary-cache-example" 1964 ],
··· 1959 "sec-pkgs-binary-cache": [ 1960 "index.html#sec-pkgs-binary-cache" 1961 ], 1962 + "sec-pkgs-binary-cache-arguments": [ 1963 + "index.html#sec-pkgs-binary-cache-arguments" 1964 + ], 1965 "sec-pkgs-binary-cache-example": [ 1966 "index.html#sec-pkgs-binary-cache-example" 1967 ],
+3 -1
nixos/tests/all-tests.nix
··· 154 beanstalkd = handleTest ./beanstalkd.nix {}; 155 bees = handleTest ./bees.nix {}; 156 benchexec = handleTest ./benchexec.nix {}; 157 - binary-cache = handleTest ./binary-cache.nix {}; 158 bind = handleTest ./bind.nix {}; 159 bird = handleTest ./bird.nix {}; 160 birdwatcher = handleTest ./birdwatcher.nix {};
··· 154 beanstalkd = handleTest ./beanstalkd.nix {}; 155 bees = handleTest ./bees.nix {}; 156 benchexec = handleTest ./benchexec.nix {}; 157 + binary-cache = handleTest ./binary-cache.nix { compression = "zstd"; }; 158 + binary-cache-no-compression = handleTest ./binary-cache.nix { compression = "none"; }; 159 + binary-cache-xz = handleTest ./binary-cache.nix { compression = "xz"; }; 160 bind = handleTest ./bind.nix {}; 161 bird = handleTest ./bird.nix {}; 162 birdwatcher = handleTest ./birdwatcher.nix {};
+10 -3
nixos/tests/binary-cache.nix
··· 1 import ./make-test-python.nix ( 2 { lib, pkgs, ... }: 3 4 { 5 - name = "binary-cache"; 6 meta.maintainers = with lib.maintainers; [ thomasjm ]; 7 8 nodes.machine = ··· 24 nativeBuildInputs = [ openssl ]; 25 } 26 '' 27 - tar -czf tmp.tar.gz -C "${mkBinaryCache { rootPaths = [ hello ]; }}" . 28 openssl enc -aes-256-cbc -salt -in tmp.tar.gz -out $out -k mysecretpassword 29 ''; 30 ··· 78 machine.succeed("[ -d %s ] || exit 1" % storePath) 79 ''; 80 } 81 - )
··· 1 + { compression, ... }@args: 2 + 3 import ./make-test-python.nix ( 4 { lib, pkgs, ... }: 5 6 { 7 + name = "binary-cache-" + compression; 8 meta.maintainers = with lib.maintainers; [ thomasjm ]; 9 10 nodes.machine = ··· 26 nativeBuildInputs = [ openssl ]; 27 } 28 '' 29 + tar -czf tmp.tar.gz -C "${ 30 + mkBinaryCache { 31 + rootPaths = [ hello ]; 32 + inherit compression; 33 + } 34 + }" . 35 openssl enc -aes-256-cbc -salt -in tmp.tar.gz -out $out -k mysecretpassword 36 ''; 37 ··· 85 machine.succeed("[ -d %s ] || exit 1" % storePath) 86 ''; 87 } 88 + ) args
+18 -8
pkgs/build-support/binary-cache/default.nix
··· 6 python3, 7 nix, 8 xz, 9 }: 10 11 # This function is for creating a flat-file binary cache, i.e. the kind created by ··· 16 17 { 18 name ? "binary-cache", 19 rootPaths, 20 }: 21 22 stdenv.mkDerivation { 23 inherit name; 24 ··· 28 29 preferLocalBuild = true; 30 31 - nativeBuildInputs = [ 32 - coreutils 33 - jq 34 - python3 35 - nix 36 - xz 37 - ]; 38 39 buildCommand = '' 40 mkdir -p $out/nar 41 42 - python ${./make-binary-cache.py} 43 44 # These directories must exist, or Nix might try to create them in LocalBinaryCacheStore::init(), 45 # which fails if mounted read-only
··· 6 python3, 7 nix, 8 xz, 9 + zstd, 10 }: 11 12 # This function is for creating a flat-file binary cache, i.e. the kind created by ··· 17 18 { 19 name ? "binary-cache", 20 + compression ? "zstd", # one of ["none" "xz" "zstd"] 21 rootPaths, 22 }: 23 24 + assert lib.elem compression [ 25 + "none" 26 + "xz" 27 + "zstd" 28 + ]; 29 + 30 stdenv.mkDerivation { 31 inherit name; 32 ··· 36 37 preferLocalBuild = true; 38 39 + nativeBuildInputs = 40 + [ 41 + coreutils 42 + jq 43 + python3 44 + nix 45 + ] 46 + ++ lib.optional (compression == "xz") xz 47 + ++ lib.optional (compression == "zstd") zstd; 48 49 buildCommand = '' 50 mkdir -p $out/nar 51 52 + python ${./make-binary-cache.py} --compression "${compression}" 53 54 # These directories must exist, or Nix might try to create them in LocalBinaryCacheStore::init(), 55 # which fails if mounted read-only
+40 -12
pkgs/build-support/binary-cache/make-binary-cache.py
··· 1 from functools import partial 2 import json 3 from multiprocessing import Pool ··· 10 return path[len(nixPrefix + "/") :] 11 12 13 - def processItem(item, nixPrefix, outDir): 14 narInfoHash = dropPrefix(item["path"], nixPrefix).split("-")[0] 15 16 - xzFile = outDir / "nar" / f"{narInfoHash}.nar.xz" 17 - with open(xzFile, "wb") as f: 18 subprocess.run( 19 - f"nix-store --dump {item['path']} | xz -c", 20 stdout=f, 21 shell=True, 22 check=True, ··· 24 25 fileHash = ( 26 subprocess.run( 27 - ["nix-hash", "--base32", "--type", "sha256", "--flat", xzFile], 28 capture_output=True, 29 check=True, 30 ) 31 .stdout.decode() 32 .strip() 33 ) 34 - fileSize = os.path.getsize(xzFile) 35 36 - finalXzFileName = Path("nar") / f"{fileHash}.nar.xz" 37 - os.rename(xzFile, outDir / finalXzFileName) 38 39 with open(outDir / f"{narInfoHash}.narinfo", "wt") as f: 40 f.write(f"StorePath: {item['path']}\n") 41 - f.write(f"URL: {finalXzFileName}\n") 42 - f.write("Compression: xz\n") 43 f.write(f"FileHash: sha256:{fileHash}\n") 44 f.write(f"FileSize: {fileSize}\n") 45 f.write(f"NarHash: {item['narHash']}\n") 46 f.write(f"NarSize: {item['narSize']}\n") 47 - f.write(f"References: {' '.join(dropPrefix(ref, nixPrefix) for ref in item['references'])}\n") 48 49 50 def main(): 51 outDir = Path(os.environ["out"]) 52 nixPrefix = os.environ["NIX_STORE"] 53 numWorkers = int(os.environ.get("NIX_BUILD_CORES", "4")) ··· 61 f.write(f"StoreDir: {nixPrefix}\n") 62 63 with Pool(processes=numWorkers) as pool: 64 - worker = partial(processItem, nixPrefix=nixPrefix, outDir=outDir) 65 pool.map(worker, closures) 66 67
··· 1 + import argparse 2 from functools import partial 3 import json 4 from multiprocessing import Pool ··· 11 return path[len(nixPrefix + "/") :] 12 13 14 + def processItem( 15 + item, nixPrefix, outDir, compression, compressionCommand, compressionExtension 16 + ): 17 narInfoHash = dropPrefix(item["path"], nixPrefix).split("-")[0] 18 19 + narFile = outDir / "nar" / f"{narInfoHash}{compressionExtension}" 20 + with open(narFile, "wb") as f: 21 subprocess.run( 22 + f"nix-store --dump {item['path']} {compressionCommand}", 23 stdout=f, 24 shell=True, 25 check=True, ··· 27 28 fileHash = ( 29 subprocess.run( 30 + ["nix-hash", "--base32", "--type", "sha256", "--flat", narFile], 31 capture_output=True, 32 check=True, 33 ) 34 .stdout.decode() 35 .strip() 36 ) 37 + fileSize = os.path.getsize(narFile) 38 39 + finalNarFileName = Path("nar") / f"{fileHash}{compressionExtension}" 40 + os.rename(narFile, outDir / finalNarFileName) 41 42 with open(outDir / f"{narInfoHash}.narinfo", "wt") as f: 43 f.write(f"StorePath: {item['path']}\n") 44 + f.write(f"URL: {finalNarFileName}\n") 45 + f.write(f"Compression: {compression}\n") 46 f.write(f"FileHash: sha256:{fileHash}\n") 47 f.write(f"FileSize: {fileSize}\n") 48 f.write(f"NarHash: {item['narHash']}\n") 49 f.write(f"NarSize: {item['narSize']}\n") 50 + f.write( 51 + f"References: {' '.join(dropPrefix(ref, nixPrefix) for ref in item['references'])}\n" 52 + ) 53 54 55 def main(): 56 + parser = argparse.ArgumentParser() 57 + parser.add_argument("--compression", choices=["none", "xz", "zstd"]) 58 + args = parser.parse_args() 59 + 60 + compressionCommand = { 61 + "none": "", 62 + "xz": "| xz -c", 63 + "zstd": "| zstd", 64 + }[args.compression] 65 + 66 + compressionExtension = { 67 + "none": "", 68 + "xz": ".xz", 69 + "zstd": ".zst", 70 + }[args.compression] 71 + 72 outDir = Path(os.environ["out"]) 73 nixPrefix = os.environ["NIX_STORE"] 74 numWorkers = int(os.environ.get("NIX_BUILD_CORES", "4")) ··· 82 f.write(f"StoreDir: {nixPrefix}\n") 83 84 with Pool(processes=numWorkers) as pool: 85 + worker = partial( 86 + processItem, 87 + nixPrefix=nixPrefix, 88 + outDir=outDir, 89 + compression=args.compression, 90 + compressionCommand=compressionCommand, 91 + compressionExtension=compressionExtension, 92 + ) 93 pool.map(worker, closures) 94 95