anvil-editor: init at 0.4
[NixPkgs.git] / pkgs / build-support / rust / build-rust-crate / default.nix
blobdb9c2d784f6b9142a3c2d30507c781573e93a91e
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.
7 { lib
8 , stdenv
9 , defaultCrateOverrides
10 , fetchCrate
11 , pkgsBuildBuild
12 , rustc
13 , cargo
14 , jq
15 , libiconv
18 let
19   # Create rustc arguments to link against the given list of dependencies
20   # and renames.
21   #
22   # See docs for crateRenames below.
23   mkRustcDepArgs = dependencies: crateRenames:
24     lib.concatMapStringsSep " "
25       (dep:
26         let
27           normalizeName = lib.replaceStrings [ "-" ] [ "_" ];
28           extern = normalizeName dep.libName;
29           # Find a choice that matches in name and optionally version.
30           findMatchOrUseExtern = choices:
31             lib.findFirst
32               (choice:
33                 (!(choice ? version)
34                   || choice.version == dep.version or ""))
35               { rename = extern; }
36               choices;
37           name =
38             if lib.hasAttr dep.crateName crateRenames then
39               let choices = crateRenames.${dep.crateName};
40               in
41               normalizeName (
42                 if builtins.isList choices
43                 then (findMatchOrUseExtern choices).rename
44                 else choices
45               )
46             else
47               extern;
48           opts = lib.optionalString (dep.stdlib or false) "noprelude:";
49           filename =
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}";
55         in
56         " --extern ${opts}${name}=${dep.lib}/lib/lib${extern}-${filename}"
57       )
58       dependencies;
60   # Create feature arguments for rustc.
61   mkRustcFeatureArgs = lib.concatMapStringsSep " " (f: ''--cfg feature=\"${f}\"'');
63   # Whether we need to use unstable command line flags
64   #
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;
76   };
78   installCrate = import ./install-crate.nix { inherit stdenv; };
81   /* The overridable pkgs.buildRustCrate function.
82     *
83     * Any unrecognized parameters will be passed as to
84     * the underlying stdenv.mkDerivation.
85   */
86 crate_: lib.makeOverridable
87   (
88     # The rust compiler to use.
89     #
90     # Default: pkgs.rustc
91     { rust ? rustc
92       # The cargo package to use for getting some metadata.
93       #
94       # Default: pkgs.cargo
95     , cargo ? cargo
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.
99     , release
100       # Whether to print rustc invocations etc.
101       #
102       # Example: false
103       # Default: true
104     , verbose
105       # A list of rust/cargo features to enable while building the crate.
106       # Example: [ "std" "async" ]
107     , features
108       # Additional native build inputs for building this crate.
109     , nativeBuildInputs
110       # Additional build inputs for building this crate.
111       #
112       # Example: [ pkgs.openssl ]
113     , buildInputs
114       # Allows to override the parameters to buildRustCrate
115       # for any rust dependency in the transitive build tree.
116       #
117       # Default: pkgs.defaultCrateOverrides
118       #
119       # Example:
120       #
121       # pkgs.defaultCrateOverrides // {
122       #   hello = attrs: { buildInputs = [ openssl ]; };
123       # }
124     , crateOverrides
125       # Rust library dependencies, i.e. other libraries that were built
126       # with buildRustCrate.
127     , dependencies
128       # Rust build dependencies, i.e. other libraries that were built
129       # with buildRustCrate and are used by a build script.
130     , buildDependencies
131       # Specify the "extern" name of a library if it differs from the library target.
132       # See above for an extended explanation.
133       #
134       # Default: no renames.
135       #
136       # Example:
137       #
138       # `crateRenames` supports two formats.
139       #
140       # The simple version is an attrset that maps the
141       # `crateName`s of the dependencies to their alternative
142       # names.
143       #
144       # ```nix
145       # {
146       #   my_crate_name = "my_alternative_name";
147       #   # ...
148       # }
149       # ```
150       #
151       # The extended version is also keyed by the `crateName`s but allows
152       # different names for different crate versions:
153       #
154       # ```nix
155       # {
156       #   my_crate_name = [
157       #       { version = "1.2.3"; rename = "my_alternative_name01"; }
158       #       { version = "3.2.3"; rename = "my_alternative_name03"; }
159       #   ]
160       #   # ...
161       # }
162       # ```
163       #
164       # This roughly corresponds to the following snippet in Cargo.toml:
165       #
166       # ```toml
167       # [dependencies]
168       # my_alternative_name01 = { package = "my_crate_name", version = "0.1" }
169       # my_alternative_name03 = { package = "my_crate_name", version = "0.3" }
170       # ```
171       #
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.
174       #
175       # Including multiple versions of a crate is very popular during
176       # ecosystem transitions, e.g. from futures 0.1 to futures 0.3.
177     , crateRenames
178       # A list of extra options to pass to rustc.
179       #
180       # Example: [ "-Z debuginfo=2" ]
181       # Default: []
182     , extraRustcOpts
183       # A list of extra options to pass to rustc when building a build.rs.
184       #
185       # Example: [ "-Z debuginfo=2" ]
186       # Default: []
187     , extraRustcOptsForBuildRs
188       # Whether to enable building tests.
189       # Use true to enable.
190       # Default: false
191     , buildTests
192       # Passed to stdenv.mkDerivation.
193     , preUnpack
194       # Passed to stdenv.mkDerivation.
195     , postUnpack
196       # Passed to stdenv.mkDerivation.
197     , prePatch
198       # Passed to stdenv.mkDerivation.
199     , patches
200       # Passed to stdenv.mkDerivation.
201     , postPatch
202       # Passed to stdenv.mkDerivation.
203     , preConfigure
204       # Passed to stdenv.mkDerivation.
205     , postConfigure
206       # Passed to stdenv.mkDerivation.
207     , preBuild
208       # Passed to stdenv.mkDerivation.
209     , postBuild
210       # Passed to stdenv.mkDerivation.
211     , preInstall
212       # Passed to stdenv.mkDerivation.
213     , postInstall
214     }:
216     let
217       crate = crate_ // (lib.attrByPath [ crate_.crateName ] (attr: { }) crateOverrides crate_);
218       dependencies_ = dependencies;
219       buildDependencies_ = buildDependencies;
220       processedAttrs = [
221         "src"
222         "nativeBuildInputs"
223         "buildInputs"
224         "crateBin"
225         "crateLib"
226         "libName"
227         "libPath"
228         "buildDependencies"
229         "dependencies"
230         "features"
231         "crateRenames"
232         "crateName"
233         "version"
234         "build"
235         "authors"
236         "colors"
237         "edition"
238         "buildTests"
239         "codegenUnits"
240         "links"
241       ];
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;
257         rustc = rust;
258       };
259     in
260     stdenv.mkDerivation (rec {
262       inherit (crate) crateName;
263       inherit
264         preUnpack
265         postUnpack
266         prePatch
267         patches
268         postPatch
269         preConfigure
270         postConfigure
271         preBuild
272         postBuild
273         preInstall
274         postInstall
275         buildTests
276         ;
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 (
292         buildDependencies
293           ++ lib.concatMap (dep: dep.completeBuildDeps ++ dep.completeDeps) buildDependencies
294       );
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)
302         (builtins.filter
303           (f: !(lib.hasInfix "/" f || lib.hasPrefix "dep:" f))
304           (crate.features ++ features)
305         );
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
312       metadata =
313         let
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);
318         in
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;
335       crateType =
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;
343       extraRustcOpts =
344         lib.optionals (crate ? extraRustcOpts) crate.extraRustcOpts
345           ++ 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;
359       };
360       buildPhase = buildCrate {
361         inherit crateName dependencies
362           crateFeatures crateRenames libName release libPath crateType
363           metadata hasCrateBin crateBin verbose colors
364           extraRustcOpts buildTests codegenUnits;
365       };
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" ];
378       meta = {
379         mainProgram = crateName;
380         badPlatforms = [
381           # Rust is currently unable to target the n32 ABI
382           lib.systems.inspect.patterns.isMips64n32
383         ];
384       };
385     } // extraDerivationAttrs
386     )
387   )
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 = [ ];
395   features = [ ];
396   nativeBuildInputs = [ ];
397   buildInputs = [ ];
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;