diff options
Diffstat (limited to 'nixpkgs/pkgs/build-support/rust/build-rust-crate/default.nix')
-rw-r--r-- | nixpkgs/pkgs/build-support/rust/build-rust-crate/default.nix | 321 |
1 files changed, 321 insertions, 0 deletions
diff --git a/nixpkgs/pkgs/build-support/rust/build-rust-crate/default.nix b/nixpkgs/pkgs/build-support/rust/build-rust-crate/default.nix new file mode 100644 index 000000000000..d559aba16165 --- /dev/null +++ b/nixpkgs/pkgs/build-support/rust/build-rust-crate/default.nix @@ -0,0 +1,321 @@ +# Code for buildRustCrate, a Nix function that builds Rust code, just +# like Cargo, but using Nix instead. +# +# This can be useful for deploying packages with NixOps, and to share +# binary dependencies between projects. + +{ lib, stdenv, defaultCrateOverrides, fetchCrate, rustc, rust, cargo, jq }: + +let + # This doesn't appear to be officially documented anywhere yet. + # See https://github.com/rust-lang-nursery/rust-forge/issues/101. + target_os = if stdenv.hostPlatform.isDarwin + then "macos" + else stdenv.hostPlatform.parsed.kernel.name; + + # 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 + 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 + 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 + " --extern ${name}=${dep.lib}/lib/lib${extern}-${dep.metadata}.rlib" + else + " --extern ${name}=${dep.lib}/lib/lib${extern}-${dep.metadata}${stdenv.hostPlatform.extensions.sharedLibrary}") + ) dependencies; + + # Create feature arguments for rustc. + mkRustcFeatureArgs = lib.concatMapStringsSep " " (f: ''--cfg feature=\"${f}\"''); + + inherit (import ./log.nix { inherit lib; }) noisily echo_colored; + + configureCrate = import ./configure-crate.nix { + inherit lib stdenv echo_colored noisily mkRustcDepArgs mkRustcFeatureArgs; + }; + + buildCrate = import ./build-crate.nix { + inherit lib stdenv mkRustcDepArgs mkRustcFeatureArgs rust; + }; + + installCrate = import ./install-crate.nix { inherit stdenv; }; +in + +/* 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; + buildDependencies_ = buildDependencies; + processedAttrs = [ + "src" "buildInputs" "crateBin" "crateLib" "libName" "libPath" + "buildDependencies" "dependencies" "features" "crateRenames" + "crateName" "version" "build" "authors" "colors" "edition" + "buildTests" + ]; + extraDerivationAttrs = builtins.removeAttrs crate processedAttrs; + buildInputs_ = buildInputs; + extraRustcOpts_ = extraRustcOpts; + buildTests_ = buildTests; + + # crate2nix has a hack for the old bash based build script that did split + # entries at `,`. No we have to work around that hack. + # https://github.com/kolloch/crate2nix/blame/5b19c1b14e1b0e5522c3e44e300d0b332dc939e7/crate2nix/templates/build.nix.tera#L89 + crateBin = lib.filter (bin: !(bin ? name && bin.name == ",")) (crate.crateBin or []); + hasCrateBin = crate ? crateBin; +in +stdenv.mkDerivation (rec { + + inherit (crate) crateName; + inherit + preUnpack + postUnpack + prePatch + patches + postPatch + preConfigure + postConfigure + preBuild + postBuild + preInstall + postInstall + buildTests + ; + + 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_; + buildDependencies = map lib.getLib buildDependencies_; + + completeDeps = lib.unique (dependencies ++ lib.concatMap (dep: dep.completeDeps) dependencies); + completeBuildDeps = lib.unique ( + buildDependencies + ++ lib.concatMap (dep: dep.completeBuildDeps ++ dep.completeDeps) buildDependencies + ); + + # Create a list of features that are enabled by the crate itself and + # through the features argument of buildRustCrate. Exclude features + # with a forward slash, since they are passed through to dependencies. + crateFeatures = lib.optionals (crate ? features) + (builtins.filter (f: !lib.hasInfix "/" f) (crate.features ++ features)); + + libName = if crate ? libName then crate.libName else crate.crateName; + libPath = if crate ? libPath then crate.libPath else ""; + + # Seed the symbol hashes with something unique every time. + # https://doc.rust-lang.org/1.0.0/rustc/metadata/loader/index.html#frobbing-symbols + metadata = let + depsMetadata = lib.foldl' (str: dep: str + dep.metadata) "" (dependencies ++ buildDependencies); + hashedMetadata = builtins.hashString "sha256" + (crateName + "-" + crateVersion + "___" + toString (mkRustcFeatureArgs crateFeatures) + + "___" + depsMetadata); + in lib.substring 0 10 hashedMetadata; + + build = crate.build or ""; + # Either set to a concrete sub path to the crate root + # or use `null` for auto-detect. + workspace_member = crate.workspace_member or "."; + crateVersion = crate.version; + crateDescription = crate.description or ""; + crateAuthors = if crate ? authors && lib.isList crate.authors then crate.authors else []; + crateHomepage = crate.homepage or ""; + crateType = + if lib.attrByPath ["procMacro"] false crate then ["proc-macro"] else + if lib.attrByPath ["plugin"] false crate then ["dylib"] else + (crate.type or ["lib"]); + colors = lib.attrByPath [ "colors" ] "always" crate; + extraLinkFlags = lib.concatStringsSep " " (crate.extraLinkFlags or []); + edition = crate.edition or null; + extraRustcOpts = + lib.optionals (crate ? extraRustcOpts) crate.extraRustcOpts + ++ extraRustcOpts_ + ++ (lib.optional (edition != null) "--edition ${edition}"); + + + configurePhase = configureCrate { + inherit crateName buildDependencies completeDeps completeBuildDeps crateDescription + crateFeatures crateRenames libName build workspace_member release libPath crateVersion + extraLinkFlags extraRustcOpts + crateAuthors crateHomepage verbose colors target_os; + }; + buildPhase = buildCrate { + inherit crateName dependencies + crateFeatures crateRenames libName release libPath crateType + metadata hasCrateBin crateBin verbose colors + extraRustcOpts buildTests; + }; + installPhase = installCrate crateName metadata buildTests; + + # depending on the test setting we are either producing something with bins + # and libs or just test binaries + outputs = if buildTests then [ "out" ] else [ "out" "lib" ]; + outputDev = if buildTests then [ "out" ] else [ "lib" ]; + +} // extraDerivationAttrs +)) { + rust = rustc; + release = crate_.release or true; + verbose = crate_.verbose or true; + extraRustcOpts = []; + features = []; + buildInputs = []; + crateOverrides = defaultCrateOverrides; + preUnpack = crate_.preUnpack or ""; + postUnpack = crate_.postUnpack or ""; + prePatch = crate_.prePatch or ""; + patches = crate_.patches or []; + postPatch = crate_.postPatch or ""; + preConfigure = crate_.preConfigure or ""; + postConfigure = crate_.postConfigure or ""; + preBuild = crate_.preBuild or ""; + postBuild = crate_.postBuild or ""; + preInstall = crate_.preInstall or ""; + postInstall = crate_.postInstall or ""; + dependencies = crate_.dependencies or []; + buildDependencies = crate_.buildDependencies or []; + crateRenames = crate_.crateRenames or {}; + buildTests = crate_.buildTests or false; +} |