Skip to content

Instantly share code, notes, and snippets.

@ryanswrt
Created February 16, 2023 02:34
Show Gist options
  • Save ryanswrt/ec1f45389740b587eddfa4613e1f120c to your computer and use it in GitHub Desktop.
Save ryanswrt/ec1f45389740b587eddfa4613e1f120c to your computer and use it in GitHub Desktop.
Flake-parts module for declaring rust/crane projects
{ self, config, lib, flake-parts-lib, ... }:
let
inherit (flake-parts-lib) mkPerSystemOption;
inherit (lib) mkOption types;
inherit (types) functionTo raw listOf;
in {
# define all of the options on our flake
options = {
perSystem = mkPerSystemOption
({ config, self', inputs', pkgs, system, ... }:
let
commonCraneArgsSubmodule = types.submodule {
options = {
src = mkOption {
type = types.path;
description = "Path to project root";
default = ./.;
};
buildInputs = mkOption {
type = listOf (types.attrsOf raw);
default = [ ];
};
nativeBuildInputs = mkOption {
type = listOf (types.attrsOf raw);
default = [ pkgs.pkgconfig ];
};
propagatedBuildInputs = mkOption {
type = listOf (types.attrsOf raw);
default = [ ];
};
};
};
cargoCoverageSubmodule = types.submodule {
options = {
enable = mkOption {
type = types.bool;
description = ''
Run coverage checks for project
'';
default = true;
};
};
};
clippySubmodule = types.submodule {
options = {
enable = mkOption {
type = types.bool;
description = ''
Run clippy checks for project
'';
default = true;
};
};
};
projectSubmodule = types.submodule {
options = {
version = mkOption {
type = types.str;
default = "0.0.0";
};
extraPythonPackages = mkOption {
type = functionTo (listOf (types.attrsOf raw));
default = _: [ ];
};
clippyCheck = mkOption {
type = clippySubmodule;
default = { };
};
cargoCoverageCheck = mkOption {
type = cargoCoverageSubmodule;
default = { };
};
devInputs = mkOption {
type = listOf (types.attrsOf raw);
default = [ ];
};
buildInputs = mkOption {
type = listOf (types.attrsOf raw);
default = [ ];
};
nativeBuildInputs = mkOption {
type = listOf (types.attrsOf raw);
default = [ ];
};
propagatedBuildInputs = mkOption {
type = listOf (types.attrsOf raw);
default = [ ];
};
};
};
in {
options.rustDevInputs = mkOption {
type = listOf (types.attrsOf raw);
default = [ ];
};
options.commonCraneArgs = mkOption {
description = "shared config across workspace crates";
type = commonCraneArgsSubmodule;
};
options.fenix = mkOption {
description = "fenix package to use";
type = types.attrsOf raw;
default = pkgs.crane;
};
options.crane = mkOption {
description = "crane package to use";
type = types.attrsOf raw;
default = pkgs.crane;
};
options.craneProjects = mkOption {
description = "crane projects";
type = types.attrsOf projectSubmodule;
};
});
};
# use the options to configure our builds and tools
config = {
perSystem = { config, self', inputs', pkgs, system, ... }:
let
inherit (config) crane fenix;
commonArgs = config.commonCraneArgs // {
src = crane.cleanCargoSource config.commonCraneArgs.src;
};
projects = lib.mapAttrs (projectKey: cfg:
let
devShell = pkgs.mkShell {
buildInputs = commonArgs.nativeBuildInputs
++ commonArgs.buildInputs ++ config.rustDevInputs
++ cfg.devInputs ++ cfg.buildInputs ++ (with pkgs; [
(fenix.complete.withComponents [
"cargo"
"clippy"
"rust-src"
"rustc"
"rustfmt"
])
cargo-tarpaulin
cargo-watch
fenix.rust-analyzer
]);
};
buildInputs = commonArgs.buildInputs ++ cfg.buildInputs;
cargoArtifacts =
crane.buildDepsOnly (commonArgs // {
inherit (cfg) propagatedBuildInputs;
inherit buildInputs;
pname = "${projectKey}-deps";
});
package = crane.buildPackage (commonArgs // {
pname = projectKey;
inherit (cfg) version ;
doCheck = false;
inherit cargoArtifacts;
cargoExtraArgs = "-p ${projectKey}";
});
clippy = crane.cargoClippy (commonArgs // {
inherit cargoArtifacts;
buildInputs = [ (fenix.complete.withComponents [ "clippy" ]) ];
cargoClippyExtraArgs =
"--all-targets -p ${projectKey} -- --deny warnings";
});
crateCoverage = crane.cargoTarpaulin (commonArgs // {
# we need the whole source, not the cleaned source, for test data
src = config.commonCraneArgs.src;
cargoExtraArgs = "--all-features";
cargoTarpaulinExtraArgs =
"-p ${projectKey} --skip-clean --out Xml --output-dir $out -t 120";
inherit cargoArtifacts;
});
checks = lib.filterAttrs (_: v: v != null) {
"${projectKey}-clippy" = if cfg.clippyCheck.enable then
(pkgs.runCommand "clippy" { buildInputs = [ clippy ]; }
"touch $out")
else
null;
"${projectKey}-coverage" = if cfg.cargoCoverageCheck.enable then
(pkgs.runCommand "coverage" { buildInputs = [ crateCoverage ]; }
"cp ${crateCoverage}/cobertura.xml $out")
else
null;
};
in { inherit devShell package checks; }) config.craneProjects;
in {
packages = (lib.mapAttrs (_: project: project.package) projects);
checks = lib.mkMerge
(lib.mapAttrsToList (_: project: project.checks) projects);
devShells = (lib.mapAttrs (_: project: project.devShell) projects) // {
# Shell with poetry to init / edit packages
cargo = pkgs.mkShell { packages = [ pkgs.cargo ]; };
default = pkgs.mkShell { packages = [ config.rustDevInputs ]; };
rust = pkgs.mkShell { packages = [ config.rustDevInputs ]; };
};
};
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment