From 0c50140da5bcc55c588ac64bfcedac4699a43711 Mon Sep 17 00:00:00 2001 From: Andreas Rammhold Date: Thu, 6 Sep 2018 15:03:13 +0200 Subject: [PATCH 1/5] buildRustCrate: add heuristic to picking the right source files Cargo has a few odd (old) ways of picking source files if the `bin.path` attribute isn't given in the Cargo.toml. This commit adds support for some of those. The previous behaviour always defaulted to `src/main.rs` which was not always the right choice. Since there is look-ahead into the unpacked sources before running the actual builder the path selection logic has to be embedded within the build script. `buildRustCrate` currently supports two ways of running building binaries when processing a crate: - Explicit definition of all the binaries (& optionally the paths to their respective `main.rs`) and, - if not binary was explictly configured all files matching the patterns `src/main.rs`, `src/bin/*.rs`. When the explicit list is given without path information paths are now being picked from a list of candidates. The first match wins. The order is the same as within the cargo compatibility code. If the crate does not provide any libraries the path `src/{bin_name}.rs` is also considered. All underscores within the binary names are translated into dashes (`-`) before the lookups are made. This seems to be a common convention. --- pkgs/build-support/rust/build-rust-crate.nix | 53 ++++-- .../rust/build-rust-crate/build-crate.nix | 159 ++++++++++++++++++ 2 files changed, 202 insertions(+), 10 deletions(-) create mode 100644 pkgs/build-support/rust/build-rust-crate/build-crate.nix diff --git a/pkgs/build-support/rust/build-rust-crate.nix b/pkgs/build-support/rust/build-rust-crate.nix index 6605aa27b21e..cfdf38958a8d 100644 --- a/pkgs/build-support/rust/build-rust-crate.nix +++ b/pkgs/build-support/rust/build-rust-crate.nix @@ -282,14 +282,48 @@ let makeDeps = dependencies: tr '\n' ' ' < target/link > target/link_ LINK=$(cat target/link_) - fi + fi mkdir -p target/bin - echo "${crateBin}" | sed -n 1'p' | tr ',' '\n' | while read BIN; do - if [[ ! -z "$BIN" ]]; then - build_bin $BIN - fi + printf "%s\n" "${crateBin}" | head -n1 | tr -s ',' '\n' | while read -r BIN_NAME BIN_PATH; do + # 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/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 $FILES" + fi + + echo $FILES + 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 == "") '' if [[ -e src/main.rs ]]; then build_bin ${crateName} src/main.rs @@ -388,11 +422,10 @@ stdenv.mkDerivation (rec { metadata = builtins.substring 0 10 (builtins.hashString "sha256" (crateName + "-" + crateVersion + "___" + toString crateFeatures + "___" + depsMetadata )); crateBin = if crate ? crateBin then - builtins.foldl' (bins: bin: - let name = - lib.strings.replaceStrings ["-"] ["_"] - (if bin ? name then bin.name else crateName); - path = if bin ? path then bin.path else "src/main.rs"; + builtins.foldl' (bins: bin: let + _name = (if bin ? name then bin.name else crateName); + name = lib.strings.replaceStrings ["-"] ["_"] _name; + path = if bin ? path then bin.path else ""; in bins + (if bin == "" then "" else ",") + "${name} ${path}" diff --git a/pkgs/build-support/rust/build-rust-crate/build-crate.nix b/pkgs/build-support/rust/build-rust-crate/build-crate.nix new file mode 100644 index 000000000000..748d48edefc2 --- /dev/null +++ b/pkgs/build-support/rust/build-rust-crate/build-crate.nix @@ -0,0 +1,159 @@ +{ lib, stdenv, echo_build_heading, noisily, makeDeps }: +{ crateName, + dependencies, + crateFeatures, libName, release, libPath, + crateType, metadata, crateBin, finalBins, + extraRustcOpts, verbose, colors }: + + let + + deps = makeDeps dependencies; + rustcOpts = + lib.lists.foldl' (opts: opt: opts + " " + opt) + (if release then "-C opt-level=3" else "-C debuginfo=2") + (["-C codegen-units=1"] ++ extraRustcOpts); + rustcMeta = "-C metadata=${metadata} -C extra-filename=-${metadata}"; + in '' + runHook preBuild + norm="" + bold="" + green="" + boldgreen="" + if [[ "${colors}" == "always" ]]; then + norm="$(printf '\033[0m')" #returns to "normal" + bold="$(printf '\033[0;1m')" #set bold + green="$(printf '\033[0;32m')" #set green + boldgreen="$(printf '\033[0;1;32m')" #set bold, and set green. + fi + ${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 --crate-type ${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 [ "$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 lib in $i; do + echo "-L $lib" >> target/link + L=$(echo $lib | sed -e "s#$(pwd)/target/build#$out/lib#") + echo "-L $L" >> target/link.final + done + fi + done + echo "$EXTRA_LINK" | while read i; do + if [[ ! -z "$i" ]]; then + for lib in $i; do + echo "-l $lib" >> target/link + echo "-l $lib" >> 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 + + mkdir -p target/bin + printf "%s\n" "${crateBin}" | head -n1 | tr -s ',' '\n' | while read -r BIN_NAME BIN_PATH; do + # 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/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 $FILES" + fi + + echo $FILES + 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 == "") '' + if [[ -e src/main.rs ]]; then + build_bin ${crateName} src/main.rs + fi + for i in src/bin/*.rs; do #*/ + 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 + '' + finalBins + '' + runHook postBuild + '' From 137181506065ebb0cfd218d696b13a1ea7642b41 Mon Sep 17 00:00:00 2001 From: Andreas Rammhold Date: Fri, 7 Sep 2018 15:08:06 +0200 Subject: [PATCH 2/5] buildRustCrate: extracted builder scripts into dedicated files The build expression got quiet large over time and to make it a bit easier to grasp the different scripts involved in the build are now separated from the nix file. --- pkgs/build-support/rust/build-rust-crate.nix | 494 ------------------ .../rust/build-rust-crate/configure-crate.nix | 129 +++++ .../rust/build-rust-crate/default.nix | 186 +++++++ .../rust/build-rust-crate/install-crate.nix | 28 + pkgs/top-level/all-packages.nix | 2 +- 5 files changed, 344 insertions(+), 495 deletions(-) delete mode 100644 pkgs/build-support/rust/build-rust-crate.nix create mode 100644 pkgs/build-support/rust/build-rust-crate/configure-crate.nix create mode 100644 pkgs/build-support/rust/build-rust-crate/default.nix create mode 100644 pkgs/build-support/rust/build-rust-crate/install-crate.nix diff --git a/pkgs/build-support/rust/build-rust-crate.nix b/pkgs/build-support/rust/build-rust-crate.nix deleted file mode 100644 index cfdf38958a8d..000000000000 --- a/pkgs/build-support/rust/build-rust-crate.nix +++ /dev/null @@ -1,494 +0,0 @@ -# Code for buildRustCrate, a Nix function that builds Rust code, just -# like Cargo, but using Nix instead. -# -# This can be useful for deploying packages with NixOps, and to share -# binary dependencies between projects. - -{ lib, stdenv, defaultCrateOverrides, fetchCrate, ncurses, rustc }: - -let makeDeps = dependencies: - (lib.concatMapStringsSep " " (dep: - let extern = lib.strings.replaceStrings ["-"] ["_"] dep.libName; in - (if dep.crateType == "lib" then - " --extern ${extern}=${dep.out}/lib/lib${extern}-${dep.metadata}.rlib" - else - " --extern ${extern}=${dep.out}/lib/lib${extern}-${dep.metadata}${stdenv.hostPlatform.extensions.sharedLibrary}") - ) dependencies); - - # This doesn't appear to be officially documented anywhere yet. - # See https://github.com/rust-lang-nursery/rust-forge/issues/101. - target_os = if stdenv.hostPlatform.isDarwin - then "macos" - else stdenv.hostPlatform.parsed.kernel.name; - - echo_build_heading = colors: '' - echo_build_heading() { - start="" - end="" - if [[ "${colors}" == "always" ]]; then - start="$(printf '\033[0;1;32m')" #set bold, and set green. - end="$(printf '\033[0m')" #returns to "normal" - fi - if (( $# == 1 )); then - echo "$start""Building $1""$end" - else - echo "$start""Building $1 ($2)""$end" - fi - } - ''; - noisily = colors: verbose: '' - noisily() { - start="" - end="" - if [[ "${colors}" == "always" ]]; then - start="$(printf '\033[0;1;32m')" #set bold, and set green. - end="$(printf '\033[0m')" #returns to "normal" - fi - ${lib.optionalString verbose '' - echo -n "$start"Running "$end" - echo $@ - ''} - $@ - } - ''; - - configureCrate = - { crateName, crateVersion, crateAuthors, build, libName, crateFeatures, colors, libPath, release, buildDependencies, completeDeps, completeBuildDeps, verbose, workspace_member, extraLinkFlags }: - let version_ = lib.splitString "-" crateVersion; - versionPre = if lib.tail version_ == [] then "" else builtins.elemAt version_ 1; - version = lib.splitString "." (lib.head version_); - rustcOpts = (if release then "-C opt-level=3" else "-C debuginfo=2"); - buildDeps = makeDeps buildDependencies; - authors = lib.concatStringsSep ":" crateAuthors; - optLevel = if release then 3 else 0; - completeDepsDir = lib.concatStringsSep " " completeDeps; - completeBuildDepsDir = lib.concatStringsSep " " completeBuildDeps; - in '' - cd ${workspace_member} - runHook preConfigure - ${echo_build_heading colors} - ${noisily colors verbose} - symlink_dependency() { - # $1 is the nix-store path of a dependency - # $2 is the target path - 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 - } - - mkdir -p target/{deps,lib,build,buildDeps} - chmod uga+w target -R - echo ${extraLinkFlags} > target/link - echo ${extraLinkFlags} > target/link.final - for i in ${completeDepsDir}; do - symlink_dependency $i target/deps - done - for i in ${completeBuildDepsDir}; do - symlink_dependency $i target/buildDeps - 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_ - fi - EXTRA_BUILD="" - BUILD_OUT_DIR="" - export CARGO_PKG_NAME=${crateName} - export CARGO_PKG_VERSION=${crateVersion} - export CARGO_PKG_AUTHORS="${authors}" - - export CARGO_CFG_TARGET_ARCH=${stdenv.hostPlatform.parsed.cpu.name} - export CARGO_CFG_TARGET_OS=${target_os} - export CARGO_CFG_TARGET_FAMILY="unix" - export CARGO_CFG_UNIX=1 - export CARGO_CFG_TARGET_ENV="gnu" - export CARGO_CFG_TARGET_ENDIAN=${if stdenv.hostPlatform.parsed.cpu.significantByte.name == "littleEndian" then "little" else "big"} - export CARGO_CFG_TARGET_POINTER_WIDTH=${toString stdenv.hostPlatform.parsed.cpu.bits} - export CARGO_CFG_TARGET_VENDOR=${stdenv.hostPlatform.parsed.vendor.name} - - export CARGO_MANIFEST_DIR="." - export DEBUG="${toString (!release)}" - export OPT_LEVEL="${toString optLevel}" - export TARGET="${stdenv.hostPlatform.config}" - export HOST="${stdenv.hostPlatform.config}" - export PROFILE=${if release then "release" else "debug"} - export OUT_DIR=$(pwd)/target/build/${crateName}.out - export CARGO_PKG_VERSION_MAJOR=${builtins.elemAt version 0} - export CARGO_PKG_VERSION_MINOR=${builtins.elemAt version 1} - export CARGO_PKG_VERSION_PATCH=${builtins.elemAt version 2} - if [[ -n "${versionPre}" ]]; then - export CARGO_PKG_VERSION_PRE="${versionPre}" - fi - - BUILD="" - if [[ ! -z "${build}" ]] ; then - BUILD=${build} - elif [[ -e "build.rs" ]]; then - BUILD="build.rs" - fi - 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)" - fi - noisily rustc --crate-name build_script_build $BUILD --crate-type bin ${rustcOpts} \ - ${crateFeatures} --out-dir target/build/${crateName} --emit=dep-info,link \ - -L dependency=target/buildDeps ${buildDeps} --cap-lints allow $EXTRA_BUILD_FLAGS --color ${colors} - - mkdir -p target/build/${crateName}.out - export RUST_BACKTRACE=1 - BUILD_OUT_DIR="-L $OUT_DIR" - mkdir -p $OUT_DIR - target/build/${crateName}/build_script_build > target/build/${crateName}.opt - 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_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 - export $env - done - - CRATENAME=$(echo ${crateName} | sed -e "s/\(.*\)-sys$/\U\1/") - grep -P "^cargo:(?!(rustc-|warning=|rerun-if-changed=|rerun-if-env-changed))" target/build/${crateName}.opt \ - | sed -e "s/cargo:\([^=]*\)=\(.*\)/export DEP_$(echo $CRATENAME)_\U\1\E=\2/" > target/env - - set -e - if [[ -n "$(ls target/build/${crateName}.out)" ]]; then - - if [[ -e "${libPath}" ]]; then - cp -r target/build/${crateName}.out/* $(dirname ${libPath}) #*/ - else - cp -r target/build/${crateName}.out/* src #*/ - fi - fi - fi - runHook postConfigure - ''; - - buildCrate = { crateName, - dependencies, - crateFeatures, libName, release, libPath, - crateType, metadata, crateBin, finalBins, - extraRustcOpts, verbose, colors }: - - let deps = makeDeps dependencies; - rustcOpts = - lib.lists.foldl' (opts: opt: opts + " " + opt) - (if release then "-C opt-level=3" else "-C debuginfo=2") - (["-C codegen-units=1"] ++ extraRustcOpts); - rustcMeta = "-C metadata=${metadata} -C extra-filename=-${metadata}"; - in '' - runHook preBuild - norm="" - bold="" - green="" - boldgreen="" - if [[ "${colors}" == "always" ]]; then - norm="$(printf '\033[0m')" #returns to "normal" - bold="$(printf '\033[0;1m')" #set bold - green="$(printf '\033[0;32m')" #set green - boldgreen="$(printf '\033[0;1;32m')" #set bold, and set green. - fi - ${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 --crate-type ${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 [ "$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 lib in $i; do - echo "-L $lib" >> target/link - L=$(echo $lib | sed -e "s#$(pwd)/target/build#$out/lib#") - echo "-L $L" >> target/link.final - done - fi - done - echo "$EXTRA_LINK" | while read i; do - if [[ ! -z "$i" ]]; then - for lib in $i; do - echo "-l $lib" >> target/link - echo "-l $lib" >> 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 - - mkdir -p target/bin - printf "%s\n" "${crateBin}" | head -n1 | tr -s ',' '\n' | while read -r BIN_NAME BIN_PATH; do - # 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/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 $FILES" - fi - - echo $FILES - 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 == "") '' - if [[ -e src/main.rs ]]; then - build_bin ${crateName} src/main.rs - fi - for i in src/bin/*.rs; do #*/ - 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 - '' + finalBins + '' - runHook postBuild - ''; - - installCrate = crateName: metadata: '' - runHook preInstall - mkdir -p $out - if [[ -s target/env ]]; then - cp target/env $out/env - fi - if [[ -s target/link.final ]]; then - mkdir -p $out/lib - cp target/link.final $out/lib/link - fi - if [[ "$(ls -A target/lib)" ]]; then - mkdir -p $out/lib - cp target/lib/* $out/lib #*/ - for lib in $out/lib/*.so $out/lib/*.dylib; do #*/ - ln -s $lib $(echo $lib | sed -e "s/-${metadata}//") - done - fi - if [[ "$(ls -A target/build)" ]]; then # */ - mkdir -p $out/lib - cp -r target/build/* $out/lib # */ - fi - if [[ "$(ls -A target/bin)" ]]; then - mkdir -p $out/bin - cp -P target/bin/* $out/bin # */ - fi - runHook postInstall - ''; -in - -crate_: lib.makeOverridable ({ rust, release, verbose, features, buildInputs, crateOverrides, - dependencies, buildDependencies, - extraRustcOpts, - preUnpack, postUnpack, prePatch, patches, postPatch, - preConfigure, postConfigure, preBuild, postBuild, preInstall, postInstall }: - -let crate = crate_ // (lib.attrByPath [ crate_.crateName ] (attr: {}) crateOverrides crate_); - dependencies_ = dependencies; - buildDependencies_ = buildDependencies; - processedAttrs = [ - "src" "buildInputs" "crateBin" "crateLib" "libName" "libPath" - "buildDependencies" "dependencies" "features" - "crateName" "version" "build" "authors" "colors" - ]; - extraDerivationAttrs = lib.filterAttrs (n: v: ! lib.elem n processedAttrs) crate; - buildInputs_ = buildInputs; -in -stdenv.mkDerivation (rec { - - inherit (crate) crateName; - inherit preUnpack postUnpack prePatch patches postPatch preConfigure postConfigure preBuild postBuild preInstall postInstall; - - src = if lib.hasAttr "src" crate then - crate.src - else - fetchCrate { inherit (crate) crateName version sha256; }; - name = "rust_${crate.crateName}-${crate.version}"; - buildInputs = [ rust ncurses ] ++ (crate.buildInputs or []) ++ buildInputs_; - dependencies = - builtins.map - (dep: dep.override { rust = rust; release = release; verbose = verbose; crateOverrides = crateOverrides; }) - dependencies_; - - buildDependencies = - builtins.map - (dep: dep.override { rust = rust; release = release; verbose = verbose; crateOverrides = crateOverrides; }) - buildDependencies_; - - completeDeps = lib.lists.unique (dependencies ++ lib.lists.concatMap (dep: dep.completeDeps) dependencies); - completeBuildDeps = lib.lists.unique ( - buildDependencies - ++ lib.lists.concatMap (dep: dep.completeBuildDeps ++ dep.completeDeps) buildDependencies - ); - - crateFeatures = if crate ? features then - lib.concatMapStringsSep " " (f: "--cfg feature=\\\"${f}\\\"") (crate.features ++ features) #" - else ""; - - libName = if crate ? libName then crate.libName else crate.crateName; - libPath = if crate ? libPath then crate.libPath else ""; - - depsMetadata = builtins.foldl' (str: dep: str + dep.metadata) "" (dependencies ++ buildDependencies); - metadata = builtins.substring 0 10 (builtins.hashString "sha256" (crateName + "-" + crateVersion + "___" + toString crateFeatures + "___" + depsMetadata )); - - crateBin = if crate ? crateBin then - builtins.foldl' (bins: bin: let - _name = (if bin ? name then bin.name else crateName); - name = lib.strings.replaceStrings ["-"] ["_"] _name; - path = if bin ? path then bin.path else ""; - in - bins + (if bin == "" then "" else ",") + "${name} ${path}" - - ) "" crate.crateBin - else ""; - - finalBins = if crate ? crateBin then - builtins.foldl' (bins: bin: - let name = lib.strings.replaceStrings ["-"] ["_"] - (if bin ? name then bin.name else crateName); - new_name = if bin ? name then bin.name else crateName; - in - if name == new_name then bins else - (bins + "mv target/bin/${name} target/bin/${new_name};") - - ) "" crate.crateBin - else ""; - - build = crate.build or ""; - workspace_member = crate.workspace_member or "."; - crateVersion = crate.version; - crateAuthors = if crate ? authors && lib.isList crate.authors then crate.authors else []; - crateType = - if lib.attrByPath ["procMacro"] false crate then "proc-macro" else - if lib.attrByPath ["plugin"] false crate then "dylib" else - (crate.type or "lib"); - colors = lib.attrByPath [ "colors" ] "always" crate; - extraLinkFlags = builtins.concatStringsSep " " (crate.extraLinkFlags or []); - configurePhase = configureCrate { - inherit crateName buildDependencies completeDeps completeBuildDeps - crateFeatures libName build workspace_member release libPath crateVersion - extraLinkFlags - crateAuthors verbose colors; - }; - extraRustcOpts = if crate ? extraRustcOpts then crate.extraRustcOpts else []; - buildPhase = buildCrate { - inherit crateName dependencies - crateFeatures libName release libPath crateType - metadata crateBin finalBins verbose colors - extraRustcOpts; - }; - installPhase = installCrate crateName metadata; - -} // extraDerivationAttrs -)) { - rust = rustc; - release = crate_.release or true; - verbose = crate_.verbose or true; - extraRustcOpts = []; - features = []; - buildInputs = []; - crateOverrides = defaultCrateOverrides; - preUnpack = crate_.preUnpack or ""; - postUnpack = crate_.postUnpack or ""; - prePatch = crate_.prePatch or ""; - patches = crate_.patches or []; - postPatch = crate_.postPatch or ""; - preConfigure = crate_.preConfigure or ""; - postConfigure = crate_.postConfigure or ""; - preBuild = crate_.preBuild or ""; - postBuild = crate_.postBuild or ""; - preInstall = crate_.preInstall or ""; - postInstall = crate_.postInstall or ""; - dependencies = crate_.dependencies or []; - buildDependencies = crate_.buildDependencies or []; -} diff --git a/pkgs/build-support/rust/build-rust-crate/configure-crate.nix b/pkgs/build-support/rust/build-rust-crate/configure-crate.nix new file mode 100644 index 000000000000..37fef2abd774 --- /dev/null +++ b/pkgs/build-support/rust/build-rust-crate/configure-crate.nix @@ -0,0 +1,129 @@ +{ lib, stdenv, echo_build_heading, noisily, makeDeps }: +{ build, buildDependencies, colors, completeBuildDeps, completeDeps, crateAuthors, crateFeatures, crateName, crateVersion, extraLinkFlags, libName, libPath, release, target_os, verbose, workspace_member }: +let version_ = lib.splitString "-" crateVersion; + versionPre = if lib.tail version_ == [] then "" else builtins.elemAt version_ 1; + version = lib.splitString "." (lib.head version_); + rustcOpts = (if release then "-C opt-level=3" else "-C debuginfo=2"); + buildDeps = makeDeps buildDependencies; + authors = lib.concatStringsSep ":" crateAuthors; + optLevel = if release then 3 else 0; + completeDepsDir = lib.concatStringsSep " " completeDeps; + completeBuildDepsDir = lib.concatStringsSep " " completeBuildDeps; +in '' + cd ${workspace_member} + runHook preConfigure + ${echo_build_heading colors} + ${noisily colors verbose} + symlink_dependency() { + # $1 is the nix-store path of a dependency + # $2 is the target path + 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 + } + + mkdir -p target/{deps,lib,build,buildDeps} + chmod uga+w target -R + echo ${extraLinkFlags} > target/link + echo ${extraLinkFlags} > target/link.final + for i in ${completeDepsDir}; do + symlink_dependency $i target/deps + done + for i in ${completeBuildDepsDir}; do + symlink_dependency $i target/buildDeps + 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_ + fi + EXTRA_BUILD="" + BUILD_OUT_DIR="" + export CARGO_PKG_NAME=${crateName} + export CARGO_PKG_VERSION=${crateVersion} + export CARGO_PKG_AUTHORS="${authors}" + + export CARGO_CFG_TARGET_ARCH=${stdenv.hostPlatform.parsed.cpu.name} + export CARGO_CFG_TARGET_OS=${target_os} + export CARGO_CFG_TARGET_FAMILY="unix" + export CARGO_CFG_UNIX=1 + export CARGO_CFG_TARGET_ENV="gnu" + export CARGO_CFG_TARGET_ENDIAN=${if stdenv.hostPlatform.parsed.cpu.significantByte.name == "littleEndian" then "little" else "big"} + export CARGO_CFG_TARGET_POINTER_WIDTH=${toString stdenv.hostPlatform.parsed.cpu.bits} + export CARGO_CFG_TARGET_VENDOR=${stdenv.hostPlatform.parsed.vendor.name} + + export CARGO_MANIFEST_DIR="." + export DEBUG="${toString (!release)}" + export OPT_LEVEL="${toString optLevel}" + export TARGET="${stdenv.hostPlatform.config}" + export HOST="${stdenv.hostPlatform.config}" + export PROFILE=${if release then "release" else "debug"} + export OUT_DIR=$(pwd)/target/build/${crateName}.out + export CARGO_PKG_VERSION_MAJOR=${builtins.elemAt version 0} + export CARGO_PKG_VERSION_MINOR=${builtins.elemAt version 1} + export CARGO_PKG_VERSION_PATCH=${builtins.elemAt version 2} + if [[ -n "${versionPre}" ]]; then + export CARGO_PKG_VERSION_PRE="${versionPre}" + fi + + BUILD="" + if [[ ! -z "${build}" ]] ; then + BUILD=${build} + elif [[ -e "build.rs" ]]; then + BUILD="build.rs" + fi + 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)" + fi + noisily rustc --crate-name build_script_build $BUILD --crate-type bin ${rustcOpts} \ + ${crateFeatures} --out-dir target/build/${crateName} --emit=dep-info,link \ + -L dependency=target/buildDeps ${buildDeps} --cap-lints allow $EXTRA_BUILD_FLAGS --color ${colors} + + mkdir -p target/build/${crateName}.out + export RUST_BACKTRACE=1 + BUILD_OUT_DIR="-L $OUT_DIR" + mkdir -p $OUT_DIR + target/build/${crateName}/build_script_build > target/build/${crateName}.opt + 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_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 + export $env + done + + CRATENAME=$(echo ${crateName} | sed -e "s/\(.*\)-sys$/\U\1/") + grep -P "^cargo:(?!(rustc-|warning=|rerun-if-changed=|rerun-if-env-changed))" target/build/${crateName}.opt \ + | sed -e "s/cargo:\([^=]*\)=\(.*\)/export DEP_$(echo $CRATENAME)_\U\1\E=\2/" > target/env + + set -e + if [[ -n "$(ls target/build/${crateName}.out)" ]]; then + + if [[ -e "${libPath}" ]]; then + cp -r target/build/${crateName}.out/* $(dirname ${libPath}) #*/ + else + cp -r target/build/${crateName}.out/* src #*/ + fi + fi + fi + runHook postConfigure +'' + diff --git a/pkgs/build-support/rust/build-rust-crate/default.nix b/pkgs/build-support/rust/build-rust-crate/default.nix new file mode 100644 index 000000000000..db7f72acfb99 --- /dev/null +++ b/pkgs/build-support/rust/build-rust-crate/default.nix @@ -0,0 +1,186 @@ +# Code for buildRustCrate, a Nix function that builds Rust code, just +# like Cargo, but using Nix instead. +# +# This can be useful for deploying packages with NixOps, and to share +# binary dependencies between projects. + +{ lib, stdenv, defaultCrateOverrides, fetchCrate, ncurses, rustc }: + +let + # This doesn't appear to be officially documented anywhere yet. + # See https://github.com/rust-lang-nursery/rust-forge/issues/101. + target_os = if stdenv.hostPlatform.isDarwin + then "macos" + else stdenv.hostPlatform.parsed.kernel.name; + + makeDeps = dependencies: + (lib.concatMapStringsSep " " (dep: + let extern = lib.strings.replaceStrings ["-"] ["_"] dep.libName; in + (if dep.crateType == "lib" then + " --extern ${extern}=${dep.out}/lib/lib${extern}-${dep.metadata}.rlib" + else + " --extern ${extern}=${dep.out}/lib/lib${extern}-${dep.metadata}${stdenv.hostPlatform.extensions.sharedLibrary}") + ) dependencies); + + echo_build_heading = colors: '' + echo_build_heading() { + start="" + end="" + if [[ "${colors}" == "always" ]]; then + start="$(printf '\033[0;1;32m')" #set bold, and set green. + end="$(printf '\033[0m')" #returns to "normal" + fi + if (( $# == 1 )); then + echo "$start""Building $1""$end" + else + echo "$start""Building $1 ($2)""$end" + fi + } + ''; + noisily = colors: verbose: '' + noisily() { + start="" + end="" + if [[ "${colors}" == "always" ]]; then + start="$(printf '\033[0;1;32m')" #set bold, and set green. + end="$(printf '\033[0m')" #returns to "normal" + fi + ${lib.optionalString verbose '' + echo -n "$start"Running "$end" + echo $@ + ''} + $@ + } + ''; + + configureCrate = import ./configure-crate.nix { inherit lib stdenv echo_build_heading noisily makeDeps; }; + buildCrate = import ./build-crate.nix { inherit lib stdenv echo_build_heading noisily makeDeps; }; + installCrate = import ./install-crate.nix; + + in + +crate_: lib.makeOverridable ({ rust, release, verbose, features, buildInputs, crateOverrides, + dependencies, buildDependencies, + extraRustcOpts, + preUnpack, postUnpack, prePatch, patches, postPatch, + preConfigure, postConfigure, preBuild, postBuild, preInstall, postInstall }: + +let crate = crate_ // (lib.attrByPath [ crate_.crateName ] (attr: {}) crateOverrides crate_); + dependencies_ = dependencies; + buildDependencies_ = buildDependencies; + processedAttrs = [ + "src" "buildInputs" "crateBin" "crateLib" "libName" "libPath" + "buildDependencies" "dependencies" "features" + "crateName" "version" "build" "authors" "colors" + ]; + extraDerivationAttrs = lib.filterAttrs (n: v: ! lib.elem n processedAttrs) crate; + buildInputs_ = buildInputs; +in +stdenv.mkDerivation (rec { + + inherit (crate) crateName; + inherit preUnpack postUnpack prePatch patches postPatch preConfigure postConfigure preBuild postBuild preInstall postInstall; + + src = if lib.hasAttr "src" crate then + crate.src + else + fetchCrate { inherit (crate) crateName version sha256; }; + name = "rust_${crate.crateName}-${crate.version}"; + buildInputs = [ rust ncurses ] ++ (crate.buildInputs or []) ++ buildInputs_; + dependencies = + builtins.map + (dep: dep.override { rust = rust; release = release; verbose = verbose; crateOverrides = crateOverrides; }) + dependencies_; + + buildDependencies = + builtins.map + (dep: dep.override { rust = rust; release = release; verbose = verbose; crateOverrides = crateOverrides; }) + buildDependencies_; + + completeDeps = lib.lists.unique (dependencies ++ lib.lists.concatMap (dep: dep.completeDeps) dependencies); + completeBuildDeps = lib.lists.unique ( + buildDependencies + ++ lib.lists.concatMap (dep: dep.completeBuildDeps ++ dep.completeDeps) buildDependencies + ); + + crateFeatures = if crate ? features then + lib.concatMapStringsSep " " (f: "--cfg feature=\\\"${f}\\\"") (crate.features ++ features) #" + else ""; + + libName = if crate ? libName then crate.libName else crate.crateName; + libPath = if crate ? libPath then crate.libPath else ""; + + depsMetadata = builtins.foldl' (str: dep: str + dep.metadata) "" (dependencies ++ buildDependencies); + metadata = builtins.substring 0 10 (builtins.hashString "sha256" (crateName + "-" + crateVersion + "___" + toString crateFeatures + "___" + depsMetadata )); + + crateBin = if crate ? crateBin then + builtins.foldl' (bins: bin: let + _name = (if bin ? name then bin.name else crateName); + name = lib.strings.replaceStrings ["-"] ["_"] _name; + path = if bin ? path then bin.path else ""; + in + bins + (if bin == "" then "" else ",") + "${name} ${path}" + + ) "" crate.crateBin + else ""; + + finalBins = if crate ? crateBin then + builtins.foldl' (bins: bin: + let name = lib.strings.replaceStrings ["-"] ["_"] + (if bin ? name then bin.name else crateName); + new_name = if bin ? name then bin.name else crateName; + in + if name == new_name then bins else + (bins + "mv target/bin/${name} target/bin/${new_name};") + + ) "" crate.crateBin + else ""; + + build = crate.build or ""; + workspace_member = crate.workspace_member or "."; + crateVersion = crate.version; + crateAuthors = if crate ? authors && lib.isList crate.authors then crate.authors else []; + crateType = + if lib.attrByPath ["procMacro"] false crate then "proc-macro" else + if lib.attrByPath ["plugin"] false crate then "dylib" else + (crate.type or "lib"); + colors = lib.attrByPath [ "colors" ] "always" crate; + extraLinkFlags = builtins.concatStringsSep " " (crate.extraLinkFlags or []); + configurePhase = configureCrate { + inherit crateName buildDependencies completeDeps completeBuildDeps + crateFeatures libName build workspace_member release libPath crateVersion + extraLinkFlags + crateAuthors verbose colors target_os; + }; + extraRustcOpts = if crate ? extraRustcOpts then crate.extraRustcOpts else []; + buildPhase = buildCrate { + inherit crateName dependencies + crateFeatures libName release libPath crateType + metadata crateBin finalBins verbose colors + extraRustcOpts; + }; + installPhase = installCrate crateName metadata; + +} // extraDerivationAttrs +)) { + rust = rustc; + release = crate_.release or true; + verbose = crate_.verbose or true; + extraRustcOpts = []; + features = []; + buildInputs = []; + crateOverrides = defaultCrateOverrides; + preUnpack = crate_.preUnpack or ""; + postUnpack = crate_.postUnpack or ""; + prePatch = crate_.prePatch or ""; + patches = crate_.patches or []; + postPatch = crate_.postPatch or ""; + preConfigure = crate_.preConfigure or ""; + postConfigure = crate_.postConfigure or ""; + preBuild = crate_.preBuild or ""; + postBuild = crate_.postBuild or ""; + preInstall = crate_.preInstall or ""; + postInstall = crate_.postInstall or ""; + dependencies = crate_.dependencies or []; + buildDependencies = crate_.buildDependencies or []; +} diff --git a/pkgs/build-support/rust/build-rust-crate/install-crate.nix b/pkgs/build-support/rust/build-rust-crate/install-crate.nix new file mode 100644 index 000000000000..c41df34ca39b --- /dev/null +++ b/pkgs/build-support/rust/build-rust-crate/install-crate.nix @@ -0,0 +1,28 @@ +crateName: metadata: +'' + runHook preInstall + mkdir -p $out + if [[ -s target/env ]]; then + cp target/env $out/env + fi + if [[ -s target/link.final ]]; then + mkdir -p $out/lib + cp target/link.final $out/lib/link + fi + if [[ "$(ls -A target/lib)" ]]; then + mkdir -p $out/lib + cp target/lib/* $out/lib #*/ + for lib in $out/lib/*.so $out/lib/*.dylib; do #*/ + ln -s $lib $(echo $lib | sed -e "s/-${metadata}//") + done + fi + if [[ "$(ls -A target/build)" ]]; then # */ + mkdir -p $out/lib + cp -r target/build/* $out/lib # */ + fi + if [[ "$(ls -A target/bin)" ]]; then + mkdir -p $out/bin + cp -P target/bin/* $out/bin # */ + fi + runHook postInstall +'' diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index b77806ddec69..784a271845ae 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -7177,7 +7177,7 @@ with pkgs; }); inherit (rust) cargo rustc; - buildRustCrate = callPackage ../build-support/rust/build-rust-crate.nix { }; + buildRustCrate = callPackage ../build-support/rust/build-rust-crate { }; cargo-vendor = callPackage ../build-support/rust/cargo-vendor { }; From fdc2017f1c6de20148bc5d7a204ca6021c026ee5 Mon Sep 17 00:00:00 2001 From: Andreas Rammhold Date: Sat, 8 Sep 2018 23:02:06 +0200 Subject: [PATCH 3/5] buildRustCrate: binary heuristic should be able to treat spaces --- pkgs/build-support/rust/build-rust-crate/build-crate.nix | 9 ++++----- 1 file changed, 4 insertions(+), 5 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 748d48edefc2..bd9d96d96cd6 100644 --- a/pkgs/build-support/rust/build-rust-crate/build-crate.nix +++ b/pkgs/build-support/rust/build-rust-crate/build-crate.nix @@ -117,15 +117,14 @@ # 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/main.rs src/main.rs" + FILES=( "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 $FILES" + FILES=( "src/$BIN_NAME_.rs" "''${FILES[@]}" ) fi - echo $FILES - for file in $FILES; + for file in "''${FILES[@]}"; do echo "checking file $file" # first file that exists wins @@ -140,7 +139,7 @@ exit 1 fi fi - build_bin $BIN_NAME $BIN_PATH + build_bin "$BIN_NAME" "$BIN_PATH" done From 0f95d05548171ba14c4424c735f05633edccd1a9 Mon Sep 17 00:00:00 2001 From: Andreas Rammhold Date: Sun, 9 Sep 2018 09:21:51 +0200 Subject: [PATCH 4/5] buildRustCrate: add test cases --- .../rust/build-rust-crate/test/default.nix | 81 +++++++++++++++++++ pkgs/top-level/all-packages.nix | 1 + 2 files changed, 82 insertions(+) create mode 100644 pkgs/build-support/rust/build-rust-crate/test/default.nix diff --git a/pkgs/build-support/rust/build-rust-crate/test/default.nix b/pkgs/build-support/rust/build-rust-crate/test/default.nix new file mode 100644 index 000000000000..e5e77c93555d --- /dev/null +++ b/pkgs/build-support/rust/build-rust-crate/test/default.nix @@ -0,0 +1,81 @@ +{ lib, stdenv, buildRustCrate, runCommand, writeTextFile, symlinkJoin }: +let + mkCrate = args: let + p = { + crateName = "nixtestcrate"; + version = "0.1.0"; + authors = [ "Test " ]; + } // args; + in buildRustCrate p; + + mkFile = destination: text: writeTextFile { + name = "src"; + destination = "/${destination}"; + inherit text; + }; + + mkBin = name: mkFile name '' + use std::env; + fn main() { + let name: String = env::args().nth(0).unwrap(); + println!("executed {}", name); + } + ''; + + mkLib = name: mkFile name "pub fn test() -> i32 { return 23; }"; + + mkTest = crateArgs: let + crate = mkCrate crateArgs; + binaries = map (v: ''"${v.name}"'') (crateArgs.crateBin or []); + isLib = crateArgs ? libName || crateArgs ? libPath; + crateName = crateArgs.crateName or "nixtestcrate"; + libName = crateArgs.libName or crateName; + + libTestBinary = if !isLib then null else mkCrate { + crateName = "run-test-${crateName}"; + dependencies = [ crate ]; + src = mkFile "src/main.rs" '' + extern crate ${libName}; + fn main() { + assert_eq!(${libName}::test(), 23); + } + ''; + }; + + 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 rec { + + tests = let + cases = { + libPath = { libPath = "src/my_lib.rs"; src = mkLib "src/my_lib.rs"; }; + srcLib = { src = mkLib "src/lib.rs"; }; + customLibName = { libName = "test_lib"; src = mkLib "src/test_lib.rs"; }; + customLibNameAndLibPath = { libName = "test_lib"; libPath = "src/best-lib.rs"; src = mkLib "src/best-lib.rs"; }; + crateBinWithPath = { crateBin = [{ name = "test_binary1"; path = "src/foobar.rs"; }]; src = mkBin "src/foobar.rs"; }; + crateBinNoPath1 = { crateBin = [{ name = "my-binary2"; }]; src = mkBin "src/my_binary2.rs"; }; + crateBinNoPath2 = { + crateBin = [{ name = "my-binary3"; } { name = "my-binary4"; }]; + src = symlinkJoin { + name = "buildRustCrateMultipleBinariesCase"; + paths = [ (mkBin "src/bin/my_binary3.rs") (mkBin "src/bin/my_binary4.rs") ]; + }; + }; + crateBinNoPath3 = { crateBin = [{ name = "my-binary5"; }]; src = mkBin "src/bin/main.rs"; }; + crateBinNoPath4 = { crateBin = [{ name = "my-binary6"; }]; src = mkBin "src/main.rs";}; + }; + in lib.mapAttrs (key: value: mkTest (value // lib.optionalAttrs (!value?crateName) { crateName = key; })) cases; + test = runCommand "run-buildRustCrate-tests" { + nativeBuildInputs = builtins.attrValues tests; + } " + touch $out + "; +} diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index 784a271845ae..1823fa7a12fc 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -7178,6 +7178,7 @@ with pkgs; inherit (rust) cargo rustc; buildRustCrate = callPackage ../build-support/rust/build-rust-crate { }; + buildRustCrateTests = recurseIntoAttrs (callPackage ../build-support/rust/build-rust-crate/test { }).tests; cargo-vendor = callPackage ../build-support/rust/cargo-vendor { }; From fc5e5950031d8af57f8b5b9e55187f3e4cb4f063 Mon Sep 17 00:00:00 2001 From: Andreas Rammhold Date: Thu, 13 Sep 2018 21:12:14 +0200 Subject: [PATCH 5/5] buildRustCrate: added some edge cases with binaries This commit adds test based on real-world crates (brotli). There were a few more edge cases that were missing beforehand. Also it turned out that we can get rid of the `finalBins` list since that will now be handled during runtime. --- .../rust/build-rust-crate/build-crate.nix | 7 +- .../rust/build-rust-crate/default.nix | 17 +--- .../build-rust-crate/test/brotli-crates.nix | 95 +++++++++++++++++++ .../rust/build-rust-crate/test/default.nix | 27 +++++- 4 files changed, 125 insertions(+), 21 deletions(-) create mode 100644 pkgs/build-support/rust/build-rust-crate/test/brotli-crates.nix 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 bd9d96d96cd6..f65118ba4a64 100644 --- a/pkgs/build-support/rust/build-rust-crate/build-crate.nix +++ b/pkgs/build-support/rust/build-rust-crate/build-crate.nix @@ -2,7 +2,7 @@ { crateName, dependencies, crateFeatures, libName, release, libPath, - crateType, metadata, crateBin, finalBins, + crateType, metadata, crateBin, extraRustcOpts, verbose, colors }: let @@ -117,11 +117,11 @@ # 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/main.rs" "src/main.rs" ) + 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" "''${FILES[@]}" ) + FILES=( "src/$BIN_NAME.rs" "src/$BIN_NAME_.rs" "''${FILES[@]}" ) fi for file in "''${FILES[@]}"; @@ -153,6 +153,5 @@ ''} # Remove object files to avoid "wrong ELF type" find target -type f -name "*.o" -print0 | xargs -0 rm -f - '' + finalBins + '' runHook postBuild '' diff --git a/pkgs/build-support/rust/build-rust-crate/default.nix b/pkgs/build-support/rust/build-rust-crate/default.nix index db7f72acfb99..a11cef9f1f46 100644 --- a/pkgs/build-support/rust/build-rust-crate/default.nix +++ b/pkgs/build-support/rust/build-rust-crate/default.nix @@ -115,8 +115,7 @@ stdenv.mkDerivation (rec { crateBin = if crate ? crateBin then builtins.foldl' (bins: bin: let - _name = (if bin ? name then bin.name else crateName); - name = lib.strings.replaceStrings ["-"] ["_"] _name; + name = (if bin ? name then bin.name else crateName); path = if bin ? path then bin.path else ""; in bins + (if bin == "" then "" else ",") + "${name} ${path}" @@ -124,18 +123,6 @@ stdenv.mkDerivation (rec { ) "" crate.crateBin else ""; - finalBins = if crate ? crateBin then - builtins.foldl' (bins: bin: - let name = lib.strings.replaceStrings ["-"] ["_"] - (if bin ? name then bin.name else crateName); - new_name = if bin ? name then bin.name else crateName; - in - if name == new_name then bins else - (bins + "mv target/bin/${name} target/bin/${new_name};") - - ) "" crate.crateBin - else ""; - build = crate.build or ""; workspace_member = crate.workspace_member or "."; crateVersion = crate.version; @@ -156,7 +143,7 @@ stdenv.mkDerivation (rec { buildPhase = buildCrate { inherit crateName dependencies crateFeatures libName release libPath crateType - metadata crateBin finalBins verbose colors + metadata crateBin verbose colors extraRustcOpts; }; installPhase = installCrate crateName metadata; diff --git a/pkgs/build-support/rust/build-rust-crate/test/brotli-crates.nix b/pkgs/build-support/rust/build-rust-crate/test/brotli-crates.nix new file mode 100644 index 000000000000..068cc5a98843 --- /dev/null +++ b/pkgs/build-support/rust/build-rust-crate/test/brotli-crates.nix @@ -0,0 +1,95 @@ +{ lib, buildPlatform, buildRustCrate, fetchgit }: +let kernel = buildPlatform.parsed.kernel.name; + abi = buildPlatform.parsed.abi.name; + include = includedFiles: src: builtins.filterSource (path: type: + lib.lists.any (f: + let p = toString (src + ("/" + f)); in + (path == p) || (type == "directory" && lib.strings.hasPrefix path p) + ) includedFiles + ) src; + updateFeatures = f: up: functions: builtins.deepSeq f (lib.lists.foldl' (features: fun: fun features) (lib.attrsets.recursiveUpdate f up) functions); + mapFeatures = features: map (fun: fun { features = features; }); + mkFeatures = feat: lib.lists.foldl (features: featureName: + if feat.${featureName} or false then + [ featureName ] ++ features + else + features + ) [] (builtins.attrNames feat); +in +rec { + alloc_no_stdlib_1_3_0_ = { dependencies?[], buildDependencies?[], features?[] }: buildRustCrate { + crateName = "alloc-no-stdlib"; + version = "1.3.0"; + authors = [ "Daniel Reiter Horn " ]; + sha256 = "1jcp27pzmqdszgp80y484g4kwbjbg7x8a589drcwbxg0i8xwkir9"; + crateBin = [ { name = "example"; } ]; + inherit dependencies buildDependencies features; + }; + brotli_2_5_0_ = { dependencies?[], buildDependencies?[], features?[] }: buildRustCrate { + crateName = "brotli"; + version = "2.5.0"; + authors = [ "Daniel Reiter Horn " "The Brotli Authors" ]; + sha256 = "1ynw4hkdwnp0kj30p86ls44ahv4s99258s019bqrq4mya8hlsb5b"; + crateBin = [ { name = "brotli"; } ]; + inherit dependencies buildDependencies features; + }; + brotli_decompressor_1_3_1_ = { dependencies?[], buildDependencies?[], features?[] }: buildRustCrate { + crateName = "brotli-decompressor"; + version = "1.3.1"; + authors = [ "Daniel Reiter Horn " "The Brotli Authors" ]; + sha256 = "022g69q1xzwdj0130qm3fa4qwpn4q1jx3lc8yz0v0v201p7bm8fb"; + crateBin = [ { name = "brotli-decompressor"; } ]; + inherit dependencies buildDependencies features; + }; + alloc_no_stdlib_1_3_0 = { features?(alloc_no_stdlib_1_3_0_features {}) }: alloc_no_stdlib_1_3_0_ { + features = mkFeatures (features.alloc_no_stdlib_1_3_0 or {}); + }; + alloc_no_stdlib_1_3_0_features = f: updateFeatures f (rec { + alloc_no_stdlib_1_3_0.default = (f.alloc_no_stdlib_1_3_0.default or true); + }) []; + brotli_2_5_0 = { features?(brotli_2_5_0_features {}) }: brotli_2_5_0_ { + dependencies = mapFeatures features ([ alloc_no_stdlib_1_3_0 brotli_decompressor_1_3_1 ]); + features = mkFeatures (features.brotli_2_5_0 or {}); + }; + brotli_2_5_0_features = f: updateFeatures f (rec { + alloc_no_stdlib_1_3_0."no-stdlib" = + (f.alloc_no_stdlib_1_3_0."no-stdlib" or false) || + (brotli_2_5_0."no-stdlib" or false) || + (f.brotli_2_5_0."no-stdlib" or false); + alloc_no_stdlib_1_3_0.default = true; + brotli_2_5_0.default = (f.brotli_2_5_0.default or true); + brotli_decompressor_1_3_1."disable-timer" = + (f.brotli_decompressor_1_3_1."disable-timer" or false) || + (brotli_2_5_0."disable-timer" or false) || + (f.brotli_2_5_0."disable-timer" or false); + brotli_decompressor_1_3_1."no-stdlib" = + (f.brotli_decompressor_1_3_1."no-stdlib" or false) || + (brotli_2_5_0."no-stdlib" or false) || + (f.brotli_2_5_0."no-stdlib" or false); + brotli_decompressor_1_3_1.benchmark = + (f.brotli_decompressor_1_3_1.benchmark or false) || + (brotli_2_5_0.benchmark or false) || + (f.brotli_2_5_0.benchmark or false); + brotli_decompressor_1_3_1.default = true; + brotli_decompressor_1_3_1.seccomp = + (f.brotli_decompressor_1_3_1.seccomp or false) || + (brotli_2_5_0.seccomp or false) || + (f.brotli_2_5_0.seccomp or false); + }) [ alloc_no_stdlib_1_3_0_features brotli_decompressor_1_3_1_features ]; + brotli_decompressor_1_3_1 = { features?(brotli_decompressor_1_3_1_features {}) }: brotli_decompressor_1_3_1_ { + dependencies = mapFeatures features ([ alloc_no_stdlib_1_3_0 ]); + features = mkFeatures (features.brotli_decompressor_1_3_1 or {}); + }; + brotli_decompressor_1_3_1_features = f: updateFeatures f (rec { + alloc_no_stdlib_1_3_0."no-stdlib" = + (f.alloc_no_stdlib_1_3_0."no-stdlib" or false) || + (brotli_decompressor_1_3_1."no-stdlib" or false) || + (f.brotli_decompressor_1_3_1."no-stdlib" or false); + alloc_no_stdlib_1_3_0.default = true; + alloc_no_stdlib_1_3_0.unsafe = + (f.alloc_no_stdlib_1_3_0.unsafe or false) || + (brotli_decompressor_1_3_1.unsafe or false) || + (f.brotli_decompressor_1_3_1.unsafe or false); + brotli_decompressor_1_3_1.default = (f.brotli_decompressor_1_3_1.default or true); + }) [ alloc_no_stdlib_1_3_0_features ]; +} 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 e5e77c93555d..08f7238c1fda 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, stdenv, buildRustCrate, runCommand, writeTextFile, symlinkJoin }: +{ lib, stdenv, buildRustCrate, runCommand, writeTextFile, symlinkJoin, callPackage }: let mkCrate = args: let p = { @@ -72,7 +72,30 @@ let crateBinNoPath3 = { crateBin = [{ name = "my-binary5"; }]; src = mkBin "src/bin/main.rs"; }; crateBinNoPath4 = { crateBin = [{ name = "my-binary6"; }]; src = mkBin "src/main.rs";}; }; - in lib.mapAttrs (key: value: mkTest (value // lib.optionalAttrs (!value?crateName) { crateName = key; })) cases; + brotliCrates = (callPackage ./brotli-crates.nix {}); + in lib.mapAttrs (key: value: mkTest (value // lib.optionalAttrs (!value?crateName) { crateName = key; })) cases // { + brotliTest = let + pkg = brotliCrates.brotli_2_5_0 {}; + in runCommand "run-brotli-test-cmd" { + nativeBuildInputs = [ pkg ]; + } '' + ${pkg}/bin/brotli -c ${pkg}/bin/brotli > /dev/null && touch $out + ''; + allocNoStdLibTest = let + pkg = brotliCrates.alloc_no_stdlib_1_3_0 {}; + in runCommand "run-alloc-no-stdlib-test-cmd" { + nativeBuildInputs = [ pkg ]; + } '' + test -e ${pkg}/bin/example && touch $out + ''; + brotliDecompressorTest = let + pkg = brotliCrates.brotli_decompressor_1_3_1 {}; + in runCommand "run-brotli-decompressor-test-cmd" { + nativeBuildInputs = [ pkg ]; + } '' + test -e ${pkg}/bin/brotli-decompressor && touch $out + ''; + }; test = runCommand "run-buildRustCrate-tests" { nativeBuildInputs = builtins.attrValues tests; } "