summary refs log blame commit diff
path: root/pkgs/build-support/rust/default.nix
blob: 6741b329a309f80ac4e6f84a4932125957300a00 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
        
     


               
                
                   



                




         
 
                                        






                  

             
                    

                   
                   
               
                  
                        
                      
                    
                      
                       
           
                                                    
                       
                       
                    

                                 


                                                                              




                                                                         
               
 
                                                                         
                                                      
 
   
 
                                       
                             
                                                                     
                         
                               
                             
                         

              

                                                                               
                                                              
 
                                              
                                                          



                                                                                                                                          
                                                                  
                
 




                                                                            
 



                                                                                          
                                                    
                               
 

  



                                                                                                     
                                                                                           

                                                               
                    
 

                                            
                                                                                     
                                                                                         
 

                                    


                                                                 
                 

                         
                               

                               

                                            


                         
                                                   
                                                                                    
                    


          
         



                                                                        
                                       
                                                                    
                            
                                                        

     
                     
 
                                                             



                                                                                     


                                




                                                                    
                                      
                                                                                                      
                                                                           
       
                                                                                    
                    
                                                                                                       
                                                                                                                                        
                     
                                                             
      


                                 

                    
                            
 
                                        
                      

                                                             
                                                                             



                                         
                              
 
                                     
                  


                                                 
                                                      
                       
     
 
                                                             




                                     
  
{ stdenv
, lib
, buildPackages
, cacert
, cargo
, cargoSetupHook
, fetchCargoTarball
, runCommandNoCC
, rustPlatform
, callPackage
, remarshal
, git
, rust
, rustc
, windows
}:

{ name ? "${args.pname}-${args.version}"

  # SRI hash
, cargoHash ? ""

  # Legacy hash
, cargoSha256 ? ""

, src ? null
, srcs ? null
, unpackPhase ? null
, cargoPatches ? []
, patches ? []
, sourceRoot ? null
, logLevel ? ""
, buildInputs ? []
, nativeBuildInputs ? []
, cargoUpdateHook ? ""
, cargoDepsHook ? ""
, cargoBuildFlags ? []
, buildType ? "release"
, meta ? {}
, target ? rust.toRustTargetSpec stdenv.hostPlatform
, cargoVendorDir ? null
, checkType ? buildType
, depsExtraArgs ? {}
, cargoParallelTestThreads ? true

# Toggles whether a custom sysroot is created when the target is a .json file.
, __internal_dontAddSysroot ? false

# Needed to `pushd`/`popd` into a subdir of a tarball if this subdir
# contains a Cargo.toml, but isn't part of a workspace (which is e.g. the
# case for `rustfmt`/etc from the `rust-sources).
# Otherwise, everything from the tarball would've been built/tested.
, buildAndTestSubdir ? null
, ... } @ args:

assert cargoVendorDir == null -> !(cargoSha256 == "" && cargoHash == "");
assert buildType == "release" || buildType == "debug";

let

  cargoDeps = if cargoVendorDir == null
    then fetchCargoTarball ({
        inherit name src srcs sourceRoot unpackPhase cargoUpdateHook;
        hash = cargoHash;
        patches = cargoPatches;
        sha256 = cargoSha256;
      } // depsExtraArgs)
    else null;

  # If we have a cargoSha256 fixed-output derivation, validate it at build time
  # against the src fixed-output derivation to check consistency.
  validateCargoDeps = !(cargoHash == "" && cargoSha256 == "");

  targetIsJSON = lib.hasSuffix ".json" target;
  useSysroot = targetIsJSON && !__internal_dontAddSysroot;

  # see https://github.com/rust-lang/cargo/blob/964a16a28e234a3d397b2a7031d4ab4a428b1391/src/cargo/core/compiler/compile_kind.rs#L151-L168
  # the "${}" is needed to transform the path into a /nix/store path before baseNameOf
  shortTarget = if targetIsJSON then
      (lib.removeSuffix ".json" (builtins.baseNameOf "${target}"))
    else target;

  sysroot = (callPackage ./sysroot {}) {
    inherit target shortTarget;
    RUSTFLAGS = args.RUSTFLAGS or "";
    originalCargoToml = src + /Cargo.toml; # profile info is later extracted
  };

  ccForBuild="${buildPackages.stdenv.cc}/bin/${buildPackages.stdenv.cc.targetPrefix}cc";
  cxxForBuild="${buildPackages.stdenv.cc}/bin/${buildPackages.stdenv.cc.targetPrefix}c++";
  ccForHost="${stdenv.cc}/bin/${stdenv.cc.targetPrefix}cc";
  cxxForHost="${stdenv.cc}/bin/${stdenv.cc.targetPrefix}c++";
  releaseDir = "target/${shortTarget}/${buildType}";
  tmpDir = "${releaseDir}-tmp";

in

# Tests don't currently work for `no_std`, and all custom sysroots are currently built without `std`.
# See https://os.phil-opp.com/testing/ for more information.
assert useSysroot -> !(args.doCheck or true);

stdenv.mkDerivation ((removeAttrs args ["depsExtraArgs"]) // lib.optionalAttrs useSysroot {
  RUSTFLAGS = "--sysroot ${sysroot} " + (args.RUSTFLAGS or "");
} // {
  inherit cargoDeps;

  patchRegistryDeps = ./patch-registry-deps;

  nativeBuildInputs = nativeBuildInputs ++ [ cacert git cargo cargoSetupHook rustc ];
  buildInputs = buildInputs ++ lib.optional stdenv.hostPlatform.isMinGW windows.pthreads;

  patches = cargoPatches ++ patches;

  PKG_CONFIG_ALLOW_CROSS =
    if stdenv.buildPlatform != stdenv.hostPlatform then 1 else 0;

  postUnpack = ''
    eval "$cargoDepsHook"

    export RUST_LOG=${logLevel}
  '' + (args.postUnpack or "");

  configurePhase = args.configurePhase or ''
    runHook preConfigure
    runHook postConfigure
  '';

  buildPhase = with builtins; args.buildPhase or ''
    ${lib.optionalString (buildAndTestSubdir != null) "pushd ${buildAndTestSubdir}"}
    runHook preBuild

    (
    set -x
    env \
      "CC_${rust.toRustTarget stdenv.buildPlatform}"="${ccForBuild}" \
      "CXX_${rust.toRustTarget stdenv.buildPlatform}"="${cxxForBuild}" \
      "CC_${rust.toRustTarget stdenv.hostPlatform}"="${ccForHost}" \
      "CXX_${rust.toRustTarget stdenv.hostPlatform}"="${cxxForHost}" \
      cargo build -j $NIX_BUILD_CORES \
        ${lib.optionalString (buildType == "release") "--release"} \
        --target ${target} \
        --frozen ${concatStringsSep " " cargoBuildFlags}
    )

    runHook postBuild

    ${lib.optionalString (buildAndTestSubdir != null) "popd"}

    # This needs to be done after postBuild: packages like `cargo` do a pushd/popd in
    # the pre/postBuild-hooks that need to be taken into account before gathering
    # all binaries to install.
    mkdir -p $tmpDir
    cp -r $releaseDir/* $tmpDir/
    bins=$(find $tmpDir \
      -maxdepth 1 \
      -type f \
      -executable ! \( -regex ".*\.\(so.[0-9.]+\|so\|a\|dylib\)" \))
  '';

  checkPhase = args.checkPhase or (let
    argstr = "${lib.optionalString (checkType == "release") "--release"} --target ${target} --frozen";
    threads = if cargoParallelTestThreads then "$NIX_BUILD_CORES" else "1";
  in ''
    ${lib.optionalString (buildAndTestSubdir != null) "pushd ${buildAndTestSubdir}"}
    runHook preCheck
    echo "Running cargo test ${argstr} -- ''${checkFlags} ''${checkFlagsArray+''${checkFlagsArray[@]}}"
    cargo test -j $NIX_BUILD_CORES ${argstr} -- --test-threads=${threads} ''${checkFlags} ''${checkFlagsArray+"''${checkFlagsArray[@]}"}
    runHook postCheck
    ${lib.optionalString (buildAndTestSubdir != null) "popd"}
  '');

  doCheck = args.doCheck or true;

  strictDeps = true;

  inherit releaseDir tmpDir;

  installPhase = args.installPhase or ''
    runHook preInstall

    # rename the output dir to a architecture independent one
    mapfile -t targets < <(find "$NIX_BUILD_TOP" -type d | grep '${tmpDir}$')
    for target in "''${targets[@]}"; do
      rm -rf "$target/../../${buildType}"
      ln -srf "$target" "$target/../../"
    done
    mkdir -p $out/bin $out/lib

    xargs -r cp -t $out/bin <<< $bins
    find $tmpDir \
      -maxdepth 1 \
      -regex ".*\.\(so.[0-9.]+\|so\|a\|dylib\)" \
      -print0 | xargs -r -0 cp -t $out/lib
    rmdir --ignore-fail-on-non-empty $out/lib $out/bin
    runHook postInstall
  '';

  passthru = { inherit cargoDeps; } // (args.passthru or {});

  meta = {
    # default to Rust's platforms
    platforms = rustc.meta.platforms;
  } // meta;
})