1 # Code for buildRustCrate, a Nix function that builds Rust code, just
2 # like Cargo, but using Nix instead.
4 # This can be useful for deploying packages with NixOps, and to share
5 # binary dependencies between projects.
9 , defaultCrateOverrides
19 # Create rustc arguments to link against the given list of dependencies
22 # See docs for crateRenames below.
23 mkRustcDepArgs = dependencies: crateRenames:
24 lib.concatMapStringsSep " "
27 normalizeName = lib.replaceStrings [ "-" ] [ "_" ];
28 extern = normalizeName dep.libName;
29 # Find a choice that matches in name and optionally version.
30 findMatchOrUseExtern = choices:
34 || choice.version == dep.version or ""))
38 if lib.hasAttr dep.crateName crateRenames then
39 let choices = crateRenames.${dep.crateName};
42 if builtins.isList choices
43 then (findMatchOrUseExtern choices).rename
48 opts = lib.optionalString (dep.stdlib or false) "noprelude:";
50 if lib.any (x: x == "lib" || x == "rlib") dep.crateType
51 then "${dep.metadata}.rlib"
52 # Adjust lib filename for crates of type proc-macro. Proc macros are compiled/run on the build platform architecture.
53 else if (lib.attrByPath [ "procMacro" ] false dep) then "${dep.metadata}${stdenv.buildPlatform.extensions.library}"
54 else "${dep.metadata}${stdenv.hostPlatform.extensions.library}";
56 " --extern ${opts}${name}=${dep.lib}/lib/lib${extern}-${filename}"
60 # Create feature arguments for rustc.
61 mkRustcFeatureArgs = lib.concatMapStringsSep " " (f: ''--cfg feature=\"${f}\"'');
63 # Whether we need to use unstable command line flags
65 # Currently just needed for standard library dependencies, which have a
66 # special "noprelude:" modifier. If in later versions of Rust this is
67 # stabilized we can account for that here, too, so we don't opt into
68 # instability unnecessarily.
69 needUnstableCLI = dependencies:
70 lib.any (dep: dep.stdlib or false) dependencies;
72 inherit (import ./log.nix { inherit lib; }) noisily echo_colored;
74 configureCrate = import ./configure-crate.nix {
75 inherit lib stdenv echo_colored noisily mkRustcDepArgs mkRustcFeatureArgs;
78 installCrate = import ./install-crate.nix { inherit stdenv; };
81 /* The overridable pkgs.buildRustCrate function.
83 * Any unrecognized parameters will be passed as to
84 * the underlying stdenv.mkDerivation.
86 crate_: lib.makeOverridable
88 # The rust compiler to use.
92 # The cargo package to use for getting some metadata.
96 # Whether to build a release version (`true`) or a debug
97 # version (`false`). Debug versions are faster to build
98 # but might be much slower at runtime.
100 # Whether to print rustc invocations etc.
105 # A list of rust/cargo features to enable while building the crate.
106 # Example: [ "std" "async" ]
108 # Additional native build inputs for building this crate.
110 # Additional build inputs for building this crate.
112 # Example: [ pkgs.openssl ]
114 # Allows to override the parameters to buildRustCrate
115 # for any rust dependency in the transitive build tree.
117 # Default: pkgs.defaultCrateOverrides
121 # pkgs.defaultCrateOverrides // {
122 # hello = attrs: { buildInputs = [ openssl ]; };
125 # Rust library dependencies, i.e. other libraries that were built
126 # with buildRustCrate.
128 # Rust build dependencies, i.e. other libraries that were built
129 # with buildRustCrate and are used by a build script.
131 # Specify the "extern" name of a library if it differs from the library target.
132 # See above for an extended explanation.
134 # Default: no renames.
138 # `crateRenames` supports two formats.
140 # The simple version is an attrset that maps the
141 # `crateName`s of the dependencies to their alternative
146 # my_crate_name = "my_alternative_name";
151 # The extended version is also keyed by the `crateName`s but allows
152 # different names for different crate versions:
157 # { version = "1.2.3"; rename = "my_alternative_name01"; }
158 # { version = "3.2.3"; rename = "my_alternative_name03"; }
164 # This roughly corresponds to the following snippet in Cargo.toml:
168 # my_alternative_name01 = { package = "my_crate_name", version = "0.1" }
169 # my_alternative_name03 = { package = "my_crate_name", version = "0.3" }
172 # Dependencies which use the lib target name as extern name, do not need
173 # to be specified in the crateRenames, even if their crate name differs.
175 # Including multiple versions of a crate is very popular during
176 # ecosystem transitions, e.g. from futures 0.1 to futures 0.3.
178 # A list of extra options to pass to rustc.
180 # Example: [ "-Z debuginfo=2" ]
183 # A list of extra options to pass to rustc when building a build.rs.
185 # Example: [ "-Z debuginfo=2" ]
187 , extraRustcOptsForBuildRs
188 # Whether to enable building tests.
189 # Use true to enable.
192 # Passed to stdenv.mkDerivation.
194 # Passed to stdenv.mkDerivation.
196 # Passed to stdenv.mkDerivation.
198 # Passed to stdenv.mkDerivation.
200 # Passed to stdenv.mkDerivation.
202 # Passed to stdenv.mkDerivation.
204 # Passed to stdenv.mkDerivation.
206 # Passed to stdenv.mkDerivation.
208 # Passed to stdenv.mkDerivation.
210 # Passed to stdenv.mkDerivation.
212 # Passed to stdenv.mkDerivation.
217 crate = crate_ // (lib.attrByPath [ crate_.crateName ] (attr: { }) crateOverrides crate_);
218 dependencies_ = dependencies;
219 buildDependencies_ = buildDependencies;
242 extraDerivationAttrs = builtins.removeAttrs crate processedAttrs;
243 nativeBuildInputs_ = nativeBuildInputs;
244 buildInputs_ = buildInputs;
245 extraRustcOpts_ = extraRustcOpts;
246 extraRustcOptsForBuildRs_ = extraRustcOptsForBuildRs;
247 buildTests_ = buildTests;
249 # crate2nix has a hack for the old bash based build script that did split
250 # entries at `,`. No we have to work around that hack.
251 # https://github.com/kolloch/crate2nix/blame/5b19c1b14e1b0e5522c3e44e300d0b332dc939e7/crate2nix/templates/build.nix.tera#L89
252 crateBin = lib.filter (bin: !(bin ? name && bin.name == ",")) (crate.crateBin or [ ]);
253 hasCrateBin = crate ? crateBin;
255 buildCrate = import ./build-crate.nix {
256 inherit lib stdenv mkRustcDepArgs mkRustcFeatureArgs needUnstableCLI;
260 stdenv.mkDerivation (rec {
262 inherit (crate) crateName;
278 src = crate.src or (fetchCrate { inherit (crate) crateName version sha256; });
279 name = "rust_${crate.crateName}-${crate.version}${lib.optionalString buildTests_ "-test"}";
280 version = crate.version;
281 depsBuildBuild = [ pkgsBuildBuild.stdenv.cc ];
282 nativeBuildInputs = [ rust cargo jq ]
283 ++ lib.optionals stdenv.hasCC [ stdenv.cc ]
284 ++ lib.optionals stdenv.buildPlatform.isDarwin [ libiconv ]
285 ++ (crate.nativeBuildInputs or [ ]) ++ nativeBuildInputs_;
286 buildInputs = lib.optionals stdenv.hostPlatform.isDarwin [ libiconv ] ++ (crate.buildInputs or [ ]) ++ buildInputs_;
287 dependencies = map lib.getLib dependencies_;
288 buildDependencies = map lib.getLib buildDependencies_;
290 completeDeps = lib.unique (dependencies ++ lib.concatMap (dep: dep.completeDeps) dependencies);
291 completeBuildDeps = lib.unique (
293 ++ lib.concatMap (dep: dep.completeBuildDeps ++ dep.completeDeps) buildDependencies
296 # Create a list of features that are enabled by the crate itself and
297 # through the features argument of buildRustCrate. Exclude features
298 # with a forward slash, since they are passed through to dependencies,
299 # and dep: features, since they're internal-only and do nothing except
300 # enable optional dependencies.
301 crateFeatures = lib.optionals (crate ? features)
303 (f: !(lib.hasInfix "/" f || lib.hasPrefix "dep:" f))
304 (crate.features ++ features)
307 libName = if crate ? libName then crate.libName else crate.crateName;
308 libPath = lib.optionalString (crate ? libPath) crate.libPath;
310 # Seed the symbol hashes with something unique every time.
311 # https://doc.rust-lang.org/1.0.0/rustc/metadata/loader/index.html#frobbing-symbols
314 depsMetadata = lib.foldl' (str: dep: str + dep.metadata) "" (dependencies ++ buildDependencies);
315 hashedMetadata = builtins.hashString "sha256"
316 (crateName + "-" + crateVersion + "___" + toString (mkRustcFeatureArgs crateFeatures) +
317 "___" + depsMetadata + "___" + stdenv.hostPlatform.rust.rustcTarget);
319 lib.substring 0 10 hashedMetadata;
321 build = crate.build or "";
322 # Either set to a concrete sub path to the crate root
323 # or use `null` for auto-detect.
324 workspace_member = crate.workspace_member or ".";
325 crateAuthors = if crate ? authors && lib.isList crate.authors then crate.authors else [ ];
326 crateDescription = crate.description or "";
327 crateHomepage = crate.homepage or "";
328 crateLicense = crate.license or "";
329 crateLicenseFile = crate.license-file or "";
330 crateLinks = crate.links or "";
331 crateReadme = crate.readme or "";
332 crateRepository = crate.repository or "";
333 crateRustVersion = crate.rust-version or "";
334 crateVersion = crate.version;
336 if lib.attrByPath [ "procMacro" ] false crate then [ "proc-macro" ] else
337 if lib.attrByPath [ "plugin" ] false crate then [ "dylib" ] else
338 (crate.type or [ "lib" ]);
339 colors = lib.attrByPath [ "colors" ] "always" crate;
340 extraLinkFlags = lib.concatStringsSep " " (crate.extraLinkFlags or [ ]);
341 edition = crate.edition or null;
342 codegenUnits = if crate ? codegenUnits then crate.codegenUnits else 1;
344 lib.optionals (crate ? extraRustcOpts) crate.extraRustcOpts
346 ++ (lib.optional (edition != null) "--edition ${edition}");
347 extraRustcOptsForBuildRs =
348 lib.optionals (crate ? extraRustcOptsForBuildRs) crate.extraRustcOptsForBuildRs
349 ++ extraRustcOptsForBuildRs_
350 ++ (lib.optional (edition != null) "--edition ${edition}");
353 configurePhase = configureCrate {
354 inherit crateName crateType buildDependencies completeDeps completeBuildDeps crateDescription
355 crateFeatures crateRenames libName build workspace_member release libPath crateVersion crateLinks
356 extraLinkFlags extraRustcOptsForBuildRs
357 crateLicense crateLicenseFile crateReadme crateRepository crateRustVersion
358 crateAuthors crateHomepage verbose colors codegenUnits;
360 buildPhase = buildCrate {
361 inherit crateName dependencies
362 crateFeatures crateRenames libName release libPath crateType
363 metadata hasCrateBin crateBin verbose colors
364 extraRustcOpts buildTests codegenUnits;
366 dontStrip = !release;
368 # We need to preserve metadata in .rlib, which might get stripped on macOS. See https://github.com/NixOS/nixpkgs/issues/218712
369 stripExclude = [ "*.rlib" ];
371 installPhase = installCrate crateName metadata buildTests;
373 # depending on the test setting we are either producing something with bins
374 # and libs or just test binaries
375 outputs = if buildTests then [ "out" ] else [ "out" "lib" ];
376 outputDev = if buildTests then [ "out" ] else [ "lib" ];
379 mainProgram = crateName;
381 # Rust is currently unable to target the n32 ABI
382 lib.systems.inspect.patterns.isMips64n32
385 } // extraDerivationAttrs
389 rust = crate_.rust or rustc;
390 cargo = crate_.cargo or cargo;
391 release = crate_.release or true;
392 verbose = crate_.verbose or true;
393 extraRustcOpts = [ ];
394 extraRustcOptsForBuildRs = [ ];
396 nativeBuildInputs = [ ];
398 crateOverrides = defaultCrateOverrides;
399 preUnpack = crate_.preUnpack or "";
400 postUnpack = crate_.postUnpack or "";
401 prePatch = crate_.prePatch or "";
402 patches = crate_.patches or [ ];
403 postPatch = crate_.postPatch or "";
404 preConfigure = crate_.preConfigure or "";
405 postConfigure = crate_.postConfigure or "";
406 preBuild = crate_.preBuild or "";
407 postBuild = crate_.postBuild or "";
408 preInstall = crate_.preInstall or "";
409 postInstall = crate_.postInstall or "";
410 dependencies = crate_.dependencies or [ ];
411 buildDependencies = crate_.buildDependencies or [ ];
412 crateRenames = crate_.crateRenames or { };
413 buildTests = crate_.buildTests or false;