crane/lib/vendorGitDeps.nix
Ivan Petkov 960d350770
vendorCargoDeps: manually splice packages to avoid cross build issues (#507)
* Source vendoring is passed as a standalone attribute to
  `mkCargoDerivation`, meaning it does not automagically get spliced
  with the correct local/cross system inputs (i.e. what would happen if
  it was a `depsBuildBuild` entry)
* To fix this we need to make sure that `vendorCargoDeps` and all of its
  transitive dependencies always use `runCommand` (and friends) from
  their `pkgsBuildBuild` equivalent. This should always be safe to do
  (even for cross-builds) since this amounts to building up a bunch of
  sources which will be read by the build system
* Unfortunately I had to manually specify `pkgsBuildBuild.whatever` in
  multiple places as I could not get things to work by messing with the
  `callPackage` definition. Perhaps we should be using
  `makeScopeWithSplicing'` instead of `makeScope` when constructing the
  library, but I couldn't get it working (and I couldn't find any decent
  docs on how to use it online) so this will make do for the time being.
2024-01-28 19:08:44 +00:00

158 lines
4.0 KiB
Nix

{ downloadCargoPackageFromGit
, lib
, pkgsBuildBuild
}:
let
inherit (pkgsBuildBuild)
runCommandLocal;
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" ];
hash = hashString "sha256";
in
{ lockPackages
, outputHashes ? { }
}:
let
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;
};
# 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;
sha256 = outputHashes.${p.package.source} or (lib.warnIf
(outputHashes != { })
"No output hash provided for ${p.package.source}"
null
);
};
# 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);
}