lol
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at 23.11-beta 262 lines 9.4 kB view raw
1{ fetchgit, fetchurl, lib, writers, python3Packages, runCommand, cargo, jq }: 2 3{ 4 # Cargo lock file 5 lockFile ? null 6 7 # Cargo lock file contents as string 8, lockFileContents ? null 9 10 # Allow `builtins.fetchGit` to be used to not require hashes for git dependencies 11, allowBuiltinFetchGit ? false 12 13 # Additional registries to pull sources from 14 # { "https://<registry index URL>" = "https://<registry download URL>"; } 15 # where: 16 # - "index URL" is the "index" value of the configuration entry for that registry 17 # https://doc.rust-lang.org/cargo/reference/registries.html#using-an-alternate-registry 18 # - "download URL" is the "dl" value of its associated index configuration 19 # https://doc.rust-lang.org/cargo/reference/registry-index.html#index-configuration 20, extraRegistries ? {} 21 22 # Hashes for git dependencies. 23, outputHashes ? {} 24} @ args: 25 26assert (lockFile == null) != (lockFileContents == null); 27 28let 29 # Parse a git source into different components. 30 parseGit = src: 31 let 32 parts = builtins.match ''git\+([^?]+)(\?(rev|tag|branch)=(.*))?#(.*)'' src; 33 type = builtins.elemAt parts 2; # rev, tag or branch 34 value = builtins.elemAt parts 3; 35 in 36 if parts == null then null 37 else { 38 url = builtins.elemAt parts 0; 39 sha = builtins.elemAt parts 4; 40 } // lib.optionalAttrs (type != null) { inherit type value; }; 41 42 # shadows args.lockFileContents 43 lockFileContents = 44 if lockFile != null 45 then builtins.readFile lockFile 46 else args.lockFileContents; 47 48 parsedLockFile = builtins.fromTOML lockFileContents; 49 50 packages = parsedLockFile.package; 51 52 # There is no source attribute for the source package itself. But 53 # since we do not want to vendor the source package anyway, we can 54 # safely skip it. 55 depPackages = builtins.filter (p: p ? "source") packages; 56 57 # Create dependent crates from packages. 58 # 59 # Force evaluation of the git SHA -> hash mapping, so that an error is 60 # thrown if there are stale hashes. We cannot rely on gitShaOutputHash 61 # being evaluated otherwise, since there could be no git dependencies. 62 depCrates = builtins.deepSeq gitShaOutputHash (builtins.map mkCrate depPackages); 63 64 # Map package name + version to git commit SHA for packages with a git source. 65 namesGitShas = builtins.listToAttrs ( 66 builtins.map nameGitSha (builtins.filter (pkg: lib.hasPrefix "git+" pkg.source) depPackages) 67 ); 68 69 nameGitSha = pkg: let gitParts = parseGit pkg.source; in { 70 name = "${pkg.name}-${pkg.version}"; 71 value = gitParts.sha; 72 }; 73 74 # Convert the attrset provided through the `outputHashes` argument to a 75 # a mapping from git commit SHA -> output hash. 76 # 77 # There may be multiple different packages with different names 78 # originating from the same git repository (typically a Cargo 79 # workspace). By using the git commit SHA as a universal identifier, 80 # the user does not have to specify the output hash for every package 81 # individually. 82 gitShaOutputHash = lib.mapAttrs' (nameVer: hash: 83 let 84 unusedHash = throw "A hash was specified for ${nameVer}, but there is no corresponding git dependency."; 85 rev = namesGitShas.${nameVer} or unusedHash; in { 86 name = rev; 87 value = hash; 88 }) outputHashes; 89 90 # We can't use the existing fetchCrate function, since it uses a 91 # recursive hash of the unpacked crate. 92 fetchCrate = pkg: downloadUrl: 93 let 94 checksum = pkg.checksum or parsedLockFile.metadata."checksum ${pkg.name} ${pkg.version} (${pkg.source})"; 95 in 96 assert lib.assertMsg (checksum != null) '' 97 Package ${pkg.name} does not have a checksum. 98 ''; 99 fetchurl { 100 name = "crate-${pkg.name}-${pkg.version}.tar.gz"; 101 url = "${downloadUrl}/${pkg.name}/${pkg.version}/download"; 102 sha256 = checksum; 103 }; 104 105 registries = { 106 "https://github.com/rust-lang/crates.io-index" = "https://crates.io/api/v1/crates"; 107 } // extraRegistries; 108 109 # Replaces values inherited by workspace members. 110 replaceWorkspaceValues = writers.writePython3 "replace-workspace-values" 111 { libraries = with python3Packages; [ tomli tomli-w ]; flakeIgnore = [ "E501" "W503" ]; } 112 (builtins.readFile ./replace-workspace-values.py); 113 114 # Fetch and unpack a crate. 115 mkCrate = pkg: 116 let 117 gitParts = parseGit pkg.source; 118 registryIndexUrl = lib.removePrefix "registry+" pkg.source; 119 in 120 if lib.hasPrefix "registry+" pkg.source && builtins.hasAttr registryIndexUrl registries then 121 let 122 crateTarball = fetchCrate pkg registries.${registryIndexUrl}; 123 in runCommand "${pkg.name}-${pkg.version}" {} '' 124 mkdir $out 125 tar xf "${crateTarball}" -C $out --strip-components=1 126 127 # Cargo is happy with largely empty metadata. 128 printf '{"files":{},"package":"${crateTarball.outputHash}"}' > "$out/.cargo-checksum.json" 129 '' 130 else if gitParts != null then 131 let 132 missingHash = throw '' 133 No hash was found while vendoring the git dependency ${pkg.name}-${pkg.version}. You can add 134 a hash through the `outputHashes` argument of `importCargoLock`: 135 136 outputHashes = { 137 "${pkg.name}-${pkg.version}" = "<hash>"; 138 }; 139 140 If you use `buildRustPackage`, you can add this attribute to the `cargoLock` 141 attribute set. 142 ''; 143 tree = 144 if gitShaOutputHash ? ${gitParts.sha} then 145 fetchgit { 146 inherit (gitParts) url; 147 rev = gitParts.sha; # The commit SHA is always available. 148 sha256 = gitShaOutputHash.${gitParts.sha}; 149 } 150 else if allowBuiltinFetchGit then 151 builtins.fetchGit { 152 inherit (gitParts) url; 153 rev = gitParts.sha; 154 allRefs = true; 155 submodules = true; 156 } 157 else 158 missingHash; 159 in runCommand "${pkg.name}-${pkg.version}" {} '' 160 tree=${tree} 161 162 # If the target package is in a workspace, or if it's the top-level 163 # crate, we should find the crate path using `cargo metadata`. 164 # Some packages do not have a Cargo.toml at the top-level, 165 # but only in nested directories. 166 # Only check the top-level Cargo.toml, if it actually exists 167 if [[ -f $tree/Cargo.toml ]]; then 168 crateCargoTOML=$(${cargo}/bin/cargo metadata --format-version 1 --no-deps --manifest-path $tree/Cargo.toml | \ 169 ${jq}/bin/jq -r '.packages[] | select(.name == "${pkg.name}") | .manifest_path') 170 fi 171 172 # If the repository is not a workspace the package might be in a subdirectory. 173 if [[ -z $crateCargoTOML ]]; then 174 for manifest in $(find $tree -name "Cargo.toml"); do 175 echo Looking at $manifest 176 crateCargoTOML=$(${cargo}/bin/cargo metadata --format-version 1 --no-deps --manifest-path "$manifest" | ${jq}/bin/jq -r '.packages[] | select(.name == "${pkg.name}") | .manifest_path' || :) 177 if [[ ! -z $crateCargoTOML ]]; then 178 break 179 fi 180 done 181 182 if [[ -z $crateCargoTOML ]]; then 183 >&2 echo "Cannot find path for crate '${pkg.name}-${pkg.version}' in the tree in: $tree" 184 exit 1 185 fi 186 fi 187 188 echo Found crate ${pkg.name} at $crateCargoTOML 189 tree=$(dirname $crateCargoTOML) 190 191 cp -prvL "$tree/" $out 192 chmod u+w $out 193 194 if grep -q workspace "$out/Cargo.toml"; then 195 chmod u+w "$out/Cargo.toml" 196 ${replaceWorkspaceValues} "$out/Cargo.toml" "${tree}/Cargo.toml" 197 fi 198 199 # Cargo is happy with empty metadata. 200 printf '{"files":{},"package":null}' > "$out/.cargo-checksum.json" 201 202 # Set up configuration for the vendor directory. 203 cat > $out/.cargo-config <<EOF 204 [source."${gitParts.url}${lib.optionalString (gitParts ? type) "?${gitParts.type}=${gitParts.value}"}"] 205 git = "${gitParts.url}" 206 ${lib.optionalString (gitParts ? type) "${gitParts.type} = \"${gitParts.value}\""} 207 replace-with = "vendored-sources" 208 EOF 209 '' 210 else throw "Cannot handle crate source: ${pkg.source}"; 211 212 vendorDir = runCommand "cargo-vendor-dir" 213 (if lockFile == null then { 214 inherit lockFileContents; 215 passAsFile = [ "lockFileContents" ]; 216 } else { 217 passthru = { 218 inherit lockFile; 219 }; 220 }) '' 221 mkdir -p $out/.cargo 222 223 ${ 224 if lockFile != null 225 then "ln -s ${lockFile} $out/Cargo.lock" 226 else "cp $lockFileContentsPath $out/Cargo.lock" 227 } 228 229 cat > $out/.cargo/config <<EOF 230[source.crates-io] 231replace-with = "vendored-sources" 232 233[source.vendored-sources] 234directory = "cargo-vendor-dir" 235EOF 236 237 declare -A keysSeen 238 239 for registry in ${toString (builtins.attrNames extraRegistries)}; do 240 cat >> $out/.cargo/config <<EOF 241 242[source."$registry"] 243registry = "$registry" 244replace-with = "vendored-sources" 245EOF 246 done 247 248 for crate in ${toString depCrates}; do 249 # Link the crate directory, removing the output path hash from the destination. 250 ln -s "$crate" $out/$(basename "$crate" | cut -c 34-) 251 252 if [ -e "$crate/.cargo-config" ]; then 253 key=$(sed 's/\[source\."\(.*\)"\]/\1/; t; d' < "$crate/.cargo-config") 254 if [[ -z ''${keysSeen[$key]} ]]; then 255 keysSeen[$key]=1 256 cat "$crate/.cargo-config" >> $out/.cargo/config 257 fi 258 fi 259 done 260 ''; 261in 262 vendorDir