Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
Patryk27 committed Mar 13, 2022
1 parent 2fc8ce9 commit 61b26a4
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 118 deletions.
81 changes: 52 additions & 29 deletions build.nix
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,6 @@ let
builtins // import ./builtins
{ inherit lib writeText remarshal runCommand; };

# All the git dependencies, as a list
gitDependenciesList =
lib.concatLists (lib.mapAttrsToList (_: ds: ds) gitDependencies);

# This unpacks all git dependencies:
# $out/rand
# $out/rand/Cargo.toml
Expand All @@ -90,46 +86,73 @@ let
# * grab whatever is surrounded with `"`s.
# The last step is very, very slow.
unpackedGitDependencies = runCommand "git-deps"
{ nativeBuildInputs = [ jq ]; }
{ nativeBuildInputs = [ jq cargo ]; }
''
log() {
>&2 echo "[naersk]" "$@"
}
unpack() {
toml=$1
nkey=$2
nkey=''${nkey/\//_}
# Most filesystems have a maximum filename length of 255
dest="$out/$(echo "$nkey" | head -c 255)"
if [ -d "$dest" ]; then
log "Crate was already unpacked at $dest"
else
cp -r $(dirname $toml) $dest
chmod +w "$dest"
echo '{"package":null,"files":{}}' > $dest/.cargo-checksum.json
log "Crate unpacked at $dest"
fi
}
mkdir -p $out
while read -r dep; do
checkout=$(echo "$dep" | jq -cMr '.checkout')
url=$(echo "$dep" | jq -cMr '.url')
checkout=$(echo "$dep" | jq -r '.checkout')
key=$(echo "$dep" | jq -r '.key')
name=$(echo "$dep" | jq -r '.name')
url=$(echo "$dep" | jq -r '.url')
if [ -f $checkout/Cargo.toml ]; then
package=$(cargo metadata --no-deps --format-version 1 \
--manifest-path $checkout/Cargo.toml \
| jq -cMr ".packages[] | select(.name == \"$name\")")
if [ -n "$package" ]; then
version=$(echo "$package" | jq -r '.version')
toml=$(echo "$package" | jq -r '.manifest_path')
nkey="$name-$version-$key"
log "$url Extracted crate '$name-$version' ($nkey)"
unpack $toml $nkey
continue
fi
fi
success=0
tomls=$(find $checkout -name Cargo.toml)
key=$(echo "$dep" | jq -cMr '.key')
while read -r toml; do
name=$(cat $toml \
pname=$(cat $toml \
| sed -n -e '/\[package\]/,$p' \
| grep -m 1 "^name\W" \
| grep -oP '(?<=").+(?=")' \
|| true)
if [ "$name" -ne "$pname" ]; then
continue
fi
version=$(cat $toml \
| sed -n -e '/\[package\]/,$p' \
| grep -m 1 "^version\W" \
| grep -oP '(?<=").+(?=")' \
|| true)
if [ -n "$name" -a -n "$version" ]; then
# Most filesystmes have a maximum filename length of 255
nkey="$(echo "$name-$version-$key" | head -c 255)"
if [ -n "$version" ]; then
nkey="$name-$version-$key"
log "$url Found crate '$name-$version' ($nkey)"
if [ -d "$out/$nkey" ]; then
log "Crate was already unpacked at $out/$nkey"
else
cp -r $(dirname $toml) $out/$nkey
chmod +w "$out/$nkey"
echo '{"package":null,"files":{}}' > $out/$nkey/.cargo-checksum.json
log "Crate unpacked at $out/$nkey"
fi
unpack $toml $nkey
success=1
fi
done <<< "$tomls"
if [ $success -eq 0 ]; then
log "$url Failed to unpack $name"
fi
done < <(cat ${
builtins.toFile "git-deps-json" (builtins.toJSON gitDependenciesList)
builtins.toFile "git-deps-json" (builtins.toJSON gitDependencies)
} | jq -cMr '.[]')
'';

Expand Down Expand Up @@ -162,20 +185,20 @@ let
(
e:
let
key = if e ? rev then "rev=${e.rev}" else
if e ? tag then "tag=${e.tag}" else
if e ? branch then "branch=${e.branch}" else
throw "No 'rev', 'tag' or 'branch' specified";
key = if e ? rev then "?rev=${e.rev}" else
if e ? tag then "?tag=${e.tag}" else
if e ? branch then "?branch=${e.branch}" else
"";
in
{
name = "${e.url}?${key}";
name = "${e.url}${key}";
value = lib.filterAttrs (n: _: n == "rev" || n == "tag" || n == "branch") e // {
git = e.url;
replace-with = "nix-sources";
};
}
)
gitDependenciesList
gitDependencies
);
};

Expand Down
2 changes: 1 addition & 1 deletion default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ let
let
config = mkConfig arg;
gitDependencies =
libb.findGitDependencies { inherit (config) cargotomls cargolock; };
libb.findGitDependencies { inherit (config) cargolock; };
cargoconfig =
if builtinz.pathExists (toString config.root + "/.cargo/config")
then builtins.readFile (config.root + "/.cargo/config")
Expand Down
137 changes: 49 additions & 88 deletions lib.nix
Original file line number Diff line number Diff line change
Expand Up @@ -64,99 +64,60 @@ rec
mkMetadataKey = name: version:
"checksum ${name} ${version} (registry+https://github.com/rust-lang/crates.io-index)";

# a record:
# { "." = # '.' is the directory of the cargotoml
# [
# {
# name = "rand";
# url = "https://github.com/...";
# checkout = "/nix/store/checkout"
# }
# ]
# Gets all git dependencies in Cargo.lock as a list.
# [
# {
# name = "rand";
# url = "https://github.com/...";
# checkout = "/nix/store/checkout"
# }
# ]
findGitDependencies =
{ cargotomls
, cargolock
}:
let
# This returns all the git dependencies of a particular Cargo.toml.
# tomlDependencies : Cargo.toml ->
# [
# {
# checkout = { rev = "abcd"; shortRev = "abcd"; outPath = ...; ... };
# key = "abcd";
# name = "rand";
# url = "https://github.com/...";
# }
# ...
# ]
#
# NOTE: this is terribly inefficient _and_ confusing:
# * the lockfile is read once for every cargo toml entry (n^2)
# * 'fromLockfile' is odd, instead of returning the first matched
# revision it'll return a pseudo lockfile entry
tomlDependencies = cargotoml:
lib.filter (x: ! isNull x) (
lib.mapAttrsToList
(entryName: v: # The dependecy name + the entry from the cargo toml
if ! (lib.isAttrs v && builtins.hasAttr "git" v)
then null
else
let
# Use the 'package' attribute if it exists, which means this is a renamed dependency
# https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#renaming-dependencies-in-cargotoml
depName = v.package or entryName;
# predicate that holds if the given lockfile entry is the
# Cargo.toml dependency being looked at (depName)
pred = entry: entry.name == depName && (lib.substring 0 (4 + lib.stringLength v.git) entry.source) == "git+${v.git}";

# extract the revision from a Cargo.lock "package.source"
# entry
# git+https://gi.../rand?rev=703452...#703452
# ^^^^^^
extractRevision = url: lib.last (lib.splitString "#" url);

# parse a lockfile entry:
# { name = "rand";
# version = ...;
# source = "git+https://...rev=703452...#703452";
# } ->
# { name = "rand";
# source = "git+https://...rev=703452...#703452";
# revision = "703452";
# }
parseLockfileEntry = entry: rec { inherit (entry) name source; revision = extractRevision source; };

# List of all entries: [ { name, source, revision } ]
packageLocks = builtins.map parseLockfileEntry (lib.filter pred cargolock.package);
{ cargolock, allRefs ? false, gitSubmodules ? false }:
let
query = p: (lib.substring 0 4 (p.source or "")) == "git+";

# Find the first revision from the lockfile that:
# * has the same name as the cargo toml entry _if_ the cargo toml does not specify a revision, or
# * has the same name and revision as the cargo toml entry
fromLockfile = lib.findFirst (p: p.name == depName && ((! v?rev) || v.rev == p.revision)) null packageLocks;
extractRevision = source: lib.last (lib.splitString "#" source);
extractPart = part: source: if lib.hasInfix part source then lib.last (lib.splitString part (lib.head (lib.splitString "#" source))) else null;
extractRepoUrl = source:
let
splitted = lib.head (lib.splitString "?" source);
split = lib.substring 4 (lib.stringLength splitted) splitted;
in lib.head (lib.splitString "#" split);

# Cargo.lock revision is prioritized, because in Cargo.toml short revisions are allowed
val = v // { rev = fromLockfile.revision or v.rev or null; };
in
lib.filterAttrs (n: _: n == "rev" || n == "tag" || n == "branch") val //
{
name = depName;
url = val.git;
key = val.rev or val.tag or val.branch or
(throw "No 'rev', 'tag' or 'branch' available to specify key, nor a git revision was found in Cargo.lock");
checkout = builtins.fetchGit ({
url = val.git;
} // lib.optionalAttrs (val ? rev) {
rev = val.rev;
} // lib.optionalAttrs (val ? branch) {
ref = val.branch;
} // lib.optionalAttrs (val ? tag) {
ref = "refs/tags/${val.tag}";
});
}
) cargotoml.dependencies or { });
parseLock = lock:
let
source = lock.source;
rev = extractPart "?rev=" source;
tag = extractPart "?tag=" source;
branch = extractPart "?branch=" source;
in
lib.mapAttrs (_: x: tomlDependencies x) cargotomls;
{
inherit (lock) name;
revision = extractRevision source;
url = extractRepoUrl source;
} // (lib.optionalAttrs (! isNull branch) { inherit branch; })
// (lib.optionalAttrs (! isNull tag) { inherit tag; })
// (lib.optionalAttrs (! isNull rev) { inherit rev; });
packageLocks = builtins.map parseLock (lib.filter query cargolock.package);

mkFetch = lock: {
key = lock.rev or lock.tag or lock.branch or lock.revision
or (throw "No 'rev', 'tag' or 'branch' available to specify key, nor a git revision was found in Cargo.lock");
checkout = builtins.fetchGit ({
url = lock.url;
rev = lock.revision;
} // lib.optionalAttrs (lock ? branch) {
ref = lock.branch;
} // lib.optionalAttrs (lock ? tag) {
ref = lock.tag;
} // lib.optionalAttrs allRefs {
allRefs = true;
} // lib.optionalAttrs gitSubmodules {
submodules = true;
});
} // lock;
in builtins.map mkFetch packageLocks;

# A very minimal 'src' which makes cargo happy nonetheless
dummySrc =
Expand Down

0 comments on commit 61b26a4

Please sign in to comment.