{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11";
flake-utils.url = "github:numtide/flake-utils";
nix-filter.url = "github:numtide/nix-filter";
rust-overlay = {
url = "github:oxalica/rust-overlay";
inputs = {
nixpkgs.follows = "nixpkgs";
};
};
crane = {
url = "github:ipetkov/crane";
};
advisory-db = {
url = "github:rustsec/advisory-db";
flake = false;
};
};
outputs = { self, nixpkgs, nix-filter, rust-overlay, crane, advisory-db, flake-utils }:
flake-utils.lib.eachDefaultSystem
(system:
let
overlays = [
(import rust-overlay)
(final: prev: {
nix-filter = nix-filter.lib;
rust-toolchain = pkgs.rust-bin.nightly.latest.default;
rust-dev-toolchain = pkgs.rust-toolchain.override {
extensions = [ "rust-src" ];
};
})
];
pkgs = import nixpkgs {
inherit system overlays;
};
craneLib =
(crane.mkLib pkgs).overrideToolchain pkgs.rust-toolchain;
lib = pkgs.lib;
stdenv = pkgs.stdenv;
commonNativeBuildInputs = with pkgs; [
libiconv
libtool
libxml2
libxslt
llvmPackages.libclang
openssl
pkg-config
xmlsec
];
fixtureFilter = path: _type:
builtins.match ".*test_vectors.*" path != null ||
builtins.match ".*\.h" path != null;
sourceAndFixtures = path: type:
(fixtureFilter path type) || (craneLib.filterCargoSources path type);
src = lib.cleanSourceWith {
src = ./.;
filter = sourceAndFixtures;
};
cargoFile = builtins.fromTOML (builtins.readFile ./Cargo.toml);
commonArgs = {
pname = "samael";
inherit src;
version = cargoFile.package.version;
# Need to tell bindgen where to find libclang
LIBCLANG_PATH = "${pkgs.llvmPackages.libclang.lib}/lib";
# Set C flags for Rust's bindgen program. Unlike ordinary C
# compilation, bindgen does not invoke $CC directly. Instead it
# uses LLVM's libclang. To make sure all necessary flags are
# included we need to look in a few places.
# See https://web.archive.org/web/20220523141208/https://hoverbear.org/blog/rust-bindgen-in-nix/
BINDGEN_EXTRA_CLANG_ARGS = "${builtins.readFile "${stdenv.cc}/nix-support/libc-crt1-cflags"} \
${builtins.readFile "${stdenv.cc}/nix-support/libc-cflags"} \
${builtins.readFile "${stdenv.cc}/nix-support/cc-cflags"} \
${builtins.readFile "${stdenv.cc}/nix-support/libcxx-cxxflags"} \
-idirafter ${pkgs.libiconv}/include \
${lib.optionalString stdenv.cc.isClang "-idirafter ${stdenv.cc.cc}/lib/clang/${lib.getVersion stdenv.cc.cc}/include"} \
${lib.optionalString stdenv.cc.isGNU "-isystem ${stdenv.cc.cc}/include/c++/${lib.getVersion stdenv.cc.cc} -isystem ${stdenv.cc.cc}/include/c++/${lib.getVersion stdenv.cc.cc}/${stdenv.hostPlatform.config} -idirafter ${stdenv.cc.cc}/lib/gcc/${stdenv.hostPlatform.config}/${lib.getVersion stdenv.cc.cc}/include"} \
";
nativeBuildInputs = commonNativeBuildInputs;
cargoExtraArgs = "--features xmlsec";
cargoTestExtraArgs = "--features xmlsec";
};
# Build *just* the cargo dependencies, so we can reuse
# all of that work (e.g. via cachix) when running in CI
cargoArtifacts = craneLib.buildDepsOnly commonArgs;
samael = craneLib.buildPackage (commonArgs // {
inherit cargoArtifacts;
});
in
rec {
# `nix build`
packages.default = samael;
# `nix develop`
devShells.default = pkgs.mkShell {
# Need to tell bindgen where to find libclang
LIBCLANG_PATH = "${pkgs.llvmPackages.libclang.lib}/lib";
# Set C flags for Rust's bindgen program. Unlike ordinary C
# compilation, bindgen does not invoke $CC directly. Instead it
# uses LLVM's libclang. To make sure all necessary flags are
# included we need to look in a few places.
# See https://web.archive.org/web/20220523141208/https://hoverbear.org/blog/rust-bindgen-in-nix/
BINDGEN_EXTRA_CLANG_ARGS = "${builtins.readFile "${stdenv.cc}/nix-support/libc-crt1-cflags"} \
${builtins.readFile "${stdenv.cc}/nix-support/libc-cflags"} \
${builtins.readFile "${stdenv.cc}/nix-support/cc-cflags"} \
${builtins.readFile "${stdenv.cc}/nix-support/libcxx-cxxflags"} \
-idirafter ${pkgs.libiconv}/include \
${lib.optionalString stdenv.cc.isClang "-idirafter ${stdenv.cc.cc}/lib/clang/${lib.getVersion stdenv.cc.cc}/include"} \
${lib.optionalString stdenv.cc.isGNU "-isystem ${stdenv.cc.cc}/include/c++/${lib.getVersion stdenv.cc.cc} -isystem ${stdenv.cc.cc}/include/c++/${lib.getVersion stdenv.cc.cc}/${stdenv.hostPlatform.config} -idirafter ${stdenv.cc.cc}/lib/gcc/${stdenv.hostPlatform.config}/${lib.getVersion stdenv.cc.cc}/include"} \
";
buildInputs = with pkgs; [ rust-dev-toolchain nixpkgs-fmt ];
nativeBuildInputs = commonNativeBuildInputs;
};
checks = {
# Build the crate as part of `nix flake check` for convenience
inherit samael;
# Run clippy (and deny all warnings) on the crate source,
# again, resuing the dependency artifacts from above.
#
# Note that this is done as a separate derivation so that
# we can block the CI if there are issues here, but not
# prevent downstream consumers from building our crate by itself.
samael-clippy = craneLib.cargoClippy (commonArgs // {
inherit cargoArtifacts;
cargoClippyExtraArgs = "--all-targets"; #-- --deny warnings
});
samael-doc = craneLib.cargoDoc (commonArgs // {
inherit cargoArtifacts;
});
# Check formatting
samael-fmt = craneLib.cargoFmt {
inherit src;
};
# Audit dependencies
samael-audit = craneLib.cargoAudit {
inherit src advisory-db;
};
# Run tests with cargo-nextest
# Consider setting `doCheck = false` on `samael` if you do not want
# the tests to run twice
samael-nextest = craneLib.cargoNextest (commonArgs // {
inherit cargoArtifacts;
cargoExtraArgs = "";
cargoNextestExtraArgs = "--features xmlsec";
partitions = 1;
partitionType = "count";
});
};
});
}