crane/lib/vendorGitDeps.nix
2022-10-23 23:20:22 +00:00

148 lines
3.8 KiB
Nix

{ downloadCargoPackageFromGit
, lib
, runCommandLocal
}:
{ lockPackages
}:
let
inherit (builtins)
any
attrNames
filter
hashString
head
isString
length
listToAttrs
placeholder
split;
inherit (lib)
concatMapStrings
concatStrings
concatStringsSep
escapeShellArg
groupBy
hasPrefix
last
mapAttrs'
mapAttrsToList
nameValuePair
removePrefix;
knownGitParams = [ "branch" "rev" "tag" ];
parseGitUrl = p:
let
lockUrl = removePrefix "git+" p.source;
revSplit = split "#" (removePrefix "git+" lockUrl);
# uniquely identifies the repo in terms of what cargo can address via
# source replacement (e.g. the url along with any branch/tag/rev).
id = head revSplit;
# NB: this is distict from the `rev` query param which may show up
# if the dependency is explicitly listed with a `rev` value.
lockedRev = if 3 == length revSplit then last revSplit else
throw ''
Cargo.lock is missing a locked revision for ${p.name}@${p.version}.
you can try to resolve this by running `cargo update -p ${lockUrl}#${p.name}@${p.version}`
'';
querySplit = split "\\?" (head revSplit);
git = head querySplit;
queryParamSplit = filter
(qp: (isString qp) && any (p: hasPrefix p qp) knownGitParams)
(split "&" (last querySplit));
extractedParams = listToAttrs (map
(qp:
let
kvSplit = (split "=" qp);
in
nameValuePair (head kvSplit) (last kvSplit)
)
queryParamSplit
);
in
extractedParams // {
inherit git id lockedRev;
};
hash = hashString "sha256";
# Local crates will show up in the lock file with no checksum/source
lockedPackagesFromGit = filter
(p: hasPrefix "git" (p.source or ""))
lockPackages;
lockedGitGroups = groupBy (p: p.id) (map
(p: (parseGitUrl p) // { package = p; })
lockedPackagesFromGit
);
sources = mapAttrs'
(id: ps:
let
p = head ps;
ref =
if p ? tag then "refs/tags/${p.tag}"
else if p ? branch then "refs/heads/${p.branch}"
else null;
extractedPackages = downloadCargoPackageFromGit {
inherit (p) git;
inherit ref;
rev = p.lockedRev;
};
# NB: we filter out any crates NOT in the lock file
# as the repo could have other crates we don't need
# (e.g. testing crates which might not even build properly)
# https://github.com/ipetkov/crane/issues/60
linkPsInLock = concatStringsSep "\n" (map
(p:
let
name = escapeShellArg p.package.name;
version = escapeShellArg p.package.version;
vendoredName = "${name}-${version}";
in
"ln -s ${extractedPackages}/${vendoredName} $out/${vendoredName}"
)
ps
);
in
nameValuePair (hash id) (runCommandLocal "linkLockedDeps" { } ''
mkdir -p $out
${linkPsInLock}
'')
)
lockedGitGroups;
configLocalSources = concatMapStrings
(hashedId: ''
[source.nix-sources-${hashedId}]
directory = "${placeholder "out"}/${hashedId}"
'')
(attrNames sources);
configReplaceGitSources = mapAttrsToList
(_hashedId: ps:
let
p = head ps;
extractAttr = attr:
if p ? ${attr} then ''
${attr} = "${p.${attr}}"
'' else "";
sourceValues = concatMapStrings extractAttr ([ "git" ] ++ knownGitParams);
in
''
[source."${p.id}"]
replace-with = "nix-sources-${hash p.id}"
${sourceValues}
''
)
lockedGitGroups;
in
{
inherit sources;
config = configLocalSources + (concatStrings configReplaceGitSources);
}