Files
project-templates/rust/default.nix
T
reed 00ab3b0148 fix(rust): add rust standard library deps to the environment when the toolchain needs it
This is a fix for "error: no matching package named
`rustc-literal-escaper` found" when adding "rust-src" to the components
array in the toolchain file
2026-10-07 19:26:39 -05:00

263 lines
9.1 KiB
Nix

{
flake-parts-lib,
inputs,
...
}: {
flake = {
templates = {
rust = {
path = ./template;
description = "A rust flake with crane and templates for command args and configs";
};
};
flakeModules.rust = {
imports = [
inputs.treefmt-nix.flakeModule
];
options.perSystem = flake-parts-lib.mkPerSystemOption ({
lib,
config,
pkgs,
self',
...
}: let
cfg = config.krantz.rust;
manifest = (lib.importTOML "${cfg.srcFiltered}/Cargo.toml").package;
craneLib =
if hasToolchain
then (inputs.crane.mkLib rustPkgs).overrideToolchain mkRustToolchain
else inputs.crane.mkLib pkgs;
# Attrs to be included for all crane functions that build our crate
cargoBuildAttrs = let
# Include the standard library Cargo.lock if we have the "rust-src" component in our toolchain
cargoVendorDir =
lib.optionalAttrs (
hasToolchain
&& lib.pipe cfg.srcToolchain [
builtins.readFile
fromTOML
(t: t ? toolchain.components && builtins.elem "rust-src" t.toolchain.components)
]
) {
cargoVendorDir = craneLib.vendorMultipleCargoDeps {
inherit (craneLib.findCargoFiles cfg.srcFiltered) cargoConfigs;
cargoLockList = [
"${cfg.srcFiltered}/Cargo.lock"
"${(mkRustToolchain rustPkgs).passthru.availableComponents.rust-src}/lib/rustlib/src/rust/library/Cargo.lock"
];
};
};
in
rec {
src = cfg.srcFiltered;
buildInputs = cfg.runtimeDeps;
nativeBuildInputs = cfg.buildDeps;
# Build *just* the cargo dependencies, so we can reuse
# all of that work (e.g. via cachix) when running in CI
cargoArtifacts = craneLib.buildDepsOnly ({
inherit src buildInputs nativeBuildInputs;
}
// cargoVendorDir
// cfg.cargoArtifacts.extraAttrs);
}
// cargoVendorDir;
hasToolchain = cfg.srcToolchain != null && builtins.pathExists cfg.srcToolchain;
rustPkgs = pkgs.extend inputs.rust-overlay.overlays.default;
mkRustToolchain = p: p.rust-bin.fromRustupToolchainFile cfg.srcToolchain;
in {
options.krantz.rust = {
enable = lib.mkEnableOption "building a cargo project with crane";
src = lib.mkOption {
description = "The path to the root of the crate.";
type = lib.types.nullOr lib.types.path;
default = null;
example = lib.literalExpression "./.";
};
srcFiltered = lib.mkOption {
description = "A path to the filtered directory of the root of the crate.";
type = lib.types.path;
default = craneLib.cleanCargoSource cfg.src;
defaultText = lib.literalExpression "config.krantz.rust.craneLib.cleanCargoSource cfg.src";
example = lib.literalExpression "./.";
};
srcToolchain = lib.mkOption {
description = "A path to the rust-toolchain(.toml) file for the repo. Will use the toolchain from nixpkgs if the file does not exist.";
type = lib.types.nullOr lib.types.path;
default =
if cfg.src != null
then cfg.src + "/rust-toolchain.toml"
else null;
defaultText =
lib.literalExpression
/*
nix
*/
''
if cfg.src != null
then cfg.src + "/rust-toolchain.toml"
else null;
'';
example = lib.literalExpression "./rust-toolchain";
};
runtimeDeps = lib.mkOption {
description = "Packages needed for the app to run.";
type = lib.types.listOf lib.types.package;
default = [];
example = with pkgs; [
openssl
];
};
buildDeps = lib.mkOption {
description = "Packages needed for the app to build.";
type = lib.types.listOf lib.types.package;
default = [];
example = with pkgs; [
pkg-config
rustPlatform.bindgenHook
];
};
devDeps = lib.mkOption {
description = "Packages to include only in the devShell.";
type = lib.types.listOf lib.types.package;
default = with pkgs; [
rust-analyzer
];
};
cargoArtifacts = {
extraAttrs = lib.mkOption {
description = "Extra attributes to merge into buildDepsOnly. See https://crane.dev/API.html#cranelibbuilddepsonly";
type = lib.types.attrs;
default = {};
example = {
doCheck = false;
};
};
};
devShell = {
enable = lib.mkEnableOption "a rust development environment" // {default = true;};
name = lib.mkOption {
description = "The name of the attribute in devShells to configure";
type = lib.types.str;
default = "default";
example = "rust";
};
extraAttrs = lib.mkOption {
description = "Extra attributes to merge into the devshell. See https://crane.dev/API.html#cranelibdevshell";
type = lib.types.attrs;
default = {};
example = {
shellHook = ''
export DEBUG=1
'';
};
};
};
package = {
enable = lib.mkEnableOption "the compiled cargo project" // {default = true;};
name = lib.mkOption {
description = "The name of the attribute in packages to configure";
type = lib.types.str;
default = "default";
example = "lib";
};
extraAttrs = lib.mkOption {
description = "Extra attributes to merge into the package. See https://crane.dev/API.html#cranelibbuildpackage";
type = lib.types.attrs;
default = {};
example = {
doCheck = false;
};
};
};
formatter = {
enable = lib.mkEnableOption "treefmt for formatting multiple types of files" // {default = true;};
nix = lib.mkEnableOption "formatting for nix files" // {default = true;};
rust = lib.mkEnableOption "formatting for rust files" // {default = true;};
toml = lib.mkEnableOption "formatting for toml files" // {default = true;};
markdown = lib.mkEnableOption "formatting for toml files" // {default = true;};
};
checks = {
enable = lib.mkEnableOption "checks for rust" // {default = true;};
linter = lib.mkEnableOption "lints for rust" // {default = true;};
};
craneLib = lib.mkOption {
description = "The crane lib instance this module is using. Useful for overriding options";
default = craneLib;
defaultText = lib.literalExpression "crane.mkLib pkgs";
readOnly = true;
};
};
config = lib.mkIf cfg.enable {
# Dev Shell that lets you enter an environment with all the necessary utilites
# Available through 'nix develop'
# Also can be activated automatically if direnv is installed on the system with 'direnv allow'
devShells = lib.mkIf cfg.devShell.enable {
${cfg.devShell.name} =
craneLib.devShell {
inherit (self') checks;
# extra tooling dependencies
packages = cfg.devDeps;
inputsFrom = [self'.packages.${cfg.package.name}];
}
// cfg.devShell.extraAttrs;
};
# Your custom packages
# Accessible through 'nix build', 'nix shell', 'nix run', etc
packages = lib.mkIf cfg.package.enable {
${cfg.package.name} = craneLib.buildPackage ({
}
// cargoBuildAttrs
// cfg.package.extraAttrs);
};
# Formatter for nix files, available through 'nix fmt'
treefmt.programs = lib.mkIf cfg.formatter.enable {
alejandra.enable = lib.mkIf cfg.formatter.nix true;
rustfmt = lib.mkIf cfg.formatter.rust {
enable = true;
edition = manifest.edition;
};
taplo.enable = lib.mkIf cfg.formatter.toml true;
rumdl-check.enable = lib.mkIf cfg.formatter.markdown true;
rumdl-format.enable = lib.mkIf cfg.formatter.markdown true;
};
checks = lib.mkIf cfg.checks.enable {
clippy = lib.mkIf cfg.checks.linter (craneLib.cargoClippy ({
cargoClippyExtraArgs = "-- --deny warnings";
}
// cargoBuildAttrs));
};
};
});
};
};
}