diff --git a/pkgs/build-support/rust/build-rust-crate/default.nix b/pkgs/build-support/rust/build-rust-crate/default.nix index a175ba9d7f6e0..d9ed26f1d94a7 100644 --- a/pkgs/build-support/rust/build-rust-crate/default.nix +++ b/pkgs/build-support/rust/build-rust-crate/default.nix @@ -13,14 +13,30 @@ let then "macos" else stdenv.hostPlatform.parsed.kernel.name; - # Create rustc arguments to link against the given list of dependencies and - # renames + # Create rustc arguments to link against the given list of dependencies + # and renames. + # + # See docs for crateRenames below. mkRustcDepArgs = dependencies: crateRenames: lib.concatMapStringsSep " " (dep: let - extern = lib.replaceStrings ["-"] ["_"] dep.libName; + normalizeName = lib.replaceStrings ["-"] ["_"]; + extern = normalizeName dep.libName; + # Find a choice that matches in name and optionally version. + findMatchOrUseExtern = choices: + lib.findFirst (choice: + (!(choice ? version) + || choice.version == dep.version or "")) + { rename = extern; } + choices; name = if lib.hasAttr dep.crateName crateRenames then - lib.strings.replaceStrings ["-"] ["_"] crateRenames.${dep.crateName} + let choices = crateRenames.${dep.crateName}; + in + normalizeName ( + if builtins.isList choices + then (findMatchOrUseExtern choices).rename + else choices + ) else extern; in (if lib.any (x: x == "lib" || x == "rlib") dep.crateType then @@ -42,11 +58,128 @@ let installCrate = import ./install-crate.nix { inherit stdenv; }; in -crate_: lib.makeOverridable ({ rust, release, verbose, features, buildInputs, crateOverrides, - dependencies, buildDependencies, crateRenames, - extraRustcOpts, buildTests, - preUnpack, postUnpack, prePatch, patches, postPatch, - preConfigure, postConfigure, preBuild, postBuild, preInstall, postInstall }: +/* The overridable pkgs.buildRustCrate function. + * + * Any unrecognized parameters will be passed as to + * the underlying stdenv.mkDerivation. + */ + crate_: lib.makeOverridable ( + # The rust compiler to use. + # + # Default: pkgs.rustc + { rust + # Whether to build a release version (`true`) or a debug + # version (`false`). Debug versions are faster to build + # but might be much slower at runtime. + , release + # Whether to print rustc invocations etc. + # + # Example: false + # Default: true + , verbose + # A list of rust/cargo features to enable while building the crate. + # Example: [ "std" "async" ] + , features + # Additional build inputs for building this crate. + # + # Example: [ pkgs.openssl ] + , buildInputs + # Allows to override the parameters to buildRustCrate + # for any rust dependency in the transitive build tree. + # + # Default: pkgs.defaultCrateOverrides + # + # Example: + # + # pkgs.defaultCrateOverrides // { + # hello = attrs: { buildInputs = [ openssl ]; }; + # } + , crateOverrides + # Rust library dependencies, i.e. other libaries that were built + # with buildRustCrate. + , dependencies + # Rust build dependencies, i.e. other libaries that were built + # with buildRustCrate and are used by a build script. + , buildDependencies + # Specify the "extern" name of a library if it differs from the library target. + # See above for an extended explanation. + # + # Default: no renames. + # + # Example: + # + # `crateRenames` supports two formats. + # + # The simple version is an attrset that maps the + # `crateName`s of the dependencies to their alternative + # names. + # + # ```nix + # { + # my_crate_name = "my_alternative_name"; + # # ... + # } + # ``` + # + # The extended version is also keyed by the `crateName`s but allows + # different names for different crate versions: + # + # ```nix + # { + # my_crate_name = [ + # { version = "1.2.3"; rename = "my_alternative_name01"; } + # { version = "3.2.3"; rename = "my_alternative_name03"; } + # ] + # # ... + # } + # ``` + # + # This roughly corresponds to the following snippet in Cargo.toml: + # + # ```toml + # [dependencies] + # my_alternative_name01 = { package = "my_crate_name", version = "0.1" } + # my_alternative_name03 = { package = "my_crate_name", version = "0.3" } + # ``` + # + # Dependencies which use the lib target name as extern name, do not need + # to be specified in the crateRenames, even if their crate name differs. + # + # Including multiple versions of a crate is very popular during + # ecosystem transitions, e.g. from futures 0.1 to futures 0.3. + , crateRenames + # A list of extra options to pass to rustc. + # + # Example: [ "-Z debuginfo=2" ] + # Default: [] + , extraRustcOpts + # Whether to enable building tests. + # Use true to enable. + # Default: false + , buildTests + # Passed to stdenv.mkDerivation. + , preUnpack + # Passed to stdenv.mkDerivation. + , postUnpack + # Passed to stdenv.mkDerivation. + , prePatch + # Passed to stdenv.mkDerivation. + , patches + # Passed to stdenv.mkDerivation. + , postPatch + # Passed to stdenv.mkDerivation. + , preConfigure + # Passed to stdenv.mkDerivation. + , postConfigure + # Passed to stdenv.mkDerivation. + , preBuild + # Passed to stdenv.mkDerivation. + , postBuild + # Passed to stdenv.mkDerivation. + , preInstall + # Passed to stdenv.mkDerivation. + , postInstall + }: let crate = crate_ // (lib.attrByPath [ crate_.crateName ] (attr: {}) crateOverrides crate_); dependencies_ = dependencies; @@ -88,6 +221,7 @@ stdenv.mkDerivation (rec { src = crate.src or (fetchCrate { inherit (crate) crateName version sha256; }); name = "rust_${crate.crateName}-${crate.version}${lib.optionalString buildTests_ "-test"}"; + version = crate.version; depsBuildBuild = [ rust stdenv.cc cargo jq ]; buildInputs = (crate.buildInputs or []) ++ buildInputs_; dependencies = map lib.getLib dependencies_; diff --git a/pkgs/build-support/rust/build-rust-crate/test/default.nix b/pkgs/build-support/rust/build-rust-crate/test/default.nix index fba938f623727..4783129bad9a9 100644 --- a/pkgs/build-support/rust/build-rust-crate/test/default.nix +++ b/pkgs/build-support/rust/build-rust-crate/test/default.nix @@ -197,6 +197,51 @@ let dependencies = [ (mkCrate { crateName = "foo"; libName = "foolib"; src = mkLib "src/lib.rs"; }) ]; crateRenames = { "foo" = "foo_renamed"; }; }; + crateBinRenameMultiVersion = let + crateWithVersion = version: mkCrate { + crateName = "my_lib"; + inherit version; + src = mkFile "src/lib.rs" '' + pub const version: &str = "${version}"; + ''; + }; + depCrate01 = crateWithVersion "0.1.2"; + depCrate02 = crateWithVersion "0.2.1"; + in { + crateName = "my_bin"; + src = symlinkJoin { + name = "my_bin_src"; + paths = [ + (mkFile "src/main.rs" '' + #[test] + fn my_lib_01() { assert_eq!(lib01::version, "0.1.2"); } + + #[test] + fn my_lib_02() { assert_eq!(lib02::version, "0.2.1"); } + + fn main() { } + '') + ]; + }; + dependencies = [ depCrate01 depCrate02 ]; + crateRenames = { + "my_lib" = [ + { + version = "0.1.2"; + rename = "lib01"; + } + { + version = "0.2.1"; + rename = "lib02"; + } + ]; + }; + buildTests = true; + expectedTestOutputs = [ + "test my_lib_01 ... ok" + "test my_lib_02 ... ok" + ]; + }; rustLibTestsDefault = { src = mkTestFile "src/lib.rs" "baz"; buildTests = true;