Merge pull request #76820 from andir/buildRustCrate-tests
buildRustCrate: add `buildTests` flag to tell rustc to build tests instead of binaries
This commit is contained in:
commit
366dc671a9
6 changed files with 213 additions and 33 deletions
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
|
@ -82,6 +82,7 @@
|
|||
|
||||
# Rust
|
||||
/pkgs/development/compilers/rust @Mic92 @LnL7
|
||||
/pkgs/build-support/rust @andir
|
||||
|
||||
# Darwin-related
|
||||
/pkgs/stdenv/darwin @NixOS/darwin-maintainers
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
crateFeatures, crateRenames, libName, release, libPath,
|
||||
crateType, metadata, crateBin, hasCrateBin,
|
||||
extraRustcOpts, verbose, colors,
|
||||
buildTests
|
||||
}:
|
||||
|
||||
let
|
||||
|
@ -30,6 +31,7 @@
|
|||
baseRustcOpts
|
||||
);
|
||||
|
||||
build_bin = if buildTests then "build_bin_test" else "build_bin";
|
||||
in ''
|
||||
runHook preBuild
|
||||
${echo_build_heading colors}
|
||||
|
@ -48,14 +50,18 @@
|
|||
setup_link_paths
|
||||
|
||||
if [[ -e "$LIB_PATH" ]]; then
|
||||
build_lib $LIB_PATH
|
||||
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"}
|
||||
elif [[ -e "src/$LIB_NAME.rs" ]]; then
|
||||
build_lib src/$LIB_NAME.rs
|
||||
${lib.optionalString buildTests ''build_lib_test "src/$LIB_NAME.rs"''}
|
||||
fi
|
||||
|
||||
|
||||
|
||||
${lib.optionalString (lib.length crateBin > 0) (lib.concatMapStringsSep "\n" (bin: ''
|
||||
mkdir -p target/bin
|
||||
BIN_NAME='${bin.name or crateName}'
|
||||
|
@ -65,19 +71,39 @@
|
|||
'' else ''
|
||||
BIN_PATH='${bin.path}'
|
||||
''}
|
||||
build_bin "$BIN_NAME" "$BIN_PATH"
|
||||
${build_bin} "$BIN_NAME" "$BIN_PATH"
|
||||
'') 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
|
||||
${build_bin} ${crateName} src/main.rs
|
||||
fi
|
||||
for i in src/bin/*.rs; do #*/
|
||||
mkdir -p target/bin
|
||||
build_bin "$(basename $i .rs)" "$i"
|
||||
${build_bin} "$(basename $i .rs)" "$i"
|
||||
done
|
||||
''}
|
||||
# Remove object files to avoid "wrong ELF type"
|
||||
|
|
|
@ -39,12 +39,12 @@ let
|
|||
inherit lib stdenv echo_build_heading noisily mkRustcDepArgs rust;
|
||||
};
|
||||
|
||||
installCrate = import ./install-crate.nix;
|
||||
installCrate = import ./install-crate.nix { inherit stdenv; };
|
||||
in
|
||||
|
||||
crate_: lib.makeOverridable ({ rust, release, verbose, features, buildInputs, crateOverrides,
|
||||
dependencies, buildDependencies, crateRenames,
|
||||
extraRustcOpts,
|
||||
extraRustcOpts, buildTests,
|
||||
preUnpack, postUnpack, prePatch, patches, postPatch,
|
||||
preConfigure, postConfigure, preBuild, postBuild, preInstall, postInstall }:
|
||||
|
||||
|
@ -55,10 +55,12 @@ let crate = crate_ // (lib.attrByPath [ crate_.crateName ] (attr: {}) crateOverr
|
|||
"src" "buildInputs" "crateBin" "crateLib" "libName" "libPath"
|
||||
"buildDependencies" "dependencies" "features" "crateRenames"
|
||||
"crateName" "version" "build" "authors" "colors" "edition"
|
||||
"buildTests"
|
||||
];
|
||||
extraDerivationAttrs = lib.filterAttrs (n: v: ! lib.elem n processedAttrs) crate;
|
||||
extraDerivationAttrs = builtins.removeAttrs crate processedAttrs;
|
||||
buildInputs_ = buildInputs;
|
||||
extraRustcOpts_ = extraRustcOpts;
|
||||
buildTests_ = buildTests;
|
||||
|
||||
# take a list of crates that we depend on and override them to fit our overrides, rustc, release, …
|
||||
makeDependencies = map (dep: lib.getLib (dep.override { inherit release verbose crateOverrides; }));
|
||||
|
@ -72,13 +74,23 @@ in
|
|||
stdenv.mkDerivation (rec {
|
||||
|
||||
inherit (crate) crateName;
|
||||
inherit preUnpack postUnpack prePatch patches postPatch preConfigure postConfigure preBuild postBuild preInstall postInstall;
|
||||
inherit
|
||||
preUnpack
|
||||
postUnpack
|
||||
prePatch
|
||||
patches
|
||||
postPatch
|
||||
preConfigure
|
||||
postConfigure
|
||||
preBuild
|
||||
postBuild
|
||||
preInstall
|
||||
postInstall
|
||||
buildTests
|
||||
;
|
||||
|
||||
src = if lib.hasAttr "src" crate then
|
||||
crate.src
|
||||
else
|
||||
fetchCrate { inherit (crate) crateName version sha256; };
|
||||
name = "rust_${crate.crateName}-${crate.version}";
|
||||
src = crate.src or (fetchCrate { inherit (crate) crateName version sha256; });
|
||||
name = "rust_${crate.crateName}-${crate.version}${lib.optionalString buildTests_ "-test"}";
|
||||
depsBuildBuild = [ rust stdenv.cc ];
|
||||
buildInputs = (crate.buildInputs or []) ++ buildInputs_;
|
||||
dependencies = makeDependencies dependencies_;
|
||||
|
@ -122,6 +134,7 @@ stdenv.mkDerivation (rec {
|
|||
++ extraRustcOpts_
|
||||
++ (lib.optional (edition != null) "--edition ${edition}");
|
||||
|
||||
|
||||
configurePhase = configureCrate {
|
||||
inherit crateName buildDependencies completeDeps completeBuildDeps crateDescription
|
||||
crateFeatures crateRenames libName build workspace_member release libPath crateVersion
|
||||
|
@ -132,12 +145,14 @@ stdenv.mkDerivation (rec {
|
|||
inherit crateName dependencies
|
||||
crateFeatures crateRenames libName release libPath crateType
|
||||
metadata hasCrateBin crateBin verbose colors
|
||||
extraRustcOpts;
|
||||
extraRustcOpts buildTests;
|
||||
};
|
||||
installPhase = installCrate crateName metadata;
|
||||
installPhase = installCrate crateName metadata buildTests;
|
||||
|
||||
outputs = [ "out" "lib" ];
|
||||
outputDev = [ "lib" ];
|
||||
# depending on the test setting we are either producing something with bins
|
||||
# and libs or just test binaries
|
||||
outputs = if buildTests then [ "out" ] else [ "out" "lib" ];
|
||||
outputDev = if buildTests then [ "out" ] else [ "lib" ];
|
||||
|
||||
} // extraDerivationAttrs
|
||||
)) {
|
||||
|
@ -162,4 +177,5 @@ stdenv.mkDerivation (rec {
|
|||
dependencies = crate_.dependencies or [];
|
||||
buildDependencies = crate_.buildDependencies or [];
|
||||
crateRenames = crate_.crateRenames or {};
|
||||
buildTests = crate_.buildTests or false;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
crateName: metadata:
|
||||
''
|
||||
{ stdenv }:
|
||||
crateName: metadata: buildTests:
|
||||
if !buildTests then ''
|
||||
runHook preInstall
|
||||
# always create $out even if we do not have binaries. We are detecting binary targets during compilation, if those are missing there is no way to only have $lib
|
||||
mkdir $out
|
||||
|
@ -28,5 +29,23 @@ crateName: metadata:
|
|||
cp -P target/bin/* $out/bin # */
|
||||
fi
|
||||
fi
|
||||
runHook postInstall
|
||||
'' else
|
||||
# for tests we just put them all in the output. No execution.
|
||||
''
|
||||
runHook preInstall
|
||||
|
||||
mkdir -p $out/tests
|
||||
if [ -e target/bin ]; then
|
||||
find target/bin/ -type f -executable -exec cp {} $out/tests \;
|
||||
fi
|
||||
if [ -e target/lib ]; then
|
||||
find target/lib/ -type f \! -name '*.rlib' \
|
||||
-a \! -name '*${stdenv.hostPlatform.extensions.sharedLibrary}' \
|
||||
-a \! -name '*.d' \
|
||||
-executable \
|
||||
-print0 | xargs --no-run-if-empty --null install --target $out/tests;
|
||||
fi
|
||||
|
||||
runHook postInstall
|
||||
''
|
||||
|
|
|
@ -13,6 +13,7 @@ build_lib() {
|
|||
$BUILD_OUT_DIR \
|
||||
$EXTRA_BUILD \
|
||||
$EXTRA_FEATURES \
|
||||
$EXTRA_RUSTC_FLAGS \
|
||||
--color $colors
|
||||
|
||||
EXTRA_LIB=" --extern $CRATE_NAME=target/lib/lib$CRATE_NAME-$metadata.rlib"
|
||||
|
@ -22,9 +23,10 @@ build_lib() {
|
|||
}
|
||||
|
||||
build_bin() {
|
||||
crate_name=$1
|
||||
crate_name_=$(echo $crate_name | tr '-' '_')
|
||||
main_file=""
|
||||
local crate_name=$1
|
||||
local crate_name_=$(echo $crate_name | tr '-' '_')
|
||||
local main_file=""
|
||||
|
||||
if [[ ! -z $2 ]]; then
|
||||
main_file=$2
|
||||
fi
|
||||
|
@ -43,6 +45,7 @@ build_bin() {
|
|||
$BUILD_OUT_DIR \
|
||||
$EXTRA_BUILD \
|
||||
$EXTRA_FEATURES \
|
||||
$EXTRA_RUSTC_FLAGS \
|
||||
--color ${colors} \
|
||||
|
||||
if [ "$crate_name_" != "$crate_name" ]; then
|
||||
|
@ -50,6 +53,24 @@ build_bin() {
|
|||
fi
|
||||
}
|
||||
|
||||
build_lib_test() {
|
||||
local file="$1"
|
||||
EXTRA_RUSTC_FLAGS="--test $EXTRA_RUSTC_FLAGS" build_lib "$1" "$2"
|
||||
}
|
||||
|
||||
build_bin_test() {
|
||||
local crate="$1"
|
||||
local file="$2"
|
||||
EXTRA_RUSTC_FLAGS="--test $EXTRA_RUSTC_FLAGS" build_bin "$1" "$2"
|
||||
}
|
||||
|
||||
build_bin_test_file() {
|
||||
local file="$1"
|
||||
local derived_crate_name="${file//\//_}"
|
||||
derived_crate_name="${derived_crate_name%.rs}"
|
||||
build_bin_test "$derived_crate_name" "$file"
|
||||
}
|
||||
|
||||
setup_link_paths() {
|
||||
EXTRA_LIB=""
|
||||
if [[ -e target/link_ ]]; then
|
||||
|
|
|
@ -29,10 +29,30 @@ let
|
|||
}
|
||||
'';
|
||||
|
||||
mkTestFile = name: functionName: mkFile name ''
|
||||
#[cfg(test)]
|
||||
#[test]
|
||||
fn ${functionName}() {
|
||||
assert!(true);
|
||||
}
|
||||
'';
|
||||
mkTestFileWithMain = name: functionName: mkFile name ''
|
||||
#[cfg(test)]
|
||||
#[test]
|
||||
fn ${functionName}() {
|
||||
assert!(true);
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
'';
|
||||
|
||||
|
||||
mkLib = name: mkFile name "pub fn test() -> i32 { return 23; }";
|
||||
|
||||
mkTest = crateArgs: let
|
||||
crate = mkCrate crateArgs;
|
||||
crate = mkCrate (builtins.removeAttrs crateArgs ["expectedTestOutput"]);
|
||||
hasTests = crateArgs.buildTests or false;
|
||||
expectedTestOutputs = crateArgs.expectedTestOutputs or null;
|
||||
binaries = map (v: ''"${v.name}"'') (crateArgs.crateBin or []);
|
||||
isLib = crateArgs ? libName || crateArgs ? libPath;
|
||||
crateName = crateArgs.crateName or "nixtestcrate";
|
||||
|
@ -44,16 +64,28 @@ let
|
|||
src = mkBinExtern "src/main.rs" libName;
|
||||
};
|
||||
|
||||
in runCommand "run-buildRustCrate-${crateName}-test" {
|
||||
nativeBuildInputs = [ crate ];
|
||||
} ''
|
||||
${lib.concatStringsSep "\n" binaries}
|
||||
${lib.optionalString isLib ''
|
||||
test -e ${crate}/lib/*.rlib || exit 1
|
||||
${libTestBinary}/bin/run-test-${crateName}
|
||||
''}
|
||||
touch $out
|
||||
'';
|
||||
in
|
||||
assert expectedTestOutputs != null -> hasTests;
|
||||
assert hasTests -> expectedTestOutputs != null;
|
||||
|
||||
runCommand "run-buildRustCrate-${crateName}-test" {
|
||||
nativeBuildInputs = [ crate ];
|
||||
} (if !hasTests then ''
|
||||
${lib.concatStringsSep "\n" binaries}
|
||||
${lib.optionalString isLib ''
|
||||
test -e ${crate}/lib/*.rlib || exit 1
|
||||
${libTestBinary}/bin/run-test-${crateName}
|
||||
''}
|
||||
touch $out
|
||||
'' else ''
|
||||
for file in ${crate}/tests/*; do
|
||||
$file 2>&1 >> $out
|
||||
done
|
||||
set -e
|
||||
${lib.concatMapStringsSep "\n" (o: "grep '${o}' $out || { echo 'output \"${o}\" not found in:'; cat $out; exit 23; }") expectedTestOutputs}
|
||||
''
|
||||
);
|
||||
|
||||
in rec {
|
||||
|
||||
tests = let
|
||||
|
@ -85,6 +117,71 @@ let
|
|||
dependencies = [ (mkCrate { crateName = "foo"; libName = "foolib"; src = mkLib "src/lib.rs"; }) ];
|
||||
crateRenames = { "foo" = "foo_renamed"; };
|
||||
};
|
||||
rustLibTestsDefault = {
|
||||
src = mkTestFile "src/lib.rs" "baz";
|
||||
buildTests = true;
|
||||
expectedTestOutputs = [ "test baz ... ok" ];
|
||||
};
|
||||
rustLibTestsCustomLibName = {
|
||||
libName = "test_lib";
|
||||
src = mkTestFile "src/test_lib.rs" "foo";
|
||||
buildTests = true;
|
||||
expectedTestOutputs = [ "test foo ... ok" ];
|
||||
};
|
||||
rustLibTestsCustomLibPath = {
|
||||
libPath = "src/test_path.rs";
|
||||
src = mkTestFile "src/test_path.rs" "bar";
|
||||
buildTests = true;
|
||||
expectedTestOutputs = [ "test bar ... ok" ];
|
||||
};
|
||||
rustLibTestsCustomLibPathWithTests = {
|
||||
libPath = "src/test_path.rs";
|
||||
src = symlinkJoin {
|
||||
name = "rust-lib-tests-custom-lib-path-with-tests-dir";
|
||||
paths = [
|
||||
(mkTestFile "src/test_path.rs" "bar")
|
||||
(mkTestFile "tests/something.rs" "something")
|
||||
];
|
||||
};
|
||||
buildTests = true;
|
||||
expectedTestOutputs = [
|
||||
"test bar ... ok"
|
||||
"test something ... ok"
|
||||
];
|
||||
};
|
||||
rustBinTestsCombined = {
|
||||
src = symlinkJoin {
|
||||
name = "rust-bin-tests-combined";
|
||||
paths = [
|
||||
(mkTestFileWithMain "src/main.rs" "src_main")
|
||||
(mkTestFile "tests/foo.rs" "tests_foo")
|
||||
(mkTestFile "tests/bar.rs" "tests_bar")
|
||||
];
|
||||
};
|
||||
buildTests = true;
|
||||
expectedTestOutputs = [
|
||||
"test src_main ... ok"
|
||||
"test tests_foo ... ok"
|
||||
"test tests_bar ... ok"
|
||||
];
|
||||
};
|
||||
rustBinTestsSubdirCombined = {
|
||||
src = symlinkJoin {
|
||||
name = "rust-bin-tests-subdir-combined";
|
||||
paths = [
|
||||
(mkTestFileWithMain "src/main.rs" "src_main")
|
||||
(mkTestFile "tests/foo/main.rs" "tests_foo")
|
||||
(mkTestFile "tests/bar/main.rs" "tests_bar")
|
||||
];
|
||||
};
|
||||
buildTests = true;
|
||||
expectedTestOutputs = [
|
||||
"test src_main ... ok"
|
||||
"test tests_foo ... ok"
|
||||
"test tests_bar ... ok"
|
||||
];
|
||||
};
|
||||
|
||||
};
|
||||
brotliCrates = (callPackage ./brotli-crates.nix {});
|
||||
in lib.mapAttrs (key: value: mkTest (value // lib.optionalAttrs (!value?crateName) { crateName = key; })) cases // {
|
||||
|
|
Loading…
Reference in a new issue