summary refs log tree commit diff
path: root/pkgs/build-support/rust/build-rust-crate/build-crate.nix
blob: 944434472b0b85445776d76d17d761430baf53e6 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
{ lib, stdenv
, mkRustcDepArgs, mkRustcFeatureArgs, needUnstableCLI
, rust
}:

{ crateName,
  dependencies,
  crateFeatures, crateRenames, libName, release, libPath,
  crateType, metadata, crateBin, hasCrateBin,
  extraRustcOpts, verbose, colors,
  buildTests,
  codegenUnits
}:

  let
    baseRustcOpts =
      [
        (if release then "-C opt-level=3" else "-C debuginfo=2")
        "-C codegen-units=${toString codegenUnits}"
        "--remap-path-prefix=$NIX_BUILD_TOP=/"
        (mkRustcDepArgs dependencies crateRenames)
        (mkRustcFeatureArgs crateFeatures)
      ] ++ lib.optionals (stdenv.hostPlatform != stdenv.buildPlatform) [
        "--target" (rust.toRustTargetSpec stdenv.hostPlatform)
      ] ++ lib.optionals (needUnstableCLI dependencies) [
        "-Z" "unstable-options"
      ] ++ extraRustcOpts
      # since rustc 1.42 the "proc_macro" crate is part of the default crate prelude
      # https://github.com/rust-lang/cargo/commit/4d64eb99a4#diff-7f98585dbf9d30aa100c8318e2c77e79R1021-R1022
      ++ lib.optional (lib.elem "proc-macro" crateType) "--extern proc_macro"
    ;
    rustcMeta = "-C metadata=${metadata} -C extra-filename=-${metadata}";


    # build the final rustc arguments that can be different between different
    # crates
    libRustcOpts = lib.concatStringsSep " " (
      baseRustcOpts
      ++ [rustcMeta]
      ++ (map (x: "--crate-type ${x}") crateType)
    );

    binRustcOpts = lib.concatStringsSep " " (
      [ "-C linker=${stdenv.cc}/bin/${stdenv.cc.targetPrefix}cc" ] ++
      baseRustcOpts
    );

    build_bin = if buildTests then "build_bin_test" else "build_bin";
  in ''
    runHook preBuild

    # configure & source common build functions
    LIB_RUSTC_OPTS="${libRustcOpts}"
    BIN_RUSTC_OPTS="${binRustcOpts}"
    LIB_EXT="${stdenv.hostPlatform.extensions.sharedLibrary}"
    LIB_PATH="${libPath}"
    LIB_NAME="${libName}"

    CRATE_NAME='${lib.replaceStrings ["-"] ["_"] libName}'

    setup_link_paths

    if [[ -e "$LIB_PATH" ]]; then
       build_lib "$LIB_PATH"
       ${lib.optionalString buildTests ''build_lib_test "$LIB_PATH"''}
    elif [[ -e src/lib.rs ]]; then
       build_lib src/lib.rs
       ${lib.optionalString buildTests "build_lib_test src/lib.rs"}
    fi



    ${lib.optionalString (lib.length crateBin > 0) (lib.concatMapStringsSep "\n" (bin:
    let
      haveRequiredFeature = if bin ? requiredFeatures then
        # Check that all element in requiredFeatures are also present in crateFeatures
        lib.intersectLists bin.requiredFeatures crateFeatures == bin.requiredFeatures
      else
        true;
    in
    if haveRequiredFeature then ''
      mkdir -p target/bin
      BIN_NAME='${bin.name or crateName}'
      ${if !bin ? path then ''
        BIN_PATH=""
        search_for_bin_path "$BIN_NAME"
      '' else ''
        BIN_PATH='${bin.path}'
      ''}
        ${build_bin} "$BIN_NAME" "$BIN_PATH"
    '' else ''
      echo Binary ${bin.name or crateName} not compiled due to not having all of the required features -- ${lib.escapeShellArg (builtins.toJSON bin.requiredFeatures)} -- enabled.
    '') crateBin)}

    ${lib.optionalString buildTests ''
    # When tests are enabled build all the files in the `tests` directory as
    # test binaries.
    if [ -d tests ]; then
      # find all the .rs files (or symlinks to those) in the tests directory, no subdirectories
      find tests -maxdepth 1 \( -type f -o -type l \) -a -name '*.rs' -print0 | while IFS= read -r -d ''' file; do
        mkdir -p target/bin
        build_bin_test_file "$file"
      done

      # find all the subdirectories of tests/ that contain a main.rs file as
      # that is also a test according to cargo
      find tests/ -mindepth 1 -maxdepth 2 \( -type f -o -type l \) -a -name 'main.rs' -print0 | while IFS= read -r -d ''' file; do
        mkdir -p target/bin
        build_bin_test_file "$file"
      done

    fi
    ''}

    # If crateBin is empty and hasCrateBin is not set then we must try to
    # detect some kind of bin target based on some files that might exist.
    ${lib.optionalString (lib.length crateBin == 0 && !hasCrateBin) ''
      if [[ -e src/main.rs ]]; then
        mkdir -p target/bin
        ${build_bin} ${crateName} src/main.rs
      fi
      for i in src/bin/*.rs; do #*/
        mkdir -p target/bin
        ${build_bin} "$(basename $i .rs)" "$i"
      done
    ''}
    # Remove object files to avoid "wrong ELF type"
    find target -type f -name "*.o" -print0 | xargs -0 rm -f
    runHook postBuild
  ''