From 1febd68e84313028e4ca1cbdbffcc9d426428f60 Mon Sep 17 00:00:00 2001 From: Andreas Rammhold Date: Thu, 6 Feb 2020 00:59:40 +0100 Subject: [PATCH 01/11] buildRustCrate: remap the current build dir to / for (more) reproducible builds (cherry picked from commit 56e11bc8dff88c59e9137badd1ede2f7b66ec6ca) --- pkgs/build-support/rust/build-rust-crate/build-crate.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/pkgs/build-support/rust/build-rust-crate/build-crate.nix b/pkgs/build-support/rust/build-rust-crate/build-crate.nix index dec49d24f524..2dcca75e299b 100644 --- a/pkgs/build-support/rust/build-rust-crate/build-crate.nix +++ b/pkgs/build-support/rust/build-rust-crate/build-crate.nix @@ -11,6 +11,7 @@ baseRustcOpts = [(if release then "-C opt-level=3" else "-C debuginfo=2")] ++ ["-C codegen-units=$NIX_BUILD_CORES"] + ++ ["--remap-path-prefix=$NIX_BUILD_TOP=/" ] ++ [(mkRustcDepArgs dependencies crateRenames)] ++ [crateFeatures] ++ extraRustcOpts From be561c771c769707102ee27c59da78c766aac01d Mon Sep 17 00:00:00 2001 From: Andreas Rammhold Date: Tue, 11 Feb 2020 11:48:45 +0100 Subject: [PATCH 02/11] buildRustCrate: remove superfluous dependency overrides MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit By overriding each dependency on every level of the dependency tree we are creating a lot of unnecessary instances of the same derivation Looking at the output size of `nix-instantiate --trace-function-calls -vvvv …` and the execution time I got about a 10x improvement after applying this change. It was probably good intentions that lead to these overrides but in practice no tooling (that I know of) really needs this. `carnix` and `crate2nix` are fine without those overrides. Furthermore I believe that it is the job of the tooling around `buildRustCrate` to provide a coherent set of overrides. By not enforcing all of the overrides, debug flags, verbosity, … to be the same throughout the closure we also allow consumers to override specific aspects of the crates. Some (older?) crates might need different `crateOverrides` then newer crates with the same name. Currently such situations can not (easily) be implemented with the override in-place. (cherry picked from commit be5597fc9d2f4fefac097d3c6e9364bedf548078) --- pkgs/build-support/rust/build-rust-crate/default.nix | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/pkgs/build-support/rust/build-rust-crate/default.nix b/pkgs/build-support/rust/build-rust-crate/default.nix index 569b48d25aea..94b64a1225cb 100644 --- a/pkgs/build-support/rust/build-rust-crate/default.nix +++ b/pkgs/build-support/rust/build-rust-crate/default.nix @@ -62,9 +62,6 @@ let crate = crate_ // (lib.attrByPath [ crate_.crateName ] (attr: {}) crateOverr 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; })); - # crate2nix has a hack for the old bash based build script that did split # entries at `,`. No we have to work around that hack. # https://github.com/kolloch/crate2nix/blame/5b19c1b14e1b0e5522c3e44e300d0b332dc939e7/crate2nix/templates/build.nix.tera#L89 @@ -93,8 +90,8 @@ stdenv.mkDerivation (rec { name = "rust_${crate.crateName}-${crate.version}${lib.optionalString buildTests_ "-test"}"; depsBuildBuild = [ rust stdenv.cc ]; buildInputs = (crate.buildInputs or []) ++ buildInputs_; - dependencies = makeDependencies dependencies_; - buildDependencies = makeDependencies buildDependencies_; + dependencies = map lib.getLib dependencies_; + buildDependencies = map lib.getLib buildDependencies_; completeDeps = lib.unique (dependencies ++ lib.concatMap (dep: dep.completeDeps) dependencies); completeBuildDeps = lib.unique ( From 7d8d24efe71ed1b428f899ea77df04cfe9edb7e5 Mon Sep 17 00:00:00 2001 From: Peter Kolloch Date: Sun, 1 Mar 2020 12:58:26 +0100 Subject: [PATCH 03/11] buildRustCrate: refactor colored logging * Make errors include the crate name and make them much more prominent. * Move more code into lib.sh * Already source generated logging code and lib.sh in configure (cherry picked from commit 04e7462ee61c3f0477b40204f4d90e6b6d5d0a32) --- .../rust/build-rust-crate/build-crate.nix | 7 +- .../rust/build-rust-crate/configure-crate.nix | 8 ++- .../rust/build-rust-crate/default.nix | 8 +-- .../rust/build-rust-crate/lib.sh | 10 ++- .../rust/build-rust-crate/log.nix | 70 +++++++++++++------ 5 files changed, 68 insertions(+), 35 deletions(-) diff --git a/pkgs/build-support/rust/build-rust-crate/build-crate.nix b/pkgs/build-support/rust/build-rust-crate/build-crate.nix index 2dcca75e299b..9759235e30e3 100644 --- a/pkgs/build-support/rust/build-rust-crate/build-crate.nix +++ b/pkgs/build-support/rust/build-rust-crate/build-crate.nix @@ -1,4 +1,4 @@ -{ lib, stdenv, echo_build_heading, noisily, mkRustcDepArgs, rust }: +{ lib, stdenv, mkRustcDepArgs, rust }: { crateName, dependencies, crateFeatures, crateRenames, libName, release, libPath, @@ -35,16 +35,13 @@ build_bin = if buildTests then "build_bin_test" else "build_bin"; in '' runHook preBuild - ${echo_build_heading colors} - ${noisily colors verbose} - + # 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}" - source ${./lib.sh} CRATE_NAME='${lib.replaceStrings ["-"] ["_"] libName}' diff --git a/pkgs/build-support/rust/build-rust-crate/configure-crate.nix b/pkgs/build-support/rust/build-rust-crate/configure-crate.nix index c146ffef5ff6..bbe7090aed3d 100644 --- a/pkgs/build-support/rust/build-rust-crate/configure-crate.nix +++ b/pkgs/build-support/rust/build-rust-crate/configure-crate.nix @@ -1,4 +1,4 @@ -{ lib, stdenv, echo_build_heading, noisily, mkRustcDepArgs }: +{ lib, stdenv, echo_colored, noisily, mkRustcDepArgs }: { build , buildDependencies , colors @@ -32,9 +32,11 @@ let version_ = lib.splitString "-" crateVersion; completeBuildDepsDir = lib.concatStringsSep " " completeBuildDeps; in '' cd ${workspace_member} - runHook preConfigure - ${echo_build_heading colors} + ${echo_colored colors} ${noisily colors verbose} + source ${./lib.sh} + + runHook preConfigure symlink_dependency() { # $1 is the nix-store path of a dependency # $2 is the target path diff --git a/pkgs/build-support/rust/build-rust-crate/default.nix b/pkgs/build-support/rust/build-rust-crate/default.nix index 94b64a1225cb..35cb49293e84 100644 --- a/pkgs/build-support/rust/build-rust-crate/default.nix +++ b/pkgs/build-support/rust/build-rust-crate/default.nix @@ -4,7 +4,7 @@ # This can be useful for deploying packages with NixOps, and to share # binary dependencies between projects. -{ lib, stdenv, defaultCrateOverrides, fetchCrate, rustc, rust }: +{ lib, stdenv, defaultCrateOverrides, fetchCrate, rustc, rust, cargo, jq }: let # This doesn't appear to be officially documented anywhere yet. @@ -29,14 +29,14 @@ let " --extern ${name}=${dep.lib}/lib/lib${extern}-${dep.metadata}${stdenv.hostPlatform.extensions.sharedLibrary}") ) dependencies; - inherit (import ./log.nix { inherit lib; }) noisily echo_build_heading; + inherit (import ./log.nix { inherit lib; }) noisily echo_colored; configureCrate = import ./configure-crate.nix { - inherit lib stdenv echo_build_heading noisily mkRustcDepArgs; + inherit lib stdenv echo_colored noisily mkRustcDepArgs; }; buildCrate = import ./build-crate.nix { - inherit lib stdenv echo_build_heading noisily mkRustcDepArgs rust; + inherit lib stdenv mkRustcDepArgs rust; }; installCrate = import ./install-crate.nix { inherit stdenv; }; diff --git a/pkgs/build-support/rust/build-rust-crate/lib.sh b/pkgs/build-support/rust/build-rust-crate/lib.sh index d4d9317496f5..35e804aa1044 100644 --- a/pkgs/build-support/rust/build-rust-crate/lib.sh +++ b/pkgs/build-support/rust/build-rust-crate/lib.sh @@ -1,3 +1,11 @@ +echo_build_heading() { + if (( $# == 1 )); then + echo_colored "Building $1" + else + echo_colored "Building $1 ($2)" + fi +} + build_lib() { lib_src=$1 echo_build_heading $lib_src ${libName} @@ -132,7 +140,7 @@ search_for_bin_path() { done if [[ -z "$BIN_PATH" ]]; then - echo "failed to find file for binary target: $BIN_NAME" >&2 + echo_error "ERROR: failed to find file for binary target: $BIN_NAME" >&2 exit 1 fi } diff --git a/pkgs/build-support/rust/build-rust-crate/log.nix b/pkgs/build-support/rust/build-rust-crate/log.nix index 25181c787e2c..a7e2cb4f4639 100644 --- a/pkgs/build-support/rust/build-rust-crate/log.nix +++ b/pkgs/build-support/rust/build-rust-crate/log.nix @@ -1,30 +1,56 @@ { lib }: -{ - echo_build_heading = colors: '' - echo_build_heading() { - start="" - end="" - ${lib.optionalString (colors == "always") '' - start="$(printf '\033[0;1;32m')" #set bold, and set green. - end="$(printf '\033[0m')" #returns to "normal" - ''} - if (( $# == 1 )); then - echo "$start""Building $1""$end" - else - echo "$start""Building $1 ($2)""$end" - fi + +let echo_colored_body = start_escape: + # Body of a function that behaves like "echo" but + # has the output colored by the given start_escape + # sequence. E.g. + # + # * echo_x "Building ..." + # * echo_x -n "Running " + # + # This is more complicated than apparent at first sight + # because: + # * The color markers and the text must be print + # in the same echo statement. Otherise, other + # intermingled text from concurrent builds will + # be colored as well. + # * We need to preserve the trailing newline of the + # echo if and only if it is present. Bash likes + # to strip those if we capture the output of echo + # in a variable. + # * Leading "-" will be interpreted by test as an + # option for itself. Therefore, we prefix it with + # an x in `[[ "x$1" =~ ^x- ]]`. + '' + local echo_args=""; + while [[ "x$1" =~ ^x- ]]; do + echo_args+=" $1" + shift + done + + local start_escape="$(printf '${start_escape}')" + local reset="$(printf '\033[0m')" + echo $echo_args $start_escape"$@"$reset + ''; + echo_conditional_colored_body = colors: start_escape: + if colors == "always" + then (echo_colored_body start_escape) + else ''echo "$@"''; +in { + echo_colored = colors: '' + echo_colored() { + ${echo_conditional_colored_body colors ''\033[0;1;32m''} } - ''; + + echo_error() { + ${echo_conditional_colored_body colors ''\033[0;1;31m''} + } + ''; + noisily = colors: verbose: '' noisily() { - start="" - end="" - ${lib.optionalString (colors == "always") '' - start="$(printf '\033[0;1;32m')" #set bold, and set green. - end="$(printf '\033[0m')" #returns to "normal" - ''} ${lib.optionalString verbose '' - echo -n "$start"Running "$end" + echo_colored -n "Running " echo $@ ''} $@ From 096520620039e17421963eb1b34cafe23c5f4853 Mon Sep 17 00:00:00 2001 From: Peter Kolloch Date: Sun, 1 Mar 2020 13:34:36 +0100 Subject: [PATCH 04/11] build-support/rust/buildRustCrate: Search for matching Cargo.toml in sub directories This is what cargo does for git repositories. See related issues: * https://github.com/kolloch/crate2nix/issues/53 * https://github.com/kolloch/crate2nix/issues/33 (cherry picked from commit 8a6638daa96318f0cf6d37a1d1d67f1f1c06bd05) --- .../rust/build-rust-crate/configure-crate.nix | 15 ++++++- .../rust/build-rust-crate/default.nix | 4 +- .../rust/build-rust-crate/lib.sh | 34 ++++++++++++++ .../rust/build-rust-crate/test/default.nix | 44 ++++++++++++++++++- 4 files changed, 94 insertions(+), 3 deletions(-) diff --git a/pkgs/build-support/rust/build-rust-crate/configure-crate.nix b/pkgs/build-support/rust/build-rust-crate/configure-crate.nix index bbe7090aed3d..013b99a77b4b 100644 --- a/pkgs/build-support/rust/build-rust-crate/configure-crate.nix +++ b/pkgs/build-support/rust/build-rust-crate/configure-crate.nix @@ -31,12 +31,25 @@ let version_ = lib.splitString "-" crateVersion; completeDepsDir = lib.concatStringsSep " " completeDeps; completeBuildDepsDir = lib.concatStringsSep " " completeBuildDeps; in '' - cd ${workspace_member} ${echo_colored colors} ${noisily colors verbose} source ${./lib.sh} + ${lib.optionalString (workspace_member != null) '' + noisily cd "${workspace_member}" +''} + ${lib.optionalString (workspace_member == null) '' + echo_colored "Searching for matching Cargo.toml (${crateName})" + local cargo_toml_dir=$(matching_cargo_toml_dir "${crateName}") + if [ -z "$cargo_toml_dir" ]; then + echo_error "ERROR configuring ${crateName}: No matching Cargo.toml in $(pwd) found." >&2 + exit 23 + fi + noisily cd "$cargo_toml_dir" +''} + runHook preConfigure + symlink_dependency() { # $1 is the nix-store path of a dependency # $2 is the target path diff --git a/pkgs/build-support/rust/build-rust-crate/default.nix b/pkgs/build-support/rust/build-rust-crate/default.nix index 35cb49293e84..70d48bef8c9b 100644 --- a/pkgs/build-support/rust/build-rust-crate/default.nix +++ b/pkgs/build-support/rust/build-rust-crate/default.nix @@ -88,7 +88,7 @@ stdenv.mkDerivation (rec { src = crate.src or (fetchCrate { inherit (crate) crateName version sha256; }); name = "rust_${crate.crateName}-${crate.version}${lib.optionalString buildTests_ "-test"}"; - depsBuildBuild = [ rust stdenv.cc ]; + depsBuildBuild = [ rust stdenv.cc cargo jq ]; buildInputs = (crate.buildInputs or []) ++ buildInputs_; dependencies = map lib.getLib dependencies_; buildDependencies = map lib.getLib buildDependencies_; @@ -114,6 +114,8 @@ stdenv.mkDerivation (rec { in lib.substring 0 10 hashedMetadata; build = crate.build or ""; + # Either set to a concrete sub path to the crate root + # or use `null` for auto-detect. workspace_member = crate.workspace_member or "."; crateVersion = crate.version; crateDescription = crate.description or ""; diff --git a/pkgs/build-support/rust/build-rust-crate/lib.sh b/pkgs/build-support/rust/build-rust-crate/lib.sh index 35e804aa1044..0f08c133e557 100644 --- a/pkgs/build-support/rust/build-rust-crate/lib.sh +++ b/pkgs/build-support/rust/build-rust-crate/lib.sh @@ -144,3 +144,37 @@ search_for_bin_path() { exit 1 fi } + +# Extracts cargo_toml_path of the matching crate. +matching_cargo_toml_path() { + local manifest_path="$1" + local expected_crate_name="$2" + + # If the Cargo.toml is not a workspace root, + # it will only contain one package in ".packages" + # because "--no-deps" suppressed dependency resolution. + # + # But to make it more general, we search for a matching + # crate in all packages and use the manifest path that + # is referenced there. + cargo metadata --no-deps --format-version 1 \ + --manifest-path "$manifest_path" \ + | jq -r '.packages[] + | select( .name == "'$expected_crate_name'") + | .manifest_path' +} + +# Find a Cargo.toml in the current or any sub directory +# with a matching crate name. +matching_cargo_toml_dir() { + local expected_crate_name="$1" + + find -L -name Cargo.toml | sort | while read manifest_path; do + echo "...checking manifest_path $manifest_path" >&2 + local matching_path="$(matching_cargo_toml_path "$manifest_path" "$expected_crate_name")" + if [ -n "${matching_path}" ]; then + echo "$(dirname $matching_path)" + break + fi + done +} \ No newline at end of file diff --git a/pkgs/build-support/rust/build-rust-crate/test/default.nix b/pkgs/build-support/rust/build-rust-crate/test/default.nix index 6aad02992c1b..710045664686 100644 --- a/pkgs/build-support/rust/build-rust-crate/test/default.nix +++ b/pkgs/build-support/rust/build-rust-crate/test/default.nix @@ -8,6 +8,14 @@ let } // args; in buildRustCrate p; + mkCargoToml = + { name, crateVersion ? "0.1.0", path ? "Cargo.toml" }: + mkFile path '' + [package] + name = ${builtins.toJSON name} + version = ${builtins.toJSON crateVersion} + ''; + mkFile = destination: text: writeTextFile { name = "src"; destination = "/${destination}"; @@ -89,7 +97,7 @@ let in rec { tests = let - cases = { + cases = rec { libPath = { libPath = "src/my_lib.rs"; src = mkLib "src/my_lib.rs"; }; srcLib = { src = mkLib "src/lib.rs"; }; @@ -220,6 +228,40 @@ let ]; }; }; + rustCargoTomlInSubDir = { + # The "workspace_member" can be set to the sub directory with the crate to build. + # By default ".", meaning the top level directory is assumed. + # Using null will trigger a search. + workspace_member = null; + src = symlinkJoin rec { + name = "find-cargo-toml"; + paths = [ + (mkCargoToml { name = "ignoreMe"; }) + (mkTestFileWithMain "src/main.rs" "ignore_main") + + (mkCargoToml { name = "rustCargoTomlInSubDir"; path = "subdir/Cargo.toml"; }) + (mkTestFileWithMain "subdir/src/main.rs" "src_main") + (mkTestFile "subdir/tests/foo/main.rs" "tests_foo") + (mkTestFile "subdir/tests/bar/main.rs" "tests_bar") + ]; + }; + buildTests = true; + expectedTestOutputs = [ + "test src_main ... ok" + "test tests_foo ... ok" + "test tests_bar ... ok" + ]; + }; + + rustCargoTomlInTopDir = + let + withoutCargoTomlSearch = builtins.removeAttrs rustCargoTomlInSubDir [ "workspace_member" ]; + in + withoutCargoTomlSearch // { + expectedTestOutputs = [ + "test ignore_main ... ok" + ]; + }; }; brotliCrates = (callPackage ./brotli-crates.nix {}); in lib.mapAttrs (key: value: mkTest (value // lib.optionalAttrs (!value?crateName) { crateName = key; })) cases // { From ed3dc871607079765547d6a45f727e9e7064e00b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20de=20Kok?= Date: Thu, 12 Mar 2020 12:29:31 +0100 Subject: [PATCH 05/11] buildRustCrate: only link build deps into build script MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to the Cargo documentation: > The build script does not have access to the dependencies listed in > the dependencies or dev-dependencies section (they’re not built > yet!). Also, build dependencies are not available to the package > itself unless also explicitly added in the [dependencies] table. https://doc.rust-lang.org/cargo/reference/build-scripts.html This change separates linkage of regular dependencies and build dependencies. (cherry picked from commit ea6e048c37eff63e057f767410acd8fc911ba078) --- .../rust/build-rust-crate/configure-crate.nix | 63 ++++++++++++++----- .../rust/build-rust-crate/lib.sh | 1 + .../rust/build-rust-crate/test/default.nix | 30 +++++++++ 3 files changed, 79 insertions(+), 15 deletions(-) diff --git a/pkgs/build-support/rust/build-rust-crate/configure-crate.nix b/pkgs/build-support/rust/build-rust-crate/configure-crate.nix index 013b99a77b4b..2f7d3b77f397 100644 --- a/pkgs/build-support/rust/build-rust-crate/configure-crate.nix +++ b/pkgs/build-support/rust/build-rust-crate/configure-crate.nix @@ -56,32 +56,66 @@ in '' i=$1 ln -s -f $i/lib/*.rlib $2 #*/ ln -s -f $i/lib/*.so $i/lib/*.dylib $2 #*/ - if [ -e "$i/lib/link" ]; then - cat $i/lib/link >> target/link - cat $i/lib/link >> target/link.final - fi if [ -e $i/env ]; then source $i/env fi } + # The following steps set up the dependencies of the crate. Two + # kinds of dependencies are distinguished: build dependencies + # (used by the build script) and crate dependencies. For each + # dependency we have to: + # + # - Make its Rust library available to rustc. This is done by + # symlinking all library dependencies into a directory that + # can be provided to rustc. + # - Accumulate linking flags. These flags are largely used for + # linking native libraries. + # + # The crate link flags are added to the `link` and `link.final` + # files. The `link` file is used for linkage in the current + # crate. The `link.final` file will be copied to the output and can + # be used by downstream crates to get the linker flags of this + # crate. + mkdir -p target/{deps,lib,build,buildDeps} chmod uga+w target -R echo ${extraLinkFlags} > target/link echo ${extraLinkFlags} > target/link.final + + # Prepare crate dependencies for i in ${completeDepsDir}; do symlink_dependency $i target/deps + if [ -e "$i/lib/link" ]; then + cat $i/lib/link >> target/link + cat $i/lib/link >> target/link.final + fi done + + # Prepare crate build dependencies that are used for the build script. for i in ${completeBuildDepsDir}; do - symlink_dependency $i target/buildDeps + symlink_dependency $i target/buildDeps + if [ -e "$i/lib/link" ]; then + cat $i/lib/link >> target/link.build + fi done - if [[ -e target/link ]]; then - sort -u target/link > target/link.sorted - mv target/link.sorted target/link - sort -u target/link.final > target/link.final.sorted - mv target/link.final.sorted target/link.final - tr '\n' ' ' < target/link > target/link_ + + # Remove duplicate linker flags from the build dependencies. + if [[ -e target/link.build ]]; then + sort -u target/link.build > target/link.build.sorted + mv target/link.build.sorted target/link.build fi + + # Remove duplicate linker flags from the dependencies. + sort -u target/link > target/link.sorted + mv target/link.sorted target/link + tr '\n' ' ' < target/link > target/link_ + + # Remove duplicate linker flags from the that are written + # to the derivation's output. + sort -u target/link.final > target/link.final.sorted + mv target/link.final.sorted target/link.final + EXTRA_BUILD="" BUILD_OUT_DIR="" export CARGO_PKG_NAME=${crateName} @@ -120,15 +154,14 @@ in '' elif [[ -e "build.rs" ]]; then BUILD="build.rs" fi + + # Compile and run the build script, when available. if [[ ! -z "$BUILD" ]] ; then echo_build_heading "$BUILD" ${libName} mkdir -p target/build/${crateName} EXTRA_BUILD_FLAGS="" - if [ -e target/link_ ]; then - EXTRA_BUILD_FLAGS=$(cat target/link_) - fi if [ -e target/link.build ]; then - EXTRA_BUILD_FLAGS="$EXTRA_BUILD_FLAGS $(cat target/link.build)" + EXTRA_BUILD_FLAGS="$EXTRA_BUILD_FLAGS $(tr '\n' ' ' < target/link.build)" fi noisily rustc --crate-name build_script_build $BUILD --crate-type bin ${rustcOpts} \ ${crateFeatures} --out-dir target/build/${crateName} --emit=dep-info,link \ diff --git a/pkgs/build-support/rust/build-rust-crate/lib.sh b/pkgs/build-support/rust/build-rust-crate/lib.sh index 0f08c133e557..5843ee98b0d5 100644 --- a/pkgs/build-support/rust/build-rust-crate/lib.sh +++ b/pkgs/build-support/rust/build-rust-crate/lib.sh @@ -79,6 +79,7 @@ build_bin_test_file() { build_bin_test "$derived_crate_name" "$file" } +# Add additional link options that were provided by the build script. setup_link_paths() { EXTRA_LIB="" if [[ -e target/link_ ]]; then diff --git a/pkgs/build-support/rust/build-rust-crate/test/default.nix b/pkgs/build-support/rust/build-rust-crate/test/default.nix index 710045664686..f24583c41fcb 100644 --- a/pkgs/build-support/rust/build-rust-crate/test/default.nix +++ b/pkgs/build-support/rust/build-rust-crate/test/default.nix @@ -207,6 +207,36 @@ let }) ]; }; + buildScriptDeps = let + depCrate = boolVal: mkCrate { + crateName = "bar"; + src = mkFile "src/lib.rs" '' + pub const baz: bool = ${boolVal}; + ''; + }; + in { + crateName = "foo"; + src = symlinkJoin { + name = "build-script-and-main"; + paths = [ + (mkFile "src/main.rs" '' + extern crate bar; + #[cfg(test)] + #[test] + fn baz_false() { assert!(!bar::baz); } + fn main() { } + '') + (mkFile "build.rs" '' + extern crate bar; + fn main() { assert!(bar::baz); } + '') + ]; + }; + buildDependencies = [ (depCrate "true") ]; + dependencies = [ (depCrate "false") ]; + buildTests = true; + expectedTestOutputs = [ "test baz_false ... ok" ]; + }; # Regression test for https://github.com/NixOS/nixpkgs/issues/74071 # Whenevever a build.rs file is generating files those should not be overlayed onto the actual source dir buildRsOutDirOverlay = { From fcb8dc70f332e85adc5803ad3616d524e68ba623 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20de=20Kok?= Date: Fri, 13 Mar 2020 11:21:07 +0100 Subject: [PATCH 06/11] buildRustCrate: sort linker options in-place (cherry picked from commit 412c72d20f067dfb45aaa041c2333fbdc82dca6d) --- .../rust/build-rust-crate/configure-crate.nix | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/pkgs/build-support/rust/build-rust-crate/configure-crate.nix b/pkgs/build-support/rust/build-rust-crate/configure-crate.nix index 2f7d3b77f397..61c39c6b21c0 100644 --- a/pkgs/build-support/rust/build-rust-crate/configure-crate.nix +++ b/pkgs/build-support/rust/build-rust-crate/configure-crate.nix @@ -102,19 +102,16 @@ in '' # Remove duplicate linker flags from the build dependencies. if [[ -e target/link.build ]]; then - sort -u target/link.build > target/link.build.sorted - mv target/link.build.sorted target/link.build + sort -uo target/link.build target/link.build fi # Remove duplicate linker flags from the dependencies. - sort -u target/link > target/link.sorted - mv target/link.sorted target/link + sort -uo target/link target/link tr '\n' ' ' < target/link > target/link_ # Remove duplicate linker flags from the that are written # to the derivation's output. - sort -u target/link.final > target/link.final.sorted - mv target/link.final.sorted target/link.final + sort -uo target/link.final target/link.final EXTRA_BUILD="" BUILD_OUT_DIR="" From eb7d4502e1d22fdf0d2961d0f41bc32887c35357 Mon Sep 17 00:00:00 2001 From: Alyssa Ross Date: Fri, 27 Mar 2020 09:55:33 +0000 Subject: [PATCH 07/11] buildRustCrate: fewer backslashes This is a slight readability boost, I think. (cherry picked from commit 75338763120e06cfe787361ac52eae302c2cc28a) --- pkgs/build-support/rust/build-rust-crate/default.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/build-support/rust/build-rust-crate/default.nix b/pkgs/build-support/rust/build-rust-crate/default.nix index 70d48bef8c9b..a175ba9d7f6e 100644 --- a/pkgs/build-support/rust/build-rust-crate/default.nix +++ b/pkgs/build-support/rust/build-rust-crate/default.nix @@ -100,7 +100,7 @@ stdenv.mkDerivation (rec { ); crateFeatures = lib.optionalString (crate ? features) - (lib.concatMapStringsSep " " (f: "--cfg feature=\\\"${f}\\\"") (crate.features ++ features)); + (lib.concatMapStringsSep " " (f: ''--cfg feature=\"${f}\"'') (crate.features ++ features)); libName = if crate ? libName then crate.libName else crate.crateName; libPath = if crate ? libPath then crate.libPath else ""; From 259787549ac4461b79eb861a9a111e497cf6e139 Mon Sep 17 00:00:00 2001 From: Symphorien Gibol Date: Wed, 25 Mar 2020 12:00:00 +0000 Subject: [PATCH 08/11] buildRustCrate: don't sort link flags Linkage order is significant and sorting can result in link errors. (cherry picked from commit d8b853799d6658582a6039be8febfbd925d967aa) --- pkgs/build-support/rust/build-rust-crate/configure-crate.nix | 2 +- pkgs/build-support/rust/build-rust-crate/lib.sh | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/pkgs/build-support/rust/build-rust-crate/configure-crate.nix b/pkgs/build-support/rust/build-rust-crate/configure-crate.nix index 61c39c6b21c0..ed8100537090 100644 --- a/pkgs/build-support/rust/build-rust-crate/configure-crate.nix +++ b/pkgs/build-support/rust/build-rust-crate/configure-crate.nix @@ -172,7 +172,7 @@ in '' set +e EXTRA_BUILD=$(sed -n "s/^cargo:rustc-flags=\(.*\)/\1/p" target/build/${crateName}.opt | tr '\n' ' ' | sort -u) EXTRA_FEATURES=$(sed -n "s/^cargo:rustc-cfg=\(.*\)/--cfg \1/p" target/build/${crateName}.opt | tr '\n' ' ') - EXTRA_LINK=$(sed -n "s/^cargo:rustc-link-lib=\(.*\)/\1/p" target/build/${crateName}.opt | tr '\n' ' ' | sort -u) + EXTRA_LINK=$(sed -n "s/^cargo:rustc-link-lib=\(.*\)/\1/p" target/build/${crateName}.opt | tr '\n' ' ') EXTRA_LINK_SEARCH=$(sed -n "s/^cargo:rustc-link-search=\(.*\)/\1/p" target/build/${crateName}.opt | tr '\n' ' ' | sort -u) for env in $(sed -n "s/^cargo:rustc-env=\(.*\)/\1/p" target/build/${crateName}.opt); do diff --git a/pkgs/build-support/rust/build-rust-crate/lib.sh b/pkgs/build-support/rust/build-rust-crate/lib.sh index 5843ee98b0d5..6cf3481754b2 100644 --- a/pkgs/build-support/rust/build-rust-crate/lib.sh +++ b/pkgs/build-support/rust/build-rust-crate/lib.sh @@ -105,11 +105,6 @@ setup_link_paths() { 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 From 9408b0cf38a031cbe273baa20f182d1d336309ac Mon Sep 17 00:00:00 2001 From: Symphorien Gibol Date: Sat, 28 Mar 2020 12:00:00 +0000 Subject: [PATCH 09/11] buildRustCrateTests: add regression test for link order (cherry picked from commit 2f7fb1c49732105c8937b66b7d92402c04546073) --- .../rust/build-rust-crate/test/default.nix | 54 ++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/pkgs/build-support/rust/build-rust-crate/test/default.nix b/pkgs/build-support/rust/build-rust-crate/test/default.nix index f24583c41fcb..2251a1b40f2e 100644 --- a/pkgs/build-support/rust/build-rust-crate/test/default.nix +++ b/pkgs/build-support/rust/build-rust-crate/test/default.nix @@ -1,4 +1,4 @@ -{ lib, buildRustCrate, runCommand, writeTextFile, symlinkJoin, callPackage, releaseTools }: +{ lib, buildRustCrate, runCommand, runCommandCC, writeTextFile, symlinkJoin, callPackage, releaseTools }: let mkCrate = args: let p = { @@ -258,6 +258,58 @@ let ]; }; }; + # Regression test for https://github.com/NixOS/nixpkgs/pull/83379 + # link flag order should be preserved + linkOrder = { + src = symlinkJoin { + name = "buildrs-out-dir-overlay"; + paths = [ + (mkFile "build.rs" '' + fn main() { + // in the other order, linkage will fail + println!("cargo:rustc-link-lib=b"); + println!("cargo:rustc-link-lib=a"); + } + '') + (mkFile "src/main.rs" '' + extern "C" { + fn hello_world(); + } + fn main() { + unsafe { + hello_world(); + } + } + '') + ]; + }; + buildInputs = let + compile = name: text: runCommandCC name {} '' + mkdir -p $out/lib + $CC -shared -o $out/lib/${name}.so ${writeTextFile { + name = "${name}-src.c"; + inherit text; + }} + ''; + b = compile "libb" '' + #include + + void hello(); + + void hello_world() { + hello(); + printf(" world!\n"); + } + ''; + a = compile "liba" '' + #include + + void hello() { + printf("hello"); + } + ''; + in [ a b ]; + }; rustCargoTomlInSubDir = { # The "workspace_member" can be set to the sub directory with the crate to build. # By default ".", meaning the top level directory is assumed. From 32cbd89e220b2f21dfcd57c8177b80afaf6932f7 Mon Sep 17 00:00:00 2001 From: Andreas Rammhold Date: Sat, 28 Mar 2020 20:45:46 +0100 Subject: [PATCH 10/11] buildRustCrateTests: Fix link order test on darwin As it turns out Darwin does most of the things differently then "normal" systems. They are using a different shared library extension and require an obscure commandline parameter that has to be added to every build system out there. That issue seems to be with clang on Darwin as on Linux that flag isn't required to build the very same tests (when using clang). After adjusting these two details the tests are running fine on the darwin box that I was able to obtain. (cherry picked from commit c8de31baa6d820c9275d4708c296c4af370cf63b) --- .../rust/build-rust-crate/test/default.nix | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/pkgs/build-support/rust/build-rust-crate/test/default.nix b/pkgs/build-support/rust/build-rust-crate/test/default.nix index 2251a1b40f2e..598b5daed1be 100644 --- a/pkgs/build-support/rust/build-rust-crate/test/default.nix +++ b/pkgs/build-support/rust/build-rust-crate/test/default.nix @@ -1,4 +1,13 @@ -{ lib, buildRustCrate, runCommand, runCommandCC, writeTextFile, symlinkJoin, callPackage, releaseTools }: +{ lib +, stdenv +, buildRustCrate +, runCommand +, runCommandCC +, writeTextFile +, symlinkJoin +, callPackage +, releaseTools +}: let mkCrate = args: let p = { @@ -284,12 +293,18 @@ let ]; }; buildInputs = let - compile = name: text: runCommandCC name {} '' - mkdir -p $out/lib - $CC -shared -o $out/lib/${name}.so ${writeTextFile { + compile = name: text: let + src = writeTextFile { name = "${name}-src.c"; inherit text; - }} + }; + in runCommandCC name {} '' + mkdir -p $out/lib + # Note: On darwin (which defaults to clang) we have to add + # `-undefined dynamic_lookup` as otherwise the compilation fails. + cc -shared \ + ${lib.optionalString stdenv.isDarwin "-undefined dynamic_lookup"} \ + -o $out/lib/${name}${stdenv.hostPlatform.extensions.sharedLibrary} ${src} ''; b = compile "libb" '' #include From 040f6116090397caae9ff1490e77bc201741db11 Mon Sep 17 00:00:00 2001 From: Peter Kolloch Date: Sat, 28 Mar 2020 15:23:12 +0100 Subject: [PATCH 11/11] buildRustCrate: Add tests for checking files in outputs. ...and remove superfluous dependency files (*.d). ...and copy dSYM directories on Mac OS when in release=false mode. (cherry picked from commit 782b304dba903ca82763b152e0727187628a32b0) --- .../rust/build-rust-crate/install-crate.nix | 4 +- .../rust/build-rust-crate/lib.sh | 2 - .../rust/build-rust-crate/test/default.nix | 138 +++++++++++++++++- 3 files changed, 134 insertions(+), 10 deletions(-) diff --git a/pkgs/build-support/rust/build-rust-crate/install-crate.nix b/pkgs/build-support/rust/build-rust-crate/install-crate.nix index 5ba7b69bedc5..f4a4dcdb0d94 100644 --- a/pkgs/build-support/rust/build-rust-crate/install-crate.nix +++ b/pkgs/build-support/rust/build-rust-crate/install-crate.nix @@ -14,7 +14,7 @@ if !buildTests then '' fi if [[ "$(ls -A target/lib)" ]]; then mkdir -p $lib/lib - cp target/lib/* $lib/lib #*/ + cp -r target/lib/* $lib/lib #*/ for library in $lib/lib/*.so $lib/lib/*.dylib; do #*/ ln -s $library $(echo $library | sed -e "s/-${metadata}//") done @@ -26,7 +26,7 @@ if !buildTests then '' if [[ -d target/bin ]]; then if [[ "$(ls -A target/bin)" ]]; then mkdir -p $out/bin - cp -P target/bin/* $out/bin # */ + cp -rP target/bin/* $out/bin # */ fi fi runHook postInstall diff --git a/pkgs/build-support/rust/build-rust-crate/lib.sh b/pkgs/build-support/rust/build-rust-crate/lib.sh index 6cf3481754b2..3bf1992cecd8 100644 --- a/pkgs/build-support/rust/build-rust-crate/lib.sh +++ b/pkgs/build-support/rust/build-rust-crate/lib.sh @@ -14,7 +14,6 @@ build_lib() { --crate-name $CRATE_NAME \ $lib_src \ --out-dir target/lib \ - --emit=dep-info,link \ -L dependency=target/deps \ --cap-lints allow \ $LIB_RUSTC_OPTS \ @@ -45,7 +44,6 @@ build_bin() { --crate-type bin \ $BIN_RUSTC_OPTS \ --out-dir target/bin \ - --emit=dep-info,link \ -L dependency=target/deps \ $LINK \ $EXTRA_LIB \ diff --git a/pkgs/build-support/rust/build-rust-crate/test/default.nix b/pkgs/build-support/rust/build-rust-crate/test/default.nix index 598b5daed1be..fba938f62372 100644 --- a/pkgs/build-support/rust/build-rust-crate/test/default.nix +++ b/pkgs/build-support/rust/build-rust-crate/test/default.nix @@ -1,13 +1,14 @@ { lib -, stdenv , buildRustCrate -, runCommand -, runCommandCC -, writeTextFile -, symlinkJoin , callPackage , releaseTools +, runCommand +, runCommandCC +, stdenv +, symlinkJoin +, writeTextFile }: + let mkCrate = args: let p = { @@ -103,6 +104,58 @@ let '' ); + /* Returns a derivation that asserts that the crate specified by `crateArgs` + has the specified files as output. + + `name` is used as part of the derivation name that performs the checking. + + `crateArgs` is passed to `mkCrate` to build the crate with `buildRustCrate`. + + `expectedFiles` contains a list of expected file paths in the output. E.g. + `[ "./bin/my_binary" ]`. + + `output` specifies the name of the output to use. By default, the default + output is used but e.g. `output = "lib";` will cause the lib output + to be checked instead. You do not need to specify any directories. + */ + assertOutputs = { name, crateArgs, expectedFiles, output? null }: + assert (builtins.isString name); + assert (builtins.isAttrs crateArgs); + assert (builtins.isList expectedFiles); + + let + crate = mkCrate (builtins.removeAttrs crateArgs ["expectedTestOutput"]); + crateOutput = if output == null then crate else crate."${output}"; + expectedFilesFile = writeTextFile { + name = "expected-files-${name}"; + text = + let sorted = builtins.sort (a: b: a$actualFiles + diff -q ${expectedFilesFile} $actualFiles >/dev/null || { + echo -e "\033[0;1;31mERROR: Difference in expected output files in ${crateOutput} \033[0m" >&2 + echo === Got: + sed -e 's/^/ /' $actualFiles + echo === Expected: + sed -e 's/^/ /' ${expectedFilesFile} + echo === Diff: + diff -u ${expectedFilesFile} $actualFiles |\ + tail -n +3 |\ + sed -e 's/^/ /' + exit 1 + } + touch $out + '' + ; + in rec { tests = let @@ -361,7 +414,80 @@ let }; }; brotliCrates = (callPackage ./brotli-crates.nix {}); - in lib.mapAttrs (key: value: mkTest (value // lib.optionalAttrs (!value?crateName) { crateName = key; })) cases // { + tests = lib.mapAttrs (key: value: mkTest (value // lib.optionalAttrs (!value?crateName) { crateName = key; })) cases; + in tests // rec { + + crateBinWithPathOutputs = assertOutputs { + name="crateBinWithPath"; + crateArgs = { + crateBin = [{ name = "test_binary1"; path = "src/foobar.rs"; }]; + src = mkBin "src/foobar.rs"; + }; + expectedFiles = [ + "./bin/test_binary1" + ]; + }; + + crateBinWithPathOutputsDebug = assertOutputs { + name="crateBinWithPath"; + crateArgs = { + release = false; + crateBin = [{ name = "test_binary1"; path = "src/foobar.rs"; }]; + src = mkBin "src/foobar.rs"; + }; + expectedFiles = [ + "./bin/test_binary1" + ] ++ lib.optionals stdenv.isDarwin [ + # On Darwin, the debug symbols are in a seperate directory. + "./bin/test_binary1.dSYM/Contents/Info.plist" + "./bin/test_binary1.dSYM/Contents/Resources/DWARF/test_binary1" + ]; + }; + + crateBinNoPath1Outputs = assertOutputs { + name="crateBinNoPath1"; + crateArgs = { + crateBin = [{ name = "my-binary2"; }]; + src = mkBin "src/my_binary2.rs"; + }; + expectedFiles = [ + "./bin/my-binary2" + ]; + }; + + crateLibOutputs = assertOutputs { + name="crateLib"; + output="lib"; + crateArgs = { + libName = "test_lib"; + type = [ "rlib" ]; + libPath = "src/lib.rs"; + src = mkLib "src/lib.rs"; + }; + expectedFiles = [ + "./nix-support/propagated-build-inputs" + "./lib/libtest_lib-042a1fdbef.rlib" + "./lib/link" + ]; + }; + + crateLibOutputsDebug = assertOutputs { + name="crateLib"; + output="lib"; + crateArgs = { + release = false; + libName = "test_lib"; + type = [ "rlib" ]; + libPath = "src/lib.rs"; + src = mkLib "src/lib.rs"; + }; + expectedFiles = [ + "./nix-support/propagated-build-inputs" + "./lib/libtest_lib-042a1fdbef.rlib" + "./lib/link" + ]; + }; + brotliTest = let pkg = brotliCrates.brotli_2_5_0 {}; in runCommand "run-brotli-test-cmd" {