summary refs log tree commit diff
path: root/pkgs/build-support/rust/build-rust-crate/build-crate.nix
blob: c3880a1fc877b0540a4f75a7096dae99f8e75ac9 (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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
{ lib, stdenv, echo_build_heading, noisily, makeDeps, rust }:
{ crateName,
  dependencies,
  crateFeatures, crateRenames, libName, release, libPath,
  crateType, metadata, crateBin, hasCrateBin,
  extraRustcOpts, verbose, colors }:

  let

    deps = makeDeps dependencies crateRenames;
    rustcOpts =
      lib.foldl' (opts: opt: opts + " " + opt)
        (if release then "-C opt-level=3" else "-C debuginfo=2")
        (["-C codegen-units=$NIX_BUILD_CORES"] ++ extraRustcOpts);
    rustcMeta = "-C metadata=${metadata} -C extra-filename=-${metadata}";
  in ''
    runHook preBuild
    ${echo_build_heading colors}
    ${noisily colors verbose}

    build_lib() {
       lib_src=$1
       echo_build_heading $lib_src ${libName}

       noisily rustc --crate-name $CRATE_NAME $lib_src \
         ${lib.strings.concatStrings (map (x: " --crate-type ${x}") crateType)}  \
         ${rustcOpts} ${rustcMeta} ${crateFeatures} --out-dir target/lib \
         --emit=dep-info,link -L dependency=target/deps ${deps} --cap-lints allow \
         $BUILD_OUT_DIR $EXTRA_BUILD $EXTRA_FEATURES --color ${colors}

       EXTRA_LIB=" --extern $CRATE_NAME=target/lib/lib$CRATE_NAME-${metadata}.rlib"
       if [ -e target/deps/lib$CRATE_NAME-${metadata}${stdenv.hostPlatform.extensions.sharedLibrary} ]; then
          EXTRA_LIB="$EXTRA_LIB --extern $CRATE_NAME=target/lib/lib$CRATE_NAME-${metadata}${stdenv.hostPlatform.extensions.sharedLibrary}"
       fi
    }

    build_bin() {
      crate_name=$1
      crate_name_=$(echo $crate_name | sed -e "s/-/_/g")
      main_file=""
      if [[ ! -z $2 ]]; then
        main_file=$2
      fi
      echo_build_heading $@
      noisily rustc --crate-name $crate_name_ $main_file --crate-type bin ${rustcOpts}\
        ${crateFeatures} --out-dir target/bin --emit=dep-info,link -L dependency=target/deps \
        $LINK ${deps}$EXTRA_LIB --cap-lints allow \
        $BUILD_OUT_DIR $EXTRA_BUILD $EXTRA_FEATURES --color ${colors} \
        ${if stdenv.hostPlatform != stdenv.buildPlatform then "--target ${rust.toRustTarget stdenv.hostPlatform} -C linker=${stdenv.hostPlatform.config}-gcc" else ""}
      if [ "$crate_name_" != "$crate_name" ]; then
        mv target/bin/$crate_name_ target/bin/$crate_name
      fi
    }


    EXTRA_LIB=""
    CRATE_NAME=$(echo ${libName} | sed -e "s/-/_/g")

    if [[ -e target/link_ ]]; then
      EXTRA_BUILD="$(cat target/link_) $EXTRA_BUILD"
    fi

    if [[ -e "${libPath}" ]]; then
       build_lib ${libPath}
    elif [[ -e src/lib.rs ]]; then
       build_lib src/lib.rs
    elif [[ -e src/${libName}.rs ]]; then
       build_lib src/${libName}.rs
    fi

    echo "$EXTRA_LINK_SEARCH" | while read i; do
       if [[ ! -z "$i" ]]; then
         for library in $i; do
           echo "-L $library" >> target/link
           L=$(echo $library | sed -e "s#$(pwd)/target/build#$lib/lib#")
           echo "-L $L" >> target/link.final
         done
       fi
    done
    echo "$EXTRA_LINK" | while read i; do
       if [[ ! -z "$i" ]]; then
         for library in $i; do
           echo "-l $library" >> target/link
           echo "-l $library" >> target/link.final
         done
       fi
    done

    if [[ -e target/link ]]; then
       sort -u target/link.final > target/link.final.sorted
       mv target/link.final.sorted target/link.final
       sort -u target/link > target/link.sorted
       mv target/link.sorted target/link

       tr '\n' ' ' < target/link > target/link_
       LINK=$(cat target/link_)
    fi
    ${lib.optionalString (crateBin != "") ''
    printf "%s\n" "${crateBin}" | head -n1 | tr -s ',' '\n' | while read -r BIN_NAME BIN_PATH; do
      mkdir -p target/bin
      # filter empty entries / empty "lines"
      if [[ -z "$BIN_NAME" ]]; then
           continue
      fi

      if [[ -z "$BIN_PATH" ]]; then
        # heuristic to "guess" the correct source file as found in cargo:
        # https://github.com/rust-lang/cargo/blob/90fc9f620190d5fa3c80b0c8c65a1e1361e6b8ae/src/cargo/util/toml/targets.rs#L308-L325

        # the first two cases are the "new" default IIRC
        BIN_NAME_=$(echo $BIN_NAME | sed -e 's/-/_/g')
        FILES=( "src/bin/$BIN_NAME.rs" "src/bin/$BIN_NAME/main.rs" "src/bin/$BIN_NAME_.rs" "src/bin/$BIN_NAME_/main.rs" "src/bin/main.rs" "src/main.rs" )

        if ! [ -e "${libPath}" -o -e src/lib.rs -o -e "src/${libName}.rs" ]; then
          # if this is not a library the following path is also valid
          FILES=( "src/$BIN_NAME.rs" "src/$BIN_NAME_.rs" "''${FILES[@]}" )
        fi

        for file in "''${FILES[@]}";
        do
          echo "checking file $file"
          # first file that exists wins
          if [[ -e "$file" ]]; then
                  BIN_PATH="$file"
                  break
          fi
        done

        if [[ -z "$BIN_PATH" ]]; then
          echo "failed to find file for binary target: $BIN_NAME" >&2
          exit 1
        fi
      fi
      build_bin "$BIN_NAME" "$BIN_PATH"
    done
    ''}

    ${lib.optionalString (crateBin == "" && !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
  ''