mirror of
https://github.com/nix-community/dream2nix.git
synced 2024-12-11 08:36:04 +03:00
Merge pull request #67 from yusdacra/fix/build-rust-package
fix(rust): handle git source vendoring properly
This commit is contained in:
commit
dcdf2f27df
@ -12,6 +12,7 @@
|
||||
getCyclicDependencies,
|
||||
getDependencies,
|
||||
getSource,
|
||||
getSourceSpec,
|
||||
produceDerivation,
|
||||
|
||||
...
|
||||
@ -30,40 +31,99 @@ let
|
||||
direct ++ (l.map (dep: getAllTransitiveDependencies dep.name dep.version) direct)
|
||||
));
|
||||
|
||||
# TODO: implement a user option that will make the vendoring
|
||||
# copy sources instead of symlinking them. This can be useful
|
||||
# for some Rust packages that modify their own dependencies
|
||||
# via their build hooks.
|
||||
vendorPackageDependencies = pname: version:
|
||||
let
|
||||
deps = getAllTransitiveDependencies pname version;
|
||||
|
||||
makeSource = dep: {
|
||||
name = "${dep.name}-${dep.version}";
|
||||
makeSource = dep:
|
||||
let
|
||||
path = getSource dep.name dep.version;
|
||||
isGit = (getSourceSpec dep.name dep.version).type == "git";
|
||||
in {
|
||||
inherit path isGit dep;
|
||||
name = "${dep.name}-${dep.version}";
|
||||
};
|
||||
sources = l.map makeSource deps;
|
||||
|
||||
findCrateSource = source:
|
||||
let
|
||||
inherit (pkgs) cargo jq;
|
||||
pkg = source.dep;
|
||||
in ''
|
||||
# If the target package is in a workspace, or if it's the top-level
|
||||
# crate, we should find the crate path using `cargo metadata`.
|
||||
crateCargoTOML=$(${cargo}/bin/cargo metadata --format-version 1 --no-deps --manifest-path $tree/Cargo.toml | \
|
||||
${jq}/bin/jq -r '.packages[] | select(.name == "${pkg.name}") | .manifest_path')
|
||||
# If the repository is not a workspace the package might be in a subdirectory.
|
||||
if [[ -z $crateCargoTOML ]]; then
|
||||
for manifest in $(find $tree -name "Cargo.toml"); do
|
||||
echo Looking at $manifest
|
||||
crateCargoTOML=$(${cargo}/bin/cargo metadata --format-version 1 --no-deps --manifest-path "$manifest" | ${jq}/bin/jq -r '.packages[] | select(.name == "${pkg.name}") | .manifest_path' || :)
|
||||
if [[ ! -z $crateCargoTOML ]]; then
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [[ -z $crateCargoTOML ]]; then
|
||||
>&2 echo "Cannot find path for crate '${pkg.name}-${pkg.version}' in the tree in: $tree"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
echo Found crate ${pkg.name} at $crateCargoTOML
|
||||
tree="$(dirname $crateCargoTOML)"
|
||||
'';
|
||||
makeScript = source:
|
||||
''
|
||||
tree="${source.path}"
|
||||
${l.optionalString source.isGit (findCrateSource source)}
|
||||
cp -prvd "$tree" $out/${source.name}
|
||||
chmod u+w $out/${source.name}
|
||||
${l.optionalString source.isGit "printf '{\"files\":{},\"package\":null}' > \"$out/${source.name}/.cargo-checksum.json\""}
|
||||
'';
|
||||
in
|
||||
pkgs.runCommand "vendor-${pname}-${version}" {} ''
|
||||
mkdir -p $out
|
||||
|
||||
${
|
||||
l.concatMapStringsSep "\n"
|
||||
(source: "ln -s ${source.path} $out/${source.name}")
|
||||
makeScript
|
||||
sources
|
||||
}
|
||||
'';
|
||||
|
||||
# Generates a shell script that writes git vendor entries to .cargo/config.
|
||||
writeGitVendorEntries =
|
||||
let
|
||||
makeEntry = source:
|
||||
''
|
||||
[source."${source.url}"]
|
||||
replace-with = "vendored-sources"
|
||||
git = "${source.url}"
|
||||
${l.optionalString (source ? type) "${source.type} = \"${source.value}\""}
|
||||
'';
|
||||
entries = l.map makeEntry subsystemAttrs.gitSources;
|
||||
in ''
|
||||
cat >> ../.cargo/config <<EOF
|
||||
${l.concatStringsSep "\n" entries}
|
||||
EOF
|
||||
'';
|
||||
|
||||
buildPackage = pname: version:
|
||||
let src = getSource pname version; in
|
||||
let
|
||||
src = getSource pname version;
|
||||
vendorDir = vendorPackageDependencies pname version;
|
||||
in
|
||||
produceDerivation pname (pkgs.rustPlatform.buildRustPackage {
|
||||
inherit pname version src;
|
||||
|
||||
postUnpack = ''
|
||||
ln -s ${vendorPackageDependencies pname version} ./${src.name}/nix-vendor
|
||||
ln -s ${vendorDir} ./nix-vendor
|
||||
'';
|
||||
|
||||
cargoVendorDir = "nix-vendor";
|
||||
cargoVendorDir = "../nix-vendor";
|
||||
|
||||
preBuild = ''
|
||||
${writeGitVendorEntries}
|
||||
'';
|
||||
});
|
||||
in
|
||||
rec {
|
||||
|
@ -302,6 +302,7 @@ let
|
||||
|
||||
inherit (dreamLockInterface)
|
||||
subsystemAttrs
|
||||
getSourceSpec
|
||||
getDependencies
|
||||
getCyclicDependencies
|
||||
mainPackageName
|
||||
|
@ -2,6 +2,20 @@
|
||||
"_subsystem": {
|
||||
"packages": [
|
||||
{ "name": "ripgrep", "version": "13.0.0" }
|
||||
],
|
||||
"gitSources": [
|
||||
{
|
||||
"url": "https://github.com/rust-random/rand.git",
|
||||
"sha": "19404d68764ed08513131f82157e2ccad69dcf83",
|
||||
"type": "branch",
|
||||
"value": "master"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/rust-random/rand.git",
|
||||
"sha": "074cb6a997f91db653f8a53c755ddc07495ab429",
|
||||
"type": "tag",
|
||||
"value": "0.7.3"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -24,17 +24,19 @@
|
||||
(n: v: if v == "directory" then recurseFiles "${path}/${n}" else "${path}/${n}")
|
||||
(l.readDir path)
|
||||
);
|
||||
getAllFiles = dirs: l.flatten (l.map recurseFiles dirs);
|
||||
|
||||
getCargoTomlPaths = l.filter (path: l.baseNameOf path == "Cargo.toml");
|
||||
getCargoTomls = l.map (path: { inherit path; value = l.fromTOML (l.readFile path); });
|
||||
getCargoPackages = l.filter (toml: l.hasAttrByPath [ "package" "name" ] toml.value);
|
||||
|
||||
# Find all Cargo.toml files and parse them
|
||||
allFiles = l.flatten (l.map recurseFiles inputDirectories);
|
||||
cargoTomlPaths = l.filter (path: l.baseNameOf path == "Cargo.toml") allFiles;
|
||||
cargoTomls = l.map (path: { inherit path; value = l.fromTOML (l.readFile path); }) cargoTomlPaths;
|
||||
allFiles = getAllFiles inputDirectories;
|
||||
cargoTomlPaths = getCargoTomlPaths allFiles;
|
||||
cargoTomls = getCargoTomls cargoTomlPaths;
|
||||
|
||||
# Filter cargo-tomls to for files that actually contain packages
|
||||
cargoPackages =
|
||||
l.filter
|
||||
(toml: l.hasAttrByPath [ "package" "name" ] toml.value)
|
||||
cargoTomls;
|
||||
cargoPackages = getCargoPackages cargoTomls;
|
||||
|
||||
packageName =
|
||||
if args.packageName == "{automatic}"
|
||||
@ -101,6 +103,33 @@
|
||||
name = toml.package.name;
|
||||
version = toml.package.version or (l.warn "no version found in Cargo.toml for ${name}, defaulting to unknown" "unknown");
|
||||
};
|
||||
|
||||
# Parses a git source, taken straight from nixpkgs.
|
||||
parseGitSource = src:
|
||||
let
|
||||
parts = builtins.match ''git\+([^?]+)(\?(rev|tag|branch)=(.*))?#(.*)'' src;
|
||||
type = builtins.elemAt parts 2; # rev, tag or branch
|
||||
value = builtins.elemAt parts 3;
|
||||
in
|
||||
if parts == null then null
|
||||
else {
|
||||
url = builtins.elemAt parts 0;
|
||||
sha = builtins.elemAt parts 4;
|
||||
} // lib.optionalAttrs (type != null) { inherit type value; };
|
||||
|
||||
# Extracts a source type from a dependency.
|
||||
getSourceTypeFrom = dependencyObject:
|
||||
let checkType = type: l.hasPrefix "${type}+" dependencyObject.source; in
|
||||
if !(l.hasAttr "source" dependencyObject)
|
||||
then "path"
|
||||
else if checkType "git" then
|
||||
"git"
|
||||
else if checkType "registry" then
|
||||
if dependencyObject.source == "registry+https://github.com/rust-lang/crates.io-index"
|
||||
then "crates-io"
|
||||
else throw "registries other than crates.io are not supported yet"
|
||||
else
|
||||
throw "unknown or unsupported source type: ${dependencyObject.source}";
|
||||
in
|
||||
|
||||
utils.simpleTranslate
|
||||
@ -140,8 +169,11 @@
|
||||
# Extract subsystem specific attributes.
|
||||
# The structure of this should be defined in:
|
||||
# ./src/specifications/{subsystem}
|
||||
subsystemAttrs = {
|
||||
subsystemAttrs = rec {
|
||||
packages = l.map (toml: { inherit (toml.value.package) name version; }) cargoPackages;
|
||||
gitSources = let
|
||||
gitDeps = l.filter (dep: (getSourceTypeFrom dep) == "git") parsedDeps;
|
||||
in l.unique (l.map (dep: parseGitSource dep.source) gitDeps);
|
||||
};
|
||||
|
||||
# FUNCTIONS
|
||||
@ -160,18 +192,7 @@
|
||||
l.map makeDepNameVersion (dependencyObject.dependencies or [ ]);
|
||||
|
||||
# return the source type of a package object
|
||||
getSourceType = dependencyObject:
|
||||
let checkType = type: l.hasPrefix "${type}+" dependencyObject.source; in
|
||||
if !(l.hasAttr "source" dependencyObject)
|
||||
then "path"
|
||||
else if checkType "git" then
|
||||
"git"
|
||||
else if checkType "registry" then
|
||||
if dependencyObject.source == "registry+https://github.com/rust-lang/crates.io-index"
|
||||
then "crates-io"
|
||||
else throw "registries other than crates.io are not supported yet"
|
||||
else
|
||||
throw "unknown or unsupported source type: ${dependencyObject.source}";
|
||||
getSourceType = getSourceTypeFrom;
|
||||
|
||||
# An attrset of constructor functions.
|
||||
# Given a dependency object and a source type, construct the
|
||||
@ -180,12 +201,12 @@
|
||||
path = dependencyObject:
|
||||
let
|
||||
findCratePath = name:
|
||||
l.dirOf (
|
||||
l.baseNameOf (l.dirOf (
|
||||
l.findFirst
|
||||
(toml: toml.value.package.name == name)
|
||||
(throw "could not find crate ${name}")
|
||||
cargoPackages
|
||||
).path;
|
||||
).path);
|
||||
in
|
||||
{
|
||||
path = findCratePath dependencyObject.name;
|
||||
@ -193,18 +214,11 @@
|
||||
|
||||
git = dependencyObject:
|
||||
let
|
||||
source = dependencyObject.source;
|
||||
|
||||
extractRevision = source: l.last (l.splitString "#" source);
|
||||
extractRepoUrl = source:
|
||||
let
|
||||
splitted = l.head (l.splitString "?" source);
|
||||
split = l.substring 4 (l.stringLength splitted) splitted;
|
||||
in l.head (l.splitString "#" split);
|
||||
parsed = parseGitSource dependencyObject.source;
|
||||
in
|
||||
{
|
||||
url = extractRepoUrl source;
|
||||
rev = extractRevision source;
|
||||
url = parsed.url;
|
||||
rev = parsed.sha;
|
||||
};
|
||||
|
||||
crates-io = dependencyObject:
|
||||
|
@ -77,6 +77,11 @@ let
|
||||
|
||||
cyclicDependencies = lock.cyclicDependencies;
|
||||
|
||||
getSourceSpec = pname: version:
|
||||
sources."${pname}"."${version}" or (
|
||||
throw "The source spec for ${pname}#${version} is not defined in lockfile."
|
||||
);
|
||||
|
||||
getDependencies = pname: version:
|
||||
b.filter
|
||||
(dep: ! b.elem dep cyclicDependencies."${pname}"."${version}" or [])
|
||||
@ -96,6 +101,7 @@ let
|
||||
subsystemAttrs
|
||||
getCyclicDependencies
|
||||
getDependencies
|
||||
getSourceSpec
|
||||
packageVersions
|
||||
subDreamLocks
|
||||
;
|
||||
|
Loading…
Reference in New Issue
Block a user