mirror of
https://codeberg.org/icewind/mill-scale.git
synced 2026-06-03 09:54:19 +02:00
487 lines
16 KiB
Nix
487 lines
16 KiB
Nix
# mill-scale -- Another rust module for flakelight
|
|
# Copyright (C) 2024 Robin Appelman <robin@icewind.nl>
|
|
# SPDX-License-Identifier: MIT
|
|
{
|
|
lib,
|
|
src,
|
|
config,
|
|
flakelight,
|
|
inputs,
|
|
...
|
|
}: let
|
|
inherit (builtins) elem readFile pathExists match any concatLists;
|
|
inherit (lib) getExe map mkDefault mkIf mkMerge mkOption warnIf optionalAttrs types optionalString genAttrs hasInfix makeSearchPathOutput;
|
|
inherit (lib.fileset) fileFilter toSource unions;
|
|
inherit (flakelight.types) fileset function optFunctionTo;
|
|
makePkgConfigPath = makeSearchPathOutput "dev" "lib/pkgconfig";
|
|
|
|
filteredSrc = toSource {
|
|
root = src;
|
|
fileset = unions (config.extraPaths ++ [config.fileset]);
|
|
};
|
|
|
|
cargoToml = fromTOML (readFile config.cargoToml);
|
|
cargoMeta =
|
|
(import ./cargo-meta.nix {
|
|
inherit lib;
|
|
})
|
|
cargoToml;
|
|
inherit (cargoMeta) tomlPackage hasMsrv hasWorkspace hasNonDefaultFeatures hasDefaultFeatures msrv;
|
|
|
|
maybeWorkspace = optionalString hasWorkspace "--workspace";
|
|
hasExamples = pathExists (src + /examples);
|
|
hasDefaultPackage = pathExists (src + /nix/package.nix);
|
|
|
|
autoDeps = import ./autodeps {
|
|
inherit (config) cargoLock;
|
|
inherit lib src config;
|
|
};
|
|
buildDeps = pkgs: rec {
|
|
buildInputs = (autoDeps pkgs).buildInputs ++ (config.buildInputs pkgs);
|
|
nativeBuildInputs = (autoDeps pkgs).nativeBuildInputs ++ (config.nativeBuildInputs pkgs);
|
|
runtimeInputs = (autoDeps pkgs).runtimeInputs ++ (config.runtimeInputs pkgs);
|
|
env =
|
|
(autoDeps pkgs).env
|
|
// (config.buildEnv pkgs)
|
|
// {
|
|
LD_LIBRARY_PATH = "/run/opengl-driver/lib/:${lib.makeLibraryPath runtimeInputs}";
|
|
};
|
|
};
|
|
|
|
defaultToolchain = pkgs:
|
|
if pathExists (src + "/rust-toolchain.toml")
|
|
then pkgs.rust-bin.fromRustupToolchainFile (src + "/rust-toolchain.toml")
|
|
else pkgs.rust-bin.stable.latest.default;
|
|
|
|
autoTools = let
|
|
definitions = import ./autotools.nix;
|
|
perDependency = map (dep: definitions.${dep} or []) (cargoMeta.dependencies ++ cargoMeta.dev-dependencies);
|
|
all = concatLists perDependency;
|
|
in
|
|
pkgs: map (pkgName: pkgs.${pkgName}) all;
|
|
in
|
|
warnIf (! builtins ? readFileType) "Unsupported Nix version in use."
|
|
{
|
|
options = {
|
|
cargoToml = mkOption {
|
|
type = with types; path;
|
|
default = src + /Cargo.toml;
|
|
};
|
|
cargoLock = mkOption {
|
|
type = with types; path;
|
|
default = src + /Cargo.lock;
|
|
};
|
|
extraFiles = mkOption {
|
|
type = with types; listOf str;
|
|
default = [];
|
|
};
|
|
extraFilesRegex = mkOption {
|
|
type = with types; listOf str;
|
|
default = [];
|
|
};
|
|
extraPaths = mkOption {
|
|
type = with types; listOf path;
|
|
default = [];
|
|
};
|
|
fileset = mkOption {
|
|
type = fileset;
|
|
default =
|
|
fileFilter
|
|
(file:
|
|
file.hasExt "rs"
|
|
|| match "snapshot__.*\.snap" file.name != null
|
|
|| elem file.name (["Cargo.toml" "Cargo.lock" "config.toml"] ++ config.extraFiles)
|
|
|| any (re: match re file.name != null) config.extraFilesRegex)
|
|
src;
|
|
};
|
|
crossTargets = mkOption {
|
|
type = with types; listOf str;
|
|
default = [];
|
|
};
|
|
buildInputs = mkOption {
|
|
type = function;
|
|
default = pkgs: [];
|
|
description = "build inputs for the package";
|
|
};
|
|
nativeBuildInputs = mkOption {
|
|
type = function;
|
|
default = pkgs: [];
|
|
description = "native build inputs for the package";
|
|
};
|
|
buildEnv = mkOption {
|
|
type = function;
|
|
default = pkgs: {};
|
|
description = "build environent variables for the package";
|
|
};
|
|
runtimeInputs = mkOption {
|
|
type = function;
|
|
default = pkgs: [];
|
|
description = "runtime inputs for the package";
|
|
};
|
|
tools = mkOption {
|
|
type = function;
|
|
default = pkgs: with pkgs; [cargo-edit bacon];
|
|
description = "extra packages to make available in the dev shells";
|
|
};
|
|
autodeps = mkOption {
|
|
type = types.bool;
|
|
default = true;
|
|
description = "Automatically detect (some) of the build dependencies";
|
|
};
|
|
packageOpts = mkOption {
|
|
type = optFunctionTo types.attrs;
|
|
default = {};
|
|
};
|
|
toolchain = mkOption {
|
|
type = function;
|
|
default = defaultToolchain;
|
|
description = "rust toolchain to use";
|
|
};
|
|
msrvToolchain = mkOption {
|
|
type = function;
|
|
default = pkgs: pkgs.rust-bin.stable.${msrv}.default;
|
|
description = "rust toolchain to use for msrv check";
|
|
};
|
|
semverToolchain = mkOption {
|
|
type = function;
|
|
default = pkgs: pkgs.rust-bin.stable."1.86.0".default;
|
|
description = "rust toolchain to use for cargo-semver-checks";
|
|
};
|
|
miriToolchain = mkOption {
|
|
type = function;
|
|
default = pkgs:
|
|
pkgs.rust-bin.selectLatestNightlyWith (toolchain:
|
|
toolchain.default.override {
|
|
extensions = ["miri" "rust-src"];
|
|
});
|
|
description = "rust toolchain to use for miri";
|
|
};
|
|
cargoTest = mkOption {
|
|
type = types.bool;
|
|
default = true;
|
|
description = "create a check for `cargo test`";
|
|
};
|
|
};
|
|
|
|
config = mkMerge [
|
|
(mkIf (pathExists config.cargoToml) {
|
|
withOverlays = [
|
|
(import inputs.rust-overlay)
|
|
(import ./overlay.nix)
|
|
(final: {
|
|
inputs,
|
|
rust-bin,
|
|
writeShellApplication,
|
|
stdenvNoCC,
|
|
...
|
|
} @ prev: rec {
|
|
commonCraneArgs =
|
|
{
|
|
inherit (config) cargoToml cargoLock;
|
|
src = filteredSrc;
|
|
strictDeps = true;
|
|
doCheck = false;
|
|
inherit ((buildDeps final)) buildInputs nativeBuildInputs;
|
|
}
|
|
// (buildDeps final).env;
|
|
allFeaturesCraneArgs =
|
|
commonCraneArgs
|
|
// {
|
|
cargoExtraArgs = "--locked --all-features ${maybeWorkspace}";
|
|
pname = "${crateName}-all-features";
|
|
};
|
|
noDefaultFeaturesCraneArgs =
|
|
commonCraneArgs
|
|
// {
|
|
cargoExtraArgs = "--locked --no-default-features ${maybeWorkspace}";
|
|
pname = "${crateName}-no-default-features";
|
|
};
|
|
msrvCraneArgs =
|
|
commonCraneArgs
|
|
// {
|
|
cargoExtraArgs = "--locked --all-features ${maybeWorkspace}";
|
|
pname = "${crateName}-msrv";
|
|
nativeBuildInputs = commonCraneArgs.nativeBuildInputs ++ [final.cargo-hack];
|
|
};
|
|
|
|
crateName =
|
|
(craneLib.crateNameFromCargoToml {
|
|
inherit (config) cargoToml;
|
|
inherit src;
|
|
}).pname;
|
|
craneLib = (inputs.crane.mkLib final).overrideToolchain (p: p.rustToolchain);
|
|
craneLibForTargets = targets: (inputs.crane.mkLib final).overrideToolchain (p: p.rustToolchain.override {inherit targets;});
|
|
craneLibMsrv = (inputs.crane.mkLib final).overrideToolchain (p: p.msrvRustToolchain);
|
|
|
|
cargoArtifacts = craneLib.buildDepsOnly commonCraneArgs;
|
|
cargoArtifactsAllFeatures = craneLib.buildDepsOnly allFeaturesCraneArgs;
|
|
cargoArtifactsNoDefault = craneLib.buildDepsOnly noDefaultFeaturesCraneArgs;
|
|
cargoArtifactsMsrv = craneLibMsrv.buildDepsOnly msrvCraneArgs;
|
|
|
|
rustToolchain = config.toolchain prev;
|
|
msrvRustToolchain = config.msrvToolchain prev;
|
|
miriRustToolchain = config.miriToolchain prev;
|
|
msrvToolchain = config.msrvToolchain prev;
|
|
semverToolchain = config.semverToolchain prev;
|
|
cargo-expand = writeShellApplication {
|
|
name = "cargo-expand";
|
|
runtimeInputs = [prev.cargo-expand];
|
|
text = ''
|
|
# shellcheck disable=SC2068
|
|
RUSTC_BOOTSTRAP=1 cargo-expand $@
|
|
'';
|
|
};
|
|
cargo-miri = writeShellApplication {
|
|
name = "cargo-miri";
|
|
runtimeInputs = [miriRustToolchain];
|
|
text = ''
|
|
# shellcheck disable=SC2068
|
|
cargo miri $@
|
|
'';
|
|
};
|
|
})
|
|
];
|
|
|
|
description = mkIf (tomlPackage ? description) tomlPackage.description;
|
|
|
|
# license will need to be set if Cargo license is a complex expression
|
|
license = mkIf (tomlPackage ? license) (mkDefault tomlPackage.license);
|
|
|
|
pname = tomlPackage.name;
|
|
|
|
packages =
|
|
(optionalAttrs (!hasDefaultPackage) {
|
|
default = {
|
|
craneLib,
|
|
cargoArtifacts,
|
|
defaultMeta,
|
|
commonCraneArgs,
|
|
pkgs,
|
|
}:
|
|
craneLib.buildPackage (commonCraneArgs
|
|
// {
|
|
inherit cargoArtifacts;
|
|
meta = defaultMeta;
|
|
}
|
|
// (config.packageOpts pkgs)
|
|
// (buildDeps pkgs).env);
|
|
})
|
|
// (genAttrs config.crossTargets (
|
|
target: {
|
|
craneLibForTargets,
|
|
cargoArtifacts,
|
|
defaultMeta,
|
|
callPackage,
|
|
crateName,
|
|
commonCraneArgs,
|
|
pkgs,
|
|
}: let
|
|
targetCraneLib = craneLibForTargets [target];
|
|
crossArgs = callPackage ./crossArgs.nix {} target;
|
|
in
|
|
targetCraneLib.buildPackage
|
|
(commonCraneArgs
|
|
// {
|
|
meta =
|
|
defaultMeta
|
|
// {
|
|
targetPlatform = target;
|
|
binarySuffix = crossArgs.BINARY_SUFFIX or "";
|
|
};
|
|
pname = "${crateName}-${target}";
|
|
cargoExtraArgs = "--target ${target}";
|
|
}
|
|
// crossArgs
|
|
// (config.packageOpts pkgs))
|
|
// (buildDeps pkgs).env
|
|
));
|
|
|
|
outputs = {
|
|
lib.crossMatrix =
|
|
map
|
|
(target: {
|
|
inherit target;
|
|
binary-suffix = optionalString (hasInfix "windows" target) ".exe";
|
|
})
|
|
config.crossTargets;
|
|
};
|
|
|
|
checks = {
|
|
craneLib,
|
|
craneLibMsrv,
|
|
cargoArtifacts,
|
|
cargoArtifactsMsrv,
|
|
cargoArtifactsAllFeatures,
|
|
cargoArtifactsNoDefault,
|
|
crateName,
|
|
commonCraneArgs,
|
|
allFeaturesCraneArgs,
|
|
noDefaultFeaturesCraneArgs,
|
|
msrvCraneArgs,
|
|
pkgs,
|
|
...
|
|
}: let
|
|
packageOpts = config.packageOpts pkgs // (buildDeps pkgs).env;
|
|
in
|
|
{
|
|
clippy = craneLib.cargoClippy (commonCraneArgs
|
|
// {
|
|
inherit cargoArtifacts;
|
|
cargoClippyExtraArgs = "--all-targets -- --deny warnings";
|
|
}
|
|
// packageOpts);
|
|
}
|
|
// (optionalAttrs config.cargoTest {
|
|
test = craneLib.cargoTest (commonCraneArgs
|
|
// {
|
|
inherit cargoArtifacts;
|
|
doCheck = true;
|
|
cargoExtraArgs = "--locked --all-targets ${maybeWorkspace}";
|
|
}
|
|
// packageOpts);
|
|
})
|
|
// (optionalAttrs hasMsrv
|
|
{
|
|
msrv =
|
|
craneLibMsrv.buildPackage
|
|
(msrvCraneArgs
|
|
// {
|
|
pname = "${crateName}-msrv";
|
|
cargoArtifacts = cargoArtifactsMsrv;
|
|
cargoBuildCommand = "cargo hack --no-dev-deps check";
|
|
cargoExtraArgs = "--release --all-features ${maybeWorkspace}";
|
|
installPhaseCommand = "mkdir $out";
|
|
}
|
|
// packageOpts);
|
|
})
|
|
// (optionalAttrs (hasNonDefaultFeatures && config.cargoTest) {
|
|
test-all-features = craneLib.cargoTest (allFeaturesCraneArgs
|
|
// {
|
|
cargoArtifacts = cargoArtifactsAllFeatures;
|
|
doCheck = true;
|
|
}
|
|
// packageOpts);
|
|
})
|
|
// (optionalAttrs hasNonDefaultFeatures {
|
|
clippy-all-features = craneLib.cargoClippy (allFeaturesCraneArgs
|
|
// {
|
|
cargoArtifacts = cargoArtifactsAllFeatures;
|
|
cargoClippyExtraArgs = "--all-targets -- --deny warnings";
|
|
}
|
|
// packageOpts);
|
|
})
|
|
// (optionalAttrs (hasDefaultFeatures && config.cargoTest) {
|
|
test-no-default-features = craneLib.cargoTest (noDefaultFeaturesCraneArgs
|
|
// {
|
|
cargoArtifacts = cargoArtifactsNoDefault;
|
|
doCheck = true;
|
|
}
|
|
// packageOpts);
|
|
})
|
|
// (optionalAttrs hasDefaultFeatures {
|
|
clippy-no-default-features = craneLib.cargoClippy (noDefaultFeaturesCraneArgs
|
|
// {
|
|
cargoArtifacts = cargoArtifactsNoDefault;
|
|
cargoClippyExtraArgs = "--all-targets -- --deny warnings";
|
|
}
|
|
// packageOpts);
|
|
})
|
|
// (optionalAttrs hasExamples {
|
|
examples = craneLibMsrv.buildPackage (commonCraneArgs
|
|
// {
|
|
pname = "${crateName}-examples";
|
|
cargoExtraArgs = "--examples ${optionalString hasNonDefaultFeatures "--all-features"} ${maybeWorkspace}";
|
|
}
|
|
// packageOpts);
|
|
});
|
|
|
|
apps = {
|
|
cargo-miri,
|
|
cargo-semver-checks,
|
|
rustToolchain,
|
|
semverToolchain,
|
|
miriRustToolchain,
|
|
writeShellApplication,
|
|
...
|
|
} @ pkgs: let
|
|
deps =
|
|
(buildDeps pkgs).buildInputs
|
|
++ (buildDeps pkgs).nativeBuildInputs
|
|
++ (with pkgs; [stdenv.cc gnumake gnused gawk gnugrep bash coreutils]);
|
|
miri-wrapped = writeShellApplication {
|
|
name = "cargo-miri";
|
|
runtimeInputs = [miriRustToolchain] ++ deps;
|
|
text = ''
|
|
PKG_CONFIG_PATH=${makePkgConfigPath deps} cargo miri "$@"
|
|
'';
|
|
};
|
|
semver-checks-wrapped = writeShellApplication {
|
|
name = "cargo-semver-checks";
|
|
runtimeInputs = [cargo-semver-checks semverToolchain] ++ deps;
|
|
text = ''
|
|
PKG_CONFIG_PATH=${makePkgConfigPath deps} cargo-semver-checks "$@"
|
|
'';
|
|
};
|
|
in {
|
|
miri = "${miri-wrapped}/bin/cargo-miri $@";
|
|
semver-checks = "${semver-checks-wrapped}/bin/cargo-semver-checks semver-checks $@";
|
|
bench = "${rustToolchain}/bin/cargo bench $@";
|
|
};
|
|
})
|
|
|
|
rec {
|
|
devShells =
|
|
rec {
|
|
default = {
|
|
packages = pkgs:
|
|
with pkgs;
|
|
[rustToolchain]
|
|
++ (config.tools pkgs)
|
|
++ (autoTools pkgs)
|
|
++ (buildDeps pkgs).buildInputs
|
|
++ (buildDeps pkgs).nativeBuildInputs;
|
|
|
|
env = {
|
|
rustPlatform,
|
|
pkgs,
|
|
...
|
|
}:
|
|
{
|
|
RUST_SRC_PATH = toString rustPlatform.rustLibSrc;
|
|
}
|
|
// (buildDeps pkgs).env;
|
|
};
|
|
miri = {
|
|
packages = pkgs:
|
|
with pkgs;
|
|
[miriRustToolchain]
|
|
++ (config.tools pkgs)
|
|
++ (autoTools pkgs)
|
|
++ (buildDeps pkgs).buildInputs
|
|
++ (buildDeps pkgs).nativeBuildInputs;
|
|
|
|
inherit (default) env;
|
|
};
|
|
}
|
|
// (optionalAttrs hasMsrv {
|
|
msrv = {
|
|
packages = pkgs:
|
|
with pkgs;
|
|
[msrvRustToolchain]
|
|
++ (config.tools pkgs)
|
|
++ (autoTools pkgs)
|
|
++ (buildDeps pkgs).buildInputs
|
|
++ (buildDeps pkgs).nativeBuildInputs;
|
|
|
|
inherit (devShells.default) env;
|
|
};
|
|
});
|
|
|
|
formatters = pkgs:
|
|
with pkgs; {
|
|
"*.nix" = getExe alejandra;
|
|
"*.rs" = getExe rustfmt;
|
|
};
|
|
}
|
|
];
|
|
}
|