nixpkgs mirror (for testing) github.com/NixOS/nixpkgs
nix
at python-updates 494 lines 16 kB view raw
1# Code for buildRustCrate, a Nix function that builds Rust code, just 2# like Cargo, but using Nix instead. 3# 4# This can be useful for deploying packages with NixOps, and to share 5# binary dependencies between projects. 6 7{ 8 lib, 9 stdenv, 10 defaultCrateOverrides, 11 fetchCrate, 12 pkgsBuildBuild, 13 rustc, 14 cargo, 15 jq, 16 libiconv, 17 # Controls codegen parallelization for all crates. 18 # May be overridden on a per-crate level. 19 # See <https://doc.rust-lang.org/rustc/codegen-options/index.html#codegen-units> 20 defaultCodegenUnits ? 1, 21}: 22 23let 24 # Create rustc arguments to link against the given list of dependencies 25 # and renames. 26 # 27 # See docs for crateRenames below. 28 mkRustcDepArgs = 29 dependencies: crateRenames: 30 lib.concatMapStringsSep " " ( 31 dep: 32 let 33 normalizeName = lib.replaceStrings [ "-" ] [ "_" ]; 34 extern = normalizeName dep.libName; 35 # Find a choice that matches in name and optionally version. 36 findMatchOrUseExtern = 37 choices: 38 lib.findFirst (choice: (!(choice ? version) || choice.version == dep.version or "")) { 39 rename = extern; 40 } choices; 41 name = 42 if lib.hasAttr dep.crateName crateRenames then 43 let 44 choices = crateRenames.${dep.crateName}; 45 in 46 normalizeName (if builtins.isList choices then (findMatchOrUseExtern choices).rename else choices) 47 else 48 extern; 49 opts = lib.optionalString (dep.stdlib or false) "noprelude:"; 50 filename = 51 if lib.any (x: x == "lib" || x == "rlib") dep.crateType then 52 "${dep.metadata}.rlib" 53 # Adjust lib filename for crates of type proc-macro. Proc macros are compiled/run on the build platform architecture. 54 else if (lib.attrByPath [ "procMacro" ] false dep) then 55 "${dep.metadata}${stdenv.buildPlatform.extensions.library}" 56 else 57 "${dep.metadata}${stdenv.hostPlatform.extensions.library}"; 58 in 59 " --extern ${opts}${name}=${dep.lib}/lib/lib${extern}-${filename}" 60 ) dependencies; 61 62 # Create feature arguments for rustc. 63 mkRustcFeatureArgs = lib.concatMapStringsSep " " (f: ''--cfg feature=\"${f}\"''); 64 65 # Whether we need to use unstable command line flags 66 # 67 # Currently just needed for standard library dependencies, which have a 68 # special "noprelude:" modifier. If in later versions of Rust this is 69 # stabilized we can account for that here, too, so we don't opt into 70 # instability unnecessarily. 71 needUnstableCLI = dependencies: lib.any (dep: dep.stdlib or false) dependencies; 72 73 inherit (import ./log.nix { inherit lib; }) noisily echo_colored; 74 75 configureCrate = import ./configure-crate.nix { 76 inherit 77 lib 78 stdenv 79 echo_colored 80 noisily 81 mkRustcDepArgs 82 mkRustcFeatureArgs 83 ; 84 }; 85 86 installCrate = import ./install-crate.nix { inherit stdenv; }; 87in 88 89/* 90 The overridable pkgs.buildRustCrate function. 91 * 92 * Any unrecognized parameters will be passed as to 93 * the underlying stdenv.mkDerivation. 94*/ 95crate_: 96lib.makeOverridable 97 ( 98 # The rust compiler to use. 99 # 100 # Default: pkgs.rustc 101 { 102 rust ? rustc, 103 # The cargo package to use for getting some metadata. 104 # 105 # Default: pkgs.cargo 106 cargo ? cargo, 107 # Whether to build a release version (`true`) or a debug 108 # version (`false`). Debug versions are faster to build 109 # but might be much slower at runtime. 110 release, 111 # Whether to print rustc invocations etc. 112 # 113 # Example: false 114 # Default: true 115 verbose, 116 # A list of rust/cargo features to enable while building the crate. 117 # Example: [ "std" "async" ] 118 features, 119 # Additional native build inputs for building this crate. 120 nativeBuildInputs, 121 # Additional build inputs for building this crate. 122 # 123 # Example: [ pkgs.openssl ] 124 buildInputs, 125 # Allows to override the parameters to buildRustCrate 126 # for any rust dependency in the transitive build tree. 127 # 128 # Default: pkgs.defaultCrateOverrides 129 # 130 # Example: 131 # 132 # pkgs.defaultCrateOverrides // { 133 # hello = attrs: { buildInputs = [ openssl ]; }; 134 # } 135 crateOverrides, 136 # Rust library dependencies, i.e. other libraries that were built 137 # with buildRustCrate. 138 dependencies, 139 # Rust build dependencies, i.e. other libraries that were built 140 # with buildRustCrate and are used by a build script. 141 buildDependencies, 142 # Specify the "extern" name of a library if it differs from the library target. 143 # See above for an extended explanation. 144 # 145 # Default: no renames. 146 # 147 # Example: 148 # 149 # `crateRenames` supports two formats. 150 # 151 # The simple version is an attrset that maps the 152 # `crateName`s of the dependencies to their alternative 153 # names. 154 # 155 # ```nix 156 # { 157 # my_crate_name = "my_alternative_name"; 158 # # ... 159 # } 160 # ``` 161 # 162 # The extended version is also keyed by the `crateName`s but allows 163 # different names for different crate versions: 164 # 165 # ```nix 166 # { 167 # my_crate_name = [ 168 # { version = "1.2.3"; rename = "my_alternative_name01"; } 169 # { version = "3.2.3"; rename = "my_alternative_name03"; } 170 # ] 171 # # ... 172 # } 173 # ``` 174 # 175 # This roughly corresponds to the following snippet in Cargo.toml: 176 # 177 # ```toml 178 # [dependencies] 179 # my_alternative_name01 = { package = "my_crate_name", version = "0.1" } 180 # my_alternative_name03 = { package = "my_crate_name", version = "0.3" } 181 # ``` 182 # 183 # Dependencies which use the lib target name as extern name, do not need 184 # to be specified in the crateRenames, even if their crate name differs. 185 # 186 # Including multiple versions of a crate is very popular during 187 # ecosystem transitions, e.g. from futures 0.1 to futures 0.3. 188 crateRenames, 189 # A list of extra options to pass to rustc. 190 # 191 # Example: [ "-Z debuginfo=2" ] 192 # Default: [] 193 extraRustcOpts, 194 # A list of extra options to pass to rustc when building a build.rs. 195 # 196 # Example: [ "-Z debuginfo=2" ] 197 # Default: [] 198 extraRustcOptsForBuildRs, 199 # Whether to enable building tests. 200 # Use true to enable. 201 # Default: false 202 buildTests, 203 # Passed to stdenv.mkDerivation. 204 preUnpack, 205 # Passed to stdenv.mkDerivation. 206 postUnpack, 207 # Passed to stdenv.mkDerivation. 208 prePatch, 209 # Passed to stdenv.mkDerivation. 210 patches, 211 # Passed to stdenv.mkDerivation. 212 postPatch, 213 # Passed to stdenv.mkDerivation. 214 preConfigure, 215 # Passed to stdenv.mkDerivation. 216 postConfigure, 217 # Passed to stdenv.mkDerivation. 218 preBuild, 219 # Passed to stdenv.mkDerivation. 220 postBuild, 221 # Passed to stdenv.mkDerivation. 222 preInstall, 223 # Passed to stdenv.mkDerivation. 224 postInstall, 225 }: 226 227 let 228 crate = crate_ // (lib.attrByPath [ crate_.crateName ] (attr: { }) crateOverrides crate_); 229 dependencies_ = dependencies; 230 buildDependencies_ = buildDependencies; 231 processedAttrs = [ 232 "src" 233 "nativeBuildInputs" 234 "buildInputs" 235 "crateBin" 236 "crateLib" 237 "libName" 238 "libPath" 239 "buildDependencies" 240 "dependencies" 241 "features" 242 "crateRenames" 243 "crateName" 244 "version" 245 "build" 246 "authors" 247 "colors" 248 "edition" 249 "buildTests" 250 "codegenUnits" 251 "links" 252 ]; 253 extraDerivationAttrs = removeAttrs crate processedAttrs; 254 nativeBuildInputs_ = nativeBuildInputs; 255 buildInputs_ = buildInputs; 256 extraRustcOpts_ = extraRustcOpts; 257 extraRustcOptsForBuildRs_ = extraRustcOptsForBuildRs; 258 buildTests_ = buildTests; 259 260 # crate2nix has a hack for the old bash based build script that did split 261 # entries at `,`. No we have to work around that hack. 262 # https://github.com/kolloch/crate2nix/blame/5b19c1b14e1b0e5522c3e44e300d0b332dc939e7/crate2nix/templates/build.nix.tera#L89 263 crateBin = lib.filter (bin: !(bin ? name && bin.name == ",")) (crate.crateBin or [ ]); 264 hasCrateBin = crate ? crateBin; 265 266 buildCrate = import ./build-crate.nix { 267 inherit 268 lib 269 stdenv 270 mkRustcDepArgs 271 mkRustcFeatureArgs 272 needUnstableCLI 273 ; 274 rustc = rust; 275 }; 276 in 277 stdenv.mkDerivation ( 278 rec { 279 280 inherit (crate) crateName; 281 inherit 282 preUnpack 283 postUnpack 284 prePatch 285 patches 286 postPatch 287 preConfigure 288 postConfigure 289 preBuild 290 postBuild 291 preInstall 292 postInstall 293 buildTests 294 ; 295 296 src = crate.src or (fetchCrate { inherit (crate) crateName version sha256; }); 297 name = "rust_${crate.crateName}-${crate.version}${lib.optionalString buildTests_ "-test"}"; 298 version = crate.version; 299 depsBuildBuild = [ pkgsBuildBuild.stdenv.cc ]; 300 nativeBuildInputs = [ 301 rust 302 cargo 303 jq 304 ] 305 ++ lib.optionals stdenv.hasCC [ stdenv.cc ] 306 ++ lib.optionals stdenv.buildPlatform.isDarwin [ libiconv ] 307 ++ (crate.nativeBuildInputs or [ ]) 308 ++ nativeBuildInputs_; 309 buildInputs = 310 lib.optionals stdenv.hostPlatform.isDarwin [ libiconv ] 311 ++ (crate.buildInputs or [ ]) 312 ++ buildInputs_; 313 dependencies = map lib.getLib dependencies_; 314 buildDependencies = map lib.getLib buildDependencies_; 315 316 completeDeps = lib.unique (dependencies ++ lib.concatMap (dep: dep.completeDeps) dependencies); 317 completeBuildDeps = lib.unique ( 318 buildDependencies 319 ++ lib.concatMap (dep: dep.completeBuildDeps ++ dep.completeDeps) buildDependencies 320 ); 321 322 # Create a list of features that are enabled by the crate itself and 323 # through the features argument of buildRustCrate. Exclude features 324 # with a forward slash, since they are passed through to dependencies, 325 # and dep: features, since they're internal-only and do nothing except 326 # enable optional dependencies. 327 crateFeatures = lib.optionals (crate ? features) ( 328 builtins.filter (f: !(lib.hasInfix "/" f || lib.hasPrefix "dep:" f)) (crate.features ++ features) 329 ); 330 331 libName = if crate ? libName then crate.libName else crate.crateName; 332 libPath = lib.optionalString (crate ? libPath) crate.libPath; 333 334 # Seed the symbol hashes with something unique every time. 335 # https://doc.rust-lang.org/1.0.0/rustc/metadata/loader/index.html#frobbing-symbols 336 metadata = 337 let 338 depsMetadata = lib.foldl' (str: dep: str + dep.metadata) "" (dependencies ++ buildDependencies); 339 hashedMetadata = builtins.hashString "sha256" ( 340 crateName 341 + "-" 342 + crateVersion 343 + "___" 344 + toString (mkRustcFeatureArgs crateFeatures) 345 + "___" 346 + depsMetadata 347 + "___" 348 + stdenv.hostPlatform.rust.rustcTarget 349 ); 350 in 351 lib.substring 0 10 hashedMetadata; 352 353 build = crate.build or ""; 354 # Either set to a concrete sub path to the crate root 355 # or use `null` for auto-detect. 356 workspace_member = crate.workspace_member or "."; 357 crateAuthors = if crate ? authors && lib.isList crate.authors then crate.authors else [ ]; 358 crateDescription = crate.description or ""; 359 crateHomepage = crate.homepage or ""; 360 crateLicense = crate.license or ""; 361 crateLicenseFile = crate.license-file or ""; 362 crateLinks = crate.links or ""; 363 crateReadme = crate.readme or ""; 364 crateRepository = crate.repository or ""; 365 crateRustVersion = crate.rust-version or ""; 366 crateVersion = crate.version; 367 crateType = 368 if lib.attrByPath [ "procMacro" ] false crate then 369 [ "proc-macro" ] 370 else if lib.attrByPath [ "plugin" ] false crate then 371 [ "dylib" ] 372 else 373 (crate.type or [ "lib" ]); 374 colors = lib.attrByPath [ "colors" ] "always" crate; 375 extraLinkFlags = lib.concatStringsSep " " (crate.extraLinkFlags or [ ]); 376 edition = crate.edition or null; 377 codegenUnits = if crate ? codegenUnits then crate.codegenUnits else defaultCodegenUnits; 378 extraRustcOpts = 379 lib.optionals (crate ? extraRustcOpts) crate.extraRustcOpts 380 ++ extraRustcOpts_ 381 ++ (lib.optional (edition != null) "--edition ${edition}"); 382 extraRustcOptsForBuildRs = 383 lib.optionals (crate ? extraRustcOptsForBuildRs) crate.extraRustcOptsForBuildRs 384 ++ extraRustcOptsForBuildRs_ 385 ++ (lib.optional (edition != null) "--edition ${edition}"); 386 387 configurePhase = configureCrate { 388 inherit 389 crateName 390 crateType 391 buildDependencies 392 completeDeps 393 completeBuildDeps 394 crateDescription 395 crateFeatures 396 crateRenames 397 libName 398 build 399 workspace_member 400 release 401 libPath 402 crateVersion 403 crateLinks 404 extraLinkFlags 405 extraRustcOptsForBuildRs 406 crateLicense 407 crateLicenseFile 408 crateReadme 409 crateRepository 410 crateRustVersion 411 crateAuthors 412 crateHomepage 413 verbose 414 colors 415 codegenUnits 416 ; 417 }; 418 buildPhase = buildCrate { 419 inherit 420 crateName 421 dependencies 422 crateFeatures 423 crateRenames 424 libName 425 release 426 libPath 427 crateType 428 metadata 429 hasCrateBin 430 crateBin 431 verbose 432 colors 433 extraRustcOpts 434 buildTests 435 codegenUnits 436 ; 437 }; 438 dontStrip = !release; 439 440 # We need to preserve metadata in .rlib, which might get stripped on macOS. See https://github.com/NixOS/nixpkgs/issues/218712 441 stripExclude = [ "*.rlib" ]; 442 443 installPhase = installCrate crateName metadata buildTests; 444 445 # depending on the test setting we are either producing something with bins 446 # and libs or just test binaries 447 outputs = 448 if buildTests then 449 [ "out" ] 450 else 451 [ 452 "out" 453 "lib" 454 ]; 455 outputDev = if buildTests then [ "out" ] else [ "lib" ]; 456 457 meta = { 458 mainProgram = crateName; 459 badPlatforms = [ 460 # Rust is currently unable to target the n32 ABI 461 lib.systems.inspect.patterns.isMips64n32 462 ]; 463 }; 464 } 465 // extraDerivationAttrs 466 ) 467 ) 468 { 469 rust = crate_.rust or rustc; 470 cargo = crate_.cargo or cargo; 471 release = crate_.release or true; 472 verbose = crate_.verbose or true; 473 extraRustcOpts = [ ]; 474 extraRustcOptsForBuildRs = [ ]; 475 features = [ ]; 476 nativeBuildInputs = [ ]; 477 buildInputs = [ ]; 478 crateOverrides = defaultCrateOverrides; 479 preUnpack = crate_.preUnpack or ""; 480 postUnpack = crate_.postUnpack or ""; 481 prePatch = crate_.prePatch or ""; 482 patches = crate_.patches or [ ]; 483 postPatch = crate_.postPatch or ""; 484 preConfigure = crate_.preConfigure or ""; 485 postConfigure = crate_.postConfigure or ""; 486 preBuild = crate_.preBuild or ""; 487 postBuild = crate_.postBuild or ""; 488 preInstall = crate_.preInstall or ""; 489 postInstall = crate_.postInstall or ""; 490 dependencies = crate_.dependencies or [ ]; 491 buildDependencies = crate_.buildDependencies or [ ]; 492 crateRenames = crate_.crateRenames or { }; 493 buildTests = crate_.buildTests or false; 494 }