Unverified Commit 7cfe0394 authored by Winter's avatar Winter Committed by GitHub
Browse files

buildRustCrate: set CARGO_BIN_EXE_<name> for integration tests (#502752)

parents cf405474 9fd2b8c7
Loading
Loading
Loading
Loading
+68 −2
Original line number Diff line number Diff line
@@ -70,6 +70,25 @@ let
  binRustcOpts = lib.concatStringsSep " " baseRustcOpts;

  build_bin = if buildTests then "build_bin_test" else "build_bin";

  # Shell snippet that builds a binary target to target/cargo-bin-exe/
  # so integration tests can exec it via CARGO_BIN_EXE_<name>.
  buildBinForTests = bin: ''
    mkdir -p target/cargo-bin-exe
    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" target/cargo-bin-exe
  '';
in
''
  runHook preBuild
@@ -88,13 +107,60 @@ in

  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

  ${
    # When building tests, first build the real (non-test) binaries so
    # integration tests can exec them via CARGO_BIN_EXE_<name>. They go
    # to target/cargo-bin-exe/ to avoid colliding with the --test
    # harnesses written to target/bin/. After building, populate the
    # CARGO_BIN_EXE_ENV array so subsequent rustc invocations see the
    # env vars (via `env` prefix in lib.sh, which unlike bash `export`
    # accepts hyphenated names).
    lib.optionalString buildTests (
      lib.concatMapStringsSep "\n" (
        bin:
        let
          haveRequiredFeature =
            if bin ? requiredFeatures then
              lib.intersectLists bin.requiredFeatures crateFeatures == bin.requiredFeatures
            else
              true;
        in
        lib.optionalString haveRequiredFeature (buildBinForTests bin)
      ) crateBin
      + lib.optionalString (lib.length crateBin == 0 && !hasCrateBin) ''
        if [[ -e src/main.rs ]]; then
          mkdir -p target/cargo-bin-exe
          build_bin ${crateName} src/main.rs target/cargo-bin-exe
        fi
        for i in src/bin/*.rs; do
          [ -e "$i" ] || continue
          mkdir -p target/cargo-bin-exe
          build_bin "$(basename $i .rs)" "$i" target/cargo-bin-exe
        done
      ''
      + ''
        if [ -d target/cargo-bin-exe ]; then
          for b in target/cargo-bin-exe/*; do
            [ -x "$b" ] || continue
            name=$(basename "$b")
            CARGO_BIN_EXE_ENV+=("CARGO_BIN_EXE_$name=$out/bin/$name")
          done
        fi
      ''
    )
  }

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

  ${lib.optionalString (lib.length crateBin > 0) (
    lib.concatMapStringsSep "\n" (
+6 −0
Original line number Diff line number Diff line
@@ -48,6 +48,12 @@ else
        -executable \
        -print0 | xargs --no-run-if-empty --null install --target $out/tests;
    fi
    # Real (non-test) binaries that integration tests may exec via
    # CARGO_BIN_EXE_<name>.
    if [ -d target/cargo-bin-exe ]; then
      mkdir -p $out/bin
      cp -rP target/cargo-bin-exe/* $out/bin
    fi

    runHook postInstall
  ''
+8 −7
Original line number Diff line number Diff line
@@ -10,7 +10,7 @@ build_lib() {
  lib_src=$1
  echo_build_heading $lib_src ${libName}

  noisily rustc \
  noisily env "${CARGO_BIN_EXE_ENV[@]}" rustc \
    --crate-name $CRATE_NAME \
    $lib_src \
    --out-dir target/lib \
@@ -36,17 +36,18 @@ build_bin() {
  local crate_name=$1
  local crate_name_=$(echo $crate_name | tr '-' '_')
  local main_file=""
  local out_dir="${3:-target/bin}"

  if [[ ! -z $2 ]]; then
    main_file=$2
  fi
  echo_build_heading $@
  noisily rustc \
  echo_build_heading $crate_name $main_file
  noisily env "${CARGO_BIN_EXE_ENV[@]}" rustc \
    --crate-name $crate_name_ \
    $main_file \
    --crate-type bin \
    $BIN_RUSTC_OPTS \
    --out-dir target/bin \
    --out-dir "$out_dir" \
    -L dependency=target/deps \
    $LINK \
    $EXTRA_LINK_ARGS \
@@ -60,10 +61,10 @@ build_bin() {
    --color ${colors} \

  if [ "$crate_name_" != "$crate_name" ]; then
    if [ -f "target/bin/$crate_name_.wasm" ]; then
      mv target/bin/$crate_name_.wasm target/bin/$crate_name.wasm
    if [ -f "$out_dir/$crate_name_.wasm" ]; then
      mv "$out_dir/$crate_name_.wasm" "$out_dir/$crate_name.wasm"
    else
      mv target/bin/$crate_name_ target/bin/$crate_name
      mv "$out_dir/$crate_name_" "$out_dir/$crate_name"
    fi
  fi
}
+68 −0
Original line number Diff line number Diff line
@@ -458,6 +458,74 @@ rec {
            "test flat_test ... ok"
          ];
        };
        rustBinTestsCargoBinExe = {
          # Integration tests locate the crate's own binary via
          # `env!("CARGO_BIN_EXE_<name>")`, which cargo sets automatically.
          crateName = "my-crate";
          src = symlinkJoin {
            name = "rust-bin-tests-cargo-bin-exe";
            paths = [
              (mkFile "src/main.rs" ''
                fn main() { println!("hello from my-crate"); }
              '')
              (mkFile "tests/run_bin.rs" ''
                #[test]
                fn runs_binary() {
                    let bin = env!("CARGO_BIN_EXE_my-crate");
                    let out = std::process::Command::new(bin)
                        .output()
                        .expect("spawn");
                    assert!(out.status.success());
                    assert_eq!(
                        String::from_utf8_lossy(&out.stdout).trim(),
                        "hello from my-crate"
                    );
                }
              '')
            ];
          };
          buildTests = true;
          expectedTestOutputs = [
            "test runs_binary ... ok"
          ];
        };
        rustBinTestsCargoBinExeAutoDetect = {
          # Verify CARGO_BIN_EXE_<name> is also set for auto-detected
          # src/bin/*.rs binaries, not just src/main.rs or explicit
          # crateBin entries.
          crateName = "multi-bin";
          src = symlinkJoin {
            name = "rust-bin-tests-cargo-bin-exe-auto";
            paths = [
              (mkFile "src/lib.rs" "")
              (mkFile "src/bin/tool-a.rs" ''
                fn main() { println!("tool-a ran"); }
              '')
              (mkFile "src/bin/tool-b.rs" ''
                fn main() { println!("tool-b ran"); }
              '')
              (mkFile "tests/run_tools.rs" ''
                #[test]
                fn runs_both() {
                    for (bin, want) in [
                        (env!("CARGO_BIN_EXE_tool-a"), "tool-a ran"),
                        (env!("CARGO_BIN_EXE_tool-b"), "tool-b ran"),
                    ] {
                        let out = std::process::Command::new(bin)
                            .output()
                            .expect("spawn");
                        assert!(out.status.success());
                        assert_eq!(String::from_utf8_lossy(&out.stdout).trim(), want);
                    }
                }
              '')
            ];
          };
          buildTests = true;
          expectedTestOutputs = [
            "test runs_both ... ok"
          ];
        };
        linkAgainstRlibCrate = {
          crateName = "foo";
          src = mkFile "src/main.rs" ''