After my successful integration of Nix with a trivial Rust application, I tried it with one that uses the opencv crate. I first added this to my buildRustPackage call:

NixbuildInputs = [
            rustPlatform.bindgenHook
            rustPlatform.cargoSetupHook
            opencv
          ];
          nativeBuildInputs = [
            pkgs.pkg-config
            pkgs.clang
          ];
          LIBCLANG_PATH="${pkgs.llvmPackages.libclang.lib}/lib";

But it failed even with a completely empty test application (created with cargo init and nothing more), ultimately showing this:

clippy-preview> auto-patchelf: 0 dependencies could not be satisfied
test> unpacking sources
test> unpacking source archive /nix/store/z8sc7a2cm3mhv6wc51vvhqkqwxdk2kjf-2x67xy58ghamzwhv4083n43lfzrf5zx2-source
test> source root is 2x67xy58ghamzwhv4083n43lfzrf5zx2-source
test> Executing cargoSetupPostUnpackHook
test> unpacking source archive /nix/store/mbak1ga8nj98z11m27ygbx09h45z5ad5-cargo-vendor-dir
test> Finished cargoSetupPostUnpackHook
test> Executing cargoSetupPostUnpackHook
test> unpacking source archive /nix/store/mbak1ga8nj98z11m27ygbx09h45z5ad5-cargo-vendor-dir
test> cp: cannot create directory 'cargo-vendor-dir/mbak1ga8nj98z11m27ygbx09h45z5ad5-cargo-vendor-dir': Permission denied
test> do not know how to unpack source archive /nix/store/mbak1ga8nj98z11m27ygbx09h45z5ad5-cargo-vendor-dir
error: builder for '/nix/store/rrs8wlwi8pzr6lxjfg67xh98j4npv4qr-test-0.1.0.drv' failed with exit code 1
error: 1 dependencies of derivation '/nix/store/f1bkzblxvp8xmaxi0px9417g5ysb0qf5-test-config.json.drv' failed to build
error: 1 dependencies of derivation '/nix/store/183k1sb6yw1rzmwb4h85cz29m9lvrs0s-docker-image-test.tar.gz.drv' failed to build

I got around that by removing cargoSetupHook, giving me:

NixbuildInputs = [
            rustPlatform.bindgenHook
            opencv
          ];

That made it fail with a permission denied error after generating the headers, which is a known issue with the crate under Nix. I hope it’s fixed soon, because the application would really benefit from Nix’s precision.

I wanted to try building just that crate with OUT_DIR set. cargoDeps sounded right, if I used buildRustPackage to build rust-opencv separately then add it to the dependencies, but all the uses I see in the nixpkgs repository show cargoDeps being set to the result of fetching Cargo packages or tarballs in special ways, so I don’t know how to do it. crateOverrides looked promising but it’s for buildRustCrate, which builds packages without Cargo, so I guess I’d have to switch to that to use it.

I decided to try nocargo, which also builds crates without Cargo but provides a clear way to customize dependencies. It failed at first because of a hardcoded -C embed-bitcode=no, which is incompatible with link-time optimization (LTO):

rust_adler> unpacking sources
rust_adler> unpacking source archive /nix/store/lhwj837snajf713x99vys2yk4mcrg1im-crate-adler-1.0.2.tar.gz
rust_adler> source root is adler-1.0.2
rust_adler> patching sources
rust_adler> configuring
rust_adler> building
rust_adler> Building lib: RUSTC 'src/lib.rs' '--out-dir=/nix/store/nfmmndz97720f1xgkl4rsy41y81qdfly-rust_adler-1.0.2/lib' '--crate-name=adler' '--crate-type=lib' '--emit=metadata,link' '-Cembed-bitcode=no' '-Cextra-filename=-11fdd89355321299' '--color=always' '-Copt-level=3' '-Cdebuginfo=0' '-Cdebug-assertions=no' '-Coverflow-checks=no' '-Clto=thin' '-Cpanic=unwind' '-Ccodegen-units=1' '--cap-lints=allow' '-Cmetadata=11fdd89355321299' '-Ldependency=/nix/store/z03r6pvjz02aac1s9ivvx4x403741137-rust_adler-1.0.2-dev/rust-support/deps-closure'
rust_adler> error: options `-C embed-bitcode=no` and `-C lto` are incompatible

I disabled LTO and liberally sprinkled nativeBuildInputs around various parts of the flake definition. In the end, my outputs looked like this:

Nixoutputs = { nixpkgs, flake-utils, nocargo, ... }@inputs:
    flake-utils.lib.eachSystem [ "x86_64-linux" ] (system:
      let
        pkgs = import nixpkgs { inherit system; config.allowUnfree = true; };
        opencv = pkgs.opencv.override (old : {
          enableEXR = false;
          enableGStreamer = false;
          enableUnfree = true;
          enableTIFF = false;
        });

        ws = nocargo.lib.${system}.mkRustPackageOrWorkspace {
          src = ./.;

          buildCrateOverrides = with nixpkgs.legacyPackages.${system}; {
            "clang-sys 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = old: {
              LIBCLANG_PATH="${pkgs.llvmPackages.libclang.lib}/lib";
              nativeBuildInputs = [ pkgs.pkg-config pkgs.llvm pkgs.clang ];
            };

            "opencv 0.65.0 (registry+https://github.com/rust-lang/crates.io-index)" = old: {
              OUT_DIR = ".";
              nativeBuildInputs = [ opencv pkgs.pkg-config pkgs.llvm pkgs.clang ];
            };

            # The crate in the workspace that needs OpenCV.
            "crate-needing-opencv" = old: {
              nativeBuildInputs = [ opencv ];
            };
          };
        };
      in rec {
        packages = ws.release
          // nixpkgs.lib.mapAttrs' (name: value: { name = "${name}-dev"; inherit value; }) ws.dev;

        dockerImage = pkgs.dockerTools.buildLayeredImage {
          name = "app-name";
          config = { Entrypoint = [ "${packages.rest.bin}/bin/crate-needing-opencv" ]; };
        };

        defaultPackage = dockerImage;
      });

Sadly, after all that effort, the final image is much larger—780 MB, compared to 198 MB for the Dockerfile-derived version—despite the theoretical precision of Nix. This is probably because it desperately needs that LTO step. I opened an issue in the nocargo repository and I’m exploring possible solutions. For the moment, I’ll continue using the Dockerfile.