From d38ae2d34218da1b8667580da835fadc062e0ca9 Mon Sep 17 00:00:00 2001 From: Pierre-Etienne Meunier Date: Tue, 17 Dec 2024 11:31:23 +0100 Subject: [PATCH] Make all functions overridable Currently, most functions take `pkgs` as an argument, and fail to use some of their other arguments, effectively replacing them with `pkgs.[blah]`. This commit fixes that. --- crate2nix/Cargo.nix | 1071 ++++++++-------- crate2nix/templates/Cargo.nix.tera | 20 +- crate2nix/templates/nix/crate2nix/default.nix | 1092 ++++++++--------- .../bin_with_git_submodule_dep/Cargo.nix | 1075 ++++++++-------- sample_projects/codegen/Cargo.nix | 1061 ++++++++-------- sample_projects/sub_dir_crates/Cargo.nix | 1075 ++++++++-------- 6 files changed, 2681 insertions(+), 2713 deletions(-) diff --git a/crate2nix/Cargo.nix b/crate2nix/Cargo.nix index cfdb08c8..5f83ae20 100644 --- a/crate2nix/Cargo.nix +++ b/crate2nix/Cargo.nix @@ -4,10 +4,16 @@ # See https://github.com/kolloch/crate2nix for more info. { nixpkgs ? ../nix/nixpkgs.nix -, pkgs ? import nixpkgs { config = {}; } +, pkgs ? import nixpkgs { } , fetchurl ? pkgs.fetchurl +, fetchgit ? pkgs.fetchgit +, callPackage ? pkgs.callPackage , lib ? pkgs.lib +, buildPackages ? pkgs.buildPackages , stdenv ? pkgs.stdenv +, stdenvNoCC ? pkgs.stdenvNoCC +, symlinkJoin ? pkgs.symlinkJoin +, runCommand ? pkgs.runCommand , buildRustCrateForPkgs ? pkgs: pkgs.buildRustCrate # This is used as the `crateOverrides` argument for `buildRustCrate`. , defaultCrateOverrides ? pkgs.defaultCrateOverrides @@ -24,7 +30,7 @@ # Additional crate2nix configuration if it exists. , crateConfig ? if builtins.pathExists ./crate-config.nix - then pkgs.callPackage ./crate-config.nix {} + then callPackage ./crate-config.nix {} else {} }: @@ -65,7 +71,7 @@ rec { # A derivation that joins the outputs of all workspace members together. - allWorkspaceMembers = pkgs.symlinkJoin { + allWorkspaceMembers = symlinkJoin { name = "all-workspace-members"; paths = let members = builtins.attrValues workspaceMembers; @@ -3172,12 +3178,12 @@ rec { }; registryUrl = - { registries - , url - , crate - , version - , sha256 - , + { + registries, + url, + crate, + version, + sha256, }: let dl = registries.${url}.dl; @@ -3217,41 +3223,41 @@ rec { let baseName = builtins.baseNameOf (builtins.toString name); in - !( - # Filter out git - baseName == ".gitignore" - || (type == "directory" && baseName == ".git") - - # Filter out build results - || ( - type == "directory" - && ( - baseName == "target" - || baseName == "_site" - || baseName == ".sass-cache" - || baseName == ".jekyll-metadata" - || baseName == "build-artifacts" - ) + !( + # Filter out git + baseName == ".gitignore" + || (type == "directory" && baseName == ".git") + + # Filter out build results + || ( + type == "directory" + && ( + baseName == "target" + || baseName == "_site" + || baseName == ".sass-cache" + || baseName == ".jekyll-metadata" + || baseName == "build-artifacts" ) + ) - # Filter out nix-build result symlinks - || (type == "symlink" && lib.hasPrefix "result" baseName) + # Filter out nix-build result symlinks + || (type == "symlink" && lib.hasPrefix "result" baseName) - # Filter out IDE config - || (type == "directory" && (baseName == ".idea" || baseName == ".vscode")) - || lib.hasSuffix ".iml" baseName + # Filter out IDE config + || (type == "directory" && (baseName == ".idea" || baseName == ".vscode")) + || lib.hasSuffix ".iml" baseName - # Filter out nix build files - || baseName == "Cargo.nix" + # Filter out nix build files + || baseName == "Cargo.nix" - # Filter out editor backup / swap files. - || lib.hasSuffix "~" baseName - || builtins.match "^\\.sw[a-z]$$" baseName != null - || builtins.match "^\\..*\\.sw[a-z]$$" baseName != null - || lib.hasSuffix ".tmp" baseName - || lib.hasSuffix ".bak" baseName - || baseName == "tests.nix" - ); + # Filter out editor backup / swap files. + || lib.hasSuffix "~" baseName + || builtins.match "^\\.sw[a-z]$$" baseName != null + || builtins.match "^\\..*\\.sw[a-z]$$" baseName != null + || lib.hasSuffix ".tmp" baseName + || lib.hasSuffix ".bak" baseName + || baseName == "tests.nix" + ); /* Returns a crate which depends on successful test execution @@ -3261,131 +3267,126 @@ rec { testInputs: list of packages that should be available during test execution */ crateWithTest = - { crate - , testCrate - , testCrateFlags - , testInputs - , testPreRun - , testPostRun - , + { + crate, + testCrate, + testCrateFlags, + testInputs, + testPreRun, + testPostRun, }: - assert builtins.typeOf testCrateFlags == "list"; - assert builtins.typeOf testInputs == "list"; - assert builtins.typeOf testPreRun == "string"; - assert builtins.typeOf testPostRun == "string"; - let - # override the `crate` so that it will build and execute tests instead of - # building the actual lib and bin targets We just have to pass `--test` - # to rustc and it will do the right thing. We execute the tests and copy - # their log and the test executables to $out for later inspection. - test = - let - drv = testCrate.override (_: { - buildTests = true; - }); - # If the user hasn't set any pre/post commands, we don't want to - # insert empty lines. This means that any existing users of crate2nix - # don't get a spurious rebuild unless they set these explicitly. - testCommand = pkgs.lib.concatStringsSep "\n" ( - pkgs.lib.filter (s: s != "") [ - testPreRun - "$f $testCrateFlags 2>&1 | tee -a $out" - testPostRun - ] - ); - in - pkgs.stdenvNoCC.mkDerivation { - name = "run-tests-${testCrate.name}"; - - inherit (crate) src; - - inherit testCrateFlags; - - buildInputs = testInputs; - - buildPhase = '' - set -e - export RUST_BACKTRACE=1 - - # build outputs - testRoot=target/debug - mkdir -p $testRoot - - # executables of the crate - # we copy to prevent std::env::current_exe() to resolve to a store location - for i in ${crate}/bin/*; do - cp "$i" "$testRoot" - done - chmod +w -R . - - # test harness executables are suffixed with a hash, like cargo does - # this allows to prevent name collision with the main - # executables of the crate - hash=$(basename $out) - for file in ${drv}/tests/*; do - f=$testRoot/$(basename $file)-$hash - cp $file $f - ${testCommand} - done - ''; - }; - in - pkgs.runCommand "${crate.name}-linked" - { - inherit (crate) outputs crateName; - passthru = (crate.passthru or { }) // { - inherit test; - }; - } - ( - lib.optionalString (stdenv.buildPlatform.canExecute stdenv.hostPlatform) '' - echo tested by ${test} - '' - + '' - ${lib.concatMapStringsSep "\n" (output: "ln -s ${crate.${output}} ${"$"}${output}") crate.outputs} - '' - ); + assert builtins.typeOf testCrateFlags == "list"; + assert builtins.typeOf testInputs == "list"; + assert builtins.typeOf testPreRun == "string"; + assert builtins.typeOf testPostRun == "string"; + let + # override the `crate` so that it will build and execute tests instead of + # building the actual lib and bin targets We just have to pass `--test` + # to rustc and it will do the right thing. We execute the tests and copy + # their log and the test executables to $out for later inspection. + test = + let + drv = testCrate.override (_: { + buildTests = true; + }); + # If the user hasn't set any pre/post commands, we don't want to + # insert empty lines. This means that any existing users of crate2nix + # don't get a spurious rebuild unless they set these explicitly. + testCommand = lib.concatStringsSep "\n" ( + lib.filter (s: s != "") [ + testPreRun + "$f $testCrateFlags 2>&1 | tee -a $out" + testPostRun + ] + ); + in + stdenvNoCC.mkDerivation { + name = "run-tests-${testCrate.name}"; + + inherit (crate) src; + + inherit testCrateFlags; + + buildInputs = testInputs; + + buildPhase = '' + set -e + export RUST_BACKTRACE=1 + + # build outputs + testRoot=target/debug + mkdir -p $testRoot + + # executables of the crate + # we copy to prevent std::env::current_exe() to resolve to a store location + for i in ${crate}/bin/*; do + cp "$i" "$testRoot" + done + chmod +w -R . + + # test harness executables are suffixed with a hash, like cargo does + # this allows to prevent name collision with the main + # executables of the crate + hash=$(basename $out) + for file in ${drv}/tests/*; do + f=$testRoot/$(basename $file)-$hash + cp $file $f + ${testCommand} + done + ''; + }; + in + runCommand "${crate.name}-linked" + { + inherit (crate) outputs crateName; + passthru = (crate.passthru or { }) // { + inherit test; + }; + } + ( + lib.optionalString (stdenv.buildPlatform.canExecute stdenv.hostPlatform) '' + echo tested by ${test} + '' + + '' + ${lib.concatMapStringsSep "\n" (output: "ln -s ${crate.${output}} ${"$"}${output}") crate.outputs} + '' + ); # A restricted overridable version of builtRustCratesWithFeatures. buildRustCrateWithFeatures = - { packageId - , features ? rootFeatures - , crateOverrides ? defaultCrateOverrides - , buildRustCrateForPkgsFunc ? null - , runTests ? false - , testCrateFlags ? [ ] - , testInputs ? [ ] - , # Any command to run immediatelly before a test is executed. - testPreRun ? "" - , # Any command run immediatelly after a test is executed. - testPostRun ? "" - , + { + packageId, + features ? rootFeatures, + crateOverrides ? defaultCrateOverrides, + buildRustCrateForPkgsFunc ? null, + runTests ? false, + testCrateFlags ? [ ], + testInputs ? [ ], + # Any command to run immediatelly before a test is executed. + testPreRun ? "", + # Any command run immediatelly after a test is executed. + testPostRun ? "", }: lib.makeOverridable ( - { features - , crateOverrides - , runTests - , testCrateFlags - , testInputs - , testPreRun - , testPostRun - , + { + features, + crateOverrides, + runTests, + testCrateFlags, + testInputs, + testPreRun, + testPostRun, }: let buildRustCrateForPkgsFuncOverriden = if buildRustCrateForPkgsFunc != null then buildRustCrateForPkgsFunc else - ( - if crateOverrides == pkgs.defaultCrateOverrides then - buildRustCrateForPkgs - else - pkgs: - (buildRustCrateForPkgs pkgs).override { - defaultCrateOverrides = crateOverrides; - } - ); + pkgs: + (buildRustCrateForPkgs pkgs).override { + defaultCrateOverrides = crateOverrides; + }; builtRustCrates = builtRustCratesWithFeatures { inherit packageId features; buildRustCrateForPkgsFunc = buildRustCrateForPkgsFuncOverriden; @@ -3400,17 +3401,16 @@ rec { testDrv = builtTestRustCrates.crates.${packageId}; derivation = if runTests then - crateWithTest - { - crate = drv; - testCrate = testDrv; - inherit - testCrateFlags - testInputs - testPreRun - testPostRun - ; - } + crateWithTest { + crate = drv; + testCrate = testDrv; + inherit + testCrateFlags + testInputs + testPreRun + testPostRun + ; + } else drv; in @@ -3433,159 +3433,157 @@ rec { for the corresponding crate. */ builtRustCratesWithFeatures = - { packageId - , features - , crateConfigs ? crates - , buildRustCrateForPkgsFunc - , runTests - , makeTarget ? makeDefaultTarget - , + { + packageId, + features, + crateConfigs ? crates, + buildRustCrateForPkgsFunc, + runTests, + makeTarget ? makeDefaultTarget, }@args: - assert (builtins.isAttrs crateConfigs); - assert (builtins.isString packageId); - assert (builtins.isList features); - assert (builtins.isAttrs (makeTarget stdenv.hostPlatform)); - assert (builtins.isBool runTests); - let - rootPackageId = packageId; - mergedFeatures = mergePackageFeatures ( - args + assert (builtins.isAttrs crateConfigs); + assert (builtins.isString packageId); + assert (builtins.isList features); + assert (builtins.isAttrs (makeTarget stdenv.hostPlatform)); + assert (builtins.isBool runTests); + let + rootPackageId = packageId; + mergedFeatures = mergePackageFeatures ( + args + // { + inherit rootPackageId; + target = makeTarget stdenv.hostPlatform // { + test = runTests; + }; + } + ); + # Memoize built packages so that reappearing packages are only built once. + builtByPackageIdByPkgs = mkBuiltByPackageIdByPkgs pkgs; + mkBuiltByPackageIdByPkgs = + pkgs: + let + self = { + crates = lib.mapAttrs ( + packageId: value: buildByPackageIdForPkgsImpl self pkgs packageId + ) crateConfigs; + target = makeTarget stdenv.hostPlatform; + build = mkBuiltByPackageIdByPkgs buildPackages; + }; + in + self; + buildByPackageIdForPkgsImpl = + self: pkgs: packageId: + let + features = mergedFeatures."${packageId}" or [ ]; + crateConfig' = crateConfigs."${packageId}"; + crateConfig = builtins.removeAttrs crateConfig' [ + "resolvedDefaultFeatures" + "devDependencies" + ]; + devDependencies = lib.optionals (runTests && packageId == rootPackageId) ( + crateConfig'.devDependencies or [ ] + ); + dependencies = dependencyDerivations { + inherit features; + inherit (self) target; + buildByPackageId = + depPackageId: + # proc_macro crates must be compiled for the build architecture + if crateConfigs.${depPackageId}.procMacro or false then + self.build.crates.${depPackageId} + else + self.crates.${depPackageId}; + dependencies = (crateConfig.dependencies or [ ]) ++ devDependencies; + }; + buildDependencies = dependencyDerivations { + inherit features; + inherit (self.build) target; + buildByPackageId = depPackageId: self.build.crates.${depPackageId}; + dependencies = crateConfig.buildDependencies or [ ]; + }; + dependenciesWithRenames = + let + buildDeps = filterEnabledDependencies { + inherit features; + inherit (self) target; + dependencies = crateConfig.dependencies or [ ] ++ devDependencies; + }; + hostDeps = filterEnabledDependencies { + inherit features; + inherit (self.build) target; + dependencies = crateConfig.buildDependencies or [ ]; + }; + in + lib.filter (d: d ? "rename") (hostDeps ++ buildDeps); + # Crate renames have the form: + # + # { + # crate_name = [ + # { version = "1.2.3"; rename = "crate_name01"; } + # ]; + # # ... + # } + crateRenames = + let + grouped = lib.groupBy (dependency: dependency.name) dependenciesWithRenames; + versionAndRename = + dep: + let + package = crateConfigs."${dep.packageId}"; + in + { + inherit (dep) rename; + inherit (package) version; + }; + in + lib.mapAttrs (name: builtins.map versionAndRename) grouped; + in + buildRustCrateForPkgsFunc pkgs ( + crateConfig // { - inherit rootPackageId; - target = makeTarget stdenv.hostPlatform // { - test = runTests; - }; + src = + crateConfig.src or (pkgs.fetchurl rec { + name = "${crateConfig.crateName}-${crateConfig.version}.tar.gz"; + # https://www.pietroalbini.org/blog/downloading-crates-io/ + # Not rate-limited, CDN URL. + url = "https://static.crates.io/crates/${crateConfig.crateName}/${crateConfig.crateName}-${crateConfig.version}.crate"; + sha256 = + assert (lib.assertMsg (crateConfig ? sha256) "Missing sha256 for ${name}"); + crateConfig.sha256; + }); + extraRustcOpts = + lib.lists.optional (targetFeatures != [ ]) + "-C target-feature=${lib.concatMapStringsSep "," (x: "+${x}") targetFeatures}"; + inherit + features + dependencies + buildDependencies + crateRenames + release + ; } ); - # Memoize built packages so that reappearing packages are only built once. - builtByPackageIdByPkgs = mkBuiltByPackageIdByPkgs pkgs; - mkBuiltByPackageIdByPkgs = - pkgs: - let - self = { - crates = lib.mapAttrs - ( - packageId: value: buildByPackageIdForPkgsImpl self pkgs packageId - ) - crateConfigs; - target = makeTarget stdenv.hostPlatform; - build = mkBuiltByPackageIdByPkgs pkgs.buildPackages; - }; - in - self; - buildByPackageIdForPkgsImpl = - self: pkgs: packageId: - let - features = mergedFeatures."${packageId}" or [ ]; - crateConfig' = crateConfigs."${packageId}"; - crateConfig = builtins.removeAttrs crateConfig' [ - "resolvedDefaultFeatures" - "devDependencies" - ]; - devDependencies = lib.optionals (runTests && packageId == rootPackageId) ( - crateConfig'.devDependencies or [ ] - ); - dependencies = dependencyDerivations { - inherit features; - inherit (self) target; - buildByPackageId = - depPackageId: - # proc_macro crates must be compiled for the build architecture - if crateConfigs.${depPackageId}.procMacro or false then - self.build.crates.${depPackageId} - else - self.crates.${depPackageId}; - dependencies = (crateConfig.dependencies or [ ]) ++ devDependencies; - }; - buildDependencies = dependencyDerivations { - inherit features; - inherit (self.build) target; - buildByPackageId = depPackageId: self.build.crates.${depPackageId}; - dependencies = crateConfig.buildDependencies or [ ]; - }; - dependenciesWithRenames = - let - buildDeps = filterEnabledDependencies { - inherit features; - inherit (self) target; - dependencies = crateConfig.dependencies or [ ] ++ devDependencies; - }; - hostDeps = filterEnabledDependencies { - inherit features; - inherit (self.build) target; - dependencies = crateConfig.buildDependencies or [ ]; - }; - in - lib.filter (d: d ? "rename") (hostDeps ++ buildDeps); - # Crate renames have the form: - # - # { - # crate_name = [ - # { version = "1.2.3"; rename = "crate_name01"; } - # ]; - # # ... - # } - crateRenames = - let - grouped = lib.groupBy (dependency: dependency.name) dependenciesWithRenames; - versionAndRename = - dep: - let - package = crateConfigs."${dep.packageId}"; - in - { - inherit (dep) rename; - inherit (package) version; - }; - in - lib.mapAttrs (name: builtins.map versionAndRename) grouped; - in - buildRustCrateForPkgsFunc pkgs ( - crateConfig - // { - src = - crateConfig.src or (pkgs.fetchurl rec { - name = "${crateConfig.crateName}-${crateConfig.version}.tar.gz"; - # https://www.pietroalbini.org/blog/downloading-crates-io/ - # Not rate-limited, CDN URL. - url = "https://static.crates.io/crates/${crateConfig.crateName}/${crateConfig.crateName}-${crateConfig.version}.crate"; - sha256 = - assert (lib.assertMsg (crateConfig ? sha256) "Missing sha256 for ${name}"); - crateConfig.sha256; - }); - extraRustcOpts = - lib.lists.optional (targetFeatures != [ ]) - "-C target-feature=${lib.concatMapStringsSep "," (x: "+${x}") targetFeatures}"; - inherit - features - dependencies - buildDependencies - crateRenames - release - ; - } - ); - in - builtByPackageIdByPkgs; + in + builtByPackageIdByPkgs; # Returns the actual derivations for the given dependencies. dependencyDerivations = - { buildByPackageId - , features - , dependencies - , target - , + { + buildByPackageId, + features, + dependencies, + target, }: - assert (builtins.isList features); - assert (builtins.isList dependencies); - assert (builtins.isAttrs target); - let - enabledDependencies = filterEnabledDependencies { - inherit dependencies features target; - }; - depDerivation = dependency: buildByPackageId dependency.packageId; - in - map depDerivation enabledDependencies; + assert (builtins.isList features); + assert (builtins.isList dependencies); + assert (builtins.isAttrs target); + let + enabledDependencies = filterEnabledDependencies { + inherit dependencies features target; + }; + depDerivation = dependency: buildByPackageId dependency.packageId; + in + map depDerivation enabledDependencies; /* Returns a sanitized version of val with all values substituted that cannot @@ -3604,39 +3602,39 @@ rec { # Returns various tools to debug a crate. debugCrate = - { packageId - , target ? makeDefaultTarget stdenv.hostPlatform - , + { + packageId, + target ? makeDefaultTarget stdenv.hostPlatform, }: - assert (builtins.isString packageId); - let - debug = rec { - # The built tree as passed to buildRustCrate. - buildTree = buildRustCrateWithFeatures { - buildRustCrateForPkgsFunc = _: lib.id; - inherit packageId; - }; - sanitizedBuildTree = sanitizeForJson buildTree; - dependencyTree = sanitizeForJson (buildRustCrateWithFeatures { - buildRustCrateForPkgsFunc = _: crate: { - "01_crateName" = crate.crateName or false; - "02_features" = crate.features or [ ]; - "03_dependencies" = crate.dependencies or [ ]; - }; - inherit packageId; - }); - mergedPackageFeatures = mergePackageFeatures { - features = rootFeatures; - inherit packageId target; - }; - diffedDefaultPackageFeatures = diffDefaultPackageFeatures { - inherit packageId target; + assert (builtins.isString packageId); + let + debug = rec { + # The built tree as passed to buildRustCrate. + buildTree = buildRustCrateWithFeatures { + buildRustCrateForPkgsFunc = _: lib.id; + inherit packageId; + }; + sanitizedBuildTree = sanitizeForJson buildTree; + dependencyTree = sanitizeForJson (buildRustCrateWithFeatures { + buildRustCrateForPkgsFunc = _: crate: { + "01_crateName" = crate.crateName or false; + "02_features" = crate.features or [ ]; + "03_dependencies" = crate.dependencies or [ ]; }; + inherit packageId; + }); + mergedPackageFeatures = mergePackageFeatures { + features = rootFeatures; + inherit packageId target; + }; + diffedDefaultPackageFeatures = diffDefaultPackageFeatures { + inherit packageId target; }; - in - { - internal = debug; }; + in + { + internal = debug; + }; /* Returns differences between cargo default features and crate2nix default @@ -3645,41 +3643,39 @@ rec { This is useful for verifying the feature resolution in crate2nix. */ diffDefaultPackageFeatures = - { crateConfigs ? crates - , packageId - , target - , + { + crateConfigs ? crates, + packageId, + target, }: - assert (builtins.isAttrs crateConfigs); - let - prefixValues = prefix: lib.mapAttrs (n: v: { "${prefix}" = v; }); - mergedFeatures = prefixValues "crate2nix" (mergePackageFeatures { - inherit crateConfigs packageId target; - features = [ "default" ]; - }); - configs = prefixValues "cargo" crateConfigs; - combined = lib.foldAttrs (a: b: a // b) { } [ - mergedFeatures - configs - ]; - onlyInCargo = builtins.attrNames ( - lib.filterAttrs (n: v: !(v ? "crate2nix") && (v ? "cargo")) combined - ); - onlyInCrate2Nix = builtins.attrNames ( - lib.filterAttrs (n: v: (v ? "crate2nix") && !(v ? "cargo")) combined - ); - differentFeatures = lib.filterAttrs - ( - n: v: - (v ? "crate2nix") - && (v ? "cargo") - && (v.crate2nix.features or [ ]) != (v."cargo".resolved_default_features or [ ]) - ) - combined; - in - builtins.toJSON { - inherit onlyInCargo onlyInCrate2Nix differentFeatures; - }; + assert (builtins.isAttrs crateConfigs); + let + prefixValues = prefix: lib.mapAttrs (n: v: { "${prefix}" = v; }); + mergedFeatures = prefixValues "crate2nix" (mergePackageFeatures { + inherit crateConfigs packageId target; + features = [ "default" ]; + }); + configs = prefixValues "cargo" crateConfigs; + combined = lib.foldAttrs (a: b: a // b) { } [ + mergedFeatures + configs + ]; + onlyInCargo = builtins.attrNames ( + lib.filterAttrs (n: v: !(v ? "crate2nix") && (v ? "cargo")) combined + ); + onlyInCrate2Nix = builtins.attrNames ( + lib.filterAttrs (n: v: (v ? "crate2nix") && !(v ? "cargo")) combined + ); + differentFeatures = lib.filterAttrs ( + n: v: + (v ? "crate2nix") + && (v ? "cargo") + && (v.crate2nix.features or [ ]) != (v."cargo".resolved_default_features or [ ]) + ) combined; + in + builtins.toJSON { + inherit onlyInCargo onlyInCrate2Nix differentFeatures; + }; /* Returns an attrset mapping packageId to the list of enabled features. @@ -3688,112 +3684,111 @@ rec { corresponding feature sets are merged. Features in rust are additive. */ mergePackageFeatures = - { crateConfigs ? crates - , packageId - , rootPackageId ? packageId - , features ? rootFeatures - , dependencyPath ? [ crates.${packageId}.crateName ] - , featuresByPackageId ? { } - , target - , # Adds devDependencies to the crate with rootPackageId. - runTests ? false - , ... + { + crateConfigs ? crates, + packageId, + rootPackageId ? packageId, + features ? rootFeatures, + dependencyPath ? [ crates.${packageId}.crateName ], + featuresByPackageId ? { }, + target, + # Adds devDependencies to the crate with rootPackageId. + runTests ? false, + ... }@args: - assert (builtins.isAttrs crateConfigs); - assert (builtins.isString packageId); - assert (builtins.isString rootPackageId); - assert (builtins.isList features); - assert (builtins.isList dependencyPath); - assert (builtins.isAttrs featuresByPackageId); - assert (builtins.isAttrs target); - assert (builtins.isBool runTests); - let - crateConfig = crateConfigs."${packageId}" or (builtins.throw "Package not found: ${packageId}"); - expandedFeatures = expandFeatures (crateConfig.features or { }) features; - enabledFeatures = enableFeatures (crateConfig.dependencies or [ ]) expandedFeatures; - depWithResolvedFeatures = - dependency: - let - inherit (dependency) packageId; - features = dependencyFeatures enabledFeatures dependency; - in - { - inherit packageId features; + assert (builtins.isAttrs crateConfigs); + assert (builtins.isString packageId); + assert (builtins.isString rootPackageId); + assert (builtins.isList features); + assert (builtins.isList dependencyPath); + assert (builtins.isAttrs featuresByPackageId); + assert (builtins.isAttrs target); + assert (builtins.isBool runTests); + let + crateConfig = crateConfigs."${packageId}" or (builtins.throw "Package not found: ${packageId}"); + expandedFeatures = expandFeatures (crateConfig.features or { }) features; + enabledFeatures = enableFeatures (crateConfig.dependencies or [ ]) expandedFeatures; + depWithResolvedFeatures = + dependency: + let + inherit (dependency) packageId; + features = dependencyFeatures enabledFeatures dependency; + in + { + inherit packageId features; + }; + resolveDependencies = + cache: path: dependencies: + assert (builtins.isAttrs cache); + assert (builtins.isList dependencies); + let + enabledDependencies = filterEnabledDependencies { + inherit dependencies target; + features = enabledFeatures; }; - resolveDependencies = - cache: path: dependencies: - assert (builtins.isAttrs cache); - assert (builtins.isList dependencies); - let - enabledDependencies = filterEnabledDependencies { - inherit dependencies target; - features = enabledFeatures; - }; - directDependencies = map depWithResolvedFeatures enabledDependencies; - foldOverCache = op: lib.foldl op cache directDependencies; - in - foldOverCache ( - cache: - { packageId, features }: - let - cacheFeatures = cache.${packageId} or [ ]; - combinedFeatures = sortedUnique (cacheFeatures ++ features); - in - if cache ? ${packageId} && cache.${packageId} == combinedFeatures then - cache - else - mergePackageFeatures { - features = combinedFeatures; - featuresByPackageId = cache; - inherit - crateConfigs - packageId - target - runTests - rootPackageId - ; - } - ); - cacheWithSelf = + directDependencies = map depWithResolvedFeatures enabledDependencies; + foldOverCache = op: lib.foldl op cache directDependencies; + in + foldOverCache ( + cache: + { packageId, features }: let - cacheFeatures = featuresByPackageId.${packageId} or [ ]; - combinedFeatures = sortedUnique (cacheFeatures ++ enabledFeatures); + cacheFeatures = cache.${packageId} or [ ]; + combinedFeatures = sortedUnique (cacheFeatures ++ features); in - featuresByPackageId - // { - "${packageId}" = combinedFeatures; - }; - cacheWithDependencies = resolveDependencies cacheWithSelf "dep" ( - crateConfig.dependencies or [ ] - ++ lib.optionals (runTests && packageId == rootPackageId) (crateConfig.devDependencies or [ ]) - ); - cacheWithAll = resolveDependencies cacheWithDependencies "build" ( - crateConfig.buildDependencies or [ ] + if cache ? ${packageId} && cache.${packageId} == combinedFeatures then + cache + else + mergePackageFeatures { + features = combinedFeatures; + featuresByPackageId = cache; + inherit + crateConfigs + packageId + target + runTests + rootPackageId + ; + } ); - in - cacheWithAll; + cacheWithSelf = + let + cacheFeatures = featuresByPackageId.${packageId} or [ ]; + combinedFeatures = sortedUnique (cacheFeatures ++ enabledFeatures); + in + featuresByPackageId + // { + "${packageId}" = combinedFeatures; + }; + cacheWithDependencies = resolveDependencies cacheWithSelf "dep" ( + crateConfig.dependencies or [ ] + ++ lib.optionals (runTests && packageId == rootPackageId) (crateConfig.devDependencies or [ ]) + ); + cacheWithAll = resolveDependencies cacheWithDependencies "build" ( + crateConfig.buildDependencies or [ ] + ); + in + cacheWithAll; # Returns the enabled dependencies given the enabled features. filterEnabledDependencies = - { dependencies - , features - , target - , + { + dependencies, + features, + target, }: - assert (builtins.isList dependencies); - assert (builtins.isList features); - assert (builtins.isAttrs target); + assert (builtins.isList dependencies); + assert (builtins.isList features); + assert (builtins.isAttrs target); - lib.filter - ( - dep: - let - targetFunc = dep.target or (features: true); - in - targetFunc { inherit features target; } - && (!(dep.optional or false) || builtins.any (doesFeatureEnableDependency dep) features) - ) - dependencies; + lib.filter ( + dep: + let + targetFunc = dep.target or (features: true); + in + targetFunc { inherit features target; } + && (!(dep.optional or false) || builtins.any (doesFeatureEnableDependency dep) features) + ) dependencies; # Returns whether the given feature should enable the given dependency. doesFeatureEnableDependency = @@ -3815,32 +3810,32 @@ rec { */ expandFeatures = featureMap: inputFeatures: - assert (builtins.isAttrs featureMap); - assert (builtins.isList inputFeatures); - let - expandFeaturesNoCycle = - oldSeen: inputFeatures: - if inputFeatures != [ ] then - let - # The feature we're currently expanding. - feature = builtins.head inputFeatures; - # All the features we've seen/expanded so far, including the one - # we're currently processing. - seen = oldSeen // { - ${feature} = 1; - }; - # Expand the feature but be careful to not re-introduce a feature - # that we've already seen: this can easily cause a cycle, see issue - # #209. - enables = builtins.filter (f: !(seen ? "${f}")) (featureMap."${feature}" or [ ]); - in - [ feature ] ++ (expandFeaturesNoCycle seen (builtins.tail inputFeatures ++ enables)) - # No more features left, nothing to expand to. - else - [ ]; - outFeatures = expandFeaturesNoCycle { } inputFeatures; - in - sortedUnique outFeatures; + assert (builtins.isAttrs featureMap); + assert (builtins.isList inputFeatures); + let + expandFeaturesNoCycle = + oldSeen: inputFeatures: + if inputFeatures != [ ] then + let + # The feature we're currently expanding. + feature = builtins.head inputFeatures; + # All the features we've seen/expanded so far, including the one + # we're currently processing. + seen = oldSeen // { + ${feature} = 1; + }; + # Expand the feature but be careful to not re-introduce a feature + # that we've already seen: this can easily cause a cycle, see issue + # #209. + enables = builtins.filter (f: !(seen ? "${f}")) (featureMap."${feature}" or [ ]); + in + [ feature ] ++ (expandFeaturesNoCycle seen (builtins.tail inputFeatures ++ enables)) + # No more features left, nothing to expand to. + else + [ ]; + outFeatures = expandFeaturesNoCycle { } inputFeatures; + in + sortedUnique outFeatures; /* This function adds optional dependencies as features if they are enabled @@ -3850,24 +3845,22 @@ rec { */ enableFeatures = dependencies: features: - assert (builtins.isList features); - assert (builtins.isList dependencies); - let - additionalFeatures = lib.concatMap - ( - dependency: - assert (builtins.isAttrs dependency); - let - enabled = builtins.any (doesFeatureEnableDependency dependency) features; - in - if (dependency.optional or false) && enabled then - [ (dependency.rename or dependency.name) ] - else - [ ] - ) - dependencies; - in - sortedUnique (features ++ additionalFeatures); + assert (builtins.isList features); + assert (builtins.isList dependencies); + let + additionalFeatures = lib.concatMap ( + dependency: + assert (builtins.isAttrs dependency); + let + enabled = builtins.any (doesFeatureEnableDependency dependency) features; + in + if (dependency.optional or false) && enabled then + [ (dependency.rename or dependency.name) ] + else + [ ] + ) dependencies; + in + sortedUnique (features ++ additionalFeatures); /* Returns the actual features for the given dependency. @@ -3876,39 +3869,39 @@ rec { */ dependencyFeatures = features: dependency: - assert (builtins.isList features); - assert (builtins.isAttrs dependency); - let - defaultOrNil = if dependency.usesDefaultFeatures or true then [ "default" ] else [ ]; - explicitFeatures = dependency.features or [ ]; - additionalDependencyFeatures = - let - name = dependency.rename or dependency.name; - stripPrefixMatch = prefix: s: if lib.hasPrefix prefix s then lib.removePrefix prefix s else null; - extractFeature = - feature: - lib.findFirst (f: f != null) null ( - map (prefix: stripPrefixMatch prefix feature) [ - (name + "/") - (name + "?/") - ] - ); - dependencyFeatures = lib.filter (f: f != null) (map extractFeature features); - in - dependencyFeatures; - in - defaultOrNil ++ explicitFeatures ++ additionalDependencyFeatures; + assert (builtins.isList features); + assert (builtins.isAttrs dependency); + let + defaultOrNil = if dependency.usesDefaultFeatures or true then [ "default" ] else [ ]; + explicitFeatures = dependency.features or [ ]; + additionalDependencyFeatures = + let + name = dependency.rename or dependency.name; + stripPrefixMatch = prefix: s: if lib.hasPrefix prefix s then lib.removePrefix prefix s else null; + extractFeature = + feature: + lib.findFirst (f: f != null) null ( + map (prefix: stripPrefixMatch prefix feature) [ + (name + "/") + (name + "?/") + ] + ); + dependencyFeatures = lib.filter (f: f != null) (map extractFeature features); + in + dependencyFeatures; + in + defaultOrNil ++ explicitFeatures ++ additionalDependencyFeatures; # Sorts and removes duplicates from a list of strings. sortedUnique = features: - assert (builtins.isList features); - assert (builtins.all builtins.isString features); - let - outFeaturesSet = lib.foldl (set: feature: set // { "${feature}" = 1; }) { } features; - outFeaturesUnique = builtins.attrNames outFeaturesSet; - in - builtins.sort (a: b: a < b) outFeaturesUnique; + assert (builtins.isList features); + assert (builtins.all builtins.isString features); + let + outFeaturesSet = lib.foldl (set: feature: set // { "${feature}" = 1; }) { } features; + outFeaturesUnique = builtins.attrNames outFeaturesSet; + in + builtins.sort (a: b: a < b) outFeaturesUnique; deprecationWarning = message: value: diff --git a/crate2nix/templates/Cargo.nix.tera b/crate2nix/templates/Cargo.nix.tera index 980d5032..29b3f8b0 100644 --- a/crate2nix/templates/Cargo.nix.tera +++ b/crate2nix/templates/Cargo.nix.tera @@ -6,10 +6,16 @@ # See https://github.com/kolloch/crate2nix for more info. { nixpkgs ? {{config.nixpkgs_path | safe}} -, pkgs ? import nixpkgs { config = {}; } +, pkgs ? import nixpkgs { } , fetchurl ? pkgs.fetchurl +, fetchgit ? pkgs.fetchgit +, callPackage ? pkgs.callPackage , lib ? pkgs.lib +, buildPackages ? pkgs.buildPackages , stdenv ? pkgs.stdenv +, stdenvNoCC ? pkgs.stdenvNoCC +, symlinkJoin ? pkgs.symlinkJoin +, runCommand ? pkgs.runCommand , buildRustCrateForPkgs ? pkgs: pkgs.buildRustCrate # This is used as the `crateOverrides` argument for `buildRustCrate`. , defaultCrateOverrides ? pkgs.defaultCrateOverrides @@ -26,7 +32,7 @@ # Additional crate2nix configuration if it exists. , crateConfig ? if builtins.pathExists ./crate-config.nix - then pkgs.callPackage ./crate-config.nix {} + then callPackage ./crate-config.nix {} else {} }: @@ -74,7 +80,7 @@ rec { registries = { {%- for name, hash in registries %} {{name}} = builtins.fromJSON (builtins.readFile (fetchurl { - url = "{{name | safe}}/config.json"; + url = "{{name | trim_end_matches(pat="/") | safe}}/config.json"; sha256 = {{hash}}; })); {%- endfor %} @@ -82,7 +88,7 @@ rec { {%- endif %} # A derivation that joins the outputs of all workspace members together. - allWorkspaceMembers = pkgs.symlinkJoin { + allWorkspaceMembers = symlinkJoin { name = "all-workspace-members"; paths = let members = builtins.attrValues workspaceMembers; @@ -147,16 +153,16 @@ rec { {%- elif crate.source.Nix.file.import and crate.source.Nix.attr %} src = (import {{crate.source.Nix.file.import | safe}}).{{crate.source.Nix.attr | safe}}; {%- elif crate.source.Nix.file.package and crate.source.Nix.attr %} - src = (pkgs.callPackage {{crate.source.Nix.file.package | safe}} {}).{{crate.source.Nix.attr | safe}}; + src = (callPackage {{crate.source.Nix.file.package | safe}} {}).{{crate.source.Nix.attr | safe}}; {%- elif crate.source.Nix.file.import %} src = import {{crate.source.Nix.file.import | safe}}; {%- elif crate.source.Nix.file.package %} - src = pkgs.callPackage {{crate.source.Nix.file.package | safe}} {}; + src = callPackage {{crate.source.Nix.file.package | safe}} {}; {%- elif crate.source.LocalDirectory.path %} src = lib.cleanSourceWith { filter = sourceFilter; src = {{crate.source.LocalDirectory.path | safe}}; }; {%- elif crate.source.Git %} workspace_member = null; - src = pkgs.fetchgit { + src = fetchgit { url = {{crate.source.Git.url}}; rev = {{crate.source.Git.rev}}; {%- if crate.source.Git.sha256 %} diff --git a/crate2nix/templates/nix/crate2nix/default.nix b/crate2nix/templates/nix/crate2nix/default.nix index 3e088032..d00e8b7a 100644 --- a/crate2nix/templates/nix/crate2nix/default.nix +++ b/crate2nix/templates/nix/crate2nix/default.nix @@ -1,21 +1,24 @@ # # crate2nix/default.nix (excerpt start) #{# -{ pkgs -, lib -, stdenv -, buildRustCrate -, buildRustCrateForPkgs ? if buildRustCrate != null then - lib.warn "`buildRustCrate` is deprecated, use `buildRustCrateForPkgs` instead" (_: buildRustCrate) - else - pkgs: pkgs.buildRustCrate -, defaultCrateOverrides -, strictDeprecation ? true -, crates ? { } -, rootFeatures ? [ ] -, targetFeatures ? [ ] -, release ? true -, +{ + lib, + stdenv, + stdenvNoCC, + buildRustCrate, + buildRustCrateForPkgs ? + if buildRustCrate != null then + lib.warn "`buildRustCrate` is deprecated, use `buildRustCrateForPkgs` instead" (_: buildRustCrate) + else + pkgs: pkgs.buildRustCrate, + buildPackages, + defaultCrateOverrides, + runCommand, + strictDeprecation ? true, + crates ? { }, + rootFeatures ? [ ], + targetFeatures ? [ ], + release ? true, }: rec { # #} @@ -43,12 +46,12 @@ rec { }; registryUrl = - { registries - , url - , crate - , version - , sha256 - , + { + registries, + url, + crate, + version, + sha256, }: let dl = registries.${url}.dl; @@ -88,41 +91,41 @@ rec { let baseName = builtins.baseNameOf (builtins.toString name); in - !( - # Filter out git - baseName == ".gitignore" - || (type == "directory" && baseName == ".git") - - # Filter out build results - || ( - type == "directory" - && ( - baseName == "target" - || baseName == "_site" - || baseName == ".sass-cache" - || baseName == ".jekyll-metadata" - || baseName == "build-artifacts" - ) + !( + # Filter out git + baseName == ".gitignore" + || (type == "directory" && baseName == ".git") + + # Filter out build results + || ( + type == "directory" + && ( + baseName == "target" + || baseName == "_site" + || baseName == ".sass-cache" + || baseName == ".jekyll-metadata" + || baseName == "build-artifacts" ) + ) - # Filter out nix-build result symlinks - || (type == "symlink" && lib.hasPrefix "result" baseName) + # Filter out nix-build result symlinks + || (type == "symlink" && lib.hasPrefix "result" baseName) - # Filter out IDE config - || (type == "directory" && (baseName == ".idea" || baseName == ".vscode")) - || lib.hasSuffix ".iml" baseName + # Filter out IDE config + || (type == "directory" && (baseName == ".idea" || baseName == ".vscode")) + || lib.hasSuffix ".iml" baseName - # Filter out nix build files - || baseName == "Cargo.nix" + # Filter out nix build files + || baseName == "Cargo.nix" - # Filter out editor backup / swap files. - || lib.hasSuffix "~" baseName - || builtins.match "^\\.sw[a-z]$$" baseName != null - || builtins.match "^\\..*\\.sw[a-z]$$" baseName != null - || lib.hasSuffix ".tmp" baseName - || lib.hasSuffix ".bak" baseName - || baseName == "tests.nix" - ); + # Filter out editor backup / swap files. + || lib.hasSuffix "~" baseName + || builtins.match "^\\.sw[a-z]$$" baseName != null + || builtins.match "^\\..*\\.sw[a-z]$$" baseName != null + || lib.hasSuffix ".tmp" baseName + || lib.hasSuffix ".bak" baseName + || baseName == "tests.nix" + ); /* Returns a crate which depends on successful test execution @@ -132,131 +135,126 @@ rec { testInputs: list of packages that should be available during test execution */ crateWithTest = - { crate - , testCrate - , testCrateFlags - , testInputs - , testPreRun - , testPostRun - , + { + crate, + testCrate, + testCrateFlags, + testInputs, + testPreRun, + testPostRun, }: - assert builtins.typeOf testCrateFlags == "list"; - assert builtins.typeOf testInputs == "list"; - assert builtins.typeOf testPreRun == "string"; - assert builtins.typeOf testPostRun == "string"; - let - # override the `crate` so that it will build and execute tests instead of - # building the actual lib and bin targets We just have to pass `--test` - # to rustc and it will do the right thing. We execute the tests and copy - # their log and the test executables to $out for later inspection. - test = - let - drv = testCrate.override (_: { - buildTests = true; - }); - # If the user hasn't set any pre/post commands, we don't want to - # insert empty lines. This means that any existing users of crate2nix - # don't get a spurious rebuild unless they set these explicitly. - testCommand = pkgs.lib.concatStringsSep "\n" ( - pkgs.lib.filter (s: s != "") [ - testPreRun - "$f $testCrateFlags 2>&1 | tee -a $out" - testPostRun - ] - ); - in - pkgs.stdenvNoCC.mkDerivation { - name = "run-tests-${testCrate.name}"; - - inherit (crate) src; - - inherit testCrateFlags; - - buildInputs = testInputs; - - buildPhase = '' - set -e - export RUST_BACKTRACE=1 - - # build outputs - testRoot=target/debug - mkdir -p $testRoot - - # executables of the crate - # we copy to prevent std::env::current_exe() to resolve to a store location - for i in ${crate}/bin/*; do - cp "$i" "$testRoot" - done - chmod +w -R . - - # test harness executables are suffixed with a hash, like cargo does - # this allows to prevent name collision with the main - # executables of the crate - hash=$(basename $out) - for file in ${drv}/tests/*; do - f=$testRoot/$(basename $file)-$hash - cp $file $f - ${testCommand} - done - ''; - }; - in - pkgs.runCommand "${crate.name}-linked" - { - inherit (crate) outputs crateName; - passthru = (crate.passthru or { }) // { - inherit test; - }; - } - ( - lib.optionalString (stdenv.buildPlatform.canExecute stdenv.hostPlatform) '' - echo tested by ${test} - '' - + '' - ${lib.concatMapStringsSep "\n" (output: "ln -s ${crate.${output}} ${"$"}${output}") crate.outputs} - '' - ); + assert builtins.typeOf testCrateFlags == "list"; + assert builtins.typeOf testInputs == "list"; + assert builtins.typeOf testPreRun == "string"; + assert builtins.typeOf testPostRun == "string"; + let + # override the `crate` so that it will build and execute tests instead of + # building the actual lib and bin targets We just have to pass `--test` + # to rustc and it will do the right thing. We execute the tests and copy + # their log and the test executables to $out for later inspection. + test = + let + drv = testCrate.override (_: { + buildTests = true; + }); + # If the user hasn't set any pre/post commands, we don't want to + # insert empty lines. This means that any existing users of crate2nix + # don't get a spurious rebuild unless they set these explicitly. + testCommand = lib.concatStringsSep "\n" ( + lib.filter (s: s != "") [ + testPreRun + "$f $testCrateFlags 2>&1 | tee -a $out" + testPostRun + ] + ); + in + stdenvNoCC.mkDerivation { + name = "run-tests-${testCrate.name}"; + + inherit (crate) src; + + inherit testCrateFlags; + + buildInputs = testInputs; + + buildPhase = '' + set -e + export RUST_BACKTRACE=1 + + # build outputs + testRoot=target/debug + mkdir -p $testRoot + + # executables of the crate + # we copy to prevent std::env::current_exe() to resolve to a store location + for i in ${crate}/bin/*; do + cp "$i" "$testRoot" + done + chmod +w -R . + + # test harness executables are suffixed with a hash, like cargo does + # this allows to prevent name collision with the main + # executables of the crate + hash=$(basename $out) + for file in ${drv}/tests/*; do + f=$testRoot/$(basename $file)-$hash + cp $file $f + ${testCommand} + done + ''; + }; + in + runCommand "${crate.name}-linked" + { + inherit (crate) outputs crateName; + passthru = (crate.passthru or { }) // { + inherit test; + }; + } + ( + lib.optionalString (stdenv.buildPlatform.canExecute stdenv.hostPlatform) '' + echo tested by ${test} + '' + + '' + ${lib.concatMapStringsSep "\n" (output: "ln -s ${crate.${output}} ${"$"}${output}") crate.outputs} + '' + ); # A restricted overridable version of builtRustCratesWithFeatures. buildRustCrateWithFeatures = - { packageId - , features ? rootFeatures - , crateOverrides ? defaultCrateOverrides - , buildRustCrateForPkgsFunc ? null - , runTests ? false - , testCrateFlags ? [ ] - , testInputs ? [ ] - , # Any command to run immediatelly before a test is executed. - testPreRun ? "" - , # Any command run immediatelly after a test is executed. - testPostRun ? "" - , + { + packageId, + features ? rootFeatures, + crateOverrides ? defaultCrateOverrides, + buildRustCrateForPkgsFunc ? null, + runTests ? false, + testCrateFlags ? [ ], + testInputs ? [ ], + # Any command to run immediatelly before a test is executed. + testPreRun ? "", + # Any command run immediatelly after a test is executed. + testPostRun ? "", }: lib.makeOverridable ( - { features - , crateOverrides - , runTests - , testCrateFlags - , testInputs - , testPreRun - , testPostRun - , + { + features, + crateOverrides, + runTests, + testCrateFlags, + testInputs, + testPreRun, + testPostRun, }: let buildRustCrateForPkgsFuncOverriden = if buildRustCrateForPkgsFunc != null then buildRustCrateForPkgsFunc else - ( - if crateOverrides == pkgs.defaultCrateOverrides then - buildRustCrateForPkgs - else - pkgs: - (buildRustCrateForPkgs pkgs).override { - defaultCrateOverrides = crateOverrides; - } - ); + pkgs: + (buildRustCrateForPkgs pkgs).override { + defaultCrateOverrides = crateOverrides; + }; builtRustCrates = builtRustCratesWithFeatures { inherit packageId features; buildRustCrateForPkgsFunc = buildRustCrateForPkgsFuncOverriden; @@ -271,17 +269,16 @@ rec { testDrv = builtTestRustCrates.crates.${packageId}; derivation = if runTests then - crateWithTest - { - crate = drv; - testCrate = testDrv; - inherit - testCrateFlags - testInputs - testPreRun - testPostRun - ; - } + crateWithTest { + crate = drv; + testCrate = testDrv; + inherit + testCrateFlags + testInputs + testPreRun + testPostRun + ; + } else drv; in @@ -304,159 +301,157 @@ rec { for the corresponding crate. */ builtRustCratesWithFeatures = - { packageId - , features - , crateConfigs ? crates - , buildRustCrateForPkgsFunc - , runTests - , makeTarget ? makeDefaultTarget - , + { + packageId, + features, + crateConfigs ? crates, + buildRustCrateForPkgsFunc, + runTests, + makeTarget ? makeDefaultTarget, }@args: - assert (builtins.isAttrs crateConfigs); - assert (builtins.isString packageId); - assert (builtins.isList features); - assert (builtins.isAttrs (makeTarget stdenv.hostPlatform)); - assert (builtins.isBool runTests); - let - rootPackageId = packageId; - mergedFeatures = mergePackageFeatures ( - args + assert (builtins.isAttrs crateConfigs); + assert (builtins.isString packageId); + assert (builtins.isList features); + assert (builtins.isAttrs (makeTarget stdenv.hostPlatform)); + assert (builtins.isBool runTests); + let + rootPackageId = packageId; + mergedFeatures = mergePackageFeatures ( + args + // { + inherit rootPackageId; + target = makeTarget stdenv.hostPlatform // { + test = runTests; + }; + } + ); + # Memoize built packages so that reappearing packages are only built once. + builtByPackageIdByPkgs = mkBuiltByPackageIdByPkgs pkgs; + mkBuiltByPackageIdByPkgs = + pkgs: + let + self = { + crates = lib.mapAttrs ( + packageId: value: buildByPackageIdForPkgsImpl self pkgs packageId + ) crateConfigs; + target = makeTarget stdenv.hostPlatform; + build = mkBuiltByPackageIdByPkgs buildPackages; + }; + in + self; + buildByPackageIdForPkgsImpl = + self: pkgs: packageId: + let + features = mergedFeatures."${packageId}" or [ ]; + crateConfig' = crateConfigs."${packageId}"; + crateConfig = builtins.removeAttrs crateConfig' [ + "resolvedDefaultFeatures" + "devDependencies" + ]; + devDependencies = lib.optionals (runTests && packageId == rootPackageId) ( + crateConfig'.devDependencies or [ ] + ); + dependencies = dependencyDerivations { + inherit features; + inherit (self) target; + buildByPackageId = + depPackageId: + # proc_macro crates must be compiled for the build architecture + if crateConfigs.${depPackageId}.procMacro or false then + self.build.crates.${depPackageId} + else + self.crates.${depPackageId}; + dependencies = (crateConfig.dependencies or [ ]) ++ devDependencies; + }; + buildDependencies = dependencyDerivations { + inherit features; + inherit (self.build) target; + buildByPackageId = depPackageId: self.build.crates.${depPackageId}; + dependencies = crateConfig.buildDependencies or [ ]; + }; + dependenciesWithRenames = + let + buildDeps = filterEnabledDependencies { + inherit features; + inherit (self) target; + dependencies = crateConfig.dependencies or [ ] ++ devDependencies; + }; + hostDeps = filterEnabledDependencies { + inherit features; + inherit (self.build) target; + dependencies = crateConfig.buildDependencies or [ ]; + }; + in + lib.filter (d: d ? "rename") (hostDeps ++ buildDeps); + # Crate renames have the form: + # + # { + # crate_name = [ + # { version = "1.2.3"; rename = "crate_name01"; } + # ]; + # # ... + # } + crateRenames = + let + grouped = lib.groupBy (dependency: dependency.name) dependenciesWithRenames; + versionAndRename = + dep: + let + package = crateConfigs."${dep.packageId}"; + in + { + inherit (dep) rename; + inherit (package) version; + }; + in + lib.mapAttrs (name: builtins.map versionAndRename) grouped; + in + buildRustCrateForPkgsFunc pkgs ( + crateConfig // { - inherit rootPackageId; - target = makeTarget stdenv.hostPlatform // { - test = runTests; - }; + src = + crateConfig.src or (pkgs.fetchurl rec { + name = "${crateConfig.crateName}-${crateConfig.version}.tar.gz"; + # https://www.pietroalbini.org/blog/downloading-crates-io/ + # Not rate-limited, CDN URL. + url = "https://static.crates.io/crates/${crateConfig.crateName}/${crateConfig.crateName}-${crateConfig.version}.crate"; + sha256 = + assert (lib.assertMsg (crateConfig ? sha256) "Missing sha256 for ${name}"); + crateConfig.sha256; + }); + extraRustcOpts = + lib.lists.optional (targetFeatures != [ ]) + "-C target-feature=${lib.concatMapStringsSep "," (x: "+${x}") targetFeatures}"; + inherit + features + dependencies + buildDependencies + crateRenames + release + ; } ); - # Memoize built packages so that reappearing packages are only built once. - builtByPackageIdByPkgs = mkBuiltByPackageIdByPkgs pkgs; - mkBuiltByPackageIdByPkgs = - pkgs: - let - self = { - crates = lib.mapAttrs - ( - packageId: value: buildByPackageIdForPkgsImpl self pkgs packageId - ) - crateConfigs; - target = makeTarget stdenv.hostPlatform; - build = mkBuiltByPackageIdByPkgs pkgs.buildPackages; - }; - in - self; - buildByPackageIdForPkgsImpl = - self: pkgs: packageId: - let - features = mergedFeatures."${packageId}" or [ ]; - crateConfig' = crateConfigs."${packageId}"; - crateConfig = builtins.removeAttrs crateConfig' [ - "resolvedDefaultFeatures" - "devDependencies" - ]; - devDependencies = lib.optionals (runTests && packageId == rootPackageId) ( - crateConfig'.devDependencies or [ ] - ); - dependencies = dependencyDerivations { - inherit features; - inherit (self) target; - buildByPackageId = - depPackageId: - # proc_macro crates must be compiled for the build architecture - if crateConfigs.${depPackageId}.procMacro or false then - self.build.crates.${depPackageId} - else - self.crates.${depPackageId}; - dependencies = (crateConfig.dependencies or [ ]) ++ devDependencies; - }; - buildDependencies = dependencyDerivations { - inherit features; - inherit (self.build) target; - buildByPackageId = depPackageId: self.build.crates.${depPackageId}; - dependencies = crateConfig.buildDependencies or [ ]; - }; - dependenciesWithRenames = - let - buildDeps = filterEnabledDependencies { - inherit features; - inherit (self) target; - dependencies = crateConfig.dependencies or [ ] ++ devDependencies; - }; - hostDeps = filterEnabledDependencies { - inherit features; - inherit (self.build) target; - dependencies = crateConfig.buildDependencies or [ ]; - }; - in - lib.filter (d: d ? "rename") (hostDeps ++ buildDeps); - # Crate renames have the form: - # - # { - # crate_name = [ - # { version = "1.2.3"; rename = "crate_name01"; } - # ]; - # # ... - # } - crateRenames = - let - grouped = lib.groupBy (dependency: dependency.name) dependenciesWithRenames; - versionAndRename = - dep: - let - package = crateConfigs."${dep.packageId}"; - in - { - inherit (dep) rename; - inherit (package) version; - }; - in - lib.mapAttrs (name: builtins.map versionAndRename) grouped; - in - buildRustCrateForPkgsFunc pkgs ( - crateConfig - // { - src = - crateConfig.src or (pkgs.fetchurl rec { - name = "${crateConfig.crateName}-${crateConfig.version}.tar.gz"; - # https://www.pietroalbini.org/blog/downloading-crates-io/ - # Not rate-limited, CDN URL. - url = "https://static.crates.io/crates/${crateConfig.crateName}/${crateConfig.crateName}-${crateConfig.version}.crate"; - sha256 = - assert (lib.assertMsg (crateConfig ? sha256) "Missing sha256 for ${name}"); - crateConfig.sha256; - }); - extraRustcOpts = - lib.lists.optional (targetFeatures != [ ]) - "-C target-feature=${lib.concatMapStringsSep "," (x: "+${x}") targetFeatures}"; - inherit - features - dependencies - buildDependencies - crateRenames - release - ; - } - ); - in - builtByPackageIdByPkgs; + in + builtByPackageIdByPkgs; # Returns the actual derivations for the given dependencies. dependencyDerivations = - { buildByPackageId - , features - , dependencies - , target - , + { + buildByPackageId, + features, + dependencies, + target, }: - assert (builtins.isList features); - assert (builtins.isList dependencies); - assert (builtins.isAttrs target); - let - enabledDependencies = filterEnabledDependencies { - inherit dependencies features target; - }; - depDerivation = dependency: buildByPackageId dependency.packageId; - in - map depDerivation enabledDependencies; + assert (builtins.isList features); + assert (builtins.isList dependencies); + assert (builtins.isAttrs target); + let + enabledDependencies = filterEnabledDependencies { + inherit dependencies features target; + }; + depDerivation = dependency: buildByPackageId dependency.packageId; + in + map depDerivation enabledDependencies; /* Returns a sanitized version of val with all values substituted that cannot @@ -475,39 +470,39 @@ rec { # Returns various tools to debug a crate. debugCrate = - { packageId - , target ? makeDefaultTarget stdenv.hostPlatform - , + { + packageId, + target ? makeDefaultTarget stdenv.hostPlatform, }: - assert (builtins.isString packageId); - let - debug = rec { - # The built tree as passed to buildRustCrate. - buildTree = buildRustCrateWithFeatures { - buildRustCrateForPkgsFunc = _: lib.id; - inherit packageId; - }; - sanitizedBuildTree = sanitizeForJson buildTree; - dependencyTree = sanitizeForJson (buildRustCrateWithFeatures { - buildRustCrateForPkgsFunc = _: crate: { - "01_crateName" = crate.crateName or false; - "02_features" = crate.features or [ ]; - "03_dependencies" = crate.dependencies or [ ]; - }; - inherit packageId; - }); - mergedPackageFeatures = mergePackageFeatures { - features = rootFeatures; - inherit packageId target; - }; - diffedDefaultPackageFeatures = diffDefaultPackageFeatures { - inherit packageId target; + assert (builtins.isString packageId); + let + debug = rec { + # The built tree as passed to buildRustCrate. + buildTree = buildRustCrateWithFeatures { + buildRustCrateForPkgsFunc = _: lib.id; + inherit packageId; + }; + sanitizedBuildTree = sanitizeForJson buildTree; + dependencyTree = sanitizeForJson (buildRustCrateWithFeatures { + buildRustCrateForPkgsFunc = _: crate: { + "01_crateName" = crate.crateName or false; + "02_features" = crate.features or [ ]; + "03_dependencies" = crate.dependencies or [ ]; }; + inherit packageId; + }); + mergedPackageFeatures = mergePackageFeatures { + features = rootFeatures; + inherit packageId target; + }; + diffedDefaultPackageFeatures = diffDefaultPackageFeatures { + inherit packageId target; }; - in - { - internal = debug; }; + in + { + internal = debug; + }; /* Returns differences between cargo default features and crate2nix default @@ -516,41 +511,39 @@ rec { This is useful for verifying the feature resolution in crate2nix. */ diffDefaultPackageFeatures = - { crateConfigs ? crates - , packageId - , target - , + { + crateConfigs ? crates, + packageId, + target, }: - assert (builtins.isAttrs crateConfigs); - let - prefixValues = prefix: lib.mapAttrs (n: v: { "${prefix}" = v; }); - mergedFeatures = prefixValues "crate2nix" (mergePackageFeatures { - inherit crateConfigs packageId target; - features = [ "default" ]; - }); - configs = prefixValues "cargo" crateConfigs; - combined = lib.foldAttrs (a: b: a // b) { } [ - mergedFeatures - configs - ]; - onlyInCargo = builtins.attrNames ( - lib.filterAttrs (n: v: !(v ? "crate2nix") && (v ? "cargo")) combined - ); - onlyInCrate2Nix = builtins.attrNames ( - lib.filterAttrs (n: v: (v ? "crate2nix") && !(v ? "cargo")) combined - ); - differentFeatures = lib.filterAttrs - ( - n: v: - (v ? "crate2nix") - && (v ? "cargo") - && (v.crate2nix.features or [ ]) != (v."cargo".resolved_default_features or [ ]) - ) - combined; - in - builtins.toJSON { - inherit onlyInCargo onlyInCrate2Nix differentFeatures; - }; + assert (builtins.isAttrs crateConfigs); + let + prefixValues = prefix: lib.mapAttrs (n: v: { "${prefix}" = v; }); + mergedFeatures = prefixValues "crate2nix" (mergePackageFeatures { + inherit crateConfigs packageId target; + features = [ "default" ]; + }); + configs = prefixValues "cargo" crateConfigs; + combined = lib.foldAttrs (a: b: a // b) { } [ + mergedFeatures + configs + ]; + onlyInCargo = builtins.attrNames ( + lib.filterAttrs (n: v: !(v ? "crate2nix") && (v ? "cargo")) combined + ); + onlyInCrate2Nix = builtins.attrNames ( + lib.filterAttrs (n: v: (v ? "crate2nix") && !(v ? "cargo")) combined + ); + differentFeatures = lib.filterAttrs ( + n: v: + (v ? "crate2nix") + && (v ? "cargo") + && (v.crate2nix.features or [ ]) != (v."cargo".resolved_default_features or [ ]) + ) combined; + in + builtins.toJSON { + inherit onlyInCargo onlyInCrate2Nix differentFeatures; + }; /* Returns an attrset mapping packageId to the list of enabled features. @@ -559,112 +552,111 @@ rec { corresponding feature sets are merged. Features in rust are additive. */ mergePackageFeatures = - { crateConfigs ? crates - , packageId - , rootPackageId ? packageId - , features ? rootFeatures - , dependencyPath ? [ crates.${packageId}.crateName ] - , featuresByPackageId ? { } - , target - , # Adds devDependencies to the crate with rootPackageId. - runTests ? false - , ... + { + crateConfigs ? crates, + packageId, + rootPackageId ? packageId, + features ? rootFeatures, + dependencyPath ? [ crates.${packageId}.crateName ], + featuresByPackageId ? { }, + target, + # Adds devDependencies to the crate with rootPackageId. + runTests ? false, + ... }@args: - assert (builtins.isAttrs crateConfigs); - assert (builtins.isString packageId); - assert (builtins.isString rootPackageId); - assert (builtins.isList features); - assert (builtins.isList dependencyPath); - assert (builtins.isAttrs featuresByPackageId); - assert (builtins.isAttrs target); - assert (builtins.isBool runTests); - let - crateConfig = crateConfigs."${packageId}" or (builtins.throw "Package not found: ${packageId}"); - expandedFeatures = expandFeatures (crateConfig.features or { }) features; - enabledFeatures = enableFeatures (crateConfig.dependencies or [ ]) expandedFeatures; - depWithResolvedFeatures = - dependency: - let - inherit (dependency) packageId; - features = dependencyFeatures enabledFeatures dependency; - in - { - inherit packageId features; + assert (builtins.isAttrs crateConfigs); + assert (builtins.isString packageId); + assert (builtins.isString rootPackageId); + assert (builtins.isList features); + assert (builtins.isList dependencyPath); + assert (builtins.isAttrs featuresByPackageId); + assert (builtins.isAttrs target); + assert (builtins.isBool runTests); + let + crateConfig = crateConfigs."${packageId}" or (builtins.throw "Package not found: ${packageId}"); + expandedFeatures = expandFeatures (crateConfig.features or { }) features; + enabledFeatures = enableFeatures (crateConfig.dependencies or [ ]) expandedFeatures; + depWithResolvedFeatures = + dependency: + let + inherit (dependency) packageId; + features = dependencyFeatures enabledFeatures dependency; + in + { + inherit packageId features; + }; + resolveDependencies = + cache: path: dependencies: + assert (builtins.isAttrs cache); + assert (builtins.isList dependencies); + let + enabledDependencies = filterEnabledDependencies { + inherit dependencies target; + features = enabledFeatures; }; - resolveDependencies = - cache: path: dependencies: - assert (builtins.isAttrs cache); - assert (builtins.isList dependencies); - let - enabledDependencies = filterEnabledDependencies { - inherit dependencies target; - features = enabledFeatures; - }; - directDependencies = map depWithResolvedFeatures enabledDependencies; - foldOverCache = op: lib.foldl op cache directDependencies; - in - foldOverCache ( - cache: - { packageId, features }: - let - cacheFeatures = cache.${packageId} or [ ]; - combinedFeatures = sortedUnique (cacheFeatures ++ features); - in - if cache ? ${packageId} && cache.${packageId} == combinedFeatures then - cache - else - mergePackageFeatures { - features = combinedFeatures; - featuresByPackageId = cache; - inherit - crateConfigs - packageId - target - runTests - rootPackageId - ; - } - ); - cacheWithSelf = + directDependencies = map depWithResolvedFeatures enabledDependencies; + foldOverCache = op: lib.foldl op cache directDependencies; + in + foldOverCache ( + cache: + { packageId, features }: let - cacheFeatures = featuresByPackageId.${packageId} or [ ]; - combinedFeatures = sortedUnique (cacheFeatures ++ enabledFeatures); + cacheFeatures = cache.${packageId} or [ ]; + combinedFeatures = sortedUnique (cacheFeatures ++ features); in - featuresByPackageId - // { - "${packageId}" = combinedFeatures; - }; - cacheWithDependencies = resolveDependencies cacheWithSelf "dep" ( - crateConfig.dependencies or [ ] - ++ lib.optionals (runTests && packageId == rootPackageId) (crateConfig.devDependencies or [ ]) - ); - cacheWithAll = resolveDependencies cacheWithDependencies "build" ( - crateConfig.buildDependencies or [ ] + if cache ? ${packageId} && cache.${packageId} == combinedFeatures then + cache + else + mergePackageFeatures { + features = combinedFeatures; + featuresByPackageId = cache; + inherit + crateConfigs + packageId + target + runTests + rootPackageId + ; + } ); - in - cacheWithAll; + cacheWithSelf = + let + cacheFeatures = featuresByPackageId.${packageId} or [ ]; + combinedFeatures = sortedUnique (cacheFeatures ++ enabledFeatures); + in + featuresByPackageId + // { + "${packageId}" = combinedFeatures; + }; + cacheWithDependencies = resolveDependencies cacheWithSelf "dep" ( + crateConfig.dependencies or [ ] + ++ lib.optionals (runTests && packageId == rootPackageId) (crateConfig.devDependencies or [ ]) + ); + cacheWithAll = resolveDependencies cacheWithDependencies "build" ( + crateConfig.buildDependencies or [ ] + ); + in + cacheWithAll; # Returns the enabled dependencies given the enabled features. filterEnabledDependencies = - { dependencies - , features - , target - , + { + dependencies, + features, + target, }: - assert (builtins.isList dependencies); - assert (builtins.isList features); - assert (builtins.isAttrs target); + assert (builtins.isList dependencies); + assert (builtins.isList features); + assert (builtins.isAttrs target); - lib.filter - ( - dep: - let - targetFunc = dep.target or (features: true); - in - targetFunc { inherit features target; } - && (!(dep.optional or false) || builtins.any (doesFeatureEnableDependency dep) features) - ) - dependencies; + lib.filter ( + dep: + let + targetFunc = dep.target or (features: true); + in + targetFunc { inherit features target; } + && (!(dep.optional or false) || builtins.any (doesFeatureEnableDependency dep) features) + ) dependencies; # Returns whether the given feature should enable the given dependency. doesFeatureEnableDependency = @@ -686,32 +678,32 @@ rec { */ expandFeatures = featureMap: inputFeatures: - assert (builtins.isAttrs featureMap); - assert (builtins.isList inputFeatures); - let - expandFeaturesNoCycle = - oldSeen: inputFeatures: - if inputFeatures != [ ] then - let - # The feature we're currently expanding. - feature = builtins.head inputFeatures; - # All the features we've seen/expanded so far, including the one - # we're currently processing. - seen = oldSeen // { - ${feature} = 1; - }; - # Expand the feature but be careful to not re-introduce a feature - # that we've already seen: this can easily cause a cycle, see issue - # #209. - enables = builtins.filter (f: !(seen ? "${f}")) (featureMap."${feature}" or [ ]); - in - [ feature ] ++ (expandFeaturesNoCycle seen (builtins.tail inputFeatures ++ enables)) - # No more features left, nothing to expand to. - else - [ ]; - outFeatures = expandFeaturesNoCycle { } inputFeatures; - in - sortedUnique outFeatures; + assert (builtins.isAttrs featureMap); + assert (builtins.isList inputFeatures); + let + expandFeaturesNoCycle = + oldSeen: inputFeatures: + if inputFeatures != [ ] then + let + # The feature we're currently expanding. + feature = builtins.head inputFeatures; + # All the features we've seen/expanded so far, including the one + # we're currently processing. + seen = oldSeen // { + ${feature} = 1; + }; + # Expand the feature but be careful to not re-introduce a feature + # that we've already seen: this can easily cause a cycle, see issue + # #209. + enables = builtins.filter (f: !(seen ? "${f}")) (featureMap."${feature}" or [ ]); + in + [ feature ] ++ (expandFeaturesNoCycle seen (builtins.tail inputFeatures ++ enables)) + # No more features left, nothing to expand to. + else + [ ]; + outFeatures = expandFeaturesNoCycle { } inputFeatures; + in + sortedUnique outFeatures; /* This function adds optional dependencies as features if they are enabled @@ -721,24 +713,22 @@ rec { */ enableFeatures = dependencies: features: - assert (builtins.isList features); - assert (builtins.isList dependencies); - let - additionalFeatures = lib.concatMap - ( - dependency: - assert (builtins.isAttrs dependency); - let - enabled = builtins.any (doesFeatureEnableDependency dependency) features; - in - if (dependency.optional or false) && enabled then - [ (dependency.rename or dependency.name) ] - else - [ ] - ) - dependencies; - in - sortedUnique (features ++ additionalFeatures); + assert (builtins.isList features); + assert (builtins.isList dependencies); + let + additionalFeatures = lib.concatMap ( + dependency: + assert (builtins.isAttrs dependency); + let + enabled = builtins.any (doesFeatureEnableDependency dependency) features; + in + if (dependency.optional or false) && enabled then + [ (dependency.rename or dependency.name) ] + else + [ ] + ) dependencies; + in + sortedUnique (features ++ additionalFeatures); /* Returns the actual features for the given dependency. @@ -747,39 +737,39 @@ rec { */ dependencyFeatures = features: dependency: - assert (builtins.isList features); - assert (builtins.isAttrs dependency); - let - defaultOrNil = if dependency.usesDefaultFeatures or true then [ "default" ] else [ ]; - explicitFeatures = dependency.features or [ ]; - additionalDependencyFeatures = - let - name = dependency.rename or dependency.name; - stripPrefixMatch = prefix: s: if lib.hasPrefix prefix s then lib.removePrefix prefix s else null; - extractFeature = - feature: - lib.findFirst (f: f != null) null ( - map (prefix: stripPrefixMatch prefix feature) [ - (name + "/") - (name + "?/") - ] - ); - dependencyFeatures = lib.filter (f: f != null) (map extractFeature features); - in - dependencyFeatures; - in - defaultOrNil ++ explicitFeatures ++ additionalDependencyFeatures; + assert (builtins.isList features); + assert (builtins.isAttrs dependency); + let + defaultOrNil = if dependency.usesDefaultFeatures or true then [ "default" ] else [ ]; + explicitFeatures = dependency.features or [ ]; + additionalDependencyFeatures = + let + name = dependency.rename or dependency.name; + stripPrefixMatch = prefix: s: if lib.hasPrefix prefix s then lib.removePrefix prefix s else null; + extractFeature = + feature: + lib.findFirst (f: f != null) null ( + map (prefix: stripPrefixMatch prefix feature) [ + (name + "/") + (name + "?/") + ] + ); + dependencyFeatures = lib.filter (f: f != null) (map extractFeature features); + in + dependencyFeatures; + in + defaultOrNil ++ explicitFeatures ++ additionalDependencyFeatures; # Sorts and removes duplicates from a list of strings. sortedUnique = features: - assert (builtins.isList features); - assert (builtins.all builtins.isString features); - let - outFeaturesSet = lib.foldl (set: feature: set // { "${feature}" = 1; }) { } features; - outFeaturesUnique = builtins.attrNames outFeaturesSet; - in - builtins.sort (a: b: a < b) outFeaturesUnique; + assert (builtins.isList features); + assert (builtins.all builtins.isString features); + let + outFeaturesSet = lib.foldl (set: feature: set // { "${feature}" = 1; }) { } features; + outFeaturesUnique = builtins.attrNames outFeaturesSet; + in + builtins.sort (a: b: a < b) outFeaturesUnique; deprecationWarning = message: value: diff --git a/sample_projects/bin_with_git_submodule_dep/Cargo.nix b/sample_projects/bin_with_git_submodule_dep/Cargo.nix index 34060349..73bf6a1e 100644 --- a/sample_projects/bin_with_git_submodule_dep/Cargo.nix +++ b/sample_projects/bin_with_git_submodule_dep/Cargo.nix @@ -4,10 +4,16 @@ # See https://github.com/kolloch/crate2nix for more info. { nixpkgs ? -, pkgs ? import nixpkgs { config = {}; } +, pkgs ? import nixpkgs { } , fetchurl ? pkgs.fetchurl +, fetchgit ? pkgs.fetchgit +, callPackage ? pkgs.callPackage , lib ? pkgs.lib +, buildPackages ? pkgs.buildPackages , stdenv ? pkgs.stdenv +, stdenvNoCC ? pkgs.stdenvNoCC +, symlinkJoin ? pkgs.symlinkJoin +, runCommand ? pkgs.runCommand , buildRustCrateForPkgs ? pkgs: pkgs.buildRustCrate # This is used as the `crateOverrides` argument for `buildRustCrate`. , defaultCrateOverrides ? pkgs.defaultCrateOverrides @@ -24,7 +30,7 @@ # Additional crate2nix configuration if it exists. , crateConfig ? if builtins.pathExists ./crate-config.nix - then pkgs.callPackage ./crate-config.nix {} + then callPackage ./crate-config.nix {} else {} }: @@ -65,7 +71,7 @@ rec { # A derivation that joins the outputs of all workspace members together. - allWorkspaceMembers = pkgs.symlinkJoin { + allWorkspaceMembers = symlinkJoin { name = "all-workspace-members"; paths = let members = builtins.attrValues workspaceMembers; @@ -485,7 +491,7 @@ rec { edition = "2018"; links = "rocksdb"; workspace_member = null; - src = pkgs.fetchgit { + src = fetchgit { url = "https://github.com/rust-rocksdb/rust-rocksdb"; rev = "66f04df013b6e6bd42b5a8c353406e09a7c7da2a"; sha256 = "1rchvjrjamdaznx26gy4bmjj10rrf00mgc1wvkc489r9z1nh4h1h"; @@ -837,7 +843,7 @@ rec { version = "0.21.0"; edition = "2018"; workspace_member = null; - src = pkgs.fetchgit { + src = fetchgit { url = "https://github.com/rust-rocksdb/rust-rocksdb"; rev = "66f04df013b6e6bd42b5a8c353406e09a7c7da2a"; sha256 = "1rchvjrjamdaznx26gy4bmjj10rrf00mgc1wvkc489r9z1nh4h1h"; @@ -1390,12 +1396,12 @@ rec { }; registryUrl = - { registries - , url - , crate - , version - , sha256 - , + { + registries, + url, + crate, + version, + sha256, }: let dl = registries.${url}.dl; @@ -1435,41 +1441,41 @@ rec { let baseName = builtins.baseNameOf (builtins.toString name); in - !( - # Filter out git - baseName == ".gitignore" - || (type == "directory" && baseName == ".git") - - # Filter out build results - || ( - type == "directory" - && ( - baseName == "target" - || baseName == "_site" - || baseName == ".sass-cache" - || baseName == ".jekyll-metadata" - || baseName == "build-artifacts" - ) + !( + # Filter out git + baseName == ".gitignore" + || (type == "directory" && baseName == ".git") + + # Filter out build results + || ( + type == "directory" + && ( + baseName == "target" + || baseName == "_site" + || baseName == ".sass-cache" + || baseName == ".jekyll-metadata" + || baseName == "build-artifacts" ) + ) - # Filter out nix-build result symlinks - || (type == "symlink" && lib.hasPrefix "result" baseName) + # Filter out nix-build result symlinks + || (type == "symlink" && lib.hasPrefix "result" baseName) - # Filter out IDE config - || (type == "directory" && (baseName == ".idea" || baseName == ".vscode")) - || lib.hasSuffix ".iml" baseName + # Filter out IDE config + || (type == "directory" && (baseName == ".idea" || baseName == ".vscode")) + || lib.hasSuffix ".iml" baseName - # Filter out nix build files - || baseName == "Cargo.nix" + # Filter out nix build files + || baseName == "Cargo.nix" - # Filter out editor backup / swap files. - || lib.hasSuffix "~" baseName - || builtins.match "^\\.sw[a-z]$$" baseName != null - || builtins.match "^\\..*\\.sw[a-z]$$" baseName != null - || lib.hasSuffix ".tmp" baseName - || lib.hasSuffix ".bak" baseName - || baseName == "tests.nix" - ); + # Filter out editor backup / swap files. + || lib.hasSuffix "~" baseName + || builtins.match "^\\.sw[a-z]$$" baseName != null + || builtins.match "^\\..*\\.sw[a-z]$$" baseName != null + || lib.hasSuffix ".tmp" baseName + || lib.hasSuffix ".bak" baseName + || baseName == "tests.nix" + ); /* Returns a crate which depends on successful test execution @@ -1479,131 +1485,126 @@ rec { testInputs: list of packages that should be available during test execution */ crateWithTest = - { crate - , testCrate - , testCrateFlags - , testInputs - , testPreRun - , testPostRun - , + { + crate, + testCrate, + testCrateFlags, + testInputs, + testPreRun, + testPostRun, }: - assert builtins.typeOf testCrateFlags == "list"; - assert builtins.typeOf testInputs == "list"; - assert builtins.typeOf testPreRun == "string"; - assert builtins.typeOf testPostRun == "string"; - let - # override the `crate` so that it will build and execute tests instead of - # building the actual lib and bin targets We just have to pass `--test` - # to rustc and it will do the right thing. We execute the tests and copy - # their log and the test executables to $out for later inspection. - test = - let - drv = testCrate.override (_: { - buildTests = true; - }); - # If the user hasn't set any pre/post commands, we don't want to - # insert empty lines. This means that any existing users of crate2nix - # don't get a spurious rebuild unless they set these explicitly. - testCommand = pkgs.lib.concatStringsSep "\n" ( - pkgs.lib.filter (s: s != "") [ - testPreRun - "$f $testCrateFlags 2>&1 | tee -a $out" - testPostRun - ] - ); - in - pkgs.stdenvNoCC.mkDerivation { - name = "run-tests-${testCrate.name}"; - - inherit (crate) src; - - inherit testCrateFlags; - - buildInputs = testInputs; - - buildPhase = '' - set -e - export RUST_BACKTRACE=1 - - # build outputs - testRoot=target/debug - mkdir -p $testRoot - - # executables of the crate - # we copy to prevent std::env::current_exe() to resolve to a store location - for i in ${crate}/bin/*; do - cp "$i" "$testRoot" - done - chmod +w -R . - - # test harness executables are suffixed with a hash, like cargo does - # this allows to prevent name collision with the main - # executables of the crate - hash=$(basename $out) - for file in ${drv}/tests/*; do - f=$testRoot/$(basename $file)-$hash - cp $file $f - ${testCommand} - done - ''; - }; - in - pkgs.runCommand "${crate.name}-linked" - { - inherit (crate) outputs crateName; - passthru = (crate.passthru or { }) // { - inherit test; - }; - } - ( - lib.optionalString (stdenv.buildPlatform.canExecute stdenv.hostPlatform) '' - echo tested by ${test} - '' - + '' - ${lib.concatMapStringsSep "\n" (output: "ln -s ${crate.${output}} ${"$"}${output}") crate.outputs} - '' - ); + assert builtins.typeOf testCrateFlags == "list"; + assert builtins.typeOf testInputs == "list"; + assert builtins.typeOf testPreRun == "string"; + assert builtins.typeOf testPostRun == "string"; + let + # override the `crate` so that it will build and execute tests instead of + # building the actual lib and bin targets We just have to pass `--test` + # to rustc and it will do the right thing. We execute the tests and copy + # their log and the test executables to $out for later inspection. + test = + let + drv = testCrate.override (_: { + buildTests = true; + }); + # If the user hasn't set any pre/post commands, we don't want to + # insert empty lines. This means that any existing users of crate2nix + # don't get a spurious rebuild unless they set these explicitly. + testCommand = lib.concatStringsSep "\n" ( + lib.filter (s: s != "") [ + testPreRun + "$f $testCrateFlags 2>&1 | tee -a $out" + testPostRun + ] + ); + in + stdenvNoCC.mkDerivation { + name = "run-tests-${testCrate.name}"; + + inherit (crate) src; + + inherit testCrateFlags; + + buildInputs = testInputs; + + buildPhase = '' + set -e + export RUST_BACKTRACE=1 + + # build outputs + testRoot=target/debug + mkdir -p $testRoot + + # executables of the crate + # we copy to prevent std::env::current_exe() to resolve to a store location + for i in ${crate}/bin/*; do + cp "$i" "$testRoot" + done + chmod +w -R . + + # test harness executables are suffixed with a hash, like cargo does + # this allows to prevent name collision with the main + # executables of the crate + hash=$(basename $out) + for file in ${drv}/tests/*; do + f=$testRoot/$(basename $file)-$hash + cp $file $f + ${testCommand} + done + ''; + }; + in + runCommand "${crate.name}-linked" + { + inherit (crate) outputs crateName; + passthru = (crate.passthru or { }) // { + inherit test; + }; + } + ( + lib.optionalString (stdenv.buildPlatform.canExecute stdenv.hostPlatform) '' + echo tested by ${test} + '' + + '' + ${lib.concatMapStringsSep "\n" (output: "ln -s ${crate.${output}} ${"$"}${output}") crate.outputs} + '' + ); # A restricted overridable version of builtRustCratesWithFeatures. buildRustCrateWithFeatures = - { packageId - , features ? rootFeatures - , crateOverrides ? defaultCrateOverrides - , buildRustCrateForPkgsFunc ? null - , runTests ? false - , testCrateFlags ? [ ] - , testInputs ? [ ] - , # Any command to run immediatelly before a test is executed. - testPreRun ? "" - , # Any command run immediatelly after a test is executed. - testPostRun ? "" - , + { + packageId, + features ? rootFeatures, + crateOverrides ? defaultCrateOverrides, + buildRustCrateForPkgsFunc ? null, + runTests ? false, + testCrateFlags ? [ ], + testInputs ? [ ], + # Any command to run immediatelly before a test is executed. + testPreRun ? "", + # Any command run immediatelly after a test is executed. + testPostRun ? "", }: lib.makeOverridable ( - { features - , crateOverrides - , runTests - , testCrateFlags - , testInputs - , testPreRun - , testPostRun - , + { + features, + crateOverrides, + runTests, + testCrateFlags, + testInputs, + testPreRun, + testPostRun, }: let buildRustCrateForPkgsFuncOverriden = if buildRustCrateForPkgsFunc != null then buildRustCrateForPkgsFunc else - ( - if crateOverrides == pkgs.defaultCrateOverrides then - buildRustCrateForPkgs - else - pkgs: - (buildRustCrateForPkgs pkgs).override { - defaultCrateOverrides = crateOverrides; - } - ); + pkgs: + (buildRustCrateForPkgs pkgs).override { + defaultCrateOverrides = crateOverrides; + }; builtRustCrates = builtRustCratesWithFeatures { inherit packageId features; buildRustCrateForPkgsFunc = buildRustCrateForPkgsFuncOverriden; @@ -1618,17 +1619,16 @@ rec { testDrv = builtTestRustCrates.crates.${packageId}; derivation = if runTests then - crateWithTest - { - crate = drv; - testCrate = testDrv; - inherit - testCrateFlags - testInputs - testPreRun - testPostRun - ; - } + crateWithTest { + crate = drv; + testCrate = testDrv; + inherit + testCrateFlags + testInputs + testPreRun + testPostRun + ; + } else drv; in @@ -1651,159 +1651,157 @@ rec { for the corresponding crate. */ builtRustCratesWithFeatures = - { packageId - , features - , crateConfigs ? crates - , buildRustCrateForPkgsFunc - , runTests - , makeTarget ? makeDefaultTarget - , + { + packageId, + features, + crateConfigs ? crates, + buildRustCrateForPkgsFunc, + runTests, + makeTarget ? makeDefaultTarget, }@args: - assert (builtins.isAttrs crateConfigs); - assert (builtins.isString packageId); - assert (builtins.isList features); - assert (builtins.isAttrs (makeTarget stdenv.hostPlatform)); - assert (builtins.isBool runTests); - let - rootPackageId = packageId; - mergedFeatures = mergePackageFeatures ( - args + assert (builtins.isAttrs crateConfigs); + assert (builtins.isString packageId); + assert (builtins.isList features); + assert (builtins.isAttrs (makeTarget stdenv.hostPlatform)); + assert (builtins.isBool runTests); + let + rootPackageId = packageId; + mergedFeatures = mergePackageFeatures ( + args + // { + inherit rootPackageId; + target = makeTarget stdenv.hostPlatform // { + test = runTests; + }; + } + ); + # Memoize built packages so that reappearing packages are only built once. + builtByPackageIdByPkgs = mkBuiltByPackageIdByPkgs pkgs; + mkBuiltByPackageIdByPkgs = + pkgs: + let + self = { + crates = lib.mapAttrs ( + packageId: value: buildByPackageIdForPkgsImpl self pkgs packageId + ) crateConfigs; + target = makeTarget stdenv.hostPlatform; + build = mkBuiltByPackageIdByPkgs buildPackages; + }; + in + self; + buildByPackageIdForPkgsImpl = + self: pkgs: packageId: + let + features = mergedFeatures."${packageId}" or [ ]; + crateConfig' = crateConfigs."${packageId}"; + crateConfig = builtins.removeAttrs crateConfig' [ + "resolvedDefaultFeatures" + "devDependencies" + ]; + devDependencies = lib.optionals (runTests && packageId == rootPackageId) ( + crateConfig'.devDependencies or [ ] + ); + dependencies = dependencyDerivations { + inherit features; + inherit (self) target; + buildByPackageId = + depPackageId: + # proc_macro crates must be compiled for the build architecture + if crateConfigs.${depPackageId}.procMacro or false then + self.build.crates.${depPackageId} + else + self.crates.${depPackageId}; + dependencies = (crateConfig.dependencies or [ ]) ++ devDependencies; + }; + buildDependencies = dependencyDerivations { + inherit features; + inherit (self.build) target; + buildByPackageId = depPackageId: self.build.crates.${depPackageId}; + dependencies = crateConfig.buildDependencies or [ ]; + }; + dependenciesWithRenames = + let + buildDeps = filterEnabledDependencies { + inherit features; + inherit (self) target; + dependencies = crateConfig.dependencies or [ ] ++ devDependencies; + }; + hostDeps = filterEnabledDependencies { + inherit features; + inherit (self.build) target; + dependencies = crateConfig.buildDependencies or [ ]; + }; + in + lib.filter (d: d ? "rename") (hostDeps ++ buildDeps); + # Crate renames have the form: + # + # { + # crate_name = [ + # { version = "1.2.3"; rename = "crate_name01"; } + # ]; + # # ... + # } + crateRenames = + let + grouped = lib.groupBy (dependency: dependency.name) dependenciesWithRenames; + versionAndRename = + dep: + let + package = crateConfigs."${dep.packageId}"; + in + { + inherit (dep) rename; + inherit (package) version; + }; + in + lib.mapAttrs (name: builtins.map versionAndRename) grouped; + in + buildRustCrateForPkgsFunc pkgs ( + crateConfig // { - inherit rootPackageId; - target = makeTarget stdenv.hostPlatform // { - test = runTests; - }; + src = + crateConfig.src or (pkgs.fetchurl rec { + name = "${crateConfig.crateName}-${crateConfig.version}.tar.gz"; + # https://www.pietroalbini.org/blog/downloading-crates-io/ + # Not rate-limited, CDN URL. + url = "https://static.crates.io/crates/${crateConfig.crateName}/${crateConfig.crateName}-${crateConfig.version}.crate"; + sha256 = + assert (lib.assertMsg (crateConfig ? sha256) "Missing sha256 for ${name}"); + crateConfig.sha256; + }); + extraRustcOpts = + lib.lists.optional (targetFeatures != [ ]) + "-C target-feature=${lib.concatMapStringsSep "," (x: "+${x}") targetFeatures}"; + inherit + features + dependencies + buildDependencies + crateRenames + release + ; } ); - # Memoize built packages so that reappearing packages are only built once. - builtByPackageIdByPkgs = mkBuiltByPackageIdByPkgs pkgs; - mkBuiltByPackageIdByPkgs = - pkgs: - let - self = { - crates = lib.mapAttrs - ( - packageId: value: buildByPackageIdForPkgsImpl self pkgs packageId - ) - crateConfigs; - target = makeTarget stdenv.hostPlatform; - build = mkBuiltByPackageIdByPkgs pkgs.buildPackages; - }; - in - self; - buildByPackageIdForPkgsImpl = - self: pkgs: packageId: - let - features = mergedFeatures."${packageId}" or [ ]; - crateConfig' = crateConfigs."${packageId}"; - crateConfig = builtins.removeAttrs crateConfig' [ - "resolvedDefaultFeatures" - "devDependencies" - ]; - devDependencies = lib.optionals (runTests && packageId == rootPackageId) ( - crateConfig'.devDependencies or [ ] - ); - dependencies = dependencyDerivations { - inherit features; - inherit (self) target; - buildByPackageId = - depPackageId: - # proc_macro crates must be compiled for the build architecture - if crateConfigs.${depPackageId}.procMacro or false then - self.build.crates.${depPackageId} - else - self.crates.${depPackageId}; - dependencies = (crateConfig.dependencies or [ ]) ++ devDependencies; - }; - buildDependencies = dependencyDerivations { - inherit features; - inherit (self.build) target; - buildByPackageId = depPackageId: self.build.crates.${depPackageId}; - dependencies = crateConfig.buildDependencies or [ ]; - }; - dependenciesWithRenames = - let - buildDeps = filterEnabledDependencies { - inherit features; - inherit (self) target; - dependencies = crateConfig.dependencies or [ ] ++ devDependencies; - }; - hostDeps = filterEnabledDependencies { - inherit features; - inherit (self.build) target; - dependencies = crateConfig.buildDependencies or [ ]; - }; - in - lib.filter (d: d ? "rename") (hostDeps ++ buildDeps); - # Crate renames have the form: - # - # { - # crate_name = [ - # { version = "1.2.3"; rename = "crate_name01"; } - # ]; - # # ... - # } - crateRenames = - let - grouped = lib.groupBy (dependency: dependency.name) dependenciesWithRenames; - versionAndRename = - dep: - let - package = crateConfigs."${dep.packageId}"; - in - { - inherit (dep) rename; - inherit (package) version; - }; - in - lib.mapAttrs (name: builtins.map versionAndRename) grouped; - in - buildRustCrateForPkgsFunc pkgs ( - crateConfig - // { - src = - crateConfig.src or (pkgs.fetchurl rec { - name = "${crateConfig.crateName}-${crateConfig.version}.tar.gz"; - # https://www.pietroalbini.org/blog/downloading-crates-io/ - # Not rate-limited, CDN URL. - url = "https://static.crates.io/crates/${crateConfig.crateName}/${crateConfig.crateName}-${crateConfig.version}.crate"; - sha256 = - assert (lib.assertMsg (crateConfig ? sha256) "Missing sha256 for ${name}"); - crateConfig.sha256; - }); - extraRustcOpts = - lib.lists.optional (targetFeatures != [ ]) - "-C target-feature=${lib.concatMapStringsSep "," (x: "+${x}") targetFeatures}"; - inherit - features - dependencies - buildDependencies - crateRenames - release - ; - } - ); - in - builtByPackageIdByPkgs; + in + builtByPackageIdByPkgs; # Returns the actual derivations for the given dependencies. dependencyDerivations = - { buildByPackageId - , features - , dependencies - , target - , + { + buildByPackageId, + features, + dependencies, + target, }: - assert (builtins.isList features); - assert (builtins.isList dependencies); - assert (builtins.isAttrs target); - let - enabledDependencies = filterEnabledDependencies { - inherit dependencies features target; - }; - depDerivation = dependency: buildByPackageId dependency.packageId; - in - map depDerivation enabledDependencies; + assert (builtins.isList features); + assert (builtins.isList dependencies); + assert (builtins.isAttrs target); + let + enabledDependencies = filterEnabledDependencies { + inherit dependencies features target; + }; + depDerivation = dependency: buildByPackageId dependency.packageId; + in + map depDerivation enabledDependencies; /* Returns a sanitized version of val with all values substituted that cannot @@ -1822,39 +1820,39 @@ rec { # Returns various tools to debug a crate. debugCrate = - { packageId - , target ? makeDefaultTarget stdenv.hostPlatform - , + { + packageId, + target ? makeDefaultTarget stdenv.hostPlatform, }: - assert (builtins.isString packageId); - let - debug = rec { - # The built tree as passed to buildRustCrate. - buildTree = buildRustCrateWithFeatures { - buildRustCrateForPkgsFunc = _: lib.id; - inherit packageId; - }; - sanitizedBuildTree = sanitizeForJson buildTree; - dependencyTree = sanitizeForJson (buildRustCrateWithFeatures { - buildRustCrateForPkgsFunc = _: crate: { - "01_crateName" = crate.crateName or false; - "02_features" = crate.features or [ ]; - "03_dependencies" = crate.dependencies or [ ]; - }; - inherit packageId; - }); - mergedPackageFeatures = mergePackageFeatures { - features = rootFeatures; - inherit packageId target; - }; - diffedDefaultPackageFeatures = diffDefaultPackageFeatures { - inherit packageId target; + assert (builtins.isString packageId); + let + debug = rec { + # The built tree as passed to buildRustCrate. + buildTree = buildRustCrateWithFeatures { + buildRustCrateForPkgsFunc = _: lib.id; + inherit packageId; + }; + sanitizedBuildTree = sanitizeForJson buildTree; + dependencyTree = sanitizeForJson (buildRustCrateWithFeatures { + buildRustCrateForPkgsFunc = _: crate: { + "01_crateName" = crate.crateName or false; + "02_features" = crate.features or [ ]; + "03_dependencies" = crate.dependencies or [ ]; }; + inherit packageId; + }); + mergedPackageFeatures = mergePackageFeatures { + features = rootFeatures; + inherit packageId target; + }; + diffedDefaultPackageFeatures = diffDefaultPackageFeatures { + inherit packageId target; }; - in - { - internal = debug; }; + in + { + internal = debug; + }; /* Returns differences between cargo default features and crate2nix default @@ -1863,41 +1861,39 @@ rec { This is useful for verifying the feature resolution in crate2nix. */ diffDefaultPackageFeatures = - { crateConfigs ? crates - , packageId - , target - , + { + crateConfigs ? crates, + packageId, + target, }: - assert (builtins.isAttrs crateConfigs); - let - prefixValues = prefix: lib.mapAttrs (n: v: { "${prefix}" = v; }); - mergedFeatures = prefixValues "crate2nix" (mergePackageFeatures { - inherit crateConfigs packageId target; - features = [ "default" ]; - }); - configs = prefixValues "cargo" crateConfigs; - combined = lib.foldAttrs (a: b: a // b) { } [ - mergedFeatures - configs - ]; - onlyInCargo = builtins.attrNames ( - lib.filterAttrs (n: v: !(v ? "crate2nix") && (v ? "cargo")) combined - ); - onlyInCrate2Nix = builtins.attrNames ( - lib.filterAttrs (n: v: (v ? "crate2nix") && !(v ? "cargo")) combined - ); - differentFeatures = lib.filterAttrs - ( - n: v: - (v ? "crate2nix") - && (v ? "cargo") - && (v.crate2nix.features or [ ]) != (v."cargo".resolved_default_features or [ ]) - ) - combined; - in - builtins.toJSON { - inherit onlyInCargo onlyInCrate2Nix differentFeatures; - }; + assert (builtins.isAttrs crateConfigs); + let + prefixValues = prefix: lib.mapAttrs (n: v: { "${prefix}" = v; }); + mergedFeatures = prefixValues "crate2nix" (mergePackageFeatures { + inherit crateConfigs packageId target; + features = [ "default" ]; + }); + configs = prefixValues "cargo" crateConfigs; + combined = lib.foldAttrs (a: b: a // b) { } [ + mergedFeatures + configs + ]; + onlyInCargo = builtins.attrNames ( + lib.filterAttrs (n: v: !(v ? "crate2nix") && (v ? "cargo")) combined + ); + onlyInCrate2Nix = builtins.attrNames ( + lib.filterAttrs (n: v: (v ? "crate2nix") && !(v ? "cargo")) combined + ); + differentFeatures = lib.filterAttrs ( + n: v: + (v ? "crate2nix") + && (v ? "cargo") + && (v.crate2nix.features or [ ]) != (v."cargo".resolved_default_features or [ ]) + ) combined; + in + builtins.toJSON { + inherit onlyInCargo onlyInCrate2Nix differentFeatures; + }; /* Returns an attrset mapping packageId to the list of enabled features. @@ -1906,112 +1902,111 @@ rec { corresponding feature sets are merged. Features in rust are additive. */ mergePackageFeatures = - { crateConfigs ? crates - , packageId - , rootPackageId ? packageId - , features ? rootFeatures - , dependencyPath ? [ crates.${packageId}.crateName ] - , featuresByPackageId ? { } - , target - , # Adds devDependencies to the crate with rootPackageId. - runTests ? false - , ... + { + crateConfigs ? crates, + packageId, + rootPackageId ? packageId, + features ? rootFeatures, + dependencyPath ? [ crates.${packageId}.crateName ], + featuresByPackageId ? { }, + target, + # Adds devDependencies to the crate with rootPackageId. + runTests ? false, + ... }@args: - assert (builtins.isAttrs crateConfigs); - assert (builtins.isString packageId); - assert (builtins.isString rootPackageId); - assert (builtins.isList features); - assert (builtins.isList dependencyPath); - assert (builtins.isAttrs featuresByPackageId); - assert (builtins.isAttrs target); - assert (builtins.isBool runTests); - let - crateConfig = crateConfigs."${packageId}" or (builtins.throw "Package not found: ${packageId}"); - expandedFeatures = expandFeatures (crateConfig.features or { }) features; - enabledFeatures = enableFeatures (crateConfig.dependencies or [ ]) expandedFeatures; - depWithResolvedFeatures = - dependency: - let - inherit (dependency) packageId; - features = dependencyFeatures enabledFeatures dependency; - in - { - inherit packageId features; + assert (builtins.isAttrs crateConfigs); + assert (builtins.isString packageId); + assert (builtins.isString rootPackageId); + assert (builtins.isList features); + assert (builtins.isList dependencyPath); + assert (builtins.isAttrs featuresByPackageId); + assert (builtins.isAttrs target); + assert (builtins.isBool runTests); + let + crateConfig = crateConfigs."${packageId}" or (builtins.throw "Package not found: ${packageId}"); + expandedFeatures = expandFeatures (crateConfig.features or { }) features; + enabledFeatures = enableFeatures (crateConfig.dependencies or [ ]) expandedFeatures; + depWithResolvedFeatures = + dependency: + let + inherit (dependency) packageId; + features = dependencyFeatures enabledFeatures dependency; + in + { + inherit packageId features; + }; + resolveDependencies = + cache: path: dependencies: + assert (builtins.isAttrs cache); + assert (builtins.isList dependencies); + let + enabledDependencies = filterEnabledDependencies { + inherit dependencies target; + features = enabledFeatures; }; - resolveDependencies = - cache: path: dependencies: - assert (builtins.isAttrs cache); - assert (builtins.isList dependencies); - let - enabledDependencies = filterEnabledDependencies { - inherit dependencies target; - features = enabledFeatures; - }; - directDependencies = map depWithResolvedFeatures enabledDependencies; - foldOverCache = op: lib.foldl op cache directDependencies; - in - foldOverCache ( - cache: - { packageId, features }: - let - cacheFeatures = cache.${packageId} or [ ]; - combinedFeatures = sortedUnique (cacheFeatures ++ features); - in - if cache ? ${packageId} && cache.${packageId} == combinedFeatures then - cache - else - mergePackageFeatures { - features = combinedFeatures; - featuresByPackageId = cache; - inherit - crateConfigs - packageId - target - runTests - rootPackageId - ; - } - ); - cacheWithSelf = + directDependencies = map depWithResolvedFeatures enabledDependencies; + foldOverCache = op: lib.foldl op cache directDependencies; + in + foldOverCache ( + cache: + { packageId, features }: let - cacheFeatures = featuresByPackageId.${packageId} or [ ]; - combinedFeatures = sortedUnique (cacheFeatures ++ enabledFeatures); + cacheFeatures = cache.${packageId} or [ ]; + combinedFeatures = sortedUnique (cacheFeatures ++ features); in - featuresByPackageId - // { - "${packageId}" = combinedFeatures; - }; - cacheWithDependencies = resolveDependencies cacheWithSelf "dep" ( - crateConfig.dependencies or [ ] - ++ lib.optionals (runTests && packageId == rootPackageId) (crateConfig.devDependencies or [ ]) - ); - cacheWithAll = resolveDependencies cacheWithDependencies "build" ( - crateConfig.buildDependencies or [ ] + if cache ? ${packageId} && cache.${packageId} == combinedFeatures then + cache + else + mergePackageFeatures { + features = combinedFeatures; + featuresByPackageId = cache; + inherit + crateConfigs + packageId + target + runTests + rootPackageId + ; + } ); - in - cacheWithAll; + cacheWithSelf = + let + cacheFeatures = featuresByPackageId.${packageId} or [ ]; + combinedFeatures = sortedUnique (cacheFeatures ++ enabledFeatures); + in + featuresByPackageId + // { + "${packageId}" = combinedFeatures; + }; + cacheWithDependencies = resolveDependencies cacheWithSelf "dep" ( + crateConfig.dependencies or [ ] + ++ lib.optionals (runTests && packageId == rootPackageId) (crateConfig.devDependencies or [ ]) + ); + cacheWithAll = resolveDependencies cacheWithDependencies "build" ( + crateConfig.buildDependencies or [ ] + ); + in + cacheWithAll; # Returns the enabled dependencies given the enabled features. filterEnabledDependencies = - { dependencies - , features - , target - , + { + dependencies, + features, + target, }: - assert (builtins.isList dependencies); - assert (builtins.isList features); - assert (builtins.isAttrs target); + assert (builtins.isList dependencies); + assert (builtins.isList features); + assert (builtins.isAttrs target); - lib.filter - ( - dep: - let - targetFunc = dep.target or (features: true); - in - targetFunc { inherit features target; } - && (!(dep.optional or false) || builtins.any (doesFeatureEnableDependency dep) features) - ) - dependencies; + lib.filter ( + dep: + let + targetFunc = dep.target or (features: true); + in + targetFunc { inherit features target; } + && (!(dep.optional or false) || builtins.any (doesFeatureEnableDependency dep) features) + ) dependencies; # Returns whether the given feature should enable the given dependency. doesFeatureEnableDependency = @@ -2033,32 +2028,32 @@ rec { */ expandFeatures = featureMap: inputFeatures: - assert (builtins.isAttrs featureMap); - assert (builtins.isList inputFeatures); - let - expandFeaturesNoCycle = - oldSeen: inputFeatures: - if inputFeatures != [ ] then - let - # The feature we're currently expanding. - feature = builtins.head inputFeatures; - # All the features we've seen/expanded so far, including the one - # we're currently processing. - seen = oldSeen // { - ${feature} = 1; - }; - # Expand the feature but be careful to not re-introduce a feature - # that we've already seen: this can easily cause a cycle, see issue - # #209. - enables = builtins.filter (f: !(seen ? "${f}")) (featureMap."${feature}" or [ ]); - in - [ feature ] ++ (expandFeaturesNoCycle seen (builtins.tail inputFeatures ++ enables)) - # No more features left, nothing to expand to. - else - [ ]; - outFeatures = expandFeaturesNoCycle { } inputFeatures; - in - sortedUnique outFeatures; + assert (builtins.isAttrs featureMap); + assert (builtins.isList inputFeatures); + let + expandFeaturesNoCycle = + oldSeen: inputFeatures: + if inputFeatures != [ ] then + let + # The feature we're currently expanding. + feature = builtins.head inputFeatures; + # All the features we've seen/expanded so far, including the one + # we're currently processing. + seen = oldSeen // { + ${feature} = 1; + }; + # Expand the feature but be careful to not re-introduce a feature + # that we've already seen: this can easily cause a cycle, see issue + # #209. + enables = builtins.filter (f: !(seen ? "${f}")) (featureMap."${feature}" or [ ]); + in + [ feature ] ++ (expandFeaturesNoCycle seen (builtins.tail inputFeatures ++ enables)) + # No more features left, nothing to expand to. + else + [ ]; + outFeatures = expandFeaturesNoCycle { } inputFeatures; + in + sortedUnique outFeatures; /* This function adds optional dependencies as features if they are enabled @@ -2068,24 +2063,22 @@ rec { */ enableFeatures = dependencies: features: - assert (builtins.isList features); - assert (builtins.isList dependencies); - let - additionalFeatures = lib.concatMap - ( - dependency: - assert (builtins.isAttrs dependency); - let - enabled = builtins.any (doesFeatureEnableDependency dependency) features; - in - if (dependency.optional or false) && enabled then - [ (dependency.rename or dependency.name) ] - else - [ ] - ) - dependencies; - in - sortedUnique (features ++ additionalFeatures); + assert (builtins.isList features); + assert (builtins.isList dependencies); + let + additionalFeatures = lib.concatMap ( + dependency: + assert (builtins.isAttrs dependency); + let + enabled = builtins.any (doesFeatureEnableDependency dependency) features; + in + if (dependency.optional or false) && enabled then + [ (dependency.rename or dependency.name) ] + else + [ ] + ) dependencies; + in + sortedUnique (features ++ additionalFeatures); /* Returns the actual features for the given dependency. @@ -2094,39 +2087,39 @@ rec { */ dependencyFeatures = features: dependency: - assert (builtins.isList features); - assert (builtins.isAttrs dependency); - let - defaultOrNil = if dependency.usesDefaultFeatures or true then [ "default" ] else [ ]; - explicitFeatures = dependency.features or [ ]; - additionalDependencyFeatures = - let - name = dependency.rename or dependency.name; - stripPrefixMatch = prefix: s: if lib.hasPrefix prefix s then lib.removePrefix prefix s else null; - extractFeature = - feature: - lib.findFirst (f: f != null) null ( - map (prefix: stripPrefixMatch prefix feature) [ - (name + "/") - (name + "?/") - ] - ); - dependencyFeatures = lib.filter (f: f != null) (map extractFeature features); - in - dependencyFeatures; - in - defaultOrNil ++ explicitFeatures ++ additionalDependencyFeatures; + assert (builtins.isList features); + assert (builtins.isAttrs dependency); + let + defaultOrNil = if dependency.usesDefaultFeatures or true then [ "default" ] else [ ]; + explicitFeatures = dependency.features or [ ]; + additionalDependencyFeatures = + let + name = dependency.rename or dependency.name; + stripPrefixMatch = prefix: s: if lib.hasPrefix prefix s then lib.removePrefix prefix s else null; + extractFeature = + feature: + lib.findFirst (f: f != null) null ( + map (prefix: stripPrefixMatch prefix feature) [ + (name + "/") + (name + "?/") + ] + ); + dependencyFeatures = lib.filter (f: f != null) (map extractFeature features); + in + dependencyFeatures; + in + defaultOrNil ++ explicitFeatures ++ additionalDependencyFeatures; # Sorts and removes duplicates from a list of strings. sortedUnique = features: - assert (builtins.isList features); - assert (builtins.all builtins.isString features); - let - outFeaturesSet = lib.foldl (set: feature: set // { "${feature}" = 1; }) { } features; - outFeaturesUnique = builtins.attrNames outFeaturesSet; - in - builtins.sort (a: b: a < b) outFeaturesUnique; + assert (builtins.isList features); + assert (builtins.all builtins.isString features); + let + outFeaturesSet = lib.foldl (set: feature: set // { "${feature}" = 1; }) { } features; + outFeaturesUnique = builtins.attrNames outFeaturesSet; + in + builtins.sort (a: b: a < b) outFeaturesUnique; deprecationWarning = message: value: diff --git a/sample_projects/codegen/Cargo.nix b/sample_projects/codegen/Cargo.nix index 91bdd375..319ce546 100644 --- a/sample_projects/codegen/Cargo.nix +++ b/sample_projects/codegen/Cargo.nix @@ -4,10 +4,16 @@ # See https://github.com/kolloch/crate2nix for more info. { nixpkgs ? -, pkgs ? import nixpkgs { config = {}; } +, pkgs ? import nixpkgs { } , fetchurl ? pkgs.fetchurl +, fetchgit ? pkgs.fetchgit +, callPackage ? pkgs.callPackage , lib ? pkgs.lib +, buildPackages ? pkgs.buildPackages , stdenv ? pkgs.stdenv +, stdenvNoCC ? pkgs.stdenvNoCC +, symlinkJoin ? pkgs.symlinkJoin +, runCommand ? pkgs.runCommand , buildRustCrateForPkgs ? pkgs: pkgs.buildRustCrate # This is used as the `crateOverrides` argument for `buildRustCrate`. , defaultCrateOverrides ? pkgs.defaultCrateOverrides @@ -24,7 +30,7 @@ # Additional crate2nix configuration if it exists. , crateConfig ? if builtins.pathExists ./crate-config.nix - then pkgs.callPackage ./crate-config.nix {} + then callPackage ./crate-config.nix {} else {} }: @@ -65,7 +71,7 @@ rec { # A derivation that joins the outputs of all workspace members together. - allWorkspaceMembers = pkgs.symlinkJoin { + allWorkspaceMembers = symlinkJoin { name = "all-workspace-members"; paths = let members = builtins.attrValues workspaceMembers; @@ -249,7 +255,7 @@ rec { version = "0.9.7"; edition = "2018"; workspace_member = null; - src = pkgs.fetchgit { + src = fetchgit { url = "https://github.com/diwic/dbus-rs.git"; rev = "618262f5e3217cdd173d46d705bbac26c5141e21"; sha256 = "0gvhz2knd1k799l7ssh4rdm5qw0vhazzr3bxpmlgq7fhy6hjazrs"; @@ -287,7 +293,7 @@ rec { edition = "2018"; crateBin = []; workspace_member = null; - src = pkgs.fetchgit { + src = fetchgit { url = "https://github.com/diwic/dbus-rs.git"; rev = "618262f5e3217cdd173d46d705bbac26c5141e21"; sha256 = "0gvhz2knd1k799l7ssh4rdm5qw0vhazzr3bxpmlgq7fhy6hjazrs"; @@ -362,7 +368,7 @@ rec { edition = "2015"; links = "dbus"; workspace_member = null; - src = pkgs.fetchgit { + src = fetchgit { url = "https://github.com/diwic/dbus-rs.git"; rev = "618262f5e3217cdd173d46d705bbac26c5141e21"; sha256 = "0gvhz2knd1k799l7ssh4rdm5qw0vhazzr3bxpmlgq7fhy6hjazrs"; @@ -565,12 +571,12 @@ rec { }; registryUrl = - { registries - , url - , crate - , version - , sha256 - , + { + registries, + url, + crate, + version, + sha256, }: let dl = registries.${url}.dl; @@ -610,41 +616,41 @@ rec { let baseName = builtins.baseNameOf (builtins.toString name); in - !( - # Filter out git - baseName == ".gitignore" - || (type == "directory" && baseName == ".git") + !( + # Filter out git + baseName == ".gitignore" + || (type == "directory" && baseName == ".git") - # Filter out build results - || ( - type == "directory" - && ( - baseName == "target" - || baseName == "_site" - || baseName == ".sass-cache" - || baseName == ".jekyll-metadata" - || baseName == "build-artifacts" - ) + # Filter out build results + || ( + type == "directory" + && ( + baseName == "target" + || baseName == "_site" + || baseName == ".sass-cache" + || baseName == ".jekyll-metadata" + || baseName == "build-artifacts" ) + ) - # Filter out nix-build result symlinks - || (type == "symlink" && lib.hasPrefix "result" baseName) + # Filter out nix-build result symlinks + || (type == "symlink" && lib.hasPrefix "result" baseName) - # Filter out IDE config - || (type == "directory" && (baseName == ".idea" || baseName == ".vscode")) - || lib.hasSuffix ".iml" baseName + # Filter out IDE config + || (type == "directory" && (baseName == ".idea" || baseName == ".vscode")) + || lib.hasSuffix ".iml" baseName - # Filter out nix build files - || baseName == "Cargo.nix" + # Filter out nix build files + || baseName == "Cargo.nix" - # Filter out editor backup / swap files. - || lib.hasSuffix "~" baseName - || builtins.match "^\\.sw[a-z]$$" baseName != null - || builtins.match "^\\..*\\.sw[a-z]$$" baseName != null - || lib.hasSuffix ".tmp" baseName - || lib.hasSuffix ".bak" baseName - || baseName == "tests.nix" - ); + # Filter out editor backup / swap files. + || lib.hasSuffix "~" baseName + || builtins.match "^\\.sw[a-z]$$" baseName != null + || builtins.match "^\\..*\\.sw[a-z]$$" baseName != null + || lib.hasSuffix ".tmp" baseName + || lib.hasSuffix ".bak" baseName + || baseName == "tests.nix" + ); /* Returns a crate which depends on successful test execution @@ -654,131 +660,126 @@ rec { testInputs: list of packages that should be available during test execution */ crateWithTest = - { crate - , testCrate - , testCrateFlags - , testInputs - , testPreRun - , testPostRun - , + { + crate, + testCrate, + testCrateFlags, + testInputs, + testPreRun, + testPostRun, }: - assert builtins.typeOf testCrateFlags == "list"; - assert builtins.typeOf testInputs == "list"; - assert builtins.typeOf testPreRun == "string"; - assert builtins.typeOf testPostRun == "string"; - let - # override the `crate` so that it will build and execute tests instead of - # building the actual lib and bin targets We just have to pass `--test` - # to rustc and it will do the right thing. We execute the tests and copy - # their log and the test executables to $out for later inspection. - test = - let - drv = testCrate.override (_: { - buildTests = true; - }); - # If the user hasn't set any pre/post commands, we don't want to - # insert empty lines. This means that any existing users of crate2nix - # don't get a spurious rebuild unless they set these explicitly. - testCommand = pkgs.lib.concatStringsSep "\n" ( - pkgs.lib.filter (s: s != "") [ - testPreRun - "$f $testCrateFlags 2>&1 | tee -a $out" - testPostRun - ] - ); - in - pkgs.stdenvNoCC.mkDerivation { - name = "run-tests-${testCrate.name}"; + assert builtins.typeOf testCrateFlags == "list"; + assert builtins.typeOf testInputs == "list"; + assert builtins.typeOf testPreRun == "string"; + assert builtins.typeOf testPostRun == "string"; + let + # override the `crate` so that it will build and execute tests instead of + # building the actual lib and bin targets We just have to pass `--test` + # to rustc and it will do the right thing. We execute the tests and copy + # their log and the test executables to $out for later inspection. + test = + let + drv = testCrate.override (_: { + buildTests = true; + }); + # If the user hasn't set any pre/post commands, we don't want to + # insert empty lines. This means that any existing users of crate2nix + # don't get a spurious rebuild unless they set these explicitly. + testCommand = lib.concatStringsSep "\n" ( + lib.filter (s: s != "") [ + testPreRun + "$f $testCrateFlags 2>&1 | tee -a $out" + testPostRun + ] + ); + in + stdenvNoCC.mkDerivation { + name = "run-tests-${testCrate.name}"; - inherit (crate) src; + inherit (crate) src; - inherit testCrateFlags; + inherit testCrateFlags; - buildInputs = testInputs; + buildInputs = testInputs; - buildPhase = '' - set -e - export RUST_BACKTRACE=1 + buildPhase = '' + set -e + export RUST_BACKTRACE=1 - # build outputs - testRoot=target/debug - mkdir -p $testRoot + # build outputs + testRoot=target/debug + mkdir -p $testRoot - # executables of the crate - # we copy to prevent std::env::current_exe() to resolve to a store location - for i in ${crate}/bin/*; do - cp "$i" "$testRoot" - done - chmod +w -R . + # executables of the crate + # we copy to prevent std::env::current_exe() to resolve to a store location + for i in ${crate}/bin/*; do + cp "$i" "$testRoot" + done + chmod +w -R . - # test harness executables are suffixed with a hash, like cargo does - # this allows to prevent name collision with the main - # executables of the crate - hash=$(basename $out) - for file in ${drv}/tests/*; do - f=$testRoot/$(basename $file)-$hash - cp $file $f - ${testCommand} - done - ''; - }; - in - pkgs.runCommand "${crate.name}-linked" - { - inherit (crate) outputs crateName; - passthru = (crate.passthru or { }) // { - inherit test; - }; - } - ( - lib.optionalString (stdenv.buildPlatform.canExecute stdenv.hostPlatform) '' - echo tested by ${test} - '' - + '' - ${lib.concatMapStringsSep "\n" (output: "ln -s ${crate.${output}} ${"$"}${output}") crate.outputs} - '' - ); + # test harness executables are suffixed with a hash, like cargo does + # this allows to prevent name collision with the main + # executables of the crate + hash=$(basename $out) + for file in ${drv}/tests/*; do + f=$testRoot/$(basename $file)-$hash + cp $file $f + ${testCommand} + done + ''; + }; + in + runCommand "${crate.name}-linked" + { + inherit (crate) outputs crateName; + passthru = (crate.passthru or { }) // { + inherit test; + }; + } + ( + lib.optionalString (stdenv.buildPlatform.canExecute stdenv.hostPlatform) '' + echo tested by ${test} + '' + + '' + ${lib.concatMapStringsSep "\n" (output: "ln -s ${crate.${output}} ${"$"}${output}") crate.outputs} + '' + ); # A restricted overridable version of builtRustCratesWithFeatures. buildRustCrateWithFeatures = - { packageId - , features ? rootFeatures - , crateOverrides ? defaultCrateOverrides - , buildRustCrateForPkgsFunc ? null - , runTests ? false - , testCrateFlags ? [ ] - , testInputs ? [ ] - , # Any command to run immediatelly before a test is executed. - testPreRun ? "" - , # Any command run immediatelly after a test is executed. - testPostRun ? "" - , + { + packageId, + features ? rootFeatures, + crateOverrides ? defaultCrateOverrides, + buildRustCrateForPkgsFunc ? null, + runTests ? false, + testCrateFlags ? [ ], + testInputs ? [ ], + # Any command to run immediatelly before a test is executed. + testPreRun ? "", + # Any command run immediatelly after a test is executed. + testPostRun ? "", }: lib.makeOverridable ( - { features - , crateOverrides - , runTests - , testCrateFlags - , testInputs - , testPreRun - , testPostRun - , + { + features, + crateOverrides, + runTests, + testCrateFlags, + testInputs, + testPreRun, + testPostRun, }: let buildRustCrateForPkgsFuncOverriden = if buildRustCrateForPkgsFunc != null then buildRustCrateForPkgsFunc else - ( - if crateOverrides == pkgs.defaultCrateOverrides then - buildRustCrateForPkgs - else - pkgs: - (buildRustCrateForPkgs pkgs).override { - defaultCrateOverrides = crateOverrides; - } - ); + pkgs: + (buildRustCrateForPkgs pkgs).override { + defaultCrateOverrides = crateOverrides; + }; builtRustCrates = builtRustCratesWithFeatures { inherit packageId features; buildRustCrateForPkgsFunc = buildRustCrateForPkgsFuncOverriden; @@ -793,17 +794,16 @@ rec { testDrv = builtTestRustCrates.crates.${packageId}; derivation = if runTests then - crateWithTest - { - crate = drv; - testCrate = testDrv; - inherit - testCrateFlags - testInputs - testPreRun - testPostRun - ; - } + crateWithTest { + crate = drv; + testCrate = testDrv; + inherit + testCrateFlags + testInputs + testPreRun + testPostRun + ; + } else drv; in @@ -826,159 +826,157 @@ rec { for the corresponding crate. */ builtRustCratesWithFeatures = - { packageId - , features - , crateConfigs ? crates - , buildRustCrateForPkgsFunc - , runTests - , makeTarget ? makeDefaultTarget - , + { + packageId, + features, + crateConfigs ? crates, + buildRustCrateForPkgsFunc, + runTests, + makeTarget ? makeDefaultTarget, }@args: - assert (builtins.isAttrs crateConfigs); - assert (builtins.isString packageId); - assert (builtins.isList features); - assert (builtins.isAttrs (makeTarget stdenv.hostPlatform)); - assert (builtins.isBool runTests); - let - rootPackageId = packageId; - mergedFeatures = mergePackageFeatures ( - args + assert (builtins.isAttrs crateConfigs); + assert (builtins.isString packageId); + assert (builtins.isList features); + assert (builtins.isAttrs (makeTarget stdenv.hostPlatform)); + assert (builtins.isBool runTests); + let + rootPackageId = packageId; + mergedFeatures = mergePackageFeatures ( + args + // { + inherit rootPackageId; + target = makeTarget stdenv.hostPlatform // { + test = runTests; + }; + } + ); + # Memoize built packages so that reappearing packages are only built once. + builtByPackageIdByPkgs = mkBuiltByPackageIdByPkgs pkgs; + mkBuiltByPackageIdByPkgs = + pkgs: + let + self = { + crates = lib.mapAttrs ( + packageId: value: buildByPackageIdForPkgsImpl self pkgs packageId + ) crateConfigs; + target = makeTarget stdenv.hostPlatform; + build = mkBuiltByPackageIdByPkgs buildPackages; + }; + in + self; + buildByPackageIdForPkgsImpl = + self: pkgs: packageId: + let + features = mergedFeatures."${packageId}" or [ ]; + crateConfig' = crateConfigs."${packageId}"; + crateConfig = builtins.removeAttrs crateConfig' [ + "resolvedDefaultFeatures" + "devDependencies" + ]; + devDependencies = lib.optionals (runTests && packageId == rootPackageId) ( + crateConfig'.devDependencies or [ ] + ); + dependencies = dependencyDerivations { + inherit features; + inherit (self) target; + buildByPackageId = + depPackageId: + # proc_macro crates must be compiled for the build architecture + if crateConfigs.${depPackageId}.procMacro or false then + self.build.crates.${depPackageId} + else + self.crates.${depPackageId}; + dependencies = (crateConfig.dependencies or [ ]) ++ devDependencies; + }; + buildDependencies = dependencyDerivations { + inherit features; + inherit (self.build) target; + buildByPackageId = depPackageId: self.build.crates.${depPackageId}; + dependencies = crateConfig.buildDependencies or [ ]; + }; + dependenciesWithRenames = + let + buildDeps = filterEnabledDependencies { + inherit features; + inherit (self) target; + dependencies = crateConfig.dependencies or [ ] ++ devDependencies; + }; + hostDeps = filterEnabledDependencies { + inherit features; + inherit (self.build) target; + dependencies = crateConfig.buildDependencies or [ ]; + }; + in + lib.filter (d: d ? "rename") (hostDeps ++ buildDeps); + # Crate renames have the form: + # + # { + # crate_name = [ + # { version = "1.2.3"; rename = "crate_name01"; } + # ]; + # # ... + # } + crateRenames = + let + grouped = lib.groupBy (dependency: dependency.name) dependenciesWithRenames; + versionAndRename = + dep: + let + package = crateConfigs."${dep.packageId}"; + in + { + inherit (dep) rename; + inherit (package) version; + }; + in + lib.mapAttrs (name: builtins.map versionAndRename) grouped; + in + buildRustCrateForPkgsFunc pkgs ( + crateConfig // { - inherit rootPackageId; - target = makeTarget stdenv.hostPlatform // { - test = runTests; - }; + src = + crateConfig.src or (pkgs.fetchurl rec { + name = "${crateConfig.crateName}-${crateConfig.version}.tar.gz"; + # https://www.pietroalbini.org/blog/downloading-crates-io/ + # Not rate-limited, CDN URL. + url = "https://static.crates.io/crates/${crateConfig.crateName}/${crateConfig.crateName}-${crateConfig.version}.crate"; + sha256 = + assert (lib.assertMsg (crateConfig ? sha256) "Missing sha256 for ${name}"); + crateConfig.sha256; + }); + extraRustcOpts = + lib.lists.optional (targetFeatures != [ ]) + "-C target-feature=${lib.concatMapStringsSep "," (x: "+${x}") targetFeatures}"; + inherit + features + dependencies + buildDependencies + crateRenames + release + ; } ); - # Memoize built packages so that reappearing packages are only built once. - builtByPackageIdByPkgs = mkBuiltByPackageIdByPkgs pkgs; - mkBuiltByPackageIdByPkgs = - pkgs: - let - self = { - crates = lib.mapAttrs - ( - packageId: value: buildByPackageIdForPkgsImpl self pkgs packageId - ) - crateConfigs; - target = makeTarget stdenv.hostPlatform; - build = mkBuiltByPackageIdByPkgs pkgs.buildPackages; - }; - in - self; - buildByPackageIdForPkgsImpl = - self: pkgs: packageId: - let - features = mergedFeatures."${packageId}" or [ ]; - crateConfig' = crateConfigs."${packageId}"; - crateConfig = builtins.removeAttrs crateConfig' [ - "resolvedDefaultFeatures" - "devDependencies" - ]; - devDependencies = lib.optionals (runTests && packageId == rootPackageId) ( - crateConfig'.devDependencies or [ ] - ); - dependencies = dependencyDerivations { - inherit features; - inherit (self) target; - buildByPackageId = - depPackageId: - # proc_macro crates must be compiled for the build architecture - if crateConfigs.${depPackageId}.procMacro or false then - self.build.crates.${depPackageId} - else - self.crates.${depPackageId}; - dependencies = (crateConfig.dependencies or [ ]) ++ devDependencies; - }; - buildDependencies = dependencyDerivations { - inherit features; - inherit (self.build) target; - buildByPackageId = depPackageId: self.build.crates.${depPackageId}; - dependencies = crateConfig.buildDependencies or [ ]; - }; - dependenciesWithRenames = - let - buildDeps = filterEnabledDependencies { - inherit features; - inherit (self) target; - dependencies = crateConfig.dependencies or [ ] ++ devDependencies; - }; - hostDeps = filterEnabledDependencies { - inherit features; - inherit (self.build) target; - dependencies = crateConfig.buildDependencies or [ ]; - }; - in - lib.filter (d: d ? "rename") (hostDeps ++ buildDeps); - # Crate renames have the form: - # - # { - # crate_name = [ - # { version = "1.2.3"; rename = "crate_name01"; } - # ]; - # # ... - # } - crateRenames = - let - grouped = lib.groupBy (dependency: dependency.name) dependenciesWithRenames; - versionAndRename = - dep: - let - package = crateConfigs."${dep.packageId}"; - in - { - inherit (dep) rename; - inherit (package) version; - }; - in - lib.mapAttrs (name: builtins.map versionAndRename) grouped; - in - buildRustCrateForPkgsFunc pkgs ( - crateConfig - // { - src = - crateConfig.src or (pkgs.fetchurl rec { - name = "${crateConfig.crateName}-${crateConfig.version}.tar.gz"; - # https://www.pietroalbini.org/blog/downloading-crates-io/ - # Not rate-limited, CDN URL. - url = "https://static.crates.io/crates/${crateConfig.crateName}/${crateConfig.crateName}-${crateConfig.version}.crate"; - sha256 = - assert (lib.assertMsg (crateConfig ? sha256) "Missing sha256 for ${name}"); - crateConfig.sha256; - }); - extraRustcOpts = - lib.lists.optional (targetFeatures != [ ]) - "-C target-feature=${lib.concatMapStringsSep "," (x: "+${x}") targetFeatures}"; - inherit - features - dependencies - buildDependencies - crateRenames - release - ; - } - ); - in - builtByPackageIdByPkgs; + in + builtByPackageIdByPkgs; # Returns the actual derivations for the given dependencies. dependencyDerivations = - { buildByPackageId - , features - , dependencies - , target - , + { + buildByPackageId, + features, + dependencies, + target, }: - assert (builtins.isList features); - assert (builtins.isList dependencies); - assert (builtins.isAttrs target); - let - enabledDependencies = filterEnabledDependencies { - inherit dependencies features target; - }; - depDerivation = dependency: buildByPackageId dependency.packageId; - in - map depDerivation enabledDependencies; + assert (builtins.isList features); + assert (builtins.isList dependencies); + assert (builtins.isAttrs target); + let + enabledDependencies = filterEnabledDependencies { + inherit dependencies features target; + }; + depDerivation = dependency: buildByPackageId dependency.packageId; + in + map depDerivation enabledDependencies; /* Returns a sanitized version of val with all values substituted that cannot @@ -997,39 +995,39 @@ rec { # Returns various tools to debug a crate. debugCrate = - { packageId - , target ? makeDefaultTarget stdenv.hostPlatform - , + { + packageId, + target ? makeDefaultTarget stdenv.hostPlatform, }: - assert (builtins.isString packageId); - let - debug = rec { - # The built tree as passed to buildRustCrate. - buildTree = buildRustCrateWithFeatures { - buildRustCrateForPkgsFunc = _: lib.id; - inherit packageId; - }; - sanitizedBuildTree = sanitizeForJson buildTree; - dependencyTree = sanitizeForJson (buildRustCrateWithFeatures { - buildRustCrateForPkgsFunc = _: crate: { - "01_crateName" = crate.crateName or false; - "02_features" = crate.features or [ ]; - "03_dependencies" = crate.dependencies or [ ]; - }; - inherit packageId; - }); - mergedPackageFeatures = mergePackageFeatures { - features = rootFeatures; - inherit packageId target; - }; - diffedDefaultPackageFeatures = diffDefaultPackageFeatures { - inherit packageId target; + assert (builtins.isString packageId); + let + debug = rec { + # The built tree as passed to buildRustCrate. + buildTree = buildRustCrateWithFeatures { + buildRustCrateForPkgsFunc = _: lib.id; + inherit packageId; + }; + sanitizedBuildTree = sanitizeForJson buildTree; + dependencyTree = sanitizeForJson (buildRustCrateWithFeatures { + buildRustCrateForPkgsFunc = _: crate: { + "01_crateName" = crate.crateName or false; + "02_features" = crate.features or [ ]; + "03_dependencies" = crate.dependencies or [ ]; }; + inherit packageId; + }); + mergedPackageFeatures = mergePackageFeatures { + features = rootFeatures; + inherit packageId target; + }; + diffedDefaultPackageFeatures = diffDefaultPackageFeatures { + inherit packageId target; }; - in - { - internal = debug; }; + in + { + internal = debug; + }; /* Returns differences between cargo default features and crate2nix default @@ -1038,41 +1036,39 @@ rec { This is useful for verifying the feature resolution in crate2nix. */ diffDefaultPackageFeatures = - { crateConfigs ? crates - , packageId - , target - , + { + crateConfigs ? crates, + packageId, + target, }: - assert (builtins.isAttrs crateConfigs); - let - prefixValues = prefix: lib.mapAttrs (n: v: { "${prefix}" = v; }); - mergedFeatures = prefixValues "crate2nix" (mergePackageFeatures { - inherit crateConfigs packageId target; - features = [ "default" ]; - }); - configs = prefixValues "cargo" crateConfigs; - combined = lib.foldAttrs (a: b: a // b) { } [ - mergedFeatures - configs - ]; - onlyInCargo = builtins.attrNames ( - lib.filterAttrs (n: v: !(v ? "crate2nix") && (v ? "cargo")) combined - ); - onlyInCrate2Nix = builtins.attrNames ( - lib.filterAttrs (n: v: (v ? "crate2nix") && !(v ? "cargo")) combined - ); - differentFeatures = lib.filterAttrs - ( - n: v: - (v ? "crate2nix") - && (v ? "cargo") - && (v.crate2nix.features or [ ]) != (v."cargo".resolved_default_features or [ ]) - ) - combined; - in - builtins.toJSON { - inherit onlyInCargo onlyInCrate2Nix differentFeatures; - }; + assert (builtins.isAttrs crateConfigs); + let + prefixValues = prefix: lib.mapAttrs (n: v: { "${prefix}" = v; }); + mergedFeatures = prefixValues "crate2nix" (mergePackageFeatures { + inherit crateConfigs packageId target; + features = [ "default" ]; + }); + configs = prefixValues "cargo" crateConfigs; + combined = lib.foldAttrs (a: b: a // b) { } [ + mergedFeatures + configs + ]; + onlyInCargo = builtins.attrNames ( + lib.filterAttrs (n: v: !(v ? "crate2nix") && (v ? "cargo")) combined + ); + onlyInCrate2Nix = builtins.attrNames ( + lib.filterAttrs (n: v: (v ? "crate2nix") && !(v ? "cargo")) combined + ); + differentFeatures = lib.filterAttrs ( + n: v: + (v ? "crate2nix") + && (v ? "cargo") + && (v.crate2nix.features or [ ]) != (v."cargo".resolved_default_features or [ ]) + ) combined; + in + builtins.toJSON { + inherit onlyInCargo onlyInCrate2Nix differentFeatures; + }; /* Returns an attrset mapping packageId to the list of enabled features. @@ -1081,112 +1077,111 @@ rec { corresponding feature sets are merged. Features in rust are additive. */ mergePackageFeatures = - { crateConfigs ? crates - , packageId - , rootPackageId ? packageId - , features ? rootFeatures - , dependencyPath ? [ crates.${packageId}.crateName ] - , featuresByPackageId ? { } - , target - , # Adds devDependencies to the crate with rootPackageId. - runTests ? false - , ... + { + crateConfigs ? crates, + packageId, + rootPackageId ? packageId, + features ? rootFeatures, + dependencyPath ? [ crates.${packageId}.crateName ], + featuresByPackageId ? { }, + target, + # Adds devDependencies to the crate with rootPackageId. + runTests ? false, + ... }@args: - assert (builtins.isAttrs crateConfigs); - assert (builtins.isString packageId); - assert (builtins.isString rootPackageId); - assert (builtins.isList features); - assert (builtins.isList dependencyPath); - assert (builtins.isAttrs featuresByPackageId); - assert (builtins.isAttrs target); - assert (builtins.isBool runTests); - let - crateConfig = crateConfigs."${packageId}" or (builtins.throw "Package not found: ${packageId}"); - expandedFeatures = expandFeatures (crateConfig.features or { }) features; - enabledFeatures = enableFeatures (crateConfig.dependencies or [ ]) expandedFeatures; - depWithResolvedFeatures = - dependency: - let - inherit (dependency) packageId; - features = dependencyFeatures enabledFeatures dependency; - in - { - inherit packageId features; + assert (builtins.isAttrs crateConfigs); + assert (builtins.isString packageId); + assert (builtins.isString rootPackageId); + assert (builtins.isList features); + assert (builtins.isList dependencyPath); + assert (builtins.isAttrs featuresByPackageId); + assert (builtins.isAttrs target); + assert (builtins.isBool runTests); + let + crateConfig = crateConfigs."${packageId}" or (builtins.throw "Package not found: ${packageId}"); + expandedFeatures = expandFeatures (crateConfig.features or { }) features; + enabledFeatures = enableFeatures (crateConfig.dependencies or [ ]) expandedFeatures; + depWithResolvedFeatures = + dependency: + let + inherit (dependency) packageId; + features = dependencyFeatures enabledFeatures dependency; + in + { + inherit packageId features; + }; + resolveDependencies = + cache: path: dependencies: + assert (builtins.isAttrs cache); + assert (builtins.isList dependencies); + let + enabledDependencies = filterEnabledDependencies { + inherit dependencies target; + features = enabledFeatures; }; - resolveDependencies = - cache: path: dependencies: - assert (builtins.isAttrs cache); - assert (builtins.isList dependencies); - let - enabledDependencies = filterEnabledDependencies { - inherit dependencies target; - features = enabledFeatures; - }; - directDependencies = map depWithResolvedFeatures enabledDependencies; - foldOverCache = op: lib.foldl op cache directDependencies; - in - foldOverCache ( - cache: - { packageId, features }: - let - cacheFeatures = cache.${packageId} or [ ]; - combinedFeatures = sortedUnique (cacheFeatures ++ features); - in - if cache ? ${packageId} && cache.${packageId} == combinedFeatures then - cache - else - mergePackageFeatures { - features = combinedFeatures; - featuresByPackageId = cache; - inherit - crateConfigs - packageId - target - runTests - rootPackageId - ; - } - ); - cacheWithSelf = + directDependencies = map depWithResolvedFeatures enabledDependencies; + foldOverCache = op: lib.foldl op cache directDependencies; + in + foldOverCache ( + cache: + { packageId, features }: let - cacheFeatures = featuresByPackageId.${packageId} or [ ]; - combinedFeatures = sortedUnique (cacheFeatures ++ enabledFeatures); + cacheFeatures = cache.${packageId} or [ ]; + combinedFeatures = sortedUnique (cacheFeatures ++ features); in - featuresByPackageId - // { - "${packageId}" = combinedFeatures; - }; - cacheWithDependencies = resolveDependencies cacheWithSelf "dep" ( - crateConfig.dependencies or [ ] - ++ lib.optionals (runTests && packageId == rootPackageId) (crateConfig.devDependencies or [ ]) - ); - cacheWithAll = resolveDependencies cacheWithDependencies "build" ( - crateConfig.buildDependencies or [ ] + if cache ? ${packageId} && cache.${packageId} == combinedFeatures then + cache + else + mergePackageFeatures { + features = combinedFeatures; + featuresByPackageId = cache; + inherit + crateConfigs + packageId + target + runTests + rootPackageId + ; + } ); - in - cacheWithAll; + cacheWithSelf = + let + cacheFeatures = featuresByPackageId.${packageId} or [ ]; + combinedFeatures = sortedUnique (cacheFeatures ++ enabledFeatures); + in + featuresByPackageId + // { + "${packageId}" = combinedFeatures; + }; + cacheWithDependencies = resolveDependencies cacheWithSelf "dep" ( + crateConfig.dependencies or [ ] + ++ lib.optionals (runTests && packageId == rootPackageId) (crateConfig.devDependencies or [ ]) + ); + cacheWithAll = resolveDependencies cacheWithDependencies "build" ( + crateConfig.buildDependencies or [ ] + ); + in + cacheWithAll; # Returns the enabled dependencies given the enabled features. filterEnabledDependencies = - { dependencies - , features - , target - , + { + dependencies, + features, + target, }: - assert (builtins.isList dependencies); - assert (builtins.isList features); - assert (builtins.isAttrs target); + assert (builtins.isList dependencies); + assert (builtins.isList features); + assert (builtins.isAttrs target); - lib.filter - ( - dep: - let - targetFunc = dep.target or (features: true); - in - targetFunc { inherit features target; } - && (!(dep.optional or false) || builtins.any (doesFeatureEnableDependency dep) features) - ) - dependencies; + lib.filter ( + dep: + let + targetFunc = dep.target or (features: true); + in + targetFunc { inherit features target; } + && (!(dep.optional or false) || builtins.any (doesFeatureEnableDependency dep) features) + ) dependencies; # Returns whether the given feature should enable the given dependency. doesFeatureEnableDependency = @@ -1208,32 +1203,32 @@ rec { */ expandFeatures = featureMap: inputFeatures: - assert (builtins.isAttrs featureMap); - assert (builtins.isList inputFeatures); - let - expandFeaturesNoCycle = - oldSeen: inputFeatures: - if inputFeatures != [ ] then - let - # The feature we're currently expanding. - feature = builtins.head inputFeatures; - # All the features we've seen/expanded so far, including the one - # we're currently processing. - seen = oldSeen // { - ${feature} = 1; - }; - # Expand the feature but be careful to not re-introduce a feature - # that we've already seen: this can easily cause a cycle, see issue - # #209. - enables = builtins.filter (f: !(seen ? "${f}")) (featureMap."${feature}" or [ ]); - in - [ feature ] ++ (expandFeaturesNoCycle seen (builtins.tail inputFeatures ++ enables)) - # No more features left, nothing to expand to. - else - [ ]; - outFeatures = expandFeaturesNoCycle { } inputFeatures; - in - sortedUnique outFeatures; + assert (builtins.isAttrs featureMap); + assert (builtins.isList inputFeatures); + let + expandFeaturesNoCycle = + oldSeen: inputFeatures: + if inputFeatures != [ ] then + let + # The feature we're currently expanding. + feature = builtins.head inputFeatures; + # All the features we've seen/expanded so far, including the one + # we're currently processing. + seen = oldSeen // { + ${feature} = 1; + }; + # Expand the feature but be careful to not re-introduce a feature + # that we've already seen: this can easily cause a cycle, see issue + # #209. + enables = builtins.filter (f: !(seen ? "${f}")) (featureMap."${feature}" or [ ]); + in + [ feature ] ++ (expandFeaturesNoCycle seen (builtins.tail inputFeatures ++ enables)) + # No more features left, nothing to expand to. + else + [ ]; + outFeatures = expandFeaturesNoCycle { } inputFeatures; + in + sortedUnique outFeatures; /* This function adds optional dependencies as features if they are enabled @@ -1243,24 +1238,22 @@ rec { */ enableFeatures = dependencies: features: - assert (builtins.isList features); - assert (builtins.isList dependencies); - let - additionalFeatures = lib.concatMap - ( - dependency: - assert (builtins.isAttrs dependency); - let - enabled = builtins.any (doesFeatureEnableDependency dependency) features; - in - if (dependency.optional or false) && enabled then - [ (dependency.rename or dependency.name) ] - else - [ ] - ) - dependencies; - in - sortedUnique (features ++ additionalFeatures); + assert (builtins.isList features); + assert (builtins.isList dependencies); + let + additionalFeatures = lib.concatMap ( + dependency: + assert (builtins.isAttrs dependency); + let + enabled = builtins.any (doesFeatureEnableDependency dependency) features; + in + if (dependency.optional or false) && enabled then + [ (dependency.rename or dependency.name) ] + else + [ ] + ) dependencies; + in + sortedUnique (features ++ additionalFeatures); /* Returns the actual features for the given dependency. @@ -1269,39 +1262,39 @@ rec { */ dependencyFeatures = features: dependency: - assert (builtins.isList features); - assert (builtins.isAttrs dependency); - let - defaultOrNil = if dependency.usesDefaultFeatures or true then [ "default" ] else [ ]; - explicitFeatures = dependency.features or [ ]; - additionalDependencyFeatures = - let - name = dependency.rename or dependency.name; - stripPrefixMatch = prefix: s: if lib.hasPrefix prefix s then lib.removePrefix prefix s else null; - extractFeature = - feature: - lib.findFirst (f: f != null) null ( - map (prefix: stripPrefixMatch prefix feature) [ - (name + "/") - (name + "?/") - ] - ); - dependencyFeatures = lib.filter (f: f != null) (map extractFeature features); - in - dependencyFeatures; - in - defaultOrNil ++ explicitFeatures ++ additionalDependencyFeatures; + assert (builtins.isList features); + assert (builtins.isAttrs dependency); + let + defaultOrNil = if dependency.usesDefaultFeatures or true then [ "default" ] else [ ]; + explicitFeatures = dependency.features or [ ]; + additionalDependencyFeatures = + let + name = dependency.rename or dependency.name; + stripPrefixMatch = prefix: s: if lib.hasPrefix prefix s then lib.removePrefix prefix s else null; + extractFeature = + feature: + lib.findFirst (f: f != null) null ( + map (prefix: stripPrefixMatch prefix feature) [ + (name + "/") + (name + "?/") + ] + ); + dependencyFeatures = lib.filter (f: f != null) (map extractFeature features); + in + dependencyFeatures; + in + defaultOrNil ++ explicitFeatures ++ additionalDependencyFeatures; # Sorts and removes duplicates from a list of strings. sortedUnique = features: - assert (builtins.isList features); - assert (builtins.all builtins.isString features); - let - outFeaturesSet = lib.foldl (set: feature: set // { "${feature}" = 1; }) { } features; - outFeaturesUnique = builtins.attrNames outFeaturesSet; - in - builtins.sort (a: b: a < b) outFeaturesUnique; + assert (builtins.isList features); + assert (builtins.all builtins.isString features); + let + outFeaturesSet = lib.foldl (set: feature: set // { "${feature}" = 1; }) { } features; + outFeaturesUnique = builtins.attrNames outFeaturesSet; + in + builtins.sort (a: b: a < b) outFeaturesUnique; deprecationWarning = message: value: diff --git a/sample_projects/sub_dir_crates/Cargo.nix b/sample_projects/sub_dir_crates/Cargo.nix index 5b16ffb6..26e24c5a 100644 --- a/sample_projects/sub_dir_crates/Cargo.nix +++ b/sample_projects/sub_dir_crates/Cargo.nix @@ -4,10 +4,16 @@ # See https://github.com/kolloch/crate2nix for more info. { nixpkgs ? -, pkgs ? import nixpkgs { config = {}; } +, pkgs ? import nixpkgs { } , fetchurl ? pkgs.fetchurl +, fetchgit ? pkgs.fetchgit +, callPackage ? pkgs.callPackage , lib ? pkgs.lib +, buildPackages ? pkgs.buildPackages , stdenv ? pkgs.stdenv +, stdenvNoCC ? pkgs.stdenvNoCC +, symlinkJoin ? pkgs.symlinkJoin +, runCommand ? pkgs.runCommand , buildRustCrateForPkgs ? pkgs: pkgs.buildRustCrate # This is used as the `crateOverrides` argument for `buildRustCrate`. , defaultCrateOverrides ? pkgs.defaultCrateOverrides @@ -24,7 +30,7 @@ # Additional crate2nix configuration if it exists. , crateConfig ? if builtins.pathExists ./crate-config.nix - then pkgs.callPackage ./crate-config.nix {} + then callPackage ./crate-config.nix {} else {} }: @@ -65,7 +71,7 @@ rec { # A derivation that joins the outputs of all workspace members together. - allWorkspaceMembers = pkgs.symlinkJoin { + allWorkspaceMembers = symlinkJoin { name = "all-workspace-members"; paths = let members = builtins.attrValues workspaceMembers; @@ -93,7 +99,7 @@ rec { version = "0.1.0"; edition = "2018"; workspace_member = null; - src = pkgs.fetchgit { + src = fetchgit { url = "https://github.com/kolloch/with_sub_crates.git"; rev = "f8ad2b98ff0eb5fea4962f55e3ced5b0b5afe973"; sha256 = "0nlw7rg28p6bya040cbipq4jdcdp4h3q9shdjygfk2xkva9bjl8w"; @@ -108,7 +114,7 @@ rec { version = "0.1.0"; edition = "2018"; workspace_member = null; - src = pkgs.fetchgit { + src = fetchgit { url = "https://github.com/kolloch/with_sub_crates.git"; rev = "f8ad2b98ff0eb5fea4962f55e3ced5b0b5afe973"; sha256 = "0nlw7rg28p6bya040cbipq4jdcdp4h3q9shdjygfk2xkva9bjl8w"; @@ -174,12 +180,12 @@ rec { }; registryUrl = - { registries - , url - , crate - , version - , sha256 - , + { + registries, + url, + crate, + version, + sha256, }: let dl = registries.${url}.dl; @@ -219,41 +225,41 @@ rec { let baseName = builtins.baseNameOf (builtins.toString name); in - !( - # Filter out git - baseName == ".gitignore" - || (type == "directory" && baseName == ".git") - - # Filter out build results - || ( - type == "directory" - && ( - baseName == "target" - || baseName == "_site" - || baseName == ".sass-cache" - || baseName == ".jekyll-metadata" - || baseName == "build-artifacts" - ) + !( + # Filter out git + baseName == ".gitignore" + || (type == "directory" && baseName == ".git") + + # Filter out build results + || ( + type == "directory" + && ( + baseName == "target" + || baseName == "_site" + || baseName == ".sass-cache" + || baseName == ".jekyll-metadata" + || baseName == "build-artifacts" ) + ) - # Filter out nix-build result symlinks - || (type == "symlink" && lib.hasPrefix "result" baseName) + # Filter out nix-build result symlinks + || (type == "symlink" && lib.hasPrefix "result" baseName) - # Filter out IDE config - || (type == "directory" && (baseName == ".idea" || baseName == ".vscode")) - || lib.hasSuffix ".iml" baseName + # Filter out IDE config + || (type == "directory" && (baseName == ".idea" || baseName == ".vscode")) + || lib.hasSuffix ".iml" baseName - # Filter out nix build files - || baseName == "Cargo.nix" + # Filter out nix build files + || baseName == "Cargo.nix" - # Filter out editor backup / swap files. - || lib.hasSuffix "~" baseName - || builtins.match "^\\.sw[a-z]$$" baseName != null - || builtins.match "^\\..*\\.sw[a-z]$$" baseName != null - || lib.hasSuffix ".tmp" baseName - || lib.hasSuffix ".bak" baseName - || baseName == "tests.nix" - ); + # Filter out editor backup / swap files. + || lib.hasSuffix "~" baseName + || builtins.match "^\\.sw[a-z]$$" baseName != null + || builtins.match "^\\..*\\.sw[a-z]$$" baseName != null + || lib.hasSuffix ".tmp" baseName + || lib.hasSuffix ".bak" baseName + || baseName == "tests.nix" + ); /* Returns a crate which depends on successful test execution @@ -263,131 +269,126 @@ rec { testInputs: list of packages that should be available during test execution */ crateWithTest = - { crate - , testCrate - , testCrateFlags - , testInputs - , testPreRun - , testPostRun - , + { + crate, + testCrate, + testCrateFlags, + testInputs, + testPreRun, + testPostRun, }: - assert builtins.typeOf testCrateFlags == "list"; - assert builtins.typeOf testInputs == "list"; - assert builtins.typeOf testPreRun == "string"; - assert builtins.typeOf testPostRun == "string"; - let - # override the `crate` so that it will build and execute tests instead of - # building the actual lib and bin targets We just have to pass `--test` - # to rustc and it will do the right thing. We execute the tests and copy - # their log and the test executables to $out for later inspection. - test = - let - drv = testCrate.override (_: { - buildTests = true; - }); - # If the user hasn't set any pre/post commands, we don't want to - # insert empty lines. This means that any existing users of crate2nix - # don't get a spurious rebuild unless they set these explicitly. - testCommand = pkgs.lib.concatStringsSep "\n" ( - pkgs.lib.filter (s: s != "") [ - testPreRun - "$f $testCrateFlags 2>&1 | tee -a $out" - testPostRun - ] - ); - in - pkgs.stdenvNoCC.mkDerivation { - name = "run-tests-${testCrate.name}"; - - inherit (crate) src; - - inherit testCrateFlags; - - buildInputs = testInputs; - - buildPhase = '' - set -e - export RUST_BACKTRACE=1 - - # build outputs - testRoot=target/debug - mkdir -p $testRoot - - # executables of the crate - # we copy to prevent std::env::current_exe() to resolve to a store location - for i in ${crate}/bin/*; do - cp "$i" "$testRoot" - done - chmod +w -R . - - # test harness executables are suffixed with a hash, like cargo does - # this allows to prevent name collision with the main - # executables of the crate - hash=$(basename $out) - for file in ${drv}/tests/*; do - f=$testRoot/$(basename $file)-$hash - cp $file $f - ${testCommand} - done - ''; - }; - in - pkgs.runCommand "${crate.name}-linked" - { - inherit (crate) outputs crateName; - passthru = (crate.passthru or { }) // { - inherit test; - }; - } - ( - lib.optionalString (stdenv.buildPlatform.canExecute stdenv.hostPlatform) '' - echo tested by ${test} - '' - + '' - ${lib.concatMapStringsSep "\n" (output: "ln -s ${crate.${output}} ${"$"}${output}") crate.outputs} - '' - ); + assert builtins.typeOf testCrateFlags == "list"; + assert builtins.typeOf testInputs == "list"; + assert builtins.typeOf testPreRun == "string"; + assert builtins.typeOf testPostRun == "string"; + let + # override the `crate` so that it will build and execute tests instead of + # building the actual lib and bin targets We just have to pass `--test` + # to rustc and it will do the right thing. We execute the tests and copy + # their log and the test executables to $out for later inspection. + test = + let + drv = testCrate.override (_: { + buildTests = true; + }); + # If the user hasn't set any pre/post commands, we don't want to + # insert empty lines. This means that any existing users of crate2nix + # don't get a spurious rebuild unless they set these explicitly. + testCommand = lib.concatStringsSep "\n" ( + lib.filter (s: s != "") [ + testPreRun + "$f $testCrateFlags 2>&1 | tee -a $out" + testPostRun + ] + ); + in + stdenvNoCC.mkDerivation { + name = "run-tests-${testCrate.name}"; + + inherit (crate) src; + + inherit testCrateFlags; + + buildInputs = testInputs; + + buildPhase = '' + set -e + export RUST_BACKTRACE=1 + + # build outputs + testRoot=target/debug + mkdir -p $testRoot + + # executables of the crate + # we copy to prevent std::env::current_exe() to resolve to a store location + for i in ${crate}/bin/*; do + cp "$i" "$testRoot" + done + chmod +w -R . + + # test harness executables are suffixed with a hash, like cargo does + # this allows to prevent name collision with the main + # executables of the crate + hash=$(basename $out) + for file in ${drv}/tests/*; do + f=$testRoot/$(basename $file)-$hash + cp $file $f + ${testCommand} + done + ''; + }; + in + runCommand "${crate.name}-linked" + { + inherit (crate) outputs crateName; + passthru = (crate.passthru or { }) // { + inherit test; + }; + } + ( + lib.optionalString (stdenv.buildPlatform.canExecute stdenv.hostPlatform) '' + echo tested by ${test} + '' + + '' + ${lib.concatMapStringsSep "\n" (output: "ln -s ${crate.${output}} ${"$"}${output}") crate.outputs} + '' + ); # A restricted overridable version of builtRustCratesWithFeatures. buildRustCrateWithFeatures = - { packageId - , features ? rootFeatures - , crateOverrides ? defaultCrateOverrides - , buildRustCrateForPkgsFunc ? null - , runTests ? false - , testCrateFlags ? [ ] - , testInputs ? [ ] - , # Any command to run immediatelly before a test is executed. - testPreRun ? "" - , # Any command run immediatelly after a test is executed. - testPostRun ? "" - , + { + packageId, + features ? rootFeatures, + crateOverrides ? defaultCrateOverrides, + buildRustCrateForPkgsFunc ? null, + runTests ? false, + testCrateFlags ? [ ], + testInputs ? [ ], + # Any command to run immediatelly before a test is executed. + testPreRun ? "", + # Any command run immediatelly after a test is executed. + testPostRun ? "", }: lib.makeOverridable ( - { features - , crateOverrides - , runTests - , testCrateFlags - , testInputs - , testPreRun - , testPostRun - , + { + features, + crateOverrides, + runTests, + testCrateFlags, + testInputs, + testPreRun, + testPostRun, }: let buildRustCrateForPkgsFuncOverriden = if buildRustCrateForPkgsFunc != null then buildRustCrateForPkgsFunc else - ( - if crateOverrides == pkgs.defaultCrateOverrides then - buildRustCrateForPkgs - else - pkgs: - (buildRustCrateForPkgs pkgs).override { - defaultCrateOverrides = crateOverrides; - } - ); + pkgs: + (buildRustCrateForPkgs pkgs).override { + defaultCrateOverrides = crateOverrides; + }; builtRustCrates = builtRustCratesWithFeatures { inherit packageId features; buildRustCrateForPkgsFunc = buildRustCrateForPkgsFuncOverriden; @@ -402,17 +403,16 @@ rec { testDrv = builtTestRustCrates.crates.${packageId}; derivation = if runTests then - crateWithTest - { - crate = drv; - testCrate = testDrv; - inherit - testCrateFlags - testInputs - testPreRun - testPostRun - ; - } + crateWithTest { + crate = drv; + testCrate = testDrv; + inherit + testCrateFlags + testInputs + testPreRun + testPostRun + ; + } else drv; in @@ -435,159 +435,157 @@ rec { for the corresponding crate. */ builtRustCratesWithFeatures = - { packageId - , features - , crateConfigs ? crates - , buildRustCrateForPkgsFunc - , runTests - , makeTarget ? makeDefaultTarget - , + { + packageId, + features, + crateConfigs ? crates, + buildRustCrateForPkgsFunc, + runTests, + makeTarget ? makeDefaultTarget, }@args: - assert (builtins.isAttrs crateConfigs); - assert (builtins.isString packageId); - assert (builtins.isList features); - assert (builtins.isAttrs (makeTarget stdenv.hostPlatform)); - assert (builtins.isBool runTests); - let - rootPackageId = packageId; - mergedFeatures = mergePackageFeatures ( - args + assert (builtins.isAttrs crateConfigs); + assert (builtins.isString packageId); + assert (builtins.isList features); + assert (builtins.isAttrs (makeTarget stdenv.hostPlatform)); + assert (builtins.isBool runTests); + let + rootPackageId = packageId; + mergedFeatures = mergePackageFeatures ( + args + // { + inherit rootPackageId; + target = makeTarget stdenv.hostPlatform // { + test = runTests; + }; + } + ); + # Memoize built packages so that reappearing packages are only built once. + builtByPackageIdByPkgs = mkBuiltByPackageIdByPkgs pkgs; + mkBuiltByPackageIdByPkgs = + pkgs: + let + self = { + crates = lib.mapAttrs ( + packageId: value: buildByPackageIdForPkgsImpl self pkgs packageId + ) crateConfigs; + target = makeTarget stdenv.hostPlatform; + build = mkBuiltByPackageIdByPkgs buildPackages; + }; + in + self; + buildByPackageIdForPkgsImpl = + self: pkgs: packageId: + let + features = mergedFeatures."${packageId}" or [ ]; + crateConfig' = crateConfigs."${packageId}"; + crateConfig = builtins.removeAttrs crateConfig' [ + "resolvedDefaultFeatures" + "devDependencies" + ]; + devDependencies = lib.optionals (runTests && packageId == rootPackageId) ( + crateConfig'.devDependencies or [ ] + ); + dependencies = dependencyDerivations { + inherit features; + inherit (self) target; + buildByPackageId = + depPackageId: + # proc_macro crates must be compiled for the build architecture + if crateConfigs.${depPackageId}.procMacro or false then + self.build.crates.${depPackageId} + else + self.crates.${depPackageId}; + dependencies = (crateConfig.dependencies or [ ]) ++ devDependencies; + }; + buildDependencies = dependencyDerivations { + inherit features; + inherit (self.build) target; + buildByPackageId = depPackageId: self.build.crates.${depPackageId}; + dependencies = crateConfig.buildDependencies or [ ]; + }; + dependenciesWithRenames = + let + buildDeps = filterEnabledDependencies { + inherit features; + inherit (self) target; + dependencies = crateConfig.dependencies or [ ] ++ devDependencies; + }; + hostDeps = filterEnabledDependencies { + inherit features; + inherit (self.build) target; + dependencies = crateConfig.buildDependencies or [ ]; + }; + in + lib.filter (d: d ? "rename") (hostDeps ++ buildDeps); + # Crate renames have the form: + # + # { + # crate_name = [ + # { version = "1.2.3"; rename = "crate_name01"; } + # ]; + # # ... + # } + crateRenames = + let + grouped = lib.groupBy (dependency: dependency.name) dependenciesWithRenames; + versionAndRename = + dep: + let + package = crateConfigs."${dep.packageId}"; + in + { + inherit (dep) rename; + inherit (package) version; + }; + in + lib.mapAttrs (name: builtins.map versionAndRename) grouped; + in + buildRustCrateForPkgsFunc pkgs ( + crateConfig // { - inherit rootPackageId; - target = makeTarget stdenv.hostPlatform // { - test = runTests; - }; + src = + crateConfig.src or (pkgs.fetchurl rec { + name = "${crateConfig.crateName}-${crateConfig.version}.tar.gz"; + # https://www.pietroalbini.org/blog/downloading-crates-io/ + # Not rate-limited, CDN URL. + url = "https://static.crates.io/crates/${crateConfig.crateName}/${crateConfig.crateName}-${crateConfig.version}.crate"; + sha256 = + assert (lib.assertMsg (crateConfig ? sha256) "Missing sha256 for ${name}"); + crateConfig.sha256; + }); + extraRustcOpts = + lib.lists.optional (targetFeatures != [ ]) + "-C target-feature=${lib.concatMapStringsSep "," (x: "+${x}") targetFeatures}"; + inherit + features + dependencies + buildDependencies + crateRenames + release + ; } ); - # Memoize built packages so that reappearing packages are only built once. - builtByPackageIdByPkgs = mkBuiltByPackageIdByPkgs pkgs; - mkBuiltByPackageIdByPkgs = - pkgs: - let - self = { - crates = lib.mapAttrs - ( - packageId: value: buildByPackageIdForPkgsImpl self pkgs packageId - ) - crateConfigs; - target = makeTarget stdenv.hostPlatform; - build = mkBuiltByPackageIdByPkgs pkgs.buildPackages; - }; - in - self; - buildByPackageIdForPkgsImpl = - self: pkgs: packageId: - let - features = mergedFeatures."${packageId}" or [ ]; - crateConfig' = crateConfigs."${packageId}"; - crateConfig = builtins.removeAttrs crateConfig' [ - "resolvedDefaultFeatures" - "devDependencies" - ]; - devDependencies = lib.optionals (runTests && packageId == rootPackageId) ( - crateConfig'.devDependencies or [ ] - ); - dependencies = dependencyDerivations { - inherit features; - inherit (self) target; - buildByPackageId = - depPackageId: - # proc_macro crates must be compiled for the build architecture - if crateConfigs.${depPackageId}.procMacro or false then - self.build.crates.${depPackageId} - else - self.crates.${depPackageId}; - dependencies = (crateConfig.dependencies or [ ]) ++ devDependencies; - }; - buildDependencies = dependencyDerivations { - inherit features; - inherit (self.build) target; - buildByPackageId = depPackageId: self.build.crates.${depPackageId}; - dependencies = crateConfig.buildDependencies or [ ]; - }; - dependenciesWithRenames = - let - buildDeps = filterEnabledDependencies { - inherit features; - inherit (self) target; - dependencies = crateConfig.dependencies or [ ] ++ devDependencies; - }; - hostDeps = filterEnabledDependencies { - inherit features; - inherit (self.build) target; - dependencies = crateConfig.buildDependencies or [ ]; - }; - in - lib.filter (d: d ? "rename") (hostDeps ++ buildDeps); - # Crate renames have the form: - # - # { - # crate_name = [ - # { version = "1.2.3"; rename = "crate_name01"; } - # ]; - # # ... - # } - crateRenames = - let - grouped = lib.groupBy (dependency: dependency.name) dependenciesWithRenames; - versionAndRename = - dep: - let - package = crateConfigs."${dep.packageId}"; - in - { - inherit (dep) rename; - inherit (package) version; - }; - in - lib.mapAttrs (name: builtins.map versionAndRename) grouped; - in - buildRustCrateForPkgsFunc pkgs ( - crateConfig - // { - src = - crateConfig.src or (pkgs.fetchurl rec { - name = "${crateConfig.crateName}-${crateConfig.version}.tar.gz"; - # https://www.pietroalbini.org/blog/downloading-crates-io/ - # Not rate-limited, CDN URL. - url = "https://static.crates.io/crates/${crateConfig.crateName}/${crateConfig.crateName}-${crateConfig.version}.crate"; - sha256 = - assert (lib.assertMsg (crateConfig ? sha256) "Missing sha256 for ${name}"); - crateConfig.sha256; - }); - extraRustcOpts = - lib.lists.optional (targetFeatures != [ ]) - "-C target-feature=${lib.concatMapStringsSep "," (x: "+${x}") targetFeatures}"; - inherit - features - dependencies - buildDependencies - crateRenames - release - ; - } - ); - in - builtByPackageIdByPkgs; + in + builtByPackageIdByPkgs; # Returns the actual derivations for the given dependencies. dependencyDerivations = - { buildByPackageId - , features - , dependencies - , target - , + { + buildByPackageId, + features, + dependencies, + target, }: - assert (builtins.isList features); - assert (builtins.isList dependencies); - assert (builtins.isAttrs target); - let - enabledDependencies = filterEnabledDependencies { - inherit dependencies features target; - }; - depDerivation = dependency: buildByPackageId dependency.packageId; - in - map depDerivation enabledDependencies; + assert (builtins.isList features); + assert (builtins.isList dependencies); + assert (builtins.isAttrs target); + let + enabledDependencies = filterEnabledDependencies { + inherit dependencies features target; + }; + depDerivation = dependency: buildByPackageId dependency.packageId; + in + map depDerivation enabledDependencies; /* Returns a sanitized version of val with all values substituted that cannot @@ -606,39 +604,39 @@ rec { # Returns various tools to debug a crate. debugCrate = - { packageId - , target ? makeDefaultTarget stdenv.hostPlatform - , + { + packageId, + target ? makeDefaultTarget stdenv.hostPlatform, }: - assert (builtins.isString packageId); - let - debug = rec { - # The built tree as passed to buildRustCrate. - buildTree = buildRustCrateWithFeatures { - buildRustCrateForPkgsFunc = _: lib.id; - inherit packageId; - }; - sanitizedBuildTree = sanitizeForJson buildTree; - dependencyTree = sanitizeForJson (buildRustCrateWithFeatures { - buildRustCrateForPkgsFunc = _: crate: { - "01_crateName" = crate.crateName or false; - "02_features" = crate.features or [ ]; - "03_dependencies" = crate.dependencies or [ ]; - }; - inherit packageId; - }); - mergedPackageFeatures = mergePackageFeatures { - features = rootFeatures; - inherit packageId target; - }; - diffedDefaultPackageFeatures = diffDefaultPackageFeatures { - inherit packageId target; + assert (builtins.isString packageId); + let + debug = rec { + # The built tree as passed to buildRustCrate. + buildTree = buildRustCrateWithFeatures { + buildRustCrateForPkgsFunc = _: lib.id; + inherit packageId; + }; + sanitizedBuildTree = sanitizeForJson buildTree; + dependencyTree = sanitizeForJson (buildRustCrateWithFeatures { + buildRustCrateForPkgsFunc = _: crate: { + "01_crateName" = crate.crateName or false; + "02_features" = crate.features or [ ]; + "03_dependencies" = crate.dependencies or [ ]; }; + inherit packageId; + }); + mergedPackageFeatures = mergePackageFeatures { + features = rootFeatures; + inherit packageId target; + }; + diffedDefaultPackageFeatures = diffDefaultPackageFeatures { + inherit packageId target; }; - in - { - internal = debug; }; + in + { + internal = debug; + }; /* Returns differences between cargo default features and crate2nix default @@ -647,41 +645,39 @@ rec { This is useful for verifying the feature resolution in crate2nix. */ diffDefaultPackageFeatures = - { crateConfigs ? crates - , packageId - , target - , + { + crateConfigs ? crates, + packageId, + target, }: - assert (builtins.isAttrs crateConfigs); - let - prefixValues = prefix: lib.mapAttrs (n: v: { "${prefix}" = v; }); - mergedFeatures = prefixValues "crate2nix" (mergePackageFeatures { - inherit crateConfigs packageId target; - features = [ "default" ]; - }); - configs = prefixValues "cargo" crateConfigs; - combined = lib.foldAttrs (a: b: a // b) { } [ - mergedFeatures - configs - ]; - onlyInCargo = builtins.attrNames ( - lib.filterAttrs (n: v: !(v ? "crate2nix") && (v ? "cargo")) combined - ); - onlyInCrate2Nix = builtins.attrNames ( - lib.filterAttrs (n: v: (v ? "crate2nix") && !(v ? "cargo")) combined - ); - differentFeatures = lib.filterAttrs - ( - n: v: - (v ? "crate2nix") - && (v ? "cargo") - && (v.crate2nix.features or [ ]) != (v."cargo".resolved_default_features or [ ]) - ) - combined; - in - builtins.toJSON { - inherit onlyInCargo onlyInCrate2Nix differentFeatures; - }; + assert (builtins.isAttrs crateConfigs); + let + prefixValues = prefix: lib.mapAttrs (n: v: { "${prefix}" = v; }); + mergedFeatures = prefixValues "crate2nix" (mergePackageFeatures { + inherit crateConfigs packageId target; + features = [ "default" ]; + }); + configs = prefixValues "cargo" crateConfigs; + combined = lib.foldAttrs (a: b: a // b) { } [ + mergedFeatures + configs + ]; + onlyInCargo = builtins.attrNames ( + lib.filterAttrs (n: v: !(v ? "crate2nix") && (v ? "cargo")) combined + ); + onlyInCrate2Nix = builtins.attrNames ( + lib.filterAttrs (n: v: (v ? "crate2nix") && !(v ? "cargo")) combined + ); + differentFeatures = lib.filterAttrs ( + n: v: + (v ? "crate2nix") + && (v ? "cargo") + && (v.crate2nix.features or [ ]) != (v."cargo".resolved_default_features or [ ]) + ) combined; + in + builtins.toJSON { + inherit onlyInCargo onlyInCrate2Nix differentFeatures; + }; /* Returns an attrset mapping packageId to the list of enabled features. @@ -690,112 +686,111 @@ rec { corresponding feature sets are merged. Features in rust are additive. */ mergePackageFeatures = - { crateConfigs ? crates - , packageId - , rootPackageId ? packageId - , features ? rootFeatures - , dependencyPath ? [ crates.${packageId}.crateName ] - , featuresByPackageId ? { } - , target - , # Adds devDependencies to the crate with rootPackageId. - runTests ? false - , ... + { + crateConfigs ? crates, + packageId, + rootPackageId ? packageId, + features ? rootFeatures, + dependencyPath ? [ crates.${packageId}.crateName ], + featuresByPackageId ? { }, + target, + # Adds devDependencies to the crate with rootPackageId. + runTests ? false, + ... }@args: - assert (builtins.isAttrs crateConfigs); - assert (builtins.isString packageId); - assert (builtins.isString rootPackageId); - assert (builtins.isList features); - assert (builtins.isList dependencyPath); - assert (builtins.isAttrs featuresByPackageId); - assert (builtins.isAttrs target); - assert (builtins.isBool runTests); - let - crateConfig = crateConfigs."${packageId}" or (builtins.throw "Package not found: ${packageId}"); - expandedFeatures = expandFeatures (crateConfig.features or { }) features; - enabledFeatures = enableFeatures (crateConfig.dependencies or [ ]) expandedFeatures; - depWithResolvedFeatures = - dependency: - let - inherit (dependency) packageId; - features = dependencyFeatures enabledFeatures dependency; - in - { - inherit packageId features; + assert (builtins.isAttrs crateConfigs); + assert (builtins.isString packageId); + assert (builtins.isString rootPackageId); + assert (builtins.isList features); + assert (builtins.isList dependencyPath); + assert (builtins.isAttrs featuresByPackageId); + assert (builtins.isAttrs target); + assert (builtins.isBool runTests); + let + crateConfig = crateConfigs."${packageId}" or (builtins.throw "Package not found: ${packageId}"); + expandedFeatures = expandFeatures (crateConfig.features or { }) features; + enabledFeatures = enableFeatures (crateConfig.dependencies or [ ]) expandedFeatures; + depWithResolvedFeatures = + dependency: + let + inherit (dependency) packageId; + features = dependencyFeatures enabledFeatures dependency; + in + { + inherit packageId features; + }; + resolveDependencies = + cache: path: dependencies: + assert (builtins.isAttrs cache); + assert (builtins.isList dependencies); + let + enabledDependencies = filterEnabledDependencies { + inherit dependencies target; + features = enabledFeatures; }; - resolveDependencies = - cache: path: dependencies: - assert (builtins.isAttrs cache); - assert (builtins.isList dependencies); - let - enabledDependencies = filterEnabledDependencies { - inherit dependencies target; - features = enabledFeatures; - }; - directDependencies = map depWithResolvedFeatures enabledDependencies; - foldOverCache = op: lib.foldl op cache directDependencies; - in - foldOverCache ( - cache: - { packageId, features }: - let - cacheFeatures = cache.${packageId} or [ ]; - combinedFeatures = sortedUnique (cacheFeatures ++ features); - in - if cache ? ${packageId} && cache.${packageId} == combinedFeatures then - cache - else - mergePackageFeatures { - features = combinedFeatures; - featuresByPackageId = cache; - inherit - crateConfigs - packageId - target - runTests - rootPackageId - ; - } - ); - cacheWithSelf = + directDependencies = map depWithResolvedFeatures enabledDependencies; + foldOverCache = op: lib.foldl op cache directDependencies; + in + foldOverCache ( + cache: + { packageId, features }: let - cacheFeatures = featuresByPackageId.${packageId} or [ ]; - combinedFeatures = sortedUnique (cacheFeatures ++ enabledFeatures); + cacheFeatures = cache.${packageId} or [ ]; + combinedFeatures = sortedUnique (cacheFeatures ++ features); in - featuresByPackageId - // { - "${packageId}" = combinedFeatures; - }; - cacheWithDependencies = resolveDependencies cacheWithSelf "dep" ( - crateConfig.dependencies or [ ] - ++ lib.optionals (runTests && packageId == rootPackageId) (crateConfig.devDependencies or [ ]) - ); - cacheWithAll = resolveDependencies cacheWithDependencies "build" ( - crateConfig.buildDependencies or [ ] + if cache ? ${packageId} && cache.${packageId} == combinedFeatures then + cache + else + mergePackageFeatures { + features = combinedFeatures; + featuresByPackageId = cache; + inherit + crateConfigs + packageId + target + runTests + rootPackageId + ; + } ); - in - cacheWithAll; + cacheWithSelf = + let + cacheFeatures = featuresByPackageId.${packageId} or [ ]; + combinedFeatures = sortedUnique (cacheFeatures ++ enabledFeatures); + in + featuresByPackageId + // { + "${packageId}" = combinedFeatures; + }; + cacheWithDependencies = resolveDependencies cacheWithSelf "dep" ( + crateConfig.dependencies or [ ] + ++ lib.optionals (runTests && packageId == rootPackageId) (crateConfig.devDependencies or [ ]) + ); + cacheWithAll = resolveDependencies cacheWithDependencies "build" ( + crateConfig.buildDependencies or [ ] + ); + in + cacheWithAll; # Returns the enabled dependencies given the enabled features. filterEnabledDependencies = - { dependencies - , features - , target - , + { + dependencies, + features, + target, }: - assert (builtins.isList dependencies); - assert (builtins.isList features); - assert (builtins.isAttrs target); + assert (builtins.isList dependencies); + assert (builtins.isList features); + assert (builtins.isAttrs target); - lib.filter - ( - dep: - let - targetFunc = dep.target or (features: true); - in - targetFunc { inherit features target; } - && (!(dep.optional or false) || builtins.any (doesFeatureEnableDependency dep) features) - ) - dependencies; + lib.filter ( + dep: + let + targetFunc = dep.target or (features: true); + in + targetFunc { inherit features target; } + && (!(dep.optional or false) || builtins.any (doesFeatureEnableDependency dep) features) + ) dependencies; # Returns whether the given feature should enable the given dependency. doesFeatureEnableDependency = @@ -817,32 +812,32 @@ rec { */ expandFeatures = featureMap: inputFeatures: - assert (builtins.isAttrs featureMap); - assert (builtins.isList inputFeatures); - let - expandFeaturesNoCycle = - oldSeen: inputFeatures: - if inputFeatures != [ ] then - let - # The feature we're currently expanding. - feature = builtins.head inputFeatures; - # All the features we've seen/expanded so far, including the one - # we're currently processing. - seen = oldSeen // { - ${feature} = 1; - }; - # Expand the feature but be careful to not re-introduce a feature - # that we've already seen: this can easily cause a cycle, see issue - # #209. - enables = builtins.filter (f: !(seen ? "${f}")) (featureMap."${feature}" or [ ]); - in - [ feature ] ++ (expandFeaturesNoCycle seen (builtins.tail inputFeatures ++ enables)) - # No more features left, nothing to expand to. - else - [ ]; - outFeatures = expandFeaturesNoCycle { } inputFeatures; - in - sortedUnique outFeatures; + assert (builtins.isAttrs featureMap); + assert (builtins.isList inputFeatures); + let + expandFeaturesNoCycle = + oldSeen: inputFeatures: + if inputFeatures != [ ] then + let + # The feature we're currently expanding. + feature = builtins.head inputFeatures; + # All the features we've seen/expanded so far, including the one + # we're currently processing. + seen = oldSeen // { + ${feature} = 1; + }; + # Expand the feature but be careful to not re-introduce a feature + # that we've already seen: this can easily cause a cycle, see issue + # #209. + enables = builtins.filter (f: !(seen ? "${f}")) (featureMap."${feature}" or [ ]); + in + [ feature ] ++ (expandFeaturesNoCycle seen (builtins.tail inputFeatures ++ enables)) + # No more features left, nothing to expand to. + else + [ ]; + outFeatures = expandFeaturesNoCycle { } inputFeatures; + in + sortedUnique outFeatures; /* This function adds optional dependencies as features if they are enabled @@ -852,24 +847,22 @@ rec { */ enableFeatures = dependencies: features: - assert (builtins.isList features); - assert (builtins.isList dependencies); - let - additionalFeatures = lib.concatMap - ( - dependency: - assert (builtins.isAttrs dependency); - let - enabled = builtins.any (doesFeatureEnableDependency dependency) features; - in - if (dependency.optional or false) && enabled then - [ (dependency.rename or dependency.name) ] - else - [ ] - ) - dependencies; - in - sortedUnique (features ++ additionalFeatures); + assert (builtins.isList features); + assert (builtins.isList dependencies); + let + additionalFeatures = lib.concatMap ( + dependency: + assert (builtins.isAttrs dependency); + let + enabled = builtins.any (doesFeatureEnableDependency dependency) features; + in + if (dependency.optional or false) && enabled then + [ (dependency.rename or dependency.name) ] + else + [ ] + ) dependencies; + in + sortedUnique (features ++ additionalFeatures); /* Returns the actual features for the given dependency. @@ -878,39 +871,39 @@ rec { */ dependencyFeatures = features: dependency: - assert (builtins.isList features); - assert (builtins.isAttrs dependency); - let - defaultOrNil = if dependency.usesDefaultFeatures or true then [ "default" ] else [ ]; - explicitFeatures = dependency.features or [ ]; - additionalDependencyFeatures = - let - name = dependency.rename or dependency.name; - stripPrefixMatch = prefix: s: if lib.hasPrefix prefix s then lib.removePrefix prefix s else null; - extractFeature = - feature: - lib.findFirst (f: f != null) null ( - map (prefix: stripPrefixMatch prefix feature) [ - (name + "/") - (name + "?/") - ] - ); - dependencyFeatures = lib.filter (f: f != null) (map extractFeature features); - in - dependencyFeatures; - in - defaultOrNil ++ explicitFeatures ++ additionalDependencyFeatures; + assert (builtins.isList features); + assert (builtins.isAttrs dependency); + let + defaultOrNil = if dependency.usesDefaultFeatures or true then [ "default" ] else [ ]; + explicitFeatures = dependency.features or [ ]; + additionalDependencyFeatures = + let + name = dependency.rename or dependency.name; + stripPrefixMatch = prefix: s: if lib.hasPrefix prefix s then lib.removePrefix prefix s else null; + extractFeature = + feature: + lib.findFirst (f: f != null) null ( + map (prefix: stripPrefixMatch prefix feature) [ + (name + "/") + (name + "?/") + ] + ); + dependencyFeatures = lib.filter (f: f != null) (map extractFeature features); + in + dependencyFeatures; + in + defaultOrNil ++ explicitFeatures ++ additionalDependencyFeatures; # Sorts and removes duplicates from a list of strings. sortedUnique = features: - assert (builtins.isList features); - assert (builtins.all builtins.isString features); - let - outFeaturesSet = lib.foldl (set: feature: set // { "${feature}" = 1; }) { } features; - outFeaturesUnique = builtins.attrNames outFeaturesSet; - in - builtins.sort (a: b: a < b) outFeaturesUnique; + assert (builtins.isList features); + assert (builtins.all builtins.isString features); + let + outFeaturesSet = lib.foldl (set: feature: set // { "${feature}" = 1; }) { } features; + outFeaturesUnique = builtins.attrNames outFeaturesSet; + in + builtins.sort (a: b: a < b) outFeaturesUnique; deprecationWarning = message: value: